Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Doctrine Rest Bundle Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

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:

  1. 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).
  2. Look first at src/Attribute/ — the attribute classes that replaced the old annotation classes. These are the ones you will actually type into your entities.
  3. Next, read MinimalEntitySerializer (what gets output) and EntityPropertyEditabilityMap (what is allowed in). Those two classes define the package's whole contract.
  4. If you previously wired 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.

Implementation Patterns

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.

Gotchas and Tips

  • Constructor signature change is the loud break. 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.
  • JMS @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.
  • No annotation fallback exists. If any consumer is still on PHP 7.x or you cannot migrate shared entities yet, pin 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.
  • The caching fixes change observable behaviour. Both 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.
  • Doctrine proxies. Attribute reflection must be performed against the real entity class, not the generated proxy. If you serialize entities straight out of a repository without initialising them, you can get empty or partial arrays. Initialise explicitly, or pass the class name rather than $entity::class where the API allows it.
  • Inherited and trait-declared properties. Attributes live on the property declaration; a property exposed only in a parent class is not automatically inherited into the child's map. Verify the map output for any entity using mapped superclasses or traits.
  • Debugging loop. In 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.
  • Extension point. Custom serialization rules belong in a subclass or decorator of 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.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox