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 Entity Manager Bundle Laravel Package

arturdoruch/doctrine-entity-manager-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require arturdoruch/doctrine-entity-manager-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        ArturDoruch\DoctrineEntityManagerBundle\ArturDoruchDoctrineEntityManagerBundle::class => ['all' => true],
    ];
    
  2. First Use Case: Create a custom manager for an entity (e.g., User):

    // src/Doctrine/UserManager.php
    namespace App\Doctrine;
    
    use App\Entity\User;
    use ArturDoruch\DoctrineEntityManagerBundle\AbstractEntityManager;
    
    class UserManager extends AbstractEntityManager
    {
        public function getRepository()
        {
            return $this->doGetRepository(User::class);
        }
    
        public function createUser(string $email, string $name): User
        {
            $user = new User();
            $user->setEmail($email);
            $user->setName($name);
            $this->persist($user);
            return $user;
        }
    }
    
  3. Register as Service: Add the service tag in config/services.yaml:

    services:
        App\Doctrine\UserManager:
            tags: ['arturdoruch.doctrine_entity_manager']
    
  4. Access the Manager: Inject the registry into a controller or service:

    use ArturDoruch\DoctrineEntityManagerBundle\EntityManagerRegistry;
    
    class UserController extends AbstractController
    {
        public function __construct(private EntityManagerRegistry $emRegistry) {}
    
        public function createUser()
        {
            $userManager = $this->emRegistry->get(UserManager::class);
            $user = $userManager->createUser('test@example.com', 'Test User');
            // ...
        }
    }
    

Implementation Patterns

Workflows

  1. Domain-Specific Managers: Group CRUD operations for a single entity in a manager (e.g., ProductManager, OrderManager). Example:

    class ProductManager extends AbstractEntityManager
    {
        public function getRepository() { return $this->doGetRepository(Product::class); }
    
        public function publish(Product $product)
        {
            $product->setPublishedAt(new \DateTime());
            $this->persist($product);
        }
    }
    
  2. Dependency Injection Between Managers: Use getManager() to inject other managers (e.g., OrderManager in PaymentManager):

    class PaymentManager extends AbstractEntityManager
    {
        private $orderManager;
    
        public function initialize()
        {
            $this->orderManager = $this->getManager(OrderManager::class);
        }
    
        public function processPayment(Order $order, float $amount)
        {
            $this->orderManager->markAsPaid($order);
            // ...
        }
    }
    
  3. Transaction Management: Leverage Doctrine’s transaction handling via the underlying EntityManager:

    public function transferFunds(User $from, User $to, float $amount)
    {
        $this->getEntityManager()->beginTransaction();
        try {
            $from->subtractBalance($amount);
            $to->addBalance($amount);
            $this->persist($from);
            $this->persist($to);
            $this->getEntityManager()->commit();
        } catch (\Exception $e) {
            $this->getEntityManager()->rollBack();
            throw $e;
        }
    }
    
  4. Query DSL: Use the repository’s methods (e.g., findBy, createQueryBuilder) within managers:

    public function findActiveProducts(): array
    {
        return $this->getRepository()->findBy(['isActive' => true]);
    }
    
  5. Event Subscribers: Attach Doctrine events (e.g., prePersist, postRemove) via the manager:

    public function initialize()
    {
        $this->getEntityManager()->getEventManager()->addEventSubscriber(new ProductEventSubscriber());
    }
    

Integration Tips

  1. Symfony Forms: Bind managers to form types for validation/processing:

    class ProductType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder->add('name');
            $builder->add('save', SubmitType::class, [
                'attr' => ['class' => 'btn btn-primary'],
            ]);
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults([
                'manager' => $options['manager'], // Inject UserManager
            ]);
        }
    }
    
  2. API Platform: Use managers in API resource classes for custom logic:

    use ApiPlatform\Core\Annotation\ApiResource;
    use App\Doctrine\UserManager;
    
    #[ApiResource]
    class User
    {
        public function __construct(private UserManager $manager) {}
    
        public function prePersist()
        {
            $this->manager->generateApiToken($this);
        }
    }
    
  3. Messenger Integration: Dispatch messages from managers for async processing:

    use Symfony\Component\Messenger\MessageBusInterface;
    
    class OrderManager extends AbstractEntityManager
    {
        public function __construct(private MessageBusInterface $bus) {}
    
        public function placeOrder(Order $order)
        {
            $this->persist($order);
            $this->bus->dispatch(new SendOrderConfirmationEmail($order));
        }
    }
    
  4. CQRS Pattern: Separate read/write operations using managers:

    // Command (Write)
    class CreateProductCommandHandler
    {
        public function __construct(private ProductManager $manager) {}
    
        public function __invoke(CreateProductCommand $command)
        {
            $product = $this->manager->createProduct($command->name, $command->price);
        }
    }
    
    // Query (Read)
    class GetProductQueryHandler
    {
        public function __construct(private ProductManager $manager) {}
    
        public function __invoke(GetProductQuery $query)
        {
            return $this->manager->findProduct($query->id);
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Circular Dependencies: Avoid circular references between managers (e.g., AManager injecting BManager which injects AManager). Fix: Use lazy initialization or refactor shared logic into a service.

  2. EntityManager Lifecycle: Managers are not scoped per-request by default. Ensure thread safety if used in async contexts (e.g., Messenger). Fix: Use initialize() to set up per-request dependencies.

  3. Repository Caching: The doGetRepository() method caches repositories. Clear the cache if repositories change dynamically. Fix: Call $this->clearRepositoryCache() if needed (exposed via AbstractEntityManager).

  4. Transaction Isolation: Managers share the same EntityManager as their parent bundle. Long-running operations may lock tables. Fix: Use setLockMode() or optimize queries.

  5. Service Tagging: Forgetting to tag managers with arturdoruch.doctrine_entity_manager will prevent them from being registered. Fix: Verify tags in debug:container or debug:autowiring.


Debugging

  1. Registry Issues: If get() returns null, check:

    • The service is tagged correctly.
    • The manager class is autowired (use debug:container).
    • The bundle is enabled in config/bundles.php.
  2. EntityManager Errors: Use getEntityManager()->getConnection()->getDatabasePlatform() to debug platform-specific issues (e.g., MySQL vs. PostgreSQL).

  3. Lazy Loading: Enable debug:orm:proxy to verify proxy classes are generated:

    php bin/console debug:orm:proxy
    

Configuration Quirks

  1. Custom EntityManager Names: Override getEntityManagerName() to use a specific connection:

    protected function getEntityManagerName(): string
    {
        return 'read_connection'; // Uses the 'read_connection' Doctrine connection
    }
    
  2. Shared EntityManagers: By default, all managers share the same EntityManager. To isolate:

    # config/packages/doctrine.yaml
    doctrine:
        dbal:
            connections:
                default: { url: '%env(DATABASE_URL)' }
                read: { url: '%env(READ_DATABASE_URL)' }
    

    Then override getEntityManagerName() in your manager.

  3. Proxy Generation: Ensure doctrine/orm is configured for proxy generation:

    doctrine:
        orm:
            proxy_dir: '%kernel.cache_dir%/doctrine/orm/Proxies'
            proxy_namespace: App\Proxy
            generate_proxies: true
    

Extension Points

  1. Custom Initialization: Override initialize() to set up dependencies or listeners:
    public function initialize
    
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.
nasirkhan/laravel-sharekit
directorytree/privacy-filter-classifier
directorytree/privacy-filter
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony
develia/geo-bundle
dreamzy/livewire-charts
touchestate-sdk/php-sdk
22h/doctrine-garbage-collection-bundle
agtp/agtp-php
agtp/mod-php
splash/sonata-admin
splash/metadata
splash/openapi
splash/scopes
splash/toolkit
testo/output-teamcity
testo/bridge-symfony