concretecms/doctrine-xml
Doctrine XML adds XML mapping support for Doctrine ORM entities in Concrete CMS. Define entity metadata in XML files instead of annotations or attributes, then use it with Concrete’s entity manager for cleaner, package-friendly custom database models.
This package is not a standalone ORM you wire into a blank app — it exists to give Concrete CMS (and packages built on it) XML-driven Doctrine schema/mapping support. The realistic entry point is:
concrete/vendor/concretecms/doctrine-xml. If you're running a Concrete site, you almost certainly have it transitively; don't composer require it separately into a non-Concrete project.composer.json first to see the doctrine/dbal (and possibly doctrine/orm) constraint it's pinned against. That constraint tells you far more about what the classes will do than any README.src/ for the XML driver/schema-manager classes. The useful surface is small — you're looking for the class implementing Doctrine's AbstractXmlDriver / schema XML handling, plus any custom DBAL type registrations.First use case: defining a custom table for a Concrete package via an XML schema file (the db.xml convention in the package root) rather than hand-writing CREATE TABLE. Install the package through the Concrete dashboard / concrete/bin/concrete and verify the table is created by inspecting SHOW TABLES — that end-to-end path exercises the whole package in one step.
Do this before writing any code: dump the introspected schema through the driver and diff it against an empty database. That establishes your baseline in ~5 minutes.
Declarative schema over migrations. The main workflow is: edit XML → compare against live schema → apply the diff. In practice:
$sm = $connection->getSchemaManager();
$fromSchema = $sm->introspectSchema(); // or createSchema() on fresh DB
$toSchema = $xmlDriver->createSchema(); // your XML file
$comparator = new Comparator();
$sql = $comparator->compareSchemas($fromSchema, $toSchema)->toSql($platform);
Run the generated SQL through review before executing — this is the pattern Concrete's package install/upgrade hooks use, and it's the one you should mirror in your own install() / upgrade() methods.
Package lifecycle integration. Hook schema creation into controller.php's install() and diff-based updates into upgrade(). Keep the XML file as the single source of truth and let the comparator do the work; don't hand-maintain ALTER statements alongside it.
Custom DBAL types. If you need enum-like or JSON columns, register the type on the Configuration object before building the driver, then reference it by name in the XML type attribute. The order matters — the XML driver resolves type names at parse time.
Testing. Load the XML schema into an in-memory SQLite connection in your test bootstrap, then run your entity/repository tests against it. This is faster than a real MySQL container and catches 90% of schema typos. Use MySQL in CI only for platform-specific behavior (indexes, collations, ALTER support).
Multi-package composition. If several of your packages ship XML schemas, merge them into one Schema object before diffing rather than comparing one file at a time — otherwise the comparator will try to drop tables owned by the other package.
MappingException with almost no context about which file or line. When debugging, validate the XML against Doctrine's XSD directly first.compareSchemas()->toSql() straight into production; always read the output. If you see a DROP you didn't intend, the XML is missing a table or column definition, not that the comparator is broken.doctrine/dbal major, and the XML driver API changed between DBAL 2 and 3. Don't float the constraint.length on string columns. DBAL will not infer a sensible length from the XML string type in all cases; omitting it produces platform-dependent defaults (often 255) that then show up as permanent diff noise.db.xml is install-time only by default. If you add a column to an existing install, the table won't change unless your upgrade() explicitly runs the comparator. This is the single most common "my schema change didn't apply" bug.Schema (e.g., force a table prefix or inject a shared index). This is cleaner than editing the XML files.createSchema(), then var_dump the table names. If that fails, the problem is the XML or a type registration, not Concrete — which halves your search space immediately.How can I help you explore Laravel packages today?