frankdejonge / doctrine-query-specification

0.4.0 2024-07-25 15:19 UTC

This package is auto-updated.

Last update: 2024-08-25 15:33:13 UTC


README

.github/workflows/main.yaml

此包简化了将领域问题翻译为Doctrine能理解的问题。

查询规范允许你在查询构建过程的三个阶段中进行钩子操作。

  1. 应用约束
  2. 修改查询构建器。
  3. 修改查询。

这允许你将查询逻辑封装在小型对象中。在命名这些对象时,你可以考虑它们所履行的功能。

安装

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);

规范组合

有三种构建组合的方法。首先,有规范集合,允许你创建 andXorX 组。

$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(),
        );
    }
}