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 Behaviors Laravel Package

eightmarq/doctrine-behaviors

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require eightmarq/doctrine-behaviors
    

    (Note: The package is a fork of knplabs/doctrine-behaviors; ensure compatibility with your Doctrine version.)

  2. First Use Case: SoftDeletable Add the trait to your entity and configure the deletedAt field:

    use Knp\DoctrineBehaviors\Model\Blameable\BlameableTrait;
    use Knp\DoctrineBehaviors\Model\Timestampable\TimestampableTrait;
    use Knp\DoctrineBehaviors\Model\SoftDeletable\SoftDeletableTrait;
    
    class Post
    {
        use SoftDeletableTrait;
    
        /**
         * @ORM\Column(type="datetime", nullable=true)
         */
        private ?\DateTimeInterface $deletedAt = null;
    }
    

    Now, Post::delete() soft-deletes records instead of hard-deleting them.

  3. First Query: Filtering Deleted Entities Use SoftDeletableListener in your repository or query:

    $queryBuilder->andWhere('entity.deletedAt IS NULL');
    

Implementation Patterns

Common Workflows

  1. Blameable (Audit Tracking) Track who created/updated an entity:

    use Knp\DoctrineBehaviors\Model\Blameable\BlameableInterface;
    
    class Post implements BlameableInterface
    {
        use BlameableTrait;
    
        /**
         * @ORM\ManyToOne(targetEntity="App\Entity\User")
         * @ORM\JoinColumn(nullable=false)
         */
        private ?User $createdBy = null;
    
        /**
         * @ORM\ManyToOne(targetEntity="App\Entity\User")
         * @ORM\JoinColumn(nullable=false)
         */
        private ?User $updatedBy = null;
    }
    
    • Integration Tip: Use a middleware to set BlameableInterface::setUserValue() from the authenticated user.
  2. Sluggable (URL-Friendly Slugs) Auto-generate slugs from titles:

    use Knp\DoctrineBehaviors\Model\Sluggable\SluggableInterface;
    
    class Post implements SluggableInterface
    {
        use SluggableTrait;
    
        /**
         * @ORM\Column(type="string", length=255, unique=true)
         */
        private string $slug;
    
        private string $title;
    }
    
    • Workflow: Call $post->updateSlug() after setting $post->title.
  3. Tree (Hierarchical Data) Manage nested categories:

    class Category
    {
        use TreeTrait;
    
        // Required fields for TreeTrait
        private ?Category $parent = null;
        private ?Category $left = null;
        private ?Category $right = null;
        private ?Category $root = null;
        private ?Category $level = null;
    }
    
    • Repository Integration:
      class CategoryRepository extends EntityRepository
      {
          use TreeRepositoryTrait;
      }
      
    • Common Queries:
      $repository->getSubtree($parentCategory);
      $repository->getChildren($category);
      
  4. Translatable (Multilingual Content) Store translations in a separate table:

    use Knp\DoctrineBehaviors\Model\Translatable\TranslatableInterface;
    
    class Post implements TranslatableInterface
    {
        use TranslatableTrait;
    
        /**
         * @ORM\OneToMany(targetEntity="App\Entity\Translation", mappedBy="translatable", cascade={"all"}, orphanRemoval=true)
         */
        private Collection $translations;
    }
    
    • Usage:
      $post->translate('en')->setTitle('Hello');
      $post->translate('es')->setTitle('Hola');
      

Integration Tips

  • Doctrine Events: Listen for prePersist/preUpdate to trigger behaviors (e.g., slug generation).
  • Symfony Forms: Use BlameableListener to auto-populate createdBy/updatedBy.
  • API Responses: Filter soft-deleted entities in DTOs or serializers.
  • Migrations: Add deletedAt columns for SoftDeletable in a separate migration.

Gotchas and Tips

Pitfalls

  1. SoftDeletable + Unique Constraints

    • Soft-deleted records may violate unique constraints (e.g., slugs). Use deletedAt IS NULL in queries.
    • Fix: Add a unique constraint on (slug, deletedAt) if needed.
  2. Tree Behavior Quirks

    • Performance: Deep trees (>100 levels) may cause slow queries. Use MaterializedPathTrait for large hierarchies.
    • Circular References: Avoid bidirectional relationships with parent/children to prevent infinite loops.
    • Repository Conflicts: Ensure your repository extends EntityRepository (not a custom base class) for TreeRepositoryTrait.
  3. Translatable Serialization

    • Nested Translations: If translations contain translatable fields, recursively call translate().
    • Missing Translations: Always check hasTranslation($locale) before accessing $post->translate($locale).
  4. Blameable Without Users

    • If createdBy/updatedBy are nullable, set a default user (e.g., system) in prePersist:
      $entity->setCreatedBy($entity->getUpdatedBy() ?? new SystemUser());
      
  5. Slug Collisions

    • SluggableTrait appends a suffix (e.g., -2) on duplicates. For custom logic, override generateSlug():
      public function generateSlug(): string
      {
          return strtolower(parent::generateSlug()) . '-' . $this->id;
      }
      

Debugging

  1. SoftDeletable Not Working

    • Verify deletedAt is nullable in the database schema.
    • Check for SoftDeletableListener in your Doctrine config (config/packages/doctrine.yaml):
      doctrine:
          orm:
              event_subscribers:
                  - Knp\DoctrineBehaviors\ORM\SoftDeletable\SoftDeletableListener
      
  2. Tree Traversal Issues

    • Use getSubtree() with fetchChildren: true to debug hierarchy:
      $subtree = $repository->getSubtree($root, true);
      
    • Enable SQL logging to inspect generated queries:
      $entityManager->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
      
  3. Translatable Not Saving

    • Ensure translatable and translation entities have proper mappedBy/inversedBy:
      // Translation.php
      /**
       * @ORM\ManyToOne(targetEntity="Post", inversedBy="translations")
       * @ORM\JoinColumn(name="translatable_id", referencedColumnName="id")
       */
      private ?Post $translatable = null;
      

Extension Points

  1. Custom Behaviors

    • Extend existing traits (e.g., add versionable logic to TimestampableTrait):
      trait VersionableTrait
      {
          private ?int $version = 1;
      
          public function incrementVersion(): void
          {
              $this->version++;
          }
      }
      
  2. Override Default Logic

    • Example: Custom slug generation for SluggableTrait:
      use Knp\DoctrineBehaviors\Model\Sluggable\SluggableTrait;
      
      class CustomSluggableTrait
      {
          use SluggableTrait {
              generateSlug as private generateDefaultSlug;
          }
      
          public function generateSlug(): string
          {
              return 'custom-' . $this->generateDefaultSlug();
          }
      }
      
  3. Event Subscribers

    • Hook into lifecycle events (e.g., log changes after preUpdate):
      $entityManager->getEventManager()->addEventSubscriber(new class {
          public function postUpdate(LifecycleEventArgs $args): void
          {
              $entity = $args->getObject();
              if ($entity instanceof LoggableInterface) {
                  $entity->logChange('updated');
              }
          }
      });
      
  4. PHPStan Extensions

    • Leverage the built-in PHPStan rules for type safety:
      // phpstan.neon
      includes:
          - vendor/knplabs/doctrine-behaviors/extension.neon
      
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.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui