【问题标题】:How to fetch categories, articles and subcategories and their articles?如何获取类别、文章和子类别及其文章?
【发布时间】:2021-12-22 14:22:02
【问题描述】:

我想输出分类名称,每个分类中的子分类名称,以及每个分类和每个子分类中的文章。

输出应在 HTML 中的<ul> 和内部<ul> 标记中。

下面是输出的样子:

我有这 5 张桌子:

table_article_categories(articlecat_id, parent_id, articlecat_status, ...)
table_article_to_article_categories(articlecat_id, articles_id)
table_articles(articles_id, articles_status, ...)
table_articles_description(articles_id, language_id, articles_heading_title, ...)
table_article_categories_description(articlecat_id, articlecat_name, language_id, ...)

到目前为止,我已经编写了这个 SQL:

  SELECT
        ac.parent_id,
        ac.articlecat_id,
        a.articles_id,
        ad.articles_heading_title AS article_name,
        acd.articlecat_name
  FROM
        table_article_categories AS ac
  LEFT JOIN
        table_article_to_article_categories AS atac
     ON
        atac.articlecat_id = ac.articlecat_id
  LEFT JOIN
        table_articles AS a
     ON
        a.articles_id = atac.articles_id
     AND                
        a.articles_status = 1
  LEFT JOIN
        table_articles_description AS ad
     ON
        ad.articles_id = a.articles_id
     AND
        ad.language_id = 1              
  INNER JOIN
        table_article_categories_description AS acd
     ON
        acd.articlecat_id = ac.articlecat_id
     AND
        acd.language_id = 1
  WHERE
        ac.parent_id = 99 # --- This is the root article-categori-id ---
     AND
        ac.articlecat_status = 1
     AND
        ac.articlecat_id != 77 # --- This is an excluded article-categori-id ---
  ORDER BY
        acd.articlecat_name ASC,
        article_name ASC

【问题讨论】:

  • 如果您的查询正确并返回结果,那么您需要循环遍历结果
  • (1) 请告诉我们你做了什么来用
    • 显示结果; (2) 与其在单个查询中进行,为什么不在单独的、多个 查询中循环呢?
  • 文章有每种语言的描述,类别有我系统中可用的每种语言的描述/名称 - SQL 查询不必是递归的,我只是想也许 PHP 代码可以输出 HTML 时递归 - 但这不是必需的
  • 将其分解为几个部分:1)您需要递归(php 或 sql)来按顺序获取类别。 2)孤儿文章总是排在类别列表之后吗? 3) 使用前两步收集的 id 来获取特定于语言的信息。 KenLee 的观点是不要试图一口气获得所有信息。得到它,因为你需要它。但试图回答你的问题的问题在于,你实际上有许多不同的概念需要答案。如果您可以将问题集中在其中一件事情上,我们可以为您提供更好的帮助。
  • @KenLee 可能会,但可能不会。您的 OP 可能会更好地点击。我鼓励你把它带回来:) 更多的想法总是更好的。

标签: php mysql sql recursion tree-structure


【解决方案1】:

当我查看您想要的输出时,我看到的是一棵带有叶子的树,它可能链接到一篇文章,也可能不链接到一篇文章。没有附加文章的叶子可以被认为是一个类别。确实有文章的叶子可以被认为是文章。

有了这个假设,您可以构建您的架构:

Category_Tree  Category      Article
------------   --------      -------
id             id            id
pid            category_id   article_id
article_id     language_id   language_id
category_id    title         title

(当然,这不是第四范式。如果需要,您可以进行标准化,但现在这将是过早的优化)

现在,应该更容易了解如何解决您的问题:您所要做的就是首先在 php 数组中构建树;然后遍历该数组并单独查询每个叶子的详细信息(或至少查询链接的 href 和文本所需的内容)。

一个简单的递归入门:

<?php
/*
 Category_Tree
 id  pid  name            article_id   category_id   
 1   0    Category 1            ...          ...
 2   1    Article 1
 3   0    Category 2
 4   3    SubCategory 1
 5   4    Article 7
 6   4    Article 8
 7   3    Article 2
 8   3    Article 3
 9   0    Category 3
 10  9    Article 4
 11  9    Article 5
 12  0    Article 6
*/
function getRecord($pid) : array
{
  // database access is left to the reader

  // $db->run('SELECT * FROM CATEGORY_TREE WHERE PID=?',[$pid]);
  // $rows = [];
  // while($row = $db->getRow()) {
  //     $rows[] = $row;
  // }

  return $rows;
}

function makeTree($pid) : array
{
    static $idList = [];
    static $indent = 0;
    $rows = getRecord($pid);
    foreach ($rows as $row) {
        $idList[] = [
            'id' => $row->id, 
            'indent' => $indent,
            'articleId' => $row->articleId,
            'categoryId' => $row->categoryId,
        ];
        $indent++;
        makeTree($row->id);
        $indent--;
    }

    return $idList;
}

$idList = makeTree(0);

*警告:这不会直接转化为无序列表。我不喜欢混合 html 和逻辑,但在这种情况下,您需要在递归中构建一个字符串。这是一个您可以提出或研究的更有针对性的问题。

现在,您遍历$idList 并查找所需的信息以填写您想要的标题

伪代码(db检索再次留给读者):

// $languageId is already assigned as you see fit

// add title to $idList
foreach($idList as $index => $row) {
    if($row->articleId ?? FALSE) {
      $db->run('SELECT TITLE FROM ARTICLE WHERE ARTICLE_ID=? AND LANGUAGE_ID=?', [$row->articleId, $languageID]);
    } else {
      $db->run('SELECT TITLE FROM CATEGORY WHERE CATEGORY_ID=? AND LANGUAGE_ID=?', [$row->categoryId, $languageId]);
    }

    $row->title = $db->get('title');
    $idList[$index] = $row;  
}

然后,当您输出 html 时,您只需再次遍历 $idList 并根据需要输入值。

显然,这不是一个剪切和粘贴的答案;有太多的编码要做。但希望这能为您指明一个可行的方向。

【讨论】:

    【解决方案2】:

    我建议不要在单个查询中执行此操作,而是在单独的多个查询上循环的解决方案。

    对于您的情况,只需要 3 个单独的查询,因为您有 3 层(Cat/Subcat/Article)。另一方面,由于层数已经定义(并且是有限的),所以这次不需要使用递归(如果有无限(或未知)的层级,我们需要递归)。

    请将数据库架构更改为正确且明确定义的架构(如下):

    category (catid, name)
    subcategory (subcatid, catid, name)
    article (articleid, catid, subcatid, name)
    

    catid is linking to category;和subcatid is linking to subcategory如果适用

    所以数据将是(参考您帖子中的图片)

    category (catid, name)
    
    1 Category1
    2 Category2
    3 Category3
    
    subcategory (subcatid, catid, name)
    
    1 2 Subcategory1
    
    article (articleid, catid, subcatid, name)
    
    1 0 0 Article6
    2 1 0 Article1
    3 2 1 Article7
    4 2 1 Article8
    5 2 0 Article2
    6 2 0 Article3
    7 3 0 Article4
    8 3 0 Article5
    

    我认为上述架构的优势和清晰性是显而易见的,因此我认为不需要进一步解释。当然,您可以在表格中添加更多数据字段,但主要结构如上。

    现在,请使用 PHP 生成以下 HTML:

    对于第一个 ul,li(只运行一次)

    (a) 遍历类别并显示名称

    然后

    (b) 遍历具有 (catid=0 or catid is null) 和 (subcatid=0 or subcatid is null) 的文章显示记录,然后显示名称

    第二个 ul,li

    对于上述 (a) 中的每条记录:

    (c) 遍历作为 catid=category.catid 的子类别并显示子类别名称

    然后

    (d) 循环遍历文章,显示具有 catid=category.catid 和 (subcatid=0 or subcatid is null) 的记录,然后显示名称

    第三个 ul,li

    对于上述 (c) 中的每条记录:

    (e) 遍历文章,显示具有 catid=category.catid 和 subcatid=subcategory.subcatid 的记录,然后显示名称

    为了演示,我将把“2nd ul, li”作为一个名为branch2()的函数;和 "3rd ul, li" 作为称为 branch3() 的函数,然后相应地调用它们。

    所以,代码将是:

    <?php
    
    $servername = "localhost";
    $username = "xxxxxxx";
    $password = "xxxxxxxxxx";
    $dbname = "xxxxxx";
    
    $conn = new mysqli($servername, $username, $password, $dbname);
    
    $sql1="(
    SELECT name, catid AS id1, '1' as subdata
    FROM category
    ORDER BY id1
    )
    UNION (
    SELECT name, articleid AS id1, '0' as subdata
    FROM article
    WHERE catid =0
    AND subcatid =0
    ORDER BY id1
    )";
    
    $result = $conn -> query($sql1);
    
    $index=0;
    $count=mysqli_num_rows($result);
    echo "<ul>";
      while ($index <$count) {
      $row = $result -> fetch_assoc() ;
      echo "<li>". $row["name"];
    
        if ($row["subdata"]=='1'){  
            branch2($row["id1"], $conn); 
         }
      
      $index++;
      }
    echo "</ul>";
    
    $result -> free_result();
    
    
    function branch2($var1, $conn){
    $sql2="(
    SELECT name, catid AS id1, subcatid as id2, 1 as subdata
    FROM subcategory where catid=". $var1 . " ORDER BY id1 )
    UNION (
    SELECT name, articleid AS id1, 0 as id2 , 0 as subdata
    FROM article
    WHERE catid =" . $var1 . " AND subcatid =0 ORDER BY id1 )";
    
    $result2 = $conn -> query($sql2);
    $index2=0;
    $count2=mysqli_num_rows($result2);
    
    echo "<ul>";
      while ($index2 <$count2) {
      $row2 = $result2 -> fetch_assoc() ;
      echo "<li>". $row2["name"];
    
         if ($row2["subdata"]=='1') {   
            branch3($row2["id1"], $row2["id2"], $conn); 
          }  
       $index2++;
      }
    echo "</ul>";
    $result2 -> free_result();
    }
    
    
    function branch3($var1, $var2, $conn){
    $sql3="(
    SELECT name, articleid AS id1, 0 as id2 , 0 as subdata
    FROM article
    WHERE catid =" . $var1 . " and subcatid=". $var2 . " ORDER BY id1 )";
    
    $result3 = $conn -> query($sql3);
    $index3=0;
    $count3=mysqli_num_rows($result3);
    
    echo "<ul>";
      while ($index3 <$count3) {
      $row3 = $result3 -> fetch_assoc() ;
      echo "<li>". $row3["name"];
    
      $index3++;
      }
    echo "</ul>";
    
    $result3 -> free_result();
    }
    ?>
    

    完整的例子可以在这里看到:

    http://www.createchhk.com/SO/testso10Nov2021.php

    【讨论】:

      猜你喜欢
      • 2014-04-25
      • 1970-01-01
      • 2014-10-03
      • 2016-02-12
      • 1970-01-01
      • 2014-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多