vrok/doctrine-addons
A collection of addons for Doctrine ORM, bundling practical extensions and integrations that streamline entity management, query building, and framework glue for PHP applications. Drop-in helpers reduce boilerplate so you can get more from Doctrine with less configuration.
Minimal install
composer require vrok/doctrine-addons
Then check two things before writing any code:
vendor/vrok/doctrine-addons/composer.json for extra.laravel.providers. If a service provider is listed, auto-discovery handles registration. If not, you'll wire it manually into config/app.php and/or config/doctrine.php.src/, tests/ and composer.json (for DBAL/ORM version constraints) as the real documentation.Where to look first
composer.json — the doctrine/dbal and doctrine/orm constraints tell you immediately whether it targets DBAL 2, 3 or 4. This is the single biggest compatibility risk.src/ — identify whether it ships custom DBAL types, event subscribers/lifecycle listeners, query functions, or schema/migration helpers. Each of these integrates differently.tests/ — if present, they are the best usage examples you'll get.config/ directory — if the package is publishable: php artisan vendor:publish --provider="Vrok\DoctrineAddons\...".First use case
Pick the smallest possible surface: register one custom type from the package on one entity field, then run the schema tooling to confirm Doctrine understands it.
php artisan doctrine:schema:validate
php artisan doctrine:migrations:diff
If the generated SQL for that column looks right, the package is wired correctly. Only then expand to traits, subscribers or whatever else it offers.
Register types centrally, not ad hoc
Prefer configuration over Type::addType() calls scattered around the codebase:
// config/doctrine.php
'types' => [
'vrok_json' => Vrok\DoctrineAddons\...\JsonType::class,
],
Then map on the entity:
#[Column(type: 'vrok_json')]
private array $payload = [];
This keeps registration in one place and survives config caching.
Wrap the package in your own provider
Even if auto-discovery works, create a thin application-level provider that registers the package's types/subscribers. This gives you a single seam to swap, patch or fork the package later — important given the near-zero community footprint.
Pair traits with lifecycle handling
If the package ships entity traits (timestamps, blameable, UUIDs), use them together with the corresponding event subscriber. Register subscribers through the Doctrine config rather than manually attaching listeners to the EntityManager — manual attachment breaks when the manager is rebuilt.
Follow the diff-first migration workflow
Day to day this looks like: change entity → doctrine:migrations:diff → read the generated SQL → doctrine:migrations:migrate → run tests. Custom types are where diff output goes wrong, so never blindly migrate generated SQL after touching a typed field.
Testing
Register the package's types in your test bootstrap before the EntityManager is created (e.g. in tests/TestCase.php or a base setUpBeforeClass). If you use in-memory SQLite for tests, expect divergence from MySQL/Postgres for custom type SQL declarations — keep at least one integration test against the real database for any package-provided type.
Keep it behind your own abstraction where cheap
If the addon is small (a few types or a listener), consider copying the relevant class into App\Doctrine under the MIT license rather than depending on a package with no downstream users. You keep the behaviour and remove the supply-chain risk.
Registration ordering is everything. Custom types must be registered before the ORM metadata is parsed. Registering in a service provider that runs after Doctrine's own provider produces Unknown column type "..." errors that look like a config problem but are a boot-order problem.
DBAL 3 vs 4. Type::addType() is deprecated in DBAL 3 and removed in DBAL 4 in favour of Type::overrideType() / configuration-based registration. A package with one release may only target one major version — check composer.json constraints before upgrading DBAL.
Config and metadata caches. After adding a type or subscriber, run php artisan config:clear and clear the Doctrine metadata cache. In production, a stale metadata cache will make a working local change appear broken.
Schema tool noise. If a type doesn't implement getSQLDeclaration() correctly, every doctrine:schema:validate and migrations:diff will report spurious changes. If you see phantom diffs, the type is the suspect — not your entities.
Release metadata looks wrong. The listed last release (2026-10-13) is in the future. Verify actual tags and commit history before assuming active maintenance; a single anomalous release is a signal to pin an exact version rather than use a caret range.
Zero dependents means you are the QA. There are no battle-tested consumers to lean on. Pin the version, add your own smoke tests around every package-provided type or subscriber, and keep a fork ready. The MIT license makes forking or vendoring trivial if the package stalls.
Proxy generation. After adding entities that use package traits, regenerate proxies (php artisan doctrine:generate:proxies) — stale proxies referencing old trait methods are a common source of confusing runtime errors.
Extension points to prefer. Favour the package's documented registration points (config keys, subscriber interfaces, type base classes) over subclassing internals. Internals of an unmaintained package are the most likely thing to change or break.
How can I help you explore Laravel packages today?