到目前为止,我在这里看到的解决方案还不错。我从另一个角度看它。所以我的解决方案允许你保持干净的存储库,排序强制一致的项目结构,你可以继续自动装配!
这就是我在 Symfony 5 中解决它的方法。
目标
我们希望拥有自动装配的存储库,并且希望它们尽可能保持干净。我们还希望它们超级好用。
问题
我们需要想办法告诉 Repository 它应该使用的实体。
解决方案
解决方案很简单,包括几件事:
- 我们有自定义 Repository 类,它扩展了
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());
}
}
或者我们可以省略该属性,让我们的新基础 Repository 类自动找到它! (稍后会详细介绍。)
代码
那么让我们从代码开始,然后我会解释它:
<?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());
}
}
它干净、自动连线、超级容易和快速创建!