frankdejonge / doctrine-query-specification
基于规范的Doctrine2查询
0.4.0
2024-07-25 15:19 UTC
Requires
- php: ^7.4|^8.0
- doctrine/orm: ^2|^3
Requires (Dev)
- doctrine/annotations: ^1.13
- doctrine/cache: ^1.1
- doctrine/data-fixtures: ^1.1
- phpunit/phpunit: ^9.5
README
此包简化了将领域问题翻译为Doctrine能理解的问题。
查询规范允许你在查询构建过程的三个阶段中进行钩子操作。
- 应用约束
- 修改查询构建器。
- 修改查询。
这允许你将查询逻辑封装在小型对象中。在命名这些对象时,你可以考虑它们所履行的功能。
安装
composer require frankdejonge/doctrine-query-specification
效果
将以下代码
// Get the newest 5 articles, needed for front-page $qb = $articleRepository->createQueryBuilder('a'); $query = $qb ->where($qb->expr()->eq('a.published', true)) ->getQuery() ->getResult();
转换为以下代码
$articles = $articleRepositoryFieldEquals->findBySpecification(new FrontPageArticles());
示例
规范感知的仓库。
use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareEntityRepository; use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareRepository; use FrankDeJonge\DoctrineQuerySpecification\SpecificationAwareRepositoryTrait; class ArticleRepository extends SpecificationAwareEntityRepository { } // OR class ArticleRepository implements SpecificationAwareRepository { use SpecificationAwareRepositoryTrait; }
查询约束。
<?php use Doctrine\ORM\QueryBuilder; use FrankDeJonge\DoctrineQuerySpecification\QueryConstraint; class IsPublished implements QueryConstraint { public function asQueryConstraint(QueryBuilder $builder, string $rootAlias): ?object { $expr = $builder->expr(); return $expr->eq("{$rootAlias}.published", true); } } $publishedArticles = $articleRepository->findBySpecification(new IsPublished);
查询约束也可以在构造函数中接受用户提供的输入。这样做时,请使用参数化查询来保护自己免受SQL注入攻击。
<?php use Doctrine\ORM\QueryBuilder; use FrankDeJonge\DoctrineQuerySpecification\QueryConstraint; class ArticleHasNameLike implements QueryConstraint { /** @var string */ private $name; public function __construct(string $name) { $this->name = $name; } public function asQueryConstraint(QueryBuilder $builder, string $rootAlias): ?object { $expr = $builder->expr(); $builder->setParameter('name_search', $this->name); return $expr->like("{$rootAlias}.name", ':name_search'); } } $publishedArticles = $articleRepository->findBySpecification(new ArticleHasNameLike('Awesome Name'));
查询修改器。
<?php use Doctrine\ORM\Query; use FrankDeJonge\DoctrineQuerySpecification\QueryModifier; class AsArray implements QueryModifier { public function modifyQuery(Query $query, string $rootAlias): void { $query->setHydrationMode(Query::HYDRATE_ARRAY); } } $publishedArticles = $articleRepository->findBySpecification(new AsArray);
QueryBuilder修改器。
<?php use Doctrine\ORM\QueryBuilder; use FrankDeJonge\DoctrineQuerySpecification\QueryBuilderModifier; class InReverseOrder implements QueryBuilderModifier { public function modifyQueryBuilder(QueryBuilder $builder, string $rootAlias): void { $builder->orderBy("{$rootAlias}.id", "DESC"); } } $publishedArticles = $articleRepository->findBySpecification(new InReverseOrder);
规范组合
有三种构建组合的方法。首先,有规范集合,允许你创建 andX
和 orX
组。
$andSpecification = SpecificationCollection::all( new IsPublished(), new InReversedOrder(), new WithAuthor(), ); $orSpecification = SpecificationCollection::any( new IsFeatured(), new IsPublishedToday(), );
第二种方法是通过创建新的规范对象,这些对象封装了一个或多个其他规范。
<?php use Doctrine\ORM\QueryBuilder; use FrankDeJonge\DoctrineQuerySpecification\QueryConstraint; class FeaturedFromAuthor implements QueryConstraint { public function __construct(Author $author) { $this->author = $author; } public function asQueryConstraint(QueryBuilder $queryBuilder, string $rootAlias): ?object { $expr = $queryBuilder->expr(); return $expr->andX( (new FromAuthor($this->author))->asQueryConstraint($queryBuilder, $rootAlias), (new FeaturedArticle)->asQueryConstraint($queryBuilder, $rootAlias), ); } }
最后,你可以扩展一个通用集合
<?php use FrankDeJonge\DoctrineQuerySpecification\SpecificationCollection\All; class FeaturedFromAuthor extends All { public function __construct(Author $author) { parent::__construct( new FromAuthor($author), new FeaturedArticle(), ); } }