【问题标题】:Applying doctrine sql filter in many to many relationships在多对多关系中应用学说 sql 过滤器
【发布时间】:2012-09-04 11:02:51
【问题描述】:

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

 $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\DefaultBundle\Entity\UserRoleGrade 实体。有人有什么想法吗?

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

【问题讨论】:

  • 您找到解决方案了吗?我正在尝试做类似的事情,但没有在解决方案上取得任何进展......
  • 我尝试寻找解决方案,但没有找到。在我的过滤器的最后,我只是添加了一个子查询(在术语或性能方面可能是个坏主意,但我得到了想要的结果)。

标签: php doctrine-orm


【解决方案1】:

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

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

它可能会在(())这样的声明中崩溃

【讨论】:

    【解决方案2】:

    您可以拨打$targetEntity->getAssociationMappings()['yourFieldName'] 如果存在joinTable 键,则意味着您有manyToMany 关系。其中yourFieldName 你的字段与ManyToMany 关系。

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

    /**
     * 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;
    }
    

    我们所有的 Doctrine 模型都有 MNEMO 常量,它是模型的简单名称。 Abc\Module\Model\Product 有 MNEMO product。这个 MNEMO 相当于 Repository 类中的_alias。这就是为什么我们将此值应用于$dqlAlias

    完整的代码解释你可以阅读here

    【讨论】:

      猜你喜欢
      • 2012-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多