goteo/benzina
A Laravel/PHP package published under the Goteo namespace, bundling reusable helpers and integration code for PHP applications built on Laravel. Early-stage project with no stars or community adoption yet, so evaluate carefully before use.
Benzina is a purpose-built, single-target migration tool: it moves data from a Goteo v3 database into a v4 (Laravel) schema. It is not a general-purpose ETL library, so treat it as a runnable reference implementation you adapt, not a black-box dependency.
Install as a dev dependency (it should never ship to production app code):
composer require --dev goteo/benzina
If the package isn't on Packagist, add the VCS repository entry and require dev-main.
Look at the source before the README. With zero stars and no dependents, the code is the documentation. First stop: the service provider (to see what commands/config it registers) and the src/ command classes (to see the actual migration order). Second stop: any config/benzina.php or .env keys, which will tell you which source/target connections it expects.
Publish and configure (adjust to whatever the provider exposes):
php artisan vendor:publish --tag=benzina-config
Define a legacy (v3) and the default mysql (v4) connection in config/database.php. Benzina generally reads from one and writes to the other, so both must be reachable from the same process.
Inventory the commands:
php artisan list | grep -i benzina
You'll typically find a per-domain or per-table migrator plus an orchestrator. Start with the smallest, most self-contained table (countries, currencies, licenses, tags) to validate connectivity and mapping before touching users or projects.
First use case: migrate a lookup/reference table into an empty v4 database, confirm row counts and spot-check values, then move up the dependency graph. Do not start with users or invests.
Migrate in dependency order, not table order. v3→v4 migrations fail almost entirely on foreign keys and orphaned rows. Build an explicit order: reference data → users → nodes → projects → rewards → invests → messages → logs. If Benzina ships an orchestrator, verify its order matches the schema's FK graph; if not, drive the individual commands yourself from a wrapper command or a shell script.
Wrap each table in its own command invocation. Running the whole migration in one process makes failures hard to isolate and restarts expensive. A thin wrapper is worth the effort:
// app/Console/Commands/MigrateLegacy.php
foreach (config('benzina.order') as $table) {
$this->call('benzina:migrate', ['table' => $table, '--chunk' => 1000]);
}
Chunk aggressively and re-run idempotently. Legacy Goteo installs are often large (projects, invests, message threads). Use chunkById on the source side, and make writes upsert/updateOrCreate keyed on the legacy ID. Benzina commonly preserves legacy IDs in a legacy_id column or maps them explicitly — confirm which, because that mapping is what makes re-runs safe.
Keep a mapping layer for renames. v3 column names rarely match v4. Rather than editing Benzina's internals, keep a per-table map in config (or a subclassed transformer) so the migration is auditable and diffable in review:
'projects' => [
'id' => 'legacy_id',
'project_name'=> 'name',
'created' => 'created_at',
],
Use it as a staging rehearsal loop. The realistic workflow is: restore a v3 dump locally, run Benzina into a fresh v4 schema, diff counts and checksums, fix mappings, wipe, repeat. Add a small verification command that compares count(*) per table and a hash of a few sensitive columns between source and target.
Integrate with jobs for the long tail. Anything over a few minutes (messages, logs, analytics) belongs in queued jobs so a single failure retries without restarting the run. Benzina's commands can be called from jobs; pass the chunk range explicitly.
Snapshot before the real run. Since this writes directly into the v4 schema, always take a mysqldump of the target first, and run with --dry-run if the package supports it (check the command signature; if absent, add it by wrapping the write step).
composer require then commit vendor/ or fork it), and be prepared to patch it. Expect hardcoded Goteo-specific assumptions and no support.utf8 (not utf8mb4) and latin1 leftovers. Set the source connection's charset explicitly and test with content containing emoji and accented characters; silent truncation is the classic failure.invests pointing at deleted projects. Decide up front whether to skip, null, or synthesize parents — and log every skipped row to a side table so nothing vanishes silently.DATETIME; v4/Laravel expects UTC. Apply a single consistent conversion in the transformer, not ad hoc per table.active, archived, numeric codes) rarely map 1:1 to v4's deleted_at/state machines. Enumerate every distinct value from the source first (SELECT status, COUNT(*) ... GROUP BY status) before writing the mapping.--id filter if missing) and dump the mapped attributes before save. This is faster than reading stack traces through chunked batches.packages/ with a path repository.SUM(amount)) between v3 and v4. Financial tables are where a silent off-by-one chunk boundary hurts most.How can I help you explore Laravel packages today?