Решения, которые я мог видеть здесь до сих пор, неплохие. Я посмотрел на это под другим углом. Таким образом, мое решение позволяет вам поддерживать чистоту репозиториев, как бы обеспечивает согласованную структуру проекта, и вы можете продолжать автомонтаж!
Вот как я решил бы это в Symfony 5.
ЦЕЛЬ
Мы хотим, чтобы репозитории были автосвязанными, и мы хотим, чтобы они были как можно более чистыми. Мы также хотим, чтобы они были очень просты в использовании.
ПРОБЛЕМА
Нам нужно найти способ сообщить репозиторию о сущности, которую он должен использовать.
РЕШЕНИЕ
Решение простое и состоит из нескольких вещей:
- У нас есть собственный класс репозитория, который расширяет класс
Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository
.
- Наш пользовательский класс имеет свойство
public string $entity
.
Когда мы создаем наш новый репозиторий и расширяем наш пользовательский класс репозитория, у нас есть два варианта: в нашем новом репозитории мы можем просто указать на класс, подобный этому
namespace App\Database\Repository\Post;
use App\Database\Repository\Repository;
use App\Entity\Blog\Post;
/**
* Class PostRepository
* @package App\Database\Repository
*/
class PostRepository extends Repository
{
public string $entity = Post::class;
public function test()
{
dd(99999, $this->getEntityName());
}
}
or we could omit that property and let our new base Repository class find it automatically! (More about that later.)
КОД
Итак, давайте начнем с кода, а затем я объясню его:
<?php
namespace App\Database\Repository;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use Laminas\Code\Reflection\ClassReflection;
use Symfony\Component\Finder\Finder;
/**
* Class Repository
* @package App\Database\Repository
*/
abstract class Repository extends ServiceEntityRepository
{
/** @var string */
private const REPOSITORY_FILE = 'repository';
/** @var string */
public string $entity = '';
/** @var string */
public string $defaultEntitiesLocation;
/** @var string */
public string $defaultEntitiesNamespace;
/**
* Repository constructor.
*
* @param ManagerRegistry $registry
* @param $defaultEntitiesLocation
* @param $defaultEntitiesNamespace
* @throws \Exception
*/
public function __construct(
ManagerRegistry $registry,
$defaultEntitiesLocation,
$defaultEntitiesNamespace
) {
$this->defaultEntitiesLocation = $defaultEntitiesLocation;
$this->defaultEntitiesNamespace = $defaultEntitiesNamespace;
$this->findEntities();
parent::__construct($registry, $this->entity);
}
/**
* Find entities.
*
* @return bool
* @throws \ReflectionException
*/
public function findEntities()
{
if (class_exists($this->entity)) {
return true;
}
$repositoryReflection = (new ClassReflection($this));
$repositoryName = strtolower(preg_replace('/Repository/', '', $repositoryReflection->getShortName()));
$finder = new Finder();
if ($finder->files()->in($this->defaultEntitiesLocation)->hasResults()) {
foreach ($finder as $file) {
if (strtolower($file->getFilenameWithoutExtension()) === $repositoryName) {
if (!empty($this->entity)) {
throw new \Exception('Entity can\'t be matched automatically. It looks like there is' .
' more than one ' . $file->getFilenameWithoutExtension() . ' entity. Please use $entity
property on your repository to provide entity you want to use.');
}
$namespacePart = preg_replace(
'#' . $this->defaultEntitiesLocation . '#',
'',
$file->getPath() . '/' . $file->getFilenameWithoutExtension()
);
$this->entity = $this->defaultEntitiesNamespace . preg_replace('#/#', '\\', $namespacePart);
}
}
}
}
}
Итак, что здесь происходит? Я привязал некоторые значения к контейнеру в services.yml
:
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
bind:
$defaultEntitiesLocation: '%kernel.project_dir%/src/Entity'
$defaultEntitiesNamespace: 'App\Entity'
Затем в нашем новом классе расширения я знаю, где по умолчанию искать мои объекты (это обеспечивает некоторую согласованность).
ОЧЕНЬ ВАЖНАЯ ИНФОРМАЦИЯ. Я предполагаю, что мы будем называть репозитории и сущности точно так же, например: Post
будет нашей сущностью, а PostRepository
— нашим репозиторием. Только обратите внимание, что слово Repository
не обязательно. Если он есть, он будет удален.
Некоторая умная логика создаст для вас пространства имен — я предполагаю, что вы будете следовать некоторым хорошим практикам, и все это будет согласовано.
Это сделано! Чтобы ваш репозиторий был автоматически связан, все, что вам нужно сделать, это расширить свой новый базовый класс репозитория и назвать Entity так же, как репозиторий. поэтому конечный результат выглядит так:
<?php
namespace App\Database\Repository\Post;
use App\Database\Repository\Repository;
use App\Entity\Blog\Post;
/**
* Class PostRepository
* @package App\Database\Repository
*/
class PostRepository extends Repository
{
public function test()
{
dd(99999, $this->getEntityName());
}
}
Это ЧИСТЫЙ, АВТОМАТИЧЕСКИЙ, ОЧЕНЬ ЛЕГКО И БЫСТРО СОЗДАЕТСЯ!
12.04.2020