Единая позиция для ограничения доступа к сущности Doctrine - PullRequest
0 голосов
/ 09 января 2019

Я только начал работать с Doctrine и создал простой проект блога. Одно из моих требований заключается в том, что пост в блоге не должен быть виден никому (для простоты пропустите интерфейс редактора), пока не будет достигнута дата публикации.

Насколько я вижу, это очевидно сделать с помощью собственного репозитория. Давайте расширим метод find следующим образом:

public function find($id, $lockMode = null, $lockVersion = null)
{
    /** @var Post $post */
    $post = parent::find($id, $lockMode, $lockVersion);
    if($post->getCreatedAt() > new \DateTime()) {
        return null;
    }

    return $post;
}

Это ограничивает доступ к странице, показывающей одну Post сущность. Для страницы обзора то же самое можно сделать с помощью пользовательского метода:

public function findForOverview()
{
    $query = $this->createQueryBuilder('p')
        ->where('p.createdAt < CURRENT_TIMESTAMP()')
        ->orderBy('p.createdAt', 'DESC')
        ->getQuery();

    return $query->getResult();
}

Итак, даже для этого простого требования я уже написал два пользовательских метода. Если я продолжу работать над своим проектом, могут возникнуть другие ограничения, и могут возникнуть дополнительные способы загрузки этого объекта. И, насколько я вижу, для каждого случая я должен реализовать логику для всех охранников доступа.

Нет ли более простого способа сделать это? Я имею в виду что-то вроде аннотации или «слушателя загрузки сущностей», который упрощает написание одной точки входа для всех таких проверок - делая невозможным забыть такие проверки ...

1 Ответ

0 голосов
/ 09 января 2019

Такие ограничения обычно реализуются с помощью механизма фильтров SQL в Doctrine. Реализация этого фильтра работает на более низком уровне, чем DQL, и позволяет применять изменения для создаваемого SQL-запроса. В вашем случае это может выглядеть так:

namespace App\ORM\Filter;

use App\Entity\Post;
use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query\Filter\SQLFilter;

class PostVisibilityFilter extends SQLFilter
{
    /**
     * Gets the SQL query part to add to a query.
     *
     * @param ClassMetadata $targetEntity
     * @param string $targetTableAlias
     * @return string The constraint SQL if there is available, empty string otherwise
     */
    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias): string
    {
        if ($targetEntity->name !== Post::class) {
            return '';
        }
        return sprintf('%s.%s >= now()', $targetTableAlias, $targetEntity->getColumnName('createdAt'));
    }
}
...