在多对多关系中应用条令sql过滤器


Applying doctrine sql filter in many to many relationships

我的项目中有很多对很多关系(user_role、grades、user_role_grades)。但我也有一个要求,不要从我的数据库中删除任何数据。因此,我在表中添加了一个状态列,连接两个表以创建多对多关系。现在我想要

 $userRole->getGrades() 

只获取联合表(user_role_grades)中没有状态"0"的记录。对于这些,我尝试使用条令sql过滤器。

namespace Bis'MpBundle'Filter;
use 'Doctrine'ORM'Mapping'ClassMetaData;
class UserRoleGradeFilter extends 'Doctrine'ORM'Query'Filter'SQLFilter
{
    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
    {
        if("Bis'DefaultBundle'Entity'UserRoleGrade" == $targetEntity->name){
            return $targetTableAlias . '.status != 0';
        }
        return '';
    }
}

因此,它被调用,用于Bis''DefaultBundle''Entity''UserRole,但不用于Bis''DfaultBundle''Entity''UserRoleGrade实体。有人有什么想法吗?

或者你可能有其他想法,我该怎么做?

我认为这是不可能的,因为它直接附加SQL。即使你尝试SQL注入:

return $targetTableAlias . '.status != 0)) LEFT join the_other_table ON  '
. $targetTableAlias . '.grades HAVING the_other_table.status = 0 ((';

它可能会在类似(()) 的语句中崩溃

您可以调用$targetEntity->getAssociationMappings()['yourFieldName']如果存在joinTable密钥,则表示您具有manyToMany关系。其中yourFieldName是您的字段与ManyToMany的关系。

可以从Doctrine'ORM'Query'SqlWalker::getSQLTableAlias()获得正确的表别名。我用debug_backtrace得到了SqlWalker,这不是一个优雅的解决方案,但我还没有找到更好的解决方案。

/**
 * Get SqlWalker with debug_backtrace
 *
 * @return null|SqlWalker
 */
protected function getSqlWalker()
{
    $caller = debug_backtrace();
    $caller = $caller[2];
    if (isset($caller['object'])) {
        return $caller['object'];
    }
    return null;
}

完全实现我的addFilterConstraint方法

    public function addFilterConstraint(ClassMetadata $targetEntity, $targetTableAlias)
{
    if (empty($this->reader)) {
        return '';
    }
    // The Doctrine filter is called for any query on any entity
    // Check if the current entity is "pool aware" (marked with an annotation)
    $poolAware = $this->reader->getClassAnnotation(
        $targetEntity->getReflectionClass(),
        PoolAware::class
    );
    if (!$poolAware) {
        return '';
    }
    if (!$poolId = $this->getParameter('poolId')) {
        return '';
    }
    $fieldName = $poolAware->getFieldName();
    if (empty($fieldName)) {
        return '';
    }
    if (!$sqlWalker = $this->getSqlWalker()) {
        return '';
    }
    if (!isset($targetEntity->getAssociationMappings()[$fieldName])) {
        return '';
    }
    $mapping = $targetEntity->getAssociationMappings()[$fieldName];
    if (isset($mapping['joinColumns'])) {
        // oneToMany relation detected
        $table = $targetEntity->getTableName();
        $columnName = $mapping['joinColumns'][0]['name'];
        $dqlAlias = constant($targetEntity->getName() . '::MNEMO');
    } elseif (isset($mapping['joinTable'])) {
        // manyToMany relation detected
        $dqlAlias = constant($mapping['targetEntity'] . '::MNEMO');
        $component = $sqlWalker->getQueryComponent($dqlAlias);
        // Only main entity in query is interesting for us,
        // otherwise do not apply any filter
        if ($component['parent']) {
            return '';
        }
        $table = $mapping['joinTable']['name'];
        $columnName = $mapping['joinTable']['inverseJoinColumns'][0]['name'];
    } else {
        return '';
    }
    $tableAlias = ($sqlWalker instanceof BasicEntityPersister)
        ? $targetTableAlias // $repository->findBy() has been called
        : $sqlWalker->getSQLTableAlias($table, $dqlAlias);
    $query = sprintf('%s.%s = %s', $tableAlias, $columnName, $this->getConnection()->quote(poolId));
    return $query;
}

我们所有的条令模型都有MNEMO常数,这是一个模型的简单名称。CCD_ 11具有MNEMO CCD_。这个MNEMO相当于Repository类中的_alias。这就是我们将此值应用于$dqlAlias 的原因

完整的代码说明,您可以在这里阅读