Weave Code
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 Common Laravel Package

api-platform/doctrine-common

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation Add the package via Composer:

    composer require api-platform/doctrine-common
    

    Ensure api-platform/core and doctrine/doctrine-bundle are also installed (dependencies).

  2. First Use Case Use this package to leverage Doctrine Common utilities (e.g., ClassMetadata, ProxyFactory, PersistenceObjectWrapper) in API Platform projects. Example: Extend a Resource class to use ClassMetadata for dynamic property checks:

    use ApiPlatform\Core\DataTransformer\CollectionDataProviderInterface;
    use Doctrine\Common\Persistence\ObjectManager;
    use Doctrine\Common\Util\ClassUtils;
    
    class CustomDataProvider implements CollectionDataProviderInterface
    {
        public function __construct(private ObjectManager $manager) {}
    
        public function supports(string $resourceClass, string $operationName = null, array $context = []): bool
        {
            return ClassUtils::getClass($resourceClass) === MyEntity::class;
        }
    
        public function getCollection(string $resourceClass, string $operationName = null, array $context = []): array
        {
            $metadata = $this->manager->getClassMetadata($resourceClass);
            // Use metadata to filter/transform data
            return [...];
        }
    }
    
  3. Where to Look First

    • Doctrine Common Docs: https://www.doctrine-project.org/projects/doctrine-common/en/latest/
    • API Platform Integration: Check api-platform/core for how it uses Doctrine Common internally (e.g., ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter).
    • Symfony Dependency Injection: The package is auto-configured via Symfony’s DI, so no extra config is needed for basic usage.

Implementation Patterns

Common Workflows

  1. Dynamic Metadata Access Use ClassMetadata to inspect entity properties at runtime (e.g., for custom serialization/deserialization):

    $metadata = $this->manager->getClassMetadata(MyEntity::class);
    $propertyNames = $metadata->getFieldNames(); // Get all field names
    $lifecycleCallbacks = $metadata->getLifecycleCallbacks(); // Get lifecycle hooks
    
  2. Proxy Object Handling Leverage Doctrine\Common\Proxy\ProxyFactory to generate proxies for lazy-loading or custom logic:

    use Doctrine\Common\Proxy\ProxyFactory;
    
    $proxyFactory = new ProxyFactory();
    $proxy = $proxyFactory->getProxy(new \ReflectionClass(MyEntity::class), $entityId);
    
  3. Persistence Object Wrapping Use Doctrine\Common\Persistence\ObjectManager to wrap entities for custom persistence logic:

    $wrapped = $this->manager->getRepository(MyEntity::class)->find($id);
    $wrapper = new \Doctrine\Common\Persistence\ObjectWrapper($wrapped);
    $wrapper->setProperty('name', 'New Name'); // Custom logic
    
  4. Integration with API Platform Filters Extend API Platform’s built-in filters (e.g., SearchFilter, OrderFilter) by using Doctrine Common’s ClassMetadata to validate or transform filter inputs:

    use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\AbstractFilter;
    use Doctrine\Common\Util\ClassUtils;
    
    class CustomFilter extends AbstractFilter
    {
        protected function filterProperty(string $property, $value, array $resourceClass, string $operationName = null, array $context = []): bool
        {
            $metadata = $this->manager->getClassMetadata($resourceClass[0]);
            if (!$metadata->hasField($property)) {
                return false; // Skip invalid properties
            }
            return true;
        }
    }
    
  5. Event Subscribers Use Doctrine Common’s EventManager to tap into lifecycle events (e.g., onFlush, postPersist):

    use Doctrine\Common\EventSubscriber;
    use Doctrine\ORM\Event\LifecycleEventArgs;
    
    class CustomSubscriber implements EventSubscriber
    {
        public function getSubscribedEvents(): array
        {
            return ['postPersist'];
        }
    
        public function postPersist(LifecycleEventArgs $args): void
        {
            $entity = $args->getObject();
            if ($entity instanceof MyEntity) {
                // Custom logic
            }
        }
    }
    

Integration Tips

  1. Dependency Injection The package is auto-wired via Symfony’s DI. Inject ObjectManager, ClassMetadataFactory, or ProxyFactory directly into services:

    # config/services.yaml
    services:
        App\Service\CustomService:
            arguments:
                $manager: '@doctrine.orm.entity_manager'
    
  2. API Platform Resource Classes Extend ApiPlatform\Core\Annotation\ApiResource classes to use Doctrine Common for dynamic behavior:

    use ApiPlatform\Core\Annotation\ApiResource;
    use Doctrine\Common\Util\ClassUtils;
    
    #[ApiResource]
    class MyEntity
    {
        public function getDynamicProperty(): string
        {
            $metadata = $this->manager->getClassMetadata(self::class);
            return $metadata->hasField('dynamic_field') ? 'Yes' : 'No';
        }
    }
    
  3. Testing Mock ClassMetadata or ObjectManager in tests using PHPUnit:

    $metadata = $this->createMock(\Doctrine\ORM\Mapping\ClassMetadata::class);
    $metadata->method('getFieldNames')->willReturn(['id', 'name']);
    $this->manager->expects($this->any())->method('getClassMetadata')->willReturn($metadata);
    

Gotchas and Tips

Pitfalls

  1. Circular Dependencies Avoid circular references when using ClassMetadata or proxies. Doctrine Common throws exceptions for invalid proxy configurations:

    // ❌ Avoid this in proxies
    $proxy->getSomething()->getSomethingElse(); // May cause infinite recursion
    
  2. Metadata Cache Invalidation Changes to entity mappings (e.g., adding/removing fields) require clearing the metadata cache:

    php bin/console doctrine:cache:clear-metadata
    

    Or programmatically:

    $this->manager->getMetadataFactory()->getCache()->deleteAll();
    
  3. Proxy Generation Issues Proxies may fail if the target class is not properly configured for proxying (e.g., missing __clone() or __sleep()). Ensure your entity uses Doctrine’s proxyable traits:

    use Doctrine\ORM\Mapping as ORM;
    
    #[ORM\Entity]
    class MyEntity
    {
        // No need for explicit traits; Doctrine handles it by default
    }
    
  4. Performance Overhead Frequent calls to getClassMetadata() can be expensive. Cache results if used repeatedly:

    $metadataCache = [];
    $metadata = $metadataCache[$resourceClass] ?? $this->manager->getClassMetadata($resourceClass);
    $metadataCache[$resourceClass] = $metadata;
    

Debugging Tips

  1. Inspect Metadata Dump metadata to debug entity mappings:

    $metadata = $this->manager->getClassMetadata(MyEntity::class);
    dump($metadata->getFieldNames(), $metadata->getAssociationNames());
    
  2. Proxy Debugging Enable proxy logging to trace proxy generation:

    # config/packages/dev/doctrine.yaml
    doctrine:
        orm:
            proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
            proxy_namespace: Proxies
            logging: true
    
  3. Event Subscriber Debugging Use Symfony’s debug toolbar to inspect Doctrine events:

    // Temporarily dump events in a subscriber
    public function getSubscribedEvents(): array
    {
        dump('Subscriber loaded'); // Check if subscriber is registered
        return ['postPersist'];
    }
    

Extension Points

  1. Custom Metadata Drivers Extend Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain to add custom metadata sources (e.g., YAML/JSON overrides):

    use Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain;
    
    class CustomDriver extends MappingDriverChain
    {
        public function loadMetadataForClass($className, ClassMetadata $metadata)
        {
            parent::loadMetadataForClass($className, $metadata);
            // Override or extend metadata
        }
    }
    
  2. Dynamic Proxies Create custom proxy factories for non-Doctrine entities:

    use Doctrine\Common\Proxy\Proxy;
    
    class CustomProxyFactory extends \Doctrine\Common\Proxy\ProxyFactory
    {
        protected function initializeProxy(Proxy $proxy): void
        {
            // Custom initialization logic
        }
    }
    
  3. API Platform State Processor Use Doctrine Common in `State

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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor