loophp/service-object-repository-bundle

Provide aliases of Doctrine repository based on their FQDN.

Fund package maintenance!
drupol
Paypal

Installs: 2

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 2

Forks: 0

Type:symfony-bundle

dev-master 2021-06-16 10:03 UTC

This package is auto-updated.

Last update: 2021-10-16 10:55:48 UTC


README

Latest Stable Version GitHub stars Total Downloads License Donate! Donate!

Service Object Repository Bundle

Description

A bundle for Symfony 5.

There are many ways to work with Doctrine repositories in Symfony.

One of the best way to work is to declare them as standalone services. Everything to know about that is in this article from Tomas Votruba.

Ideally, we would not need this bundle if each repository would have its own interface properly typed. However, most of the time we do not create them.

This bundle fix that by declaring new aliases in the Symfony container when your repository implements particular interfaces.

This way you can inject repositories using interfaces instead of implementations.

I also opened a pull request symfony/maker-bundle to improve the bin/console make:entity command and create proper standalone repositories and interfaces when creating entities.

In order to adhere to S.O.L.I.D. principles in your code, and especially the Open-Closed Principle, we have to use interfaces or abstracted classes when injecting repositories in a service or in a controller.

Usually we do not respect that principle and we do:

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Repository\UserRepository;

final class MyTestController
{
    public function __invoke(UserRepository $userRepository): Response
    {
        // Do stuff.
    }
}

This bundle fix that. It let users rely on repository interfaces instead of repository implementations.

<?php

declare(strict_types=1);

namespace App\Controller;

use Doctrine\Persistence\ObjectRepository;

final class MyTestController
{
    public function __invoke(ObjectRepository $appRepositoryUserRepository): Response
    {
        // Do stuff.
    }
}

We can even do better by injecting it in the constructor. Then we can use the interface when injecting, and we can use the implementation in the property. Best of both world.

<?php

declare(strict_types=1);

namespace App\Controller;

use App\Repository\UserRepository;
use Doctrine\Persistence\ObjectRepository;

final class MyTestController
{
    private UserRepository $userRepository;

    public function __construct(ObjectRepository $appRepositoryUserRepository)
    {
        $this->userRepository = $appRepositoryUserRepository;
    }

    public function __invoke(): Response
    {
        // Do stuff.
    }
}

Usage

In order to use this package, your repositories must be declared as services in Symfony.

Example of a repository class generated by symfony/maker-bundle:

<?php

declare(strict_types=1);

namespace App\Repository;

use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method User|null find($id, $lockMode = null, $lockVersion = null)
 * @method User|null findOneBy(array $criteria, array $orderBy = null)
 * @method User[]    findAll()
 * @method User[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class UserRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, User::class);
    }
}

Example of a standalone service repository (built using composition over inheritance):

<?php

declare(strict_types=1);

namespace App\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\ObjectRepository;

final class UserRepository implements ObjectRepository
{
    private EntityManagerInterface $entityManager;

    private ObjectRepository $repository;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->repository = $entityManager->getRepository(User::class);
        $this->entityManager = $entityManager;
    }

    public function find($id, $lockMode = null, $lockVersion = null): ?User
    {
        return $this->repository->find($id, $lockMode, $lockVersion);
    }

    public function findOneBy(array $criteria, array $orderBy = null): ?User
    {
        return $this->repository->findOneBy($criteria, $orderBy);
    }

    /**
     * @return User[]
     */
    public function findAll(): array
    {
        return $this->repository->findAll();
    }

    /**
     * @return User[]
     */
    public function findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null): array
    {
        return $this->repository->findBy($criteria, $orderBy, $limit, $offset);
    }

    public function getClassName()
    {
        return User::class;
    }
}

Once that is done, your repository must implements ObjectRepository (provided by doctrine/persistence) or ServiceEntityRepositoryInterface (provided by symfony/doctrine-bundle).

Once installed, this bundle will create container aliases for repositories based on their fully qualified domain name.

So basically, if your repository is:

<?php

namespace MyApp\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepositoryInterface;

final class CatRepository implements ServiceEntityRepositoryInterface {
    // ...
}

or

<?php

namespace MyApp\Repository;

use Doctrine\Persistence\ObjectRepository;

final class CatRepository implements ObjectRepository {
    // ...
}

Then you'll be able to use the typed named services anywhere:

  • ServiceEntityRepositoryInterface $myAppRepositoryCatRepository
  • ObjectRepository $myAppRepositoryCatRepository

Installation

composer require loophp/service-entity-repository-bundle

There is no configuration to do, once installed, the bundle will alter the container directly.

Contributing

Feel free to contribute by sending Github pull requests. I'm quite responsive :-)

If you can't contribute to the code, you can also sponsor me on Github or Paypal.

Changelog

See CHANGELOG.md for a changelog based on git commits.

For more detailed changelogs, please check the release changelogs.