ems/versatile
EMS Versatile is a Laravel/PHP package offering flexible, reusable components for common application tasks. It aims to streamline configuration and extend core behavior through a versatile, developer-friendly API for faster builds.
ems/versatile bills itself as "extensions for Laravel's Eloquent ORM." With 0 stars, 0 dependents, and a single 2024 release, treat it as reference material first, dependency second. Before you composer require anything, spend ten minutes reading the source.
Minimal steps:
composer.json first. Check the require block for the minimum PHP and illuminate/database versions. If it pins ^8.0 or ^9.0, it will fight a Laravel 11/12 app and you should stop here.src/ for traits, not classes. Eloquent extension packages almost always ship as traits you use inside a model (HasSomething, Sortable, Sluggable, etc.). List every trait and every public method before writing code — there is no docs site to rely on.VersatileServiceProvider exists, look at what it binds and whether it publishes a config. If there is no provider, the package is purely trait-based and needs nothing beyond autoloading.main:
composer require ems/versatile --dev
Using --dev keeps it out of production until you've proven it works.First use case: pick the smallest model in your app (a lookup table, a tag, a settings row). Wire the trait there, confirm the generated SQL with DB::listen() or ->toSql(), and only then consider a real domain model.
If the package turns out to be a thin set of helpers with no tests, the pragmatic move is to vendor the trait you need into app/Models/Concerns/ under the MIT license and drop the dependency. You keep the behavior, you lose the supply-chain and version-constraint risk.
Because there is no documented public API, the patterns below are about how to integrate an opaque Eloquent extension safely rather than about specific method names.
Trait composition on a base model. If the package adds behavior you want everywhere (e.g., a UUID or ordering concern), apply it once on your App\Models\BaseModel (or Model subclass) rather than on fifty models. This gives you a single place to remove it later:
abstract class BaseModel extends Model
{
use \Ems\Versatile\...\TheTrait;
}
Scope-then-override. Eloquent extension traits usually register global scopes or boot{TraitName} hooks. After adding the trait, immediately check Model::getGlobalScopes() in a test. If it adds an implicit where, every query in your app changes — you want to know that on day one, not in production.
$scopes = (new Post)->getGlobalScopes();
dump(array_keys($scopes)); // verify nothing unexpected
Wrap, don't scatter. Expose the package behavior through your own repository/query-builder methods so call sites never import the package namespace:
class PostRepository
{
public function latestVisible(): Builder
{
return Post::query()->visible()->latestFirst(); // package scopes wrapped here
}
}
When you eventually remove or replace the package, you change one file.
Test against SQL, not just results. For anything that touches query building, assert the generated SQL in at least one test. A trait that silently appends order by can break paginate() counts or chunkById():
$this->assertStringContainsString('order by', Post::query()->toSql());
Pair with static analysis. Run PHPStan/Larastan at a high level against a model using the trait. Magic methods and __call forwarding are the usual failure mode for these packages; Larastan will surface Call to undefined method that PHP itself won't catch until runtime.
Fork-and-pin if you commit. If the package proves genuinely useful, fork it to your own org, add a test suite, and require it via a VCS repository. With zero dependents upstream, you are the maintainer whether you like it or not.
Zero dependents is a signal. No one else is running this in production that we can see. That does not make the code bad — mtils writes reasonable PHP — but it means no one has hit the Laravel 11 upgrade for you. Budget time for compatibility work.
Version constraints will bite. A 2024-05 release likely targets Laravel 10/11 and PHP 8.1/8.2. Verify against your actual composer.lock. If composer require resolves but tests explode with BadMethodCallException or TypeError in Illuminate\Database, it's an incompatibility, not your bug.
Global scopes and withoutGlobalScopes(). If the trait adds a scope, remember it affects exists(), count(), relationship existence checks (whereHas), and Model::find(). Audit admin panels and reporting queries specifically — they usually want unscoped data.
Event hook ordering. Traits that boot via boot{TraitName} register model events at class-load time. If your model also defines booted() or an observer, the firing order is not obvious. Log inside both to confirm which runs first when behavior looks wrong.
No docs means read the tests — if there are any. Check for a tests/ directory. If it exists, it is the documentation; copy its fixtures into your own test suite. If it doesn't, assume every method is unverified and write your own coverage before trusting it.
Debugging checklist when something misbehaves:
DB::listen(fn ($q) => logger($q->sql, $q->bindings)); — see what the trait actually emits.(new Model)->getGlobalScopes() — catch implicit filters.class_uses_recursive(Model::class) — confirm the trait is actually applied (a typo in a use statement fails silently until the method is called).php artisan model:show YourModel (Laravel 9+) — quick sanity check on attributes and relations.Extension points are yours to create. With no upstream config or contracts, do not monkey-patch the package. Subclass the trait in your own Concerns/ namespace, override the method, and call parent:: — that's the only stable extension seam you have.
License is fine, maintenance is not. MIT means you can copy the useful 200 lines into your codebase legally. Given the score (0.2) and opportunity rating (34.67), the highest-value play is usually: read it, learn the technique, reimplement the two methods you need, and skip the dependency entirely. Reserve composer require for the case where the package does something genuinely non-trivial that you cannot reproduce in an afternoon.
How can I help you explore Laravel packages today?