Laravel Doctrine ORM

repository·3.3.x·Indexed 21 days ago

https://github.com/laravel-doctrine/orm

An integration library that bridges the Laravel ecosystem with Doctrine ORM's data mapping capabilities. It provides support for multiple entity managers, custom cache drivers, and database connection management. The package includes tools for implementing Laravel's Authenticatable contract on Doctrine entities, managing database schemas via Artisan commands, and configuring metadata, query, and result caching.

Tokens
21.2K
Snippets
74
Records
98
Agent score
74%

What's inside laravel-doctrine-orm

  1. Introduction to Laravel Doctrine ORM

    3.3.x
    Laravel Doctrine ORM is an integration library that brings Doctrine ORM to the Laravel ecosystem. It allows you to use the Data Mapper pattern instead of the traditional Active Record pattern used by Eloquent. This provides a complete separation between your domain/business logic and the relational database persistence layer, allowing you to focus on object-oriented logic.
  2. Manage entity relations using Collections

    3.3.x

    When defining one-to-many or many-to-many relations, the property type should be Doctrine\Common\Collections\Collection.

    To ensure relations are ready for use upon instantiation, you should initialize collection properties with an empty Doctrine\Common\Collections\ArrayCollection in the entity's constructor. This allows you to use standard collection methods immediately:

    • ->add($element): Add a new relation.
    • ->removeElement($element): Remove a relation.
    • ->contains($element): Check if a relation is already defined.
    use Doctrine\
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    
    class Scientist
    {
        #[ORM\OneToMany(targetEntity: Theory::class, mappedBy: "scientist")]
        private Collection $theories;
    
        public function __construct()
        {
            $this->theories = new ArrayCollection();
        }
    }
  3. How DoctrineManager provides advanced configuration

    3.3.x

    The DoctrineManager allows you to hook into the internals of a Doctrine Entity Manager to perform advanced configuration that cannot be achieved via the standard doctrine.php configuration file.

    It provides access to three core Doctrine facets for a specific Entity Manager (identified by the name configured in doctrine.php):

    1. Doctrine\ORM\Configuration
    2. Doctrine\DBAL\Connection
    3. Doctrine\Common\EventManager
  4. How the Identity Map Pattern works in Doctrine

    3.3.x

    Doctrine implements the Identity Map Pattern. This means the EntityManager keeps a map of all entities and their IDs retrieved during a single request. If you attempt to find the same entity multiple times, Doctrine will return the exact same instance from its internal map rather than creating a new object.

    Because they are the same instance, any changes made to an entity will be reflected in subsequent find calls for that same ID, even before the changes are flushed to the database.

    $entity = EntityManager::find('App\Entities\Article', 1);
    $entity->title = 'Different title';
    
    // This returns the exact same instance as $entity
    $entityCopy = EntityManager::find('App\Entities\Article', 1);
    
    assert($entityCopy->title === 'Different title');
  5. Handle Password Hashing

    3.3.x

    LaravelDoctrine treats passwords as plain strings. It does not handle hashing automatically. You are responsible for hashing passwords within your application logic before persisting them to the entity.

    It is recommended to decouple hashing and strength validation from storage by using a dedicated service that utilizes Laravel's \Illuminate\Contracts\Hashing\Hasher.

    use \Illuminate\Contracts\Hashing\Hasher;
    
    class PasswordService
    {
        private $hasher;
        private $passwordStrengthValidator;
    
        public function __construct(
          Hasher $hasher,
          MyPasswordStrengthValidator $passwordStrength
        ) {
            $this->hasher = $hasher;
            $this->passwordStrengthValidator = $passwordStrength;
        }
    
        public function changePassword(User $user, $password)
        {
            if ($this->passwordStrengthValidator->isStrongEnough($password)) {
                // Hash the password before setting it on the entity
                $user->setPassword($this->hasher->make($password));
            } else {
                throw new PasswordTooWeakException();
            }
        }
    }
  6. Supported database connections

    3.3.x

    Laravel Doctrine ORM supports all database connections configured in your Laravel application's config/database.php. This includes:

    • mysql
    • sqlite
    • pqsql
    • sqlsrv
    • oci8

    Read/write connection splitting is supported. Swapping the DB_CONNECTION environment variable will automatically swap the database connection used by Doctrine.

  7. Configure Namespace Aliases

    3.3.x

    To avoid using fully qualified class names in queries, you can define namespace aliases in your entity manager configuration. This allows you to use a short alias in DQL or when retrieving repositories.

    1. Define the alias as a key in the namespaces array.
    2. Map the alias to the actual entity namespace.
    3. Use the alias in queries using the Alias:Entity format.
    'namespaces' => [
        'Foo' => 'App\Model\Foo\Entities',
        'Bar' => 'App\Model\Bar\Entities',
    ],

    Usage in SQL/DQL:

    SELECT f FROM Foo:SomeEntity

    Usage in PHP:

    EntityManager::getRepository('Bar:SomeEntity');
  8. Best practice for injecting repositories

    3.3.x
    It is not recommended to inject specific repository instances directly into your classes. Instead, you should inject the EntityManager. The EntityManager acts as a container (similar to a PSR-11 container) that can provide the necessary repositories when needed. This approach keeps your dependency graph cleaner and avoids issues with repository lifecycles.
  9. Understand the Data Mapper pattern in Doctrine ORM

    3.3.x

    Unlike Eloquent, which uses the Active Record pattern where models contain both business logic and persistence logic, Doctrine ORM uses the Data Mapper pattern.

    In Doctrine, entities are plain PHP classes that represent domain objects with identity. They do not extend any base class provided by the ORM. This ensures a complete separation between your domain/business logic and the persistence logic. To bridge this gap, you must provide metadata to tell Doctrine how to map database columns to your entity properties.

  10. Recommended Application Folder Structure

    3.3.x

    Doctrine entities should not be placed in the standard Laravel Model directory. It is recommended to use a dedicated app/Doctrine/ORM directory.

    For an application with a single entity manager, the following structure is suggested:

    • app/Doctrine/ORM/Entity
    • app/Doctrine/ORM/Repository
    • app/Doctrine/ORM/Subscriber
    • app/Doctrine/ORM/Listener
    ~/app/Doctrine/ORM
  11. Extend repositories using inheritance

    3.3.x

    You can create a custom repository by extending Doctrine's EntityRepository. This allows you to inherit all the standard Doctrine repository methods (like find(), findAll(), etc.) while adding your own domain-specific methods. To use this in Laravel, you must bind your custom repository to an interface in a ServiceProvider.

    use Doctrine\ORM\EntityRepository;
    
    // 1. Define your interface
    interface ScientistRepository
    {
        public function find($id);
        public function findByName($name);
    }
    
    // 2. Implement via inheritance
    class DoctrineScientistRepository extends EntityRepository implements ScientistRepository
    {
        public function findByName($name)
        {
            return $this->findBy(['name' => $name]);
        }
    }
    
    // 3. Bind in AppServiceProvider
    class AppServiceProvider
    {
        public function register()
        {
            $this->app->bind(ScientistRepository::class, function($app) {
                return new DoctrineScientistRepository(
                    $app['em'],
                    $app['em']->getClassMetaData(Scientist::class)
                );
            });
        }
    }