TYPO3 9.5 存储库查询以获取具有多个 sys_categories 的元素

TYPO3 9.5 repository query to fetch elements with multiple sys_categories

我正在尝试弄清楚如何编写以下查询来获取一些具有多个类别的元素。

$query->matching(
    $query->logicalAnd(
        [
            // the following 4 lines are the problem lines
            $query->logicalAnd(
                $query->in('categories.uid', $categories),
                $query->in('categories.uid', $countryCategories)
            ),
            // $query->in('categories.uid', $categories),
            // $query->in('categories.uid', $countryCategories),

            $query->logicalOr(
                [
                    $query->equals('is_pinned', 0),
                    $query->lessThan('pinned_until', time())
                ]
            ),
        ]
    )
);

想法是获取 categories.uid$categories 中的至少一个 uid 匹配且至少与 $countryCategories 中的一个匹配的元素。 $categories$countryCategories 都是用类别 uid 填充的数组。

在插入第二行 $query->in('categories.uid' [...] 之前查询工作正常。插入第二行后,查询结果为空。这可能是查询中的一个错误,但我和我的同事都找不到可行的解决方案。

在搜索时,我找到了 sql UNION,我以前从未使用过它,但我猜如果我必须编写语句而不是构建,这将是可行的方法查询。

我想知道的是是否可以用"query builder"获取元素或者是否真的需要写一个语句?如果查询生成器有解决方案,您能为我指出吗?如果不是,我将如何使用 UNION 构建查询以根据需要获取元素?

如果有什么不清楚的地方,请不要犹豫,我会尽量详细说明。谢谢。


编辑

我们也调试了查询,我直接在phpmyadmin中执行了它。它在没有“AND (sys_category.uid IN ( 41, 2 ))”的情况下工作,但结果是空的。以下是调试查询:

SELECT `tx_gijakobnews_domain_model_news`.* 
 FROM `tx_gijakobnews_domain_model_news` `tx_gijakobnews_domain_model_news` 
    LEFT JOIN `sys_category_record_mm` `sys_category_record_mm` ON ( `tx_gijakobnews_domain_model_news`.`uid` = `sys_category_record_mm`.`uid_foreign`)  AND (( `sys_category_record_mm`.`tablenames` = 'tx_gijakobnews_domain_model_news') AND ( `sys_category_record_mm`.`fieldname` = 'categories'))  
    LEFT JOIN `sys_category` `sys_category` ON `sys_category_record_mm`.`uid_local` = `sys_category`.`uid` 

    WHERE ((
            (`sys_category`.`uid` IN ( 15, 17, 10, 11, 12, 16, 13, 14 ))
            ////// this following line is where the problem begins
            AND (`sys_category`.`uid` IN ( 41, 2 ))
    )
/////////// the following lines are additional restrictions
/////////// which have no influence on the problem
        AND ((`tx_gijakobnews_domain_model_news`.`is_pinned` = 0) OR ( `tx_gijakobnews_domain_model_news`.`pinned_until` < 1560867383))
    )
 AND ( `tx_gijakobnews_domain_model_news`.`sys_language_uid` IN ( 0, -1) )  
 AND ( `tx_gijakobnews_domain_model_news`.`pid` = 31)  
 AND ( ( `tx_gijakobnews_domain_model_news`.`deleted` = 0)  
 AND ( `tx_gijakobnews_domain_model_news`.`t3ver_state` <= 0)  
 AND ( `tx_gijakobnews_domain_model_news`.`pid` <> -1)  
 AND ( `tx_gijakobnews_domain_model_news`.`hidden` = 0)  
 AND ( `tx_gijakobnews_domain_model_news`.`starttime` <= 1560867360)  
 AND ( ( `tx_gijakobnews_domain_model_news`.`endtime` = 0)  
 OR ( `tx_gijakobnews_domain_model_news`.`endtime` > 1560867360) ) )  
 AND ( ( ( `sys_category`.`deleted` = 0)  
 AND ( `sys_category`.`t3ver_state` <= 0)  
 AND ( `sys_category`.`pid` <> -1)  
 AND ( `sys_category`.`hidden` = 0)  
 AND ( `sys_category`.`starttime` <= 1560867360)  
 AND ( ( `sys_category`.`endtime` = 0)  
 OR ( `sys_category`.`endtime` > 1560867360) ) )  
 OR ( `sys_category`.`uid` 
 IS NULL) )  
 ORDER BY `tx_gijakobnews_domain_model_news`.`publish_date` DESC

如果缺少括号,我可能在格式化时不小心删除了它...

您可以使用 QueryBuilder 构建您自己的自定义查询。像这样:

use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Utility\DebuggerUtility;


$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
    ->getQueryBuilderForTable('table_to_select_from');

$result = $queryBuilder->select('*')
        ->from('table_to_select_from')
        ->where($queryBuilder->expr()->in('field', ['1','2','3'])
        ->execute()
        ->fetchAll();

DebuggerUtility::var_dump($result);

这是文档:
https://docs.typo3.org/m/typo3/reference-coreapi/master/en-us/ApiOverview/Database/QueryBuilder/Index.html

我认为问题在于 where 子句是在 "per row" 基础上应用的。

含义如果您有如下查询(基于您的查询):

SELECT *
FROM news
LEFT JOIN sys_category_record_mm mm
  ON (news.uid = mm.uid_foreign) /* AND (...) */
LEFT JOIN sys_category
  ON mm.uid_local = sys_category.uid
WHERE
  sys_category.uid IN (1,2,3)
  AND sys_category.uid IN (4,5,6)

您可能有一个新闻条目,即在类别 1 和类别 4 中。但结果集将是两个不同的行:

news.uid | sys_category.uid
1        | 1
1        | 4

并且 WHERE 子句将它们都过滤掉,因为 sys_category.uid 不是 both in (1, 2, 3) and in (4, 5, 6) 分别为每一行。

在 SQL 级别上执行此操作的方法可能是对 sys_category 进行两次连接。但我不相信(相当简单的)extbase 查询构建器是可能的。

编辑:

作为解决方案,您可以使用 $query->statement() 方法,该方法允许使用自定义 sql 查询。

$result = $query->statement('SELECT news.* FROM news');

https://docs.typo3.org/m/typo3/book-extbasefluid/master/en-us/6-Persistence/3-implement-individual-database-queries.html

最后我做得更简单了。

我没有通过查询添加两个限制,而是遍历受第一个 sys_category 条件限制的结果,然后删除不满足第二个 sys_category 限制的结果。

存储库

    $query->matching(
        $query->logicalAnd([

            $query->in('categories.uid', $categories),
            $query->logicalOr(
                [
                    $query->equals('is_pinned', 0),
                    $query->lessThan('pinned_until', time())
                ]
            ),
        ])
    );

控制器

public function getRestrictedNews($news, $countryCategories) {
    $newNews = array();

    foreach ($news as $newsItem) {
        $newsCategories = $newsItem->getCategories();
        $shouldKeep = false;
        foreach ($newsCategories as $categoryItem) {
            if (in_array($categoryItem->getUid(), $countryCategories)) {
                $shouldKeep = true;
            }
        }

        if ($shouldKeep) {
            array_push($newNews, $newsItem);
        }
    }

    return $newNews;
}

这可能不是最好的解决方案,但它是一个有效的解决方案。 :-)