【问题标题】:TYPO3 8.7 Query Sorting with Flexform uid´sTYPO3 8.7 使用 Flexform uid 进行查询排序
【发布时间】:2018-03-10 08:06:25
【问题描述】:

我遇到了许多 TYPO3 扩展的问题,它们按 Uid 排序查询结果,这些结果来自后端中的 flexform 插件设置。我尝试创建一个查询,它以相同的顺序为我提供结果 uid,就像 flexform 来自插件设置一样。就像我选择 data.uid 5 7 和 3 一样,我的查询结果按这个顺序给我。

例如:

网站信息:

  • PHP 7.0
  • TYPO3 8.7
  • mariadb:10.1
  • Debian 服务器

这个函数是从控制器调用的。

$partners = $this->partnerRepository->findByUids($this->settings['showMainSponsor']);

$this->settings['showMainSponsor'] 中的值为 ="3, 4 ,1"。

这些是 TYPO3 插件设置中选定区域的 Uid。

存储库函数“findByUids”如下所示。

public function findByUids($uids){

    if(!isset($uids) || empty($uids)){
        return NULL;
    }

    $uidListString = $uids;
    if(!is_array($uids)){
        $uidListString = explode(',', $uids);
    }

    $query = $this->createQuery();
    $query->getQuerySettings()->setRespectStoragePage(FALSE);

    //here i set the orderings
    $orderings = $this->orderByField('uid', $uidListString);
    $query->setOrderings($orderings);

    $query->matching(
        $query->logicalAnd(
            $query->in('uid', $uidListString)
        )
    );



    return $query->execute();
}

这里调用了一个名为“orderByField”的函数来设置所有的排序。

/**
 * @param string $field
 * @param array $values
 *
 * @return array
 */
protected function orderByField($field, $values) {
    $orderings = array();
    foreach ($values as $value) {
        $orderings["$field={$value}"] =  \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING;
    }
    return $orderings;
}

这些根据来自 Flexform 的给定 uid 列表对查询结果进行排序的方法适用于 TYPO3 6.27.6。现在我尝试将此扩展附加到 TYPO3 8.6 项目,但此方法不再起作用。我试图调试它并在查询中查找。在那里我找到了打破这个查询的原因。不起作用的查询如下所示:

SELECT `tx_partner_domain_model_partner`.* FROM `tx_partner_domain_model_partner` `tx_partner_domain_model_partner` WHERE (`tx_partner_domain_model_partner`.`uid` IN (3, 4, 1)) AND (`tx_partner_domain_model_partner`.`sys_language_uid` IN (0, -1)) AND ((`tx_partner_domain_model_partner`.`deleted` = 0) AND (`tx_partner_domain_model_partner`.`t3ver_state` <= 0) AND (`tx_partner_domain_model_partner`.`pid` <> -1) AND (`tx_partner_domain_model_partner`.`hidden` = 0) AND (`tx_partner_domain_model_partner`.`starttime` <= 1506603780) AND ((`tx_partner_domain_model_partner`.`endtime` = 0) OR (`tx_partner_domain_model_partner`.`endtime` > 1506603780))) ORDER BY `tx_partner_domain_model_partner`.`uid=3` DESC, `tx_partner_domain_model_partner`.`uid=4` DESC, `tx_partner_domain_model_partner`.`uid=1` DESC

我在我的 DBMS 上尝试过,但失败了。原因是最后 3 个语句。

`tx_partner_domain_model_partner`.`uid=3` DESC, `tx_partner_domain_model_partner`.`uid=4` DESC, `tx_partner_domain_model_partner`.`uid=1` DESC

TYPO3 用 ``like 转义了 uid

 `tx_partner_domain_model_partner`.`uid=4` DESC

如果我们在没有这些 `` 的情况下像这样调用 uid=3 ..

`tx_partner_domain_model_partner`.uid=3 DESC, `tx_partner_domain_model_partner`.uid=4 DESC, `tx_brapartner_domain_model_partner`.uid=1 DESC

它工作正常。也许 TYPO3 在他的最新版本上这样做是出于安全原因,但我没有找到任何其他好的解决方案来解决这个基本案例。 目前我得到了一个 foreach,我通过 findByUid 自己查询每个 uid,但这在我看来并不是一种“最佳实践”方式。对于这种从数据库获取数据的情况,有人有更清洁的方法吗?或者这可能是一个错误?

我希望有人可以帮助我。

最好的问候

粉丝

【问题讨论】:

    标签: php doctrine typo3 extbase


    【解决方案1】:

    不覆盖:

    清除默认排序:

    $query->setOrderings(array());
    

    将您的 QueryBuilder 转换为新的 Doctrine QB:

    /** @var Typo3DbQueryParser $queryParser */
    $queryParser = $this->objectManager->get(Typo3DbQueryParser::class);
    /** @var QueryBuilder $doctrineQueryBuilder */
    $doctrineQueryBuilder = $queryParser->convertQueryToDoctrineQueryBuilder($query);
    

    通过concreteQb添加UID

    $concreteQb = $doctrineQueryBuilder->getConcreteQueryBuilder();
    foreach ($uidList as $uid) {
      $concreteQb->addOrderBy("$key={$uid}", QueryInterface::ORDER_DESCENDING);
    }
    

    获取映射结果:

    /** @var DataMapper $dataMapper */
    $dataMapper = $this->objectManager->get(DataMapper::class);
    return $dataMapper->map(YourDataClass::class, $$doctrineQueryBuilder->execute()->fetchAll());
    

    【讨论】:

    • 这是最好的答案,因为您可以使用通用查询对象“准备”查询(执行所有约束),并在存储库查询结束时将其传递给教义以进行排序东西。
    【解决方案2】:

    我认为问题出在\TYPO3\CMS\Core\Database\Query\QueryBuilder::orderBy 中,其中 fieldName 被引用。您可以覆盖此类并将 fieldName 拆分为 = 并构建一个带引号的字符串和 intval() 剩余的字符串,如:

    public function orderBy(string $fieldName, string $order = null): QueryBuilder
    {
        if (strpos($fieldName, '=') !== false) {
            list($field, $value) = GeneralUtility::trimExplode('=', $fieldName);
            $field = $this->connection->quoteIdentifier($field);
            $value = intval($value);
            $fieldName = $field . $value;
        }
        else {
            $fieldName = $this->connection->quoteIdentifier($fieldName);
        }
        $this->concreteQueryBuilder->orderBy($fieldName, $order);
    
        return $this;
    }
    

    【讨论】:

      【解决方案3】:

      控制器的替代方案:

      $ids = explode(',',$this->settings['showMainSponsor'])
      foreach($ids as $key => $id){
        $partners[$id] = $this->partnerRepository->findByUid($id);
      }
      

      这是有点脏的方式,因为这个逻辑在错误的地方,但它有效:)

      此外,它也不像您的解决方案那样高效。 您的解决方案在 7.6 of Cause 中运行良好。但我不知道为什么它会在第 8 版中中断。也许在 TYPO3 的这个订购系统中“修复了安全问题”。

      【讨论】:

      • 是的,没错,可能是因为我的方法是怎么来的问题,我们的徒弟才在这里录用的。
      【解决方案4】:
      public function findByUidList($uidList)
      {
          $uids = GeneralUtility::intExplode(',', $uidList, true);
          if ($uidList === '' || count($uids) === 0) {
              return [];
          }
      
          $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->getTableName());
          $queryBuilder->setRestrictions(GeneralUtility::makeInstance(FrontendRestrictionContainer::class));
      
          $records = $queryBuilder
              ->select('*')
              ->from($this->getTableName())
              ->where($queryBuilder->expr()->in('uid', $uids))
              ->add('orderBy', 'FIELD('.$this->getTableName().'.uid,' . implode(',', $uids) . ')')
              ->execute()
              ->fetchAll();
      
          $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
          $dataMapper = $objectManager->get(DataMapper::class);
          $result = $dataMapper->map($this->objectType, $records);
      
          return $result;
      }
      
      /**
       * Return the current table name
       *
       * @return string
       */
      protected function getTableName()
      {
          $objectManager = GeneralUtility::makeInstance(ObjectManager::class);
          $dataMapper = $objectManager->get(DataMapper::class);
          $tableName = $dataMapper->getDataMap($this->objectType)->getTableName();
          return $tableName;
      }
      

      这非常适用于 TYPO3 v10.4! :)

      【讨论】:

      • 在你的仓库顶部添加这个:使用 TYPO3\CMS\Core\Database\ConnectionPool;使用 TYPO3\CMS\Core\Database\Query\Restriction\FrontendRestrictionContainer;使用 TYPO3\CMS\Core\Utility\GeneralUtility;使用 TYPO3\CMS\Extbase\Object\ObjectManager;使用 TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;使用 TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
      • 另外,您能否进一步解释一下您做了哪些更改以及为什么这会解决问题?
      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2018-06-25
      • 1970-01-01
      • 2013-02-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多