huluti/doctrine-relations-analyser
Doctrine Relations Analyser inspects Doctrine ORM entity associations to flag missing or misconfigured relations—such as absent inversedBy/mappedBy, cascade, orphanRemoval, and nullable settings—helping you catch mapping issues early and keep your model consistent.
This is a small, niche Symfony bundle (15 stars, no dependents), so treat it as a development and CI aid rather than production infrastructure. Its value is in surfacing Doctrine entity relationship problems—missing inverses, circular mappings, cascade/orphan-removal mistakes, and general graph shape—before they cause runtime or persistence bugs. As of 0.8.0 it also ships Symfony 8 support, so it can live alongside a current Symfony 7/8 stack; if you are still on Symfony 5.4/6.x, check the constraint in composer.json before upgrading.
Minimal setup:
composer require --dev huluti/doctrine-relations-analyser
Register the bundle unless a Flex recipe does it for you. In modern Symfony, add it to config/bundles.php:
return [
// ...
Huluti\DoctrineRelationsAnalyser\DoctrineRelationsAnalyserBundle::class => ['dev' => true, 'test' => true],
];
If the namespace differs, confirm it from the package’s composer.json (autoload.psr-4) or the bundle class under src/.
Where to look first:
src/Command/ — the console entry points are the primary day-to-day interface.src/DependencyInjection/ and config/ — configuration tree, service IDs, and extension points.tests/ — expected output formats and edge cases the author actually covers.bin/console list — after registration, filter for relation/doctrine commands:bin/console list | grep -i -E 'relation|doctrine'
First use case: run the bundle’s analysis command against a local database before a refactor. Use it to answer: “Which entities have unidirectional relationships that should be bidirectional?”, “Where do I have cascade cycles?”, and “Which mappings look suspicious?” Treat the output as a review checklist, not an automatic fix.
A sensible first command might look like:
bin/console doctrine:relations:analyse
# or whatever exact command name appears in `bin/console list`
If the bundle only exposes services rather than commands, inspect the container:
bin/console debug:container --tag=doctrine.relations_analyser
bin/console debug:container RelationsAnalyser
The highest-value workflow is using the analyser as a mapping review tool before changing entity relationships. Run it on a branch that is already migrated locally, then compare its findings with the actual mapping annotations/attributes:
bin/console doctrine:schema:validate
bin/console <relations-analyser-command> --env=dev
Use the analyser output to drive a checklist:
inversedBy/mappedBy;cascade: ["remove"] combined with shared parent relationships;orphanRemoval: true on relations where children are reassigned;If the bundle returns a meaningful exit code, wire it into CI as a non-blocking report first, then promote to blocking once you understand its false positives:
# .github/workflows/relations.yml
- name: Doctrine relationship analysis
run: bin/console <relations-analyser-command> --env=test
If it does not return failure exit codes, capture the output and grep it in CI:
bin/console <relations-analyser-command> --env=test | tee relations.txt
! grep -qi 'critical\|error' relations.txt
Only make it blocking after validating the command against your actual entity set. Small packages often flag legitimate “unidirectional by design” relations.
This package complements, rather than replaces, Doctrine’s built-in checks:
bin/console doctrine:schema:validate
bin/console doctrine:mapping:info
bin/console doctrine:query:sql "SELECT 1"
Recommended sequence:
doctrine:mapping:info — confirm all entities are discovered.doctrine:schema:validate — mapping/schema sync.If the bundle exposes an analyser service, inject it into a dev-only command or test rather than calling it from production code. First locate the service:
bin/console debug:container --show-private | grep -i analyser
Then, if available:
final class RelationAuditCommand extends Command
{
protected static $defaultName = 'app:relations:audit';
public function __construct(
private readonly RelationsAnalyserInterface $analyser,
) {
parent::__construct();
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$result = $this->analyser->analyse(/* entity names or metadata */);
// Format for humans and CI logs.
$output->writeln(json_encode($result, JSON_PRETTY_PRINT));
return Command::SUCCESS;
}
}
The exact interface name will differ; confirm from src/. If the package has no stable public API, wrap it behind your own app:relations:audit command so the rest of your tooling depends on your wrapper, not the package.
For applications with deeply nested entities, use the analyser before loading fixtures. A common failure mode is fixture order or cascade configuration that works in one test but fails in another. Add a lightweight test:
public function testEntityRelationshipsAreAnalysable(): void
{
$analyser = static::getContainer()->get('test.service_container')
->get(RelationsAnalyserInterface::class);
$result = $analyser->analyse();
self::assertEmpty($result->getErrors(), print_r($result->getErrors(), true));
}
Guard with if (class_exists(...)) or keep it in a separate test suite if the bundle is dev-only.
OneToMany, ManyToMany, cascade, or orphanRemoval.composer.json if you depend on its output in CI, and review changelogs before upgrading. The 0.7 → 0.8 bump is a good example: the only change is Symfony 8 support, but because there is no SemVer contract on the console interface, treat every release as potentially output-format-breaking.dev and test environments. Do not make production runtime depend on it.bin/console list. Do not assume the README command matches the installed version.ResolveTargetEntityListener setups can confuse metadata-based analysers. Cross-check results against doctrine:mapping:info.doctrine:mapping:info fails, the analyser will too. Fix mapping errors first.json_encode with depth limits or a normalizer; entity graphs can be cyclic.DoctrineBundle in config/bundles.php (normally automatic, but relevant in manual setups).src/DependencyInjection/Configuration.php for supported options—entity filters, output format, ignored relations. If there is no config tree, expect convention-based behaviour and hard-coded assumptions.app:relations:audit command and formatter. If the package is abandoned or breaks on a Doctrine upgrade, only your wrapper changes.-vvv, check var/log/dev.log, and verify the Doctrine entity manager is available:bin/console debug:container doctrine.orm.default_entity_manager
bin/console doctrine:mapping:info -vvv
How can I help you explore Laravel packages today?