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 Adoption Bundle Laravel Package

benkle/doctrine-adoption-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require benkle/doctrine-adoption-bundle
    

    Ensure your project uses Doctrine ORM (Laravel's Eloquent is not compatible).

  2. Enable the Bundle: Add to config/bundles.php (Symfony) or AppKernel.php (legacy):

    Benkle\DoctrineAdoptionBundle\BenkleDoctrineAdoptionBundle::class => ['all' => true],
    
  3. First Use Case: Define a parent entity with @InheritanceType("JOINED") and @DiscriminatorMap. Example:

    use Doctrine\ORM\Mapping as ORM;
    
    /**
     * @ORM\Entity(repositoryClass="Benkle\DoctrineAdoptionBundle\Repository\AdoptionRepository")
     * @ORM\InheritanceType("JOINED")
     * @ORM\DiscriminatorColumn(name="type", type="string")
     * @ORM\DiscriminatorMap({"parent" = "App\Entity\ParentEntity"})
     */
    class ParentEntity { ... }
    
  4. Key Services:

    • AdoptionRepository: Extends Doctrine’s EntityRepository with inheritance-aware methods.
    • AdoptionManager: Handles dynamic adoption of child entities to parent types.

Implementation Patterns

Core Workflows

1. Dynamic Adoption of Child Entities

Use AdoptionManager to programmatically reclassify entities:

$manager = $this->get('benkle.doctrine_adoption.manager');
$childEntity = $entityManager->find(ChildEntity::class, 1);
$manager->adopt($childEntity, ParentEntity::class); // Reclassifies to ParentEntity

2. Querying Inheritance Hierarchies

Leverage AdoptionRepository for type-agnostic queries:

$repository = $this->get('doctrine')->getRepository(ParentEntity::class);
$results = $repository->findByAdoptedType(ChildEntity::class); // Finds all ChildEntity instances

3. Hybrid Inheritance Strategies

Combine JOINED, SINGLE_TABLE, or MAPPED_SUPERCLASS with adoption logic:

// Example: Mixed inheritance in a single hierarchy
@ORM\InheritanceType("JOINED")
@ORM\DiscriminatorMap({
    "parent" = "App\Entity\ParentEntity",
    "single_table_child" = "App\Entity\SingleTableChild"
})

4. Event-Driven Adoption

Hook into Doctrine lifecycle events for automatic adoption:

// In a Doctrine event subscriber
$event->getObject()->setType('new_type'); // Triggers adoption

Integration Tips

Laravel-Specific Adaptations

  1. Service Container Binding: Bind the bundle’s services in AppServiceProvider:

    $this->app->bind('benkle.doctrine_adoption.manager', function ($app) {
        return new AdoptionManager($app->make('doctrine')->getManager());
    });
    
  2. Eloquent Compatibility Note: This bundle does not work with Eloquent. Use native Doctrine entities or a hybrid approach (e.g., Doctrine entities with Laravel’s query builder).

  3. Custom Adoption Logic: Extend AdoptionManager to add business rules:

    class CustomAdoptionManager extends AdoptionManager {
        public function adoptWithValidation($entity, string $targetType) {
            if (!$this->isValidTransition($entity, $targetType)) {
                throw new \RuntimeException("Invalid adoption");
            }
            $this->adopt($entity, $targetType);
        }
    }
    

Performance Considerations

  • Indexing: Ensure discriminator_column is indexed for large hierarchies.
  • Batch Adoption: Use Doctrine’s EntityManager#flush() strategically to avoid N+1 queries during bulk operations.

Gotchas and Tips

Pitfalls

  1. Discriminator Column Conflicts:

    • If discriminator_column clashes with existing columns, rename it or use a unique prefix (e.g., dt_type).
    • Fix: Explicitly define the column in @DiscriminatorColumn.
  2. Circular References:

    • Adopting an entity to a parent that’s already a child of another adopted entity may cause infinite loops.
    • Fix: Implement depth checks in custom adoption logic.
  3. Transaction Boundaries:

    • Adoption operations must be atomic. Wrap in a transaction:
      $entityManager->beginTransaction();
      try {
          $manager->adopt($entity, $newType);
          $entityManager->flush();
          $entityManager->commit();
      } catch (\Exception $e) {
          $entityManager->rollback();
          throw $e;
      }
      
  4. Legacy Data Issues:

    • Existing records with NULL discriminator values may break adoption logic.
    • Fix: Run a data migration to populate discriminator columns.

Debugging Tips

  1. Enable Doctrine Logging:

    # config/packages/dev/doctrine.yaml
    doctrine:
        dbal:
            logging: true
            profiling: true
    

    Check logs for SQL errors during adoption (e.g., foreign key violations).

  2. Query Builder Pitfalls:

    • Avoid DQL queries that ignore the discriminator column. Use:
      $qb = $repository->createQueryBuilder('e');
      $qb->where('e.type = :type')->setParameter('type', 'child_type');
      
  3. Caching Quirks:

    • Clear metadata cache after schema changes:
      php bin/console doctrine:cache:clear-metadata
      

Extension Points

  1. Custom Adoption Strategies:

    • Implement Benkle\DoctrineAdoptionBundle\Strategy\AdoptionStrategyInterface for custom validation/transformation.
  2. Event Listeners:

    • Subscribe to preAdopt/postAdopt events (if the bundle exposes them) to add pre/post-processing:
      $eventDispatcher->addListener(
          'benkle.doctrine_adoption.pre_adopt',
          [$this, 'validateAdoption']
      );
      
  3. Repository Decorators:

    • Decorate AdoptionRepository to add hierarchy-specific methods:
      class CustomAdoptionRepository extends AdoptionRepository {
          public function findByHierarchyLevel(int $level) { ... }
      }
      
    • Register as a service with the correct tag:
      services:
          app.custom_adoption.repository:
              class: App\Repository\CustomAdoptionRepository
              decorates: benkle.doctrine_adoption.repository
              arguments: ['@app.custom_adoption.repository.inner']
      
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.
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui
babelqueue/php-sdk
facebook/capi-param-builder-php
babelqueue/symfony
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle