scrumble-nl/popo
Popo is a Laravel package for generating plain old PHP objects (POPOs) from Eloquent models, giving you lightweight, framework-independent data containers that keep application layers clean and decoupled, with simple Artisan commands and an expressive API.
laravel/helpers) rather than adding one. A package that shrinks over time is easier to defend in a security review than one that accretes.Consider it when:
array_first()/array_last() signature clash) that would otherwise surface as a fatal error during an upgrade. That is exactly the class of work you want a dependency doing for you, not your team discovering it mid-migration.Look elsewhere when:
spatie/laravel-data, Laravel's native casts, or a custom FormRequest → DTO pipeline instead. Bolting those features onto a minimal package is how you end up maintaining a worse fork.Versioning note (updated): at 2.0.2 this is no longer a 0.x package. It has been through at least one major bump, which is a mild positive — breaking changes are being versioned and signposted rather than shipped silently. Treat it as stable-but-thin, not experimental.
Migration note for anyone already piloting: popo no longer pulls in laravel/helpers. If your code called array_get(), str_*(), or snake_case() and only had them because popo was installed, run composer require laravel/helpers explicitly. This is not an API change to popo itself, but it is a real break for apps that were free-riding on its transitive dependencies. Before upgrading, run composer why laravel/helpers — if the only answer is popo, you have work to do.
Verdict posture: Trial, not Adopt. Scope it to one integration boundary, timebox it, and keep the exit cost near zero — which for a package this small it is. 2.0.2 improves the case modestly; it does not change the posture.
To executives: This is a small, free utility that helps us standardise how data enters our application. The real decision isn't "buy this package" — it's "do we invest in a typed data boundary between our integrations and our core product logic?" That boundary reduces a recurring category of production defects (malformed payloads, silent key changes, weak test coverage) and speeds up onboarding. Cost of trying: days, not weeks. Cost of reversing: negligible, because the objects we define are plain PHP and outlive whatever hydrates them. The latest release also removed a dependency and added PHP 8.5 / Laravel 13 coverage — a small net reduction in risk and one less blocker on our upgrade calendar.
To engineering: The package is a thin hydrator for turning arrays into typed objects. It buys us convention and a smaller attack surface than "arrays all the way down," at the price of a dependency with one maintainer and no visible downstream users. Recommendation: pilot it on a single inbound webhook or API endpoint, measure test speed and defect rate over one cycle, and hold a decision point. If it doesn't demonstrably beat a 40-line internal helper, we fork or replace and keep the object contracts. We are not standardising the platform on it.
On 2.0.2 specifically: no API changes, so the pilot is unaffected. Two things worth logging: (1) the laravel/helpers removal drops the package's transitive footprint, which is what a security reviewer wants to see; (2) the PHP 8.5 fix is early, upstream work we would otherwise absorb ourselves during the next runtime upgrade — evidence the maintainer is at least watching the ecosystem. Check whether your app was relying on helpers that arrived transitively; if so, declare them explicitly.
Honest caveat to state up front: adoption signals are still weak (5 stars, 0 dependents) and we have no fresh dependent-count data on this release. A responsive 2.0.2 is encouraging but is a single data point, not a trend. The risk that this goes unmaintained is ours to own — plan for it (vendor or fork on a major Laravel upgrade) rather than discover it later.
How can I help you explore Laravel packages today?