K2的相关文章如何限定分类?

K2的文章,自带有相关文章的功能,它是由K2的标签系统来实现的,也就是说,如果都使用了同样的标签,那么相关文章中,就会显示这些文章。

但K2的默认相关文章功能,并不能设定分类,来筛选输出文章。在具体建站项目中,例如产品栏目和技术栏目或新闻栏目,共用了“无线通讯”这个标签,产品详情页面,我想显示相关产品,实际上可能显示新闻文章,这就不合理。或者如果我们需要在产品详情页面,显示“相关产品”、“相关技术”、“相关新闻”,那应该怎样实现呢?

思路有三种,分别是

方法一:使用 K2 Content 模块

这是最简单且灵活的方法(但实际是换个思路和方式,并不是实际解决方式)。你可以不使用item.php自带的相关项目代码,而是通过模块实现:

  1. 新建模块:进入模块管理器,新建一个K2 Content模块。
  2. 设置关联模式:在模块设置的“Select items”选项中,选择Retrieve items from categories
  3. 限定类别:在分类列表中选择你想要限定的那个“指定类别”,并确保开启Fetch items from children categories(获取子类项目)。
  4. 开启标签匹配:找到“Item matching”或类似选项,设置为Matching on tags(仅匹配当前页面的标签)。
  5. 分配位置:将模块分配到项目页面使用的位置(或使用{loadposition}插入item.php)。

关于如何定制K2 Content 模块,我们之前在《自定义输出mod k2 content模块的文章》的教程中已经有详细介绍。

方法二:修改 K2 模板(Template Override)

如果你想在 item.php 模板中通过代码过滤,可以通过修改数据库查询逻辑来实现。K2 的相关项目数据是通过 $this->relatedItems 传递的。你可以在 item.php 中使用 PHP 对其进行过滤:

在 item.php 中使用的 $this->relatedItems 是 K2 在后台预先查询好的结果(通常默认只取 5 条或 10 条)。如果你在前端用 if 过滤,程序逻辑如下: K2 从数据库抓取了 5 条标签相关的文章。你的代码检查这 5 条。如果这 5 条都不在 ID 10 的分类里,页面上将什么都不显示,即使数据库里其实还有第 6、7 条符合条件的文章。

解决方案:重新构建数据库查询

为了确保能显示出属于指定分类的相关项目,你不能在 PHP 里过滤,而必须在 item.php 中重新向数据库发起一次带条件的请求。

<?php
/** 
 * 逻辑处理:获取分类ID为1及其所有子类的相关项目
 */
$targetParentCatId = 1; // 指定分类ID
$db = JFactory::getDbo();

// 1. 获取所有子类 ID (包含无限层级)
$allCatIds = array($targetParentCatId);
$query = "SELECT id FROM #__k2_categories WHERE parent = ".(int)$targetParentCatId." AND published = 1";
$db->setQuery($query);
$subCatIds = $db->loadColumn();
if (!empty($subCatIds)) {
    $allCatIds = array_merge($allCatIds, $subCatIds);
    // 再次尝试获取孙子类 (为了覆盖更深层级)
    $query = "SELECT id FROM #__k2_categories WHERE parent IN (".implode(',', $subCatIds).") AND published = 1";
    $db->setQuery($query);
    $grandChildIds = $db->loadColumn();
    if (!empty($grandChildIds)) {
        $allCatIds = array_merge($allCatIds, $grandChildIds);
    }
}
$allCatIds = array_unique($allCatIds);
$catCondition = implode(',', $allCatIds);

// 2. 获取当前项目标签并查询
$tagIds = array();
if (isset($this->item->tags) && count($this->item->tags)) {
    foreach($this->item->tags as $tag) { $tagIds[] = (int)$tag->id; }
}

$customRelatedItems = array();
if (!empty($tagIds)) {
    $tagIdsCondition = implode(',', $tagIds);
    $sql = "SELECT DISTINCT i.* 
            FROM #__k2_items AS i
            INNER JOIN #__k2_tags_xref AS x ON i.id = x.itemID
            WHERE x.tagID IN ($tagIdsCondition) 
              AND i.catid IN ($catCondition)
              AND i.id != ".(int)$this->item->id."
              AND i.published = 1
              AND i.trash = 0
            ORDER BY i.created DESC LIMIT 4"; // 根据你的 col-md-3 排版,建议限制为 4 条
    $db->setQuery($sql);
    $customRelatedItems = $db->loadObjectList();
}
?>

<?php if(count($customRelatedItems)): ?>
    <h2 class="itemRelated-title"><span><?php echo JText::_("K2_RELATED_ITEMS_BY_TAG_PRO"); ?></span></h2> 
    <div class="itemRelated">
        <div class="row">
            <?php foreach($customRelatedItems as $item): 
                // 手动构建链接
                $item->link = JRoute::_(K2HelperRoute::getItemRoute($item->id.':'.urlencode($item->alias), $item->catid));
                // 手动构建图片路径 (MD5 规则),使用中等尺寸 _M (如需小图改 _S)
                $imageName = md5("Image".$item->id);
                $item->image = JURI::root().'media/k2/items/cache/'.$imageName.'_M.jpg';
            ?>
            <div class="col-md-3 col-sm-6">
                <a href="/<?php echo $item->link; ?>" class="uk-thumbnail uk-overlay-toggle">
                    <img src="/<?php echo $item->image; ?>" alt="<?php echo K2HelperUtilities::cleanHtml($item->title); ?>" />
                </a>                
                <h4><?php echo $item->title; ?></h4>
            </div>
            <?php endforeach; ?>
        </div>
    </div>
<?php endif; ?>

方法三:使用第三方扩展插件

如果你的过滤逻辑更复杂(例如需要根据当前项目动态决定类别范围),可以考虑专业的关联项目插件:

  • RAXO Related Articles:支持极其精细的过滤条件,包括限定 K2 类别和子类。
  • BT Related Items:针对 K2 优化的关联插件。