binarytorch/model-updater
Laravel package for bulk-updating Eloquent models from CSV or Excel files. Map spreadsheet columns to model attributes, match records by key, and apply changes through a simple, configurable workflow—ideal for admin data imports and maintenance.
composer require binarytorch/model-updater
The package is a thin trait/helper layer over Eloquent's fill()/save() cycle, so there is nothing to configure and no service provider to register in most setups. Start by reading the README.md and the tests/ directory — for a package this small (40 stars, last release January 2020) the tests are the most reliable spec of the actual API surface, since docs may have drifted.
Add the trait to the model you want to update through:
use BinaryTorch\ModelUpdater\HasUpdater; // verify exact namespace in src/
class Post extends Model
{
use HasUpdater;
}
Your first use case is the one this package is built for: replacing ad-hoc $model->fill($request->all()); $model->save(); calls in a controller with a single, explicit update call that respects $fillable and applies the model's casts/mutators exactly once. Start with a controller update action, run it against a model with a few casts (datetime, boolean, array), and confirm the resulting attributes match what plain fill() would produce. Once that parity is verified, you can rely on it for the rest of the app.
Because the package is archived, treat "Getting Started" as a spike, not a commitment — validate against your Laravel version before wiring it through more than one model.
Controller update actions. The canonical pattern is a thin controller that delegates:
public function update(UpdatePostRequest $request, Post $post)
{
$post->updateFrom($request->validated()); // check exact method name in src/
return redirect()->route('posts.show', $post);
}
Keep validated() (not all()) as the input — the package's value proposition is consistency with fillable, not validation, so still rely on FormRequests.
DTO / array-map updates. Use it as the single funnel for heterogeneous input: queue jobs, imports, and API payloads can all hit the same update method, so casts and mutators behave identically regardless of source. This is the most defensible reason to adopt the package on a legacy codebase — it lets you standardize update semantics without rewriting every call site at once.
Chunked / bulk workflows. For batch jobs, resolve models in chunks and call the updater per model rather than mass-updating; this preserves observers, events (updating, updated), and audit-trail packages. If the package offers a static/bulk helper, verify it fires model events before using it in production.
Integration with FormRequests and resources. Pair it with spatie/laravel-data or plain DTOs by passing $dto->toArray() into the updater. The updater does not replace validation or authorization — those stay in the request layer.
Testing pattern. Assert behavior, not the package: feature-test the endpoint and check the database state via assertDatabaseHas. Unit-test tricky models (nullable dates, enum casts, JSON columns) directly against the updater to prove parity with fill() before trusting it broadly.
casts() method vs $casts property, enum casts, etc.). Test on your exact version or vendor/fork it.src/, the whole trait is a few dozen lines over fill() + save(), the honest recommendation is to copy the logic into your own base model or a single UpdatesModels trait rather than take an archived dependency. You keep identical behavior with no supply-chain risk.$fillable/$guarded or blindly fill()s. If it doesn't filter, it silently reintroduces mass-assignment risk that validated() alone doesn't fully mitigate when payload keys don't match attribute names.[] should be a no-op or still fire saving/updated: this matters for updated_at churn and for updated event listeners (search indexers, webhook dispatchers). Verify against the source.$model->update([...]) — double mutating JSON/array casts or overwriting a dirty attribute. Pick one path per request.$post->tags()->sync(...), hasMany creation), handle them explicitly after the model update and wrap in DB::transaction(); don't assume the package cascades.$fillable, $casts, and whether the key was stripped by the FormRequest before blaming the updater. DB::listen() and $model->getDirty() are your tools.ModelUpdater contract on your base model) so swapping to native fill()/update() later is a one-file change.How can I help you explore Laravel packages today?