我刚刚开始使用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();
}
因此,即使对于这个简单的需求,我也已经编写了两个自定义方法。如果我继续进行我的项目,可能会出现其他限制,并且可能会出现加载该实体的其他方法。在我看来,对于每种情况,我都必须实现所有访问保护的逻辑。
没有比这更简单的方法了吗?我正在考虑一个类似于注释或"实体加载侦听器"的东西,它可以简单地为所有此类检查编写一个入口点,从而不可能忘记此类检查。。。
此类限制通常通过使用Doctrine中的SQL过滤器机制来实现。该过滤器的实现在比DQL更低的级别上工作,并允许您对正在构建的SQL查询应用修改。在你的情况下,它可能看起来像这样:
namespace AppORMFilter;
use AppEntityPost;
use DoctrineORMMappingClassMetadata;
use DoctrineORMQueryFilterSQLFilter;
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'));
}
}