mandango/mandango-bundle
MandangoBundle integrates the Mandango MongoDB ODM with Symfony, providing bundle configuration, document mapping, repository services, and event integration for PHP applications backed by MongoDB. It streamlines setup and service wiring for Mandango documents within Symfony projects.
First, reset expectations: mandango/mandango-bundle is a Symfony2 bundle, not a Laravel package. There is no Illuminate\Support\ServiceProvider, no facade alias, no config/mandango.php, and no php artisan vendor:publish target. Composer-installing it into a Laravel app will pull Symfony 2.x components into your dependency graph and still leave you with nothing Laravel can boot. So "getting started" here really means deciding whether to use the underlying ODM at all — the bundle itself is a reference implementation, not a runtime dependency.
The realistic starting path for a Laravel developer:
ext-mongo extension (MongoClient, MongoCollection), removed in PHP 7. Run php -m | grep -i mongo. If you see mongodb (the modern driver) but not mongo, stop — you cannot run this without downgrading PHP. This is a hard stop, not a configuration annoyance.DependencyInjection/MandangoExtension.php and Resources/config/services.xml. That is the entire value proposition of the bundle: it wires a Mandango\Mandango instance, one or more Mandango\Connection objects, and a logger into the container. That wiring is ~40 lines and you can reproduce it as an AppServiceProvider binding.mandango/mandango (the library), not the bundle. The library is what you actually call at runtime. Everything the bundle does — config parsing, service registration, console commands — has to be hand-rolled in Laravel.mandango-metadata.php file. These generated artifacts are what your app actually references. In the bundle this is mandango:generate; in Laravel you would port it to an Artisan command (php artisan mandango:generate) that runs as a build step, with the output committed to version control.A minimal hand-wired binding that would replace the bundle:
// AppServiceProvider::register()
$this->app->singleton(\Mandango\Mandango::class, function ($app) {
$config = $app['config']['mandango'];
$connection = new \Mandango\Connection(
$config['server'], // e.g. mongodb://127.0.0.1:27017
$config['database'],
$config['options'] ?? []
);
return new \Mandango\Mandango(
['default' => $connection],
'default',
require base_path('mandango-metadata.php') // generated by Mondator
);
});
Your first genuinely useful use case is a read-heavy collection where you want identity-mapped documents and a fluent query builder instead of raw MongoCollection calls. That was the bundle's pitch in Symfony2 and it is the only reason to tolerate Mandango at all.
An honest recommendation: for any new Laravel work, stop here and use mongodb/laravel-mongodb (Eloquent-style) or the raw mongodb/mongodb driver. Come back to this section only if you are supporting an inherited Symfony2 codebase and need to understand it.
The patterns below are what "day to day" would look like if the stack were viable. Treat them as a mental model of the ODM, not a green light to adopt it in Laravel.
Singleton service + metadata bootstrap. Bind Mandango\Mandango once, always with the Mondator-generated metadata array. The metadata file is the ODM's schema registry — without it the document manager cannot hydrate anything. Never instantiate Mandango\Mandango per request or per repository.
Config as a first-class Laravel config file. Port the bundle's YAML block verbatim into config/mandango.php: server, database, options (connect, timeout, replicaSet, readPreference), plus the metadata path and generated-class directory. Centralizing this in config is the one thing the bundle genuinely teaches — resist hardcoded connection strings.
Repositories as the application boundary. Mandango ships Mandango\Repository and generated subclasses (e.g. PostRepository). Resolve them through $mandango->getRepository('Model\Post'), not new. Wrap each in your own interface so domain code depends on your contract, not on Mandango:
interface PostRepository {
public function findPublished(int $limit): iterable;
public function save(Post $post): void;
}
Then bind the Mandango implementation in a service provider. This isolates the legacy code behind a seam you can later replace with mongodb/laravel-mongodb without touching domain logic.
Codegen as an explicit, committed build step. Treat Mondator the way you treat a migrations or IDE-helper step: run it deliberately, commit the output, and diff it in CI. Do not regenerate at deploy time — silent drift between schema definitions and generated classes is a classic Mandango failure mode that produces subtle query bugs. Add mandango:generate as an Artisan command that wraps the Mondator API and fail the build if git diff is non-empty after regeneration.
Query builders, not magic scopes. Mandango query builders are explicit and chainable (->field('status')->equals('published')->sort('createdAt', 'desc')->limit(20)). This is the strongest part of the ODM API and the thing most easily ported. Keep query construction out of controllers — build query-builder methods on the repository so they stay unit-testable.
Unit-of-work awareness. Mandango tracks loaded documents; a save() persists the document, but reads do not implicitly refresh. Document this in your team notes: after mutating a document, persist it explicitly; do not assume the in-memory object is canonical.
A single persistence paradigm per bounded context. Do not read a collection with Eloquent's MongoDB driver and write it with Mandango. If you must run a hybrid (e.g. migrating), pick a cutover point and route one direction only: writes flow through the new layer, reads dual-run until verified, then reads flip. Never let both layers own writes to the same collection.
Testing strategy. Because Mandango\Mandango is a concrete class without an interface, do not unit-test around it directly. Depend on your repository interface and fake it; reserve integration tests for a Mongo instance in Docker where a mongo-capable PHP image is actually built. This keeps the legacy SDK at the edge of your test suite where it can be swapped.
ext-mongo vs ext-mongodb is the whole story. Mandango calls MongoClient/MongoCollection. PHP 7 removed ext-mongo and the replacement ext-mongodb has a completely different API (MongoDB\Driver\Manager, MongoDB\Collection). This is not patchable — it is a driver-layer rewrite. If you need confirmation, spend a day on a spike and expect it to fail at this step.
Composer will happily install a pile of Symfony 2 code into a Laravel app. It can look "successful" while making your vendor/ and PHP requirement constraints fragile. Check composer why mandango/mandango-bundle after install — if anything unexpected resolved, remove it immediately. The bundle should not survive as a transitive dependency.
The bundle ships zero Laravel surface. Don't hunt for a facade class, config/mandango.php, or auto-discovery; they don't exist. If you see a tutorial claiming otherwise, it's either the wrong package or wrong framework. The bundle's MandangoBundle class only works inside Symfony's kernel.
Metadata file staleness. Mondator emits mandango-metadata.php alongside the generated classes. Change a document definition, forget to regenerate, and the ODM will hydrate a stale schema — often without an error, just wrong fields. Guard this with a CI check that regenerates and fails on diff.
Generated classes are not PSR-4-friendly by default. Mondator writes into a configured output directory with a fixed namespace. You must either point the directory at a PSR-4 root in composer.json ("Model\\": "src/Generated/") or add a classmap. If you skip this, require order becomes load-order-sensitive and you'll get intermittent "class not found" errors in queued jobs — a genuinely nasty debugging experience.
Do not rely on connection pooling or retry behavior. Mandango predates modern MongoDB driver conventions. Read/write concern, retryable writes, transactions (MongoDB 4.0+ with driver support), and topology handling are all things you would own yourself. If a production issue involves "the primary changed role," Mandango will not help you and the modern driver would have.
Extension disappearance on base-image rebuilds. Even if you got it working on a legacy PHP image, a docker build that upgrades the base breaks deploys with no code change. Pin the base image by digest and treat it as frozen.
Upstream support is zero. 25 stars, 0 dependents, unmaintained for ~a decade. There will be no fix for a CVE, a PHP deprecation, or a MongoDB server version bump. Anything you file is filed against yourself.
The right extension point is your own interface, not the ODM. Since the package offers no plugin points useful to Laravel, put all of your "someday we'll migrate" energy into the repository interface described above. That interface is the only durable thing you should let the team learn from Mandango.
Migration path (the actual advice). For greenfield: mongodb/laravel-mongodb — Eloquent idioms, current PHP, active maintenance, real hiring pool. For an inherited Mandango codebase: freeze it, pin versions, and budget a timeboxed exit (export via mongodb/mongodb, remap to the target layer, cut over reads before writes). The decision to make is not "how do we wire Mandango into Laravel" — it's "when do we schedule the migration off it."
How can I help you explore Laravel packages today?