zan/doctrine-rest-bundle
Zan Doctrine REST Bundle provides a Symfony integration for exposing Doctrine entities through RESTful endpoints, reducing boilerplate for CRUD APIs. Configure resources, routing, and serialization to quickly turn your ORM models into JSON APIs.
This release (1.2.0) is a BC-breaking release: annotation-based configuration is gone, replaced entirely by PHP 8 attributes. If you are installing fresh, this is the only supported style. If you are upgrading from 1.1.x, read Gotchas and Tips before touching composer.json.
Fresh install:
composer require <vendor>/<package>:^1.2 — confirm your CI matrix runs PHP 8.0+ (attributes are mandatory now; PHP 7.x is not a viable path).src/Attribute/ — the attribute classes that replaced the old annotation classes. These are the ones you will actually type into your entities.MinimalEntitySerializer (what gets output) and EntityPropertyEditabilityMap (what is allowed in). Those two classes define the package's whole contract.GenericEntityResultSet in a service provider, delete the AnnotationReader argument from the definition — the constructor no longer accepts it.First use case — expose a Doctrine entity as an array for a JSON:API-style endpoint:
use <Vendor>\<Package>\Attribute\Expose; // verify exact namespace in src/Attribute
use <Vendor>\<Package>\Attribute\Ignore;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Product
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
#[Expose]
private int $id;
#[ORM\Column]
#[Expose]
private string $name;
#[ORM\Column]
#[Ignore] // replaced JMS @Expose / Symfony @Ignore checks
private string $internalCost;
}
Then feed the entity (or a query result) through MinimalEntitySerializer and assert the array shape in a unit test before wiring it into a controller.
Result sets for list endpoints. GenericEntityResultSet is the collection-oriented path: hand it a Doctrine Query/Paginator result and get back a serializable array per entity. Use it for GET /resources and let MinimalEntitySerializer handle single-resource GET/nested payloads. Keeping both paths means one place decides what is exposed.
Default-deny serialization. MinimalEntitySerializer is minimal by design — unmarked properties do not leak. Resist the temptation to mark everything #[Expose] "just in case"; the value of this package is that a new column added to an entity is invisible until someone deliberately exposes it.
Request filtering via the editability map. The natural day-to-day pattern is to derive validation/allowed-field lists from EntityPropertyEditabilityMap so a PATCH payload cannot touch non-editable fields:
$allowed = array_keys($this->editabilityMap->getEditableProperties(Product::class));
$data = $request->validate(array_intersect_key($rules, array_flip($allowed)));
Register the map as a singleton — building it per request is wasted reflection work, and the 1.2.0 fix specifically makes that caching reliable.
Convert annotations with a codemod, not by hand. For any non-trivial entity count, run Rector (DoctrineAnnotationToAttributeRector plus a custom rule mapping the old serializer annotations onto the package's attributes) and then review the diff. Hand-editing dozens of entities is where the "I forgot one @Expose" regressions come from.
Contract-test the migration. Serialize a fixture of every entity type before and after the upgrade, json_encode both, and diff. This is the single highest-value test you can write for this release.
Wire the serializer once. Bind MinimalEntitySerializer in a service provider and inject it; do not instantiate it inline in controllers, otherwise the attribute/property cache gets rebuilt per call site and is harder to reason about.
GenericEntityResultSet no longer takes an annotation reader. Anyone with an explicit container definition, a factory, or a new GenericEntityResultSet($reader, ...) call will get a fatal ArgumentCountError/TypeError. Grep the codebase for the class name before upgrading.Gedmo\Mapping\Annotation\SortablePosition support is removed. If your API previously emitted a position/sortablePosition field implicitly, it will silently vanish from responses. Add it as an explicitly exposed property if consumers depend on it.@Expose and Symfony @Ignore are no longer honoured. This is the dangerous one: it fails open for ignore-annotated properties. Any property you hid with #[Ignore] (Symfony) or implied through JMS exposure will now be serialized unless migrated. Treat the output diff as a security review, not just a regression check.1.1.* and treat 1.2 as a separate migration project. Do not attempt to mix: partial migration produces half-exposed entities.doctrine/annotations may still be required elsewhere. Removing the dependency because this package dropped it will break other Doctrine/Gedmo/Symfony bundles that still use annotation drivers. Check composer why doctrine/annotations first.MinimalEntitySerializer and EntityPropertyEditabilityMap previously failed to cache whether attribute-tagged properties were available; after upgrading, fields that were inconsistently missing (or present) may flip. Clear any PSR-6/APCu/Octane-level caches on deploy, and re-run the output diff on a cold cache and a warm one.$entity::class where the API allows it.tinker: dump $serializer->serialize($entity) next to (new \ReflectionClass($entity))->getProperties() and confirm each attribute is actually reachable — private properties on the proxy are the usual culprit.MinimalEntitySerializer registered in the container, not in controller conditionals; the cache keying is per class, so a decorator keeps the caching benefit while adding logic.How can I help you explore Laravel packages today?