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

byteincoffee/doctrine-extensions

Laravel package integrating Doctrine Extensions with Eloquent models, adding behaviors like timestampable, sluggable, soft delete, and more. Provides easy configuration, listeners/subscribers, and seamless use of Doctrine-style extensions in Laravel apps.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation Add the package via Composer:

    composer require byteincoffee/doctrine-extensions
    

    Ensure Doctrine\ORM\Tools\Setup is properly configured in your Laravel app (typically via config/database.php or a custom Doctrine setup).

  2. First Use Case: Sluggable Behavior Extend your entity with Sluggable trait:

    use DoctrineExtensions\Query\Mysql\Sluggable;
    
    /**
     * @ORM\Entity
     */
    class Post
    {
        use Sluggable;
    
        /**
         * @ORM\Column(type="string", length=255, unique=true)
         */
        private $slug;
    
        /**
         * @ORM\Column(type="string", length=255)
         */
        private $title;
    
        // ... getters/setters
    }
    

    Define slug generation in buildSlug():

    public function buildSlug()
    {
        return strtolower($this->title);
    }
    
  3. Enable Extensions in Doctrine Register the extensions in your Doctrine configuration (e.g., in AppServiceProvider):

    use DoctrineExtensions\ORM\Query\AST\Functions\Mysql;
    
    $em->getConfiguration()->addCustomStringFunction(
        'CONCAT_WS', Mysql\Functions\StringFunctions::class
    );
    

Implementation Patterns

Common Workflows

  1. Sluggable Entities

    • Use Sluggable trait for dynamic slug generation.
    • Override buildSlug() to customize slug logic (e.g., include categories):
      public function buildSlug()
      {
          return strtolower($this->category->name . '-' . $this->title);
      }
      
    • Ensure unique=true in @ORM\Column for slugs.
  2. Tree Queries (Nested Sets)

    • Use NestedSet trait for hierarchical data (e.g., categories):
      use DoctrineExtensions\Query\TreeRepository;
      
      class Category
      {
          use NestedSet;
      
          // ... fields
      }
      
    • Query children/parents:
      $children = $category->getChildren();
      $parent = $category->getParent();
      
  3. Soft Deletes

    • Extend SoftDelete trait for soft-deletion logic:
      use DoctrineExtensions\Query\SoftDelete;
      
      class Post
      {
          use SoftDelete;
      
          // ... fields
      }
      
    • Filter soft-deleted records in queries:
      $query->andWhere('e.deletedAt IS NULL');
      
  4. Custom DQL Functions

    • Register custom functions (e.g., CONCAT_WS):
      $em->getConfiguration()->addCustomStringFunction(
          'MY_CUSTOM_FUNC', CustomFunction::class
      );
      
    • Use in queries:
      $query->select('MY_CUSTOM_FUNC(e.field1, e.field2) AS result');
      

Integration Tips

  • Laravel Eloquent Bridge: Use doctrine/orm alongside Laravel’s Eloquent for hybrid ORM usage. Map entities to Eloquent models via Model::setConnection().
  • Migrations: Handle Doctrine-specific fields (e.g., lft, rgt for NestedSet) in migrations:
    Schema::table('categories', function (Blueprint $table) {
        $table->integer('lft')->unsigned();
        $table->integer('rgt')->unsigned();
        $table->integer('level')->unsigned();
    });
    
  • Caching: Cache complex queries (e.g., tree traversals) to avoid performance hits:
    $children = Cache::remember("category_{$id}_children", 3600, function () use ($category) {
        return $category->getChildren();
    });
    

Gotchas and Tips

Pitfalls

  1. Doctrine vs. Eloquent Conflicts

    • Avoid mixing Doctrine and Eloquent for the same entity unless explicitly bridged. Doctrine’s lifecycle callbacks (e.g., prePersist) may conflict with Eloquent events.
    • Fix: Use Model::setConnection() carefully and ensure no duplicate event listeners.
  2. Sluggable Uniqueness

    • Forgetting unique=true on slug columns causes duplicate-slug errors.
    • Fix: Add validation in buildSlug():
      public function buildSlug()
      {
          $slug = strtolower($this->title);
          if (!$this->isSlugUnique($slug)) {
              $slug .= '-' . $this->id;
          }
          return $slug;
      }
      
  3. Nested Set Performance

    • Deep trees (level > 10) degrade query performance. Use MaterializedPath for deeper hierarchies.
    • Fix: Switch to MaterializedPath trait if needed:
      use DoctrineExtensions\Query\MaterializedPath;
      
  4. Custom Function Registration

    • Functions must match Doctrine’s DQL syntax. Incorrect registration causes SyntaxError.
    • Fix: Test functions in a small query first:
      $query->select('MY_CUSTOM_FUNC(e.field) AS test');
      
  5. Soft Delete Conflicts

    • Soft-deleted records may still appear in queries if not filtered.
    • Fix: Always add SoftDelete filters:
      $query->andWhere('e.deletedAt IS NULL');
      

Debugging

  • Enable Doctrine Logging Add to config/logging.php:

    'doctrine' => [
        'driver' => 'single',
        'path' => storage_path('logs/doctrine.log'),
        'level' => 'debug',
    ],
    

    Then enable logging in AppServiceProvider:

    $em->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger());
    
  • Query AST Errors For custom DQL functions, check the AST (Abstract Syntax Tree) path:

    vendor/bin/doctrine-orm-cli ast:dump
    

Extension Points

  1. Custom Traits Extend existing traits (e.g., Sluggable) for domain-specific logic:

    trait CustomSluggable extends Sluggable
    {
        protected function buildSlug()
        {
            return parent::buildSlug() . '-custom';
        }
    }
    
  2. Event Subscribers Hook into Doctrine events (e.g., onFlush) for pre/post operations:

    use Doctrine\Common\EventSubscriber;
    
    class MySubscriber implements EventSubscriber
    {
        public function getSubscribedEvents()
        {
            return ['onFlush'];
        }
    
        public function onFlush(OnFlushEventArgs $args)
        {
            // Custom logic before flush
        }
    }
    
  3. Custom Query Functions Implement DoctrineExtensions\Query\AST\Functions\AbstractFunctionNode for advanced DQL:

    class CustomFunction extends AbstractFunctionNode
    {
        public function parse(\Doctrine\ORM\Query\Parser $parser)
        {
            $parser->match(Lexer::T_IDENTIFIER);
            $parser->match(Lexer::T_OPEN_PARENTHESIS);
            // ... parse arguments
        }
    }
    
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.
codifyo/ts-generator-bundle
andydefer/laravel-cluster
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