From 68c3e105eb2619258cc867f81e3fb8ae3575d2cf Mon Sep 17 00:00:00 2001 From: alikon Date: Sun, 19 Oct 2025 10:04:49 +0200 Subject: [PATCH 1/3] logic-and --- .../com_content/src/Model/ArticlesModel.php | 26 +++++++++++++++++-- .../src/Controller/ArticlesController.php | 3 +++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/administrator/components/com_content/src/Model/ArticlesModel.php b/administrator/components/com_content/src/Model/ArticlesModel.php index aa5ba437569ee..d9a8da8830761 100644 --- a/administrator/components/com_content/src/Model/ArticlesModel.php +++ b/administrator/components/com_content/src/Model/ArticlesModel.php @@ -147,6 +147,8 @@ protected function populateState($ordering = 'a.id', $direction = 'desc') $this->getUserStateFromRequest($this->context . '.filter.access', 'filter_access'); $this->getUserStateFromRequest($this->context . '.filter.language', 'filter_language', ''); $this->getUserStateFromRequest($this->context . '.filter.checked_out', 'filter_checked_out', ''); + $this->getUserStateFromRequest($this->context . '.filter.tag_mode', 'filter_tag_mode', 'any'); + // List state information. parent::populateState($ordering, $direction); @@ -527,7 +529,26 @@ protected function getListQuery() $tag = array_filter($tag); $includeNone = true; } - + $tagMode = $this->getState('filter.tag_mode', 'any'); + if ($tagMode === 'all') { + // AND logic: article must have ALL tags + foreach ($tag as $tagId) { + $subQuery = $db->createQuery() + ->select('DISTINCT ' . $db->quoteName('content_item_id')) + ->from($db->quoteName('#__contentitem_tag_map')) + ->where([ + $db->quoteName('tag_id') . ' = ' . $db->quote($tagId), + $db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'), + ]); + + $query->join( + 'INNER', + '(' . $subQuery . ') AS ' . $db->quoteName('tagmap_' . $tagId), + $db->quoteName('tagmap_' . $tagId . '.content_item_id') . ' = ' . $db->quoteName('a.id') + ); + } + } else { + // OR logic: $subQuery = $db->createQuery() ->select('DISTINCT ' . $db->quoteName('content_item_id')) ->from($db->quoteName('#__contentitem_tag_map')) @@ -559,7 +580,8 @@ protected function getListQuery() . $db->quoteName('tagmap2.content_item_id') . ' IS NULL)' ); } - } elseif (is_numeric($tag)) { + } + } elseif (is_numeric($tag)) { $tag = (int) $tag; if ($tag === 0) { diff --git a/api/components/com_content/src/Controller/ArticlesController.php b/api/components/com_content/src/Controller/ArticlesController.php index 83565ffe649d6..f7f5214c256d4 100644 --- a/api/components/com_content/src/Controller/ArticlesController.php +++ b/api/components/com_content/src/Controller/ArticlesController.php @@ -76,6 +76,9 @@ public function displayList() if (\array_key_exists('tag', $apiFilterInfo)) { $this->modelState->set('filter.tag', $filter->clean($apiFilterInfo['tag'], 'INT')); + if (\array_key_exists('tag_mode', $apiFilterInfo)) { + $this->modelState->set('filter.tag_mode', $filter->clean($apiFilterInfo['tag_mode'], 'STRING')); + } } if (\array_key_exists('language', $apiFilterInfo)) { From c17cac12c21ef5e9c569bb10e3645f60dbc06c13 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 19 Oct 2025 08:23:36 +0000 Subject: [PATCH 2/3] cs --- .../com_content/src/Model/ArticlesModel.php | 183 ++++++++++-------- 1 file changed, 104 insertions(+), 79 deletions(-) diff --git a/administrator/components/com_content/src/Model/ArticlesModel.php b/administrator/components/com_content/src/Model/ArticlesModel.php index d9a8da8830761..71e8edbfd5bc9 100644 --- a/administrator/components/com_content/src/Model/ArticlesModel.php +++ b/administrator/components/com_content/src/Model/ArticlesModel.php @@ -48,33 +48,58 @@ public function __construct($config = [], ?MVCFactoryInterface $factory = null) { if (empty($config['filter_fields'])) { $config['filter_fields'] = [ - 'id', 'a.id', - 'title', 'a.title', - 'alias', 'a.alias', - 'checked_out', 'a.checked_out', - 'checked_out_time', 'a.checked_out_time', - 'catid', 'a.catid', 'category_title', - 'state', 'a.state', - 'access', 'a.access', 'access_level', - 'created', 'a.created', - 'modified', 'a.modified', - 'created_by', 'a.created_by', - 'created_by_alias', 'a.created_by_alias', - 'ordering', 'a.ordering', - 'featured', 'a.featured', - 'featured_up', 'fp.featured_up', - 'featured_down', 'fp.featured_down', - 'language', 'a.language', - 'hits', 'a.hits', - 'publish_up', 'a.publish_up', - 'publish_down', 'a.publish_down', - 'published', 'a.published', + 'id', + 'a.id', + 'title', + 'a.title', + 'alias', + 'a.alias', + 'checked_out', + 'a.checked_out', + 'checked_out_time', + 'a.checked_out_time', + 'catid', + 'a.catid', + 'category_title', + 'state', + 'a.state', + 'access', + 'a.access', + 'access_level', + 'created', + 'a.created', + 'modified', + 'a.modified', + 'created_by', + 'a.created_by', + 'created_by_alias', + 'a.created_by_alias', + 'ordering', + 'a.ordering', + 'featured', + 'a.featured', + 'featured_up', + 'fp.featured_up', + 'featured_down', + 'fp.featured_down', + 'language', + 'a.language', + 'hits', + 'a.hits', + 'publish_up', + 'a.publish_up', + 'publish_down', + 'a.publish_down', + 'published', + 'a.published', 'author_id', 'category_id', 'level', 'tag', - 'rating_count', 'rating', - 'stage', 'wa.stage_id', + 'rating_count', + 'rating', + 'stage', + 'wa.stage_id', 'ws.title', 'fp.ordering', ]; @@ -452,7 +477,7 @@ protected function getListQuery() (!empty($authorId) ? ' OR ' . $db->quoteName('a.created_by') . ' IN (' . implode(',', $query->bindArray($authorId)) . ')' : '') . - ')'); + ')'); } else { $query->whereIn($db->quoteName('a.created_by'), $authorId); } @@ -531,57 +556,57 @@ protected function getListQuery() } $tagMode = $this->getState('filter.tag_mode', 'any'); if ($tagMode === 'all') { - // AND logic: article must have ALL tags - foreach ($tag as $tagId) { - $subQuery = $db->createQuery() - ->select('DISTINCT ' . $db->quoteName('content_item_id')) - ->from($db->quoteName('#__contentitem_tag_map')) - ->where([ - $db->quoteName('tag_id') . ' = ' . $db->quote($tagId), - $db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'), - ]); - - $query->join( - 'INNER', - '(' . $subQuery . ') AS ' . $db->quoteName('tagmap_' . $tagId), - $db->quoteName('tagmap_' . $tagId . '.content_item_id') . ' = ' . $db->quoteName('a.id') - ); - } - } else { - // OR logic: - $subQuery = $db->createQuery() - ->select('DISTINCT ' . $db->quoteName('content_item_id')) - ->from($db->quoteName('#__contentitem_tag_map')) - ->where( - [ - $db->quoteName('tag_id') . ' IN (' . implode(',', $query->bindArray($tag)) . ')', - $db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'), - ] - ); - - $query->join( - $includeNone ? 'LEFT' : 'INNER', - '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'), - $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') - ); - - if ($includeNone) { - $subQuery2 = $db->createQuery() + // AND logic: article must have ALL tags + foreach ($tag as $tagId) { + $subQuery = $db->createQuery() + ->select('DISTINCT ' . $db->quoteName('content_item_id')) + ->from($db->quoteName('#__contentitem_tag_map')) + ->where([ + $db->quoteName('tag_id') . ' = ' . $db->quote($tagId), + $db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'), + ]); + + $query->join( + 'INNER', + '(' . $subQuery . ') AS ' . $db->quoteName('tagmap_' . $tagId), + $db->quoteName('tagmap_' . $tagId . '.content_item_id') . ' = ' . $db->quoteName('a.id') + ); + } + } else { + // OR logic: + $subQuery = $db->createQuery() ->select('DISTINCT ' . $db->quoteName('content_item_id')) ->from($db->quoteName('#__contentitem_tag_map')) - ->where($db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article')); + ->where( + [ + $db->quoteName('tag_id') . ' IN (' . implode(',', $query->bindArray($tag)) . ')', + $db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article'), + ] + ); + $query->join( - 'LEFT', - '(' . $subQuery2 . ') AS ' . $db->quoteName('tagmap2'), - $db->quoteName('tagmap2.content_item_id') . ' = ' . $db->quoteName('a.id') - ) - ->where( - '(' . $db->quoteName('tagmap.content_item_id') . ' IS NOT NULL OR ' - . $db->quoteName('tagmap2.content_item_id') . ' IS NULL)' + $includeNone ? 'LEFT' : 'INNER', + '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'), + $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') ); + + if ($includeNone) { + $subQuery2 = $db->createQuery() + ->select('DISTINCT ' . $db->quoteName('content_item_id')) + ->from($db->quoteName('#__contentitem_tag_map')) + ->where($db->quoteName('type_alias') . ' = ' . $db->quote('com_content.article')); + $query->join( + 'LEFT', + '(' . $subQuery2 . ') AS ' . $db->quoteName('tagmap2'), + $db->quoteName('tagmap2.content_item_id') . ' = ' . $db->quoteName('a.id') + ) + ->where( + '(' . $db->quoteName('tagmap.content_item_id') . ' IS NOT NULL OR ' + . $db->quoteName('tagmap2.content_item_id') . ' IS NULL)' + ); + } } - } - } elseif (is_numeric($tag)) { + } elseif (is_numeric($tag)) { $tag = (int) $tag; if ($tag === 0) { @@ -596,20 +621,20 @@ protected function getListQuery() '(' . $subQuery . ') AS ' . $db->quoteName('tagmap'), $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') ) - ->where($db->quoteName('tagmap.content_item_id') . ' IS NULL'); + ->where($db->quoteName('tagmap.content_item_id') . ' IS NULL'); } else { $query->join( 'INNER', $db->quoteName('#__contentitem_tag_map', 'tagmap'), $db->quoteName('tagmap.content_item_id') . ' = ' . $db->quoteName('a.id') ) - ->where( - [ - $db->quoteName('tagmap.tag_id') . ' = :tag', - $db->quoteName('tagmap.type_alias') . ' = ' . $db->quote('com_content.article'), - ] - ) - ->bind(':tag', $tag, ParameterType::INTEGER); + ->where( + [ + $db->quoteName('tagmap.tag_id') . ' = :tag', + $db->quoteName('tagmap.type_alias') . ' = ' . $db->quote('com_content.article'), + ] + ) + ->bind(':tag', $tag, ParameterType::INTEGER); } } @@ -689,7 +714,7 @@ public function getTransitions() $query = $db->createQuery(); - $query ->select( + $query->select( [ $db->quoteName('t.id', 'value'), $db->quoteName('t.title', 'text'), From 76f8e5c143c085aa5a237564312398d9c3dea750 Mon Sep 17 00:00:00 2001 From: Nicola Galgano Date: Sun, 19 Oct 2025 12:15:18 +0200 Subject: [PATCH 3/3] reduntant line --- administrator/components/com_content/src/Model/ArticlesModel.php | 1 - 1 file changed, 1 deletion(-) diff --git a/administrator/components/com_content/src/Model/ArticlesModel.php b/administrator/components/com_content/src/Model/ArticlesModel.php index 71e8edbfd5bc9..dad03a460d711 100644 --- a/administrator/components/com_content/src/Model/ArticlesModel.php +++ b/administrator/components/com_content/src/Model/ArticlesModel.php @@ -174,7 +174,6 @@ protected function populateState($ordering = 'a.id', $direction = 'desc') $this->getUserStateFromRequest($this->context . '.filter.checked_out', 'filter_checked_out', ''); $this->getUserStateFromRequest($this->context . '.filter.tag_mode', 'filter_tag_mode', 'any'); - // List state information. parent::populateState($ordering, $direction);