avdb/doctrine-extra

Doctrine 的扩展

该包的规范仓库似乎已不存在,因此该包已被冻结。

dev-master / 1.0.x-dev 2017-02-24 10:52 UTC

This package is not auto-updated.

Last update: 2024-03-30 17:37:22 UTC


README

构建状态 Packagist #Doctrine 扩展

1. 示例

在此包中包含了一个 BaseManager,它使用 Assert 和 Resolvable 特性。如果我们使用此 Manager,可以执行以下代码示例;

分页选择所有新订单和处理订单

$paginator = $manager->paginate()->filter(
    new AggregateFilter([new StatusFilter('new'),new StatusFilter('processing')]
));

作为迭代器选择所有 'new' 和 'processing' 订单

$iterator = $manager->iterate()->filter(
    new AggregateFilter([new StatusFilter('new'),new StatusFilter('finished')]
));

计算所有 'new' 和 'processing' 订单的数量

$count = $manager->count()->filter(
    new AggregateFilter([new StatusFilter('new'),new StatusFilter('finished')]
));

向现有查询添加一个额外参数

$queryBuilder = $this->repository->builder()->filter(
    new AggregateFilter([new StatusFilter('new'),new StatusFilter('finished')]
));

$queryBuilder->andWhere('category = :category');
$queryBuilder->setParameter('category', 'books');

$orders = $queryBuilder->getResult();

更多示例请参阅文档。

2. 过滤器

创建 Filter 对象以轻松过滤您的存储库。此包包含两个默认 Filter 对象以供使用;'AggregateFilter' 和 'PropertyFilter'。两者都是多功能的,但建议扩展 AbstractFilter 并创建自己的。

过滤本身可以通过任何使用 Resolver 特性的类来完成,并使用 Doctrine QueryBuilder 对象作为起点。

让我们看看以下示例;

class OrderRepository extends \Doctrine\ORM\EntityRepository
{
    /** Resolves filters **/
    use Resolver;

    public function filter($filters)
    {
        $builder = $this->createQueryBuilder('root');

        return $this->resolve($filters, $builder)->getQuery()->getResult();
    }
}

我们有一个 OrderRepository,它实现了 Resolver 特性,并有一个名为 "filter" 的方法,该方法接受一个 Doctrine Filters 数组或一个 DoctrineFilter。

使用 PropertyFilter

属性过滤器可以用于对任何实体的属性进行过滤。让我们想象一个订单有一个 "category" 属性,我们想要过滤。

/**
 * @var OrderRepository
 */
private $repository;

public function categoryAction($category)
{
    $orders = $this->repository->filter(new PropertyFilter($category, 'category'));
}

到目前为止,我们已经过滤了某个类别的订单。我们还可以将多个过滤器作为数组传递给 filter() 方法作为参数,它们将组合成一个 andX 表达式。

使用 AggregateFilter

如果我们想在 orX 表达式中使用多个过滤器,我们可以使用 AggregateFilter。在以下示例中,我们将过滤所有 'new' 或 'finished' 订单;

$orders = $this->repository->filter(
    new AggregateFilter([new StatusFilter('new'),new StatusFilter('finished')]
));

我们可以组合多个 AggregateFilters 来创建更复杂的表达式。在以下示例中,我们将过滤所有 'new' 或 'finished' 订单,它们具有 'multimedia' 或 'books' 类别。

$orders = $this->repository->filter(
    new AggregateFilter([new StatusFilter('new'), new StatusFilter('finished')]),
    new AggregateFilter([new CategoryFilter('multimedia'), new CategoryFilter('books')])
);

创建自定义 DoctrineFilter

简单的属性过滤器

如果我们想实现 CategoryFilter,并且有更多实体具有类别属性并且经常使用它,我们可以这样做;

如果我们想创建自己的 CategoryFilter,它是通过过滤单个直接属性进行过滤的最基本形式的过滤,我们可以这样做;

class CategoryFilter extends AbstractFilter
{
    public function createExpression($root)
    {
        return $this->expr()->eq(
            sprintf('%s.category', $root),
            $this->expr()->literal($this->parameter)
        );
    }
}

我们只需要实现 CreateExpression() 方法,并创建一个简单的 expression,它将在 QueryBuilder 中的 Where 子句中使用。现在这个过滤器可以用于具有 "category" 属性的所有实体。

有时我们需要一个更复杂的过滤器。假设我们的 Order 实体与 User 存在关系,User 有一个用户名。我们想要根据用户的用户名过滤所有订单。我们可以创建以下过滤器;

更高级的示例

class UserEmailFilter extends AbstractFilter
{
    public function createExpression($root)
    {
        return $this->expr()->eq('user.email', $this->expr()->literal($this->parameter));
    }

    public function getAlias()
    {
        return 'user';
    }

    public function addAlias(QueryBuilder $builder, $root)
    {
       $builder->leftJoin(sprintf('%s.user', $root), 'user');
    }
}

此过滤器稍微复杂一些。因为我们不是直接过滤订单的属性,而是过滤与我们的订单对象有关联的用户属性,因此我们需要实现两个额外的方法。

函数 getAlias() 将返回一个字符串,别名定义了具有我们正在过滤的属性的对象的别名。在这种情况下,它是 'user',因为我们将在user.email上过滤

AddAlias() 方法将在解析器检查QueryBuilder中是否存在 'user' 别名时被触发,别名存在当它是 OR 选择或连接时。在这种情况下,我们将 将用户对象左连接到我们的 'root' 对象 上,在这个例子中是 'Order'。

现在我们已经完成了UserEmailFilter,我们可以在任何具有用户关系的对象上使用此过滤器。例如,我们可以有一个预订实体和票据实体,它们都与用户有关联。

因此,到目前为止,我们可以执行以下操作;

$reservations = $this->reservationRepository->filter(new UserEmailFilter('example@doctrine.com'));
$orders = $this->orderRepository->filter(new UserEmailFilter('example@doctrine.com'));
$tickets = $this->ticketRepository->filter(new UserEmailFilter('example@doctrine.com'));

##3. 断言结果 此包包括 Assertable 特性,它允许任何类传递一个 QueryBuilder 对象并从该对象检索不同的结果。

例如,我们有一个使用 Assertable 特性的存储库,我们使用以下代码;

class OrderRepository extends EntityRepository
{
    use Assertable;

    public function getOrdersByCategory($category)
    {
        $builder = $this->createQueryBuilder('root');
        $builder->andWhere('root.category = :category');
        $builder->setParameter('category', $category);

        return $this->assertResult($builder);
    }
}

因为我们使用了 assertable 特性,并通过 assertResult() 方法传递了我们的 QueryBuilder 对象,我们可以操作这个函数将返回什么类型的结果。默认情况下,此函数将按预期执行并返回一个包含特定类别的所有订单实体的数组。

  $orders = $this->repository->getOrdersByCategory('some_category');

如果我们想检索分页的订单,我们可以在调用 getOrdersByCategory 之前简单地调用 builder() 方法,Assert 特性将断言传递的结果将是一个 Paginator 对象;

 $paginator = $this->repository->builder()->getOrdersByCategory('some_category');

要查看 assertResult() 方法的所有可能结果,请参阅以下代码;

protected function assertResult(QueryBuilder $builder, $hydration = Query::HYDRATE_OBJECT)
{
    switch($this->assert) {
        case Result::ARRAY:
            return $builder->getQuery()->getResult($hydration);
        case Result::PAGINATE:
            return new Paginator($builder);
        case Result::SINGLE:
            return $builder->getQuery()->getOneOrNullResult($hydration);
        case Result::FIRST:
            $result = $builder->setMaxResults(1)->getQuery()->getResult();
            return count($result) > 0 ? $result[0] : null;
        case Result::ITERATE:
            return $builder->getQuery()->iterate();
        case Result::COUNT:
            $paginator = new Paginator($builder);
            return $paginator->count();
        case Result::QUERY:
            return $builder->getQuery();
        case Result::BUILDER:
            return $builder;
        default:
            throw new AssertResultException(sprintf('Unknown result assertion "%s"', $this->assert));
    }
}

上述代码位于 Assert 特性中。