1

我有一个名为 category 的 MySQL 表,它将分层数据保存为相邻列表。然后,我将所有类别及其子项的列表作为 html 列表回显:

Food 
    Fruit  
        Red  
           Cherry  
        Yellow  
           Banana  
    Meat  
        Beef  
        Pork
Sports
    Soccer
        Spanish Soccer
        French Soccer
    Golf
        US Open
           Tiger Woods

我使用的代码是:

$refs = array();
$list = array();

$sql = "SELECT catid, parentid, name FROM category ORDER BY name";
$result = mysql_query($sql);
while($data = mysql_fetch_assoc($result)) {
    $thisref = &$refs[ $data['catid'] ];

    $thisref['parentid'] = $data['parentid'];
    $thisref['name'] = $data['name'];
    $thisref['catid'] = $data['catid'];    

    if ($data['parentid'] == 0) {
        $list[ $data['catid'] ] = &$thisref;
    } else {
        $refs[ $data['parentid'] ]['children'][ $data['catid'] ] = &$thisref;
    }
}

function toUL($arr){
    $html = '<ul>';
    foreach ($arr as $v){
        $html .= '<li><a href="category.php?id=' . $v['catid'] . '">' . $v['name'] . '</a>';
        if (array_key_exists('children', $v)){
            $html .= toUL($v['children']);
        }
        $html .= '</li>';
    }
    $html .= '</ul>';
    return $html;
}

// build the list and output it
echo toUL($list);

MySQL 表“类别”的行称为:catid, name, description, parentid, and level

问题是我希望该表仅显示用户喜欢的类别。如:

Food 
    Fruit             
        Yellow  
           Banana  
    Meat  
        Beef    
Sports
    Soccer
        Spanish Soccer
        French Soccer
    Tiger Woods

"likes"行称为:likeid, userid, catid其中userid引用表的主键"users"userid引用表的主键"category"

我可以实现什么样的代码来创建用户喜欢的单个主题列表?我看到但目前没有答案的问题是:如果用户喜欢"Tiger Woods"但不喜欢 的父主题"Golf"怎么办?

我怎样才能输出用户可能喜欢的类别列表?

4

3 回答 3

2

首先,您需要将用户的所有匹配喜欢及其类别连接在一起,然后选择 catid、parentid 和连接的类别部分的名称。

$userid = 123; 
$sql = "SELECT c.catid, c.parentid, c.name
        FROM category c JOIN likes l ON c.catid=l.catid
        WHERE l.userid=$userid ORDER BY name";

请注意,您需要使用定义要从中选择哪个“catid”,否则mysql会给您一条错误消息(它无法在category中的catid或join的likes表之间做出决定;我们知道它不相关,因为它具有相同的值,但 mysql 没有“意识到”)。我给类别起了别名“c”并且喜欢别名“l”,所以你可以写 c.catid 而不是 category.catid。

关于第二个问题:如果用户不喜欢其中一个父类别。即使他不喜欢它们,您也应该显示父类别,但要以不同的方式显示(作为不同的颜色)。但是您至少需要所有父节点,因为您需要知道在哪里附加喜欢的节点。如果没有中间节点,您将无法实现这一点。

最简单的方法是选择表“类别”和“喜欢”的“外部”连接结果;“外部联接”意味着如果一个表的其他联接信息不可用(例如,该用户没有类似行的类别行),则表的这一行(例如类别)也将添加到结果中,但部分不可用的表(此处为“喜欢”)设置为 NULL。

更确切地说:我们想要一个“类别 LEFT OUTER JOIN likes”,这意味着:有或没有喜欢的类别 (=LEFT),但没有没有类别的喜欢。这不应该发生。但是,如果出于某种原因,您删除了一个类别但不删除它的喜欢,您就会遇到麻烦......

匹配标准是 catid 列,因此我们写“ON c.catid=l.catid”(或“ON categories.catid = likes.catid”,如果您不想使用名称别名......如解释以下)。

让我们看一下这个 SQL 命令的 JOIN 结果:

$userid = 123;

$sql = "SELECT * FROM category c RIGHT OUTER JOIN likes l ON c.catid=l.catid
WHERE l.userid=$userid ORDER BY l.name";

现在我们从“like”表中获得所有类别的表,如果可用,则添加匹配列......或者如果不可用,则设置为NULL:

c.catid c.parentid c.name l.catid l.userid
1       0          Food   1       123
2       1          Fruits 2       123
...
9       0          Sports NULL    NULL
10      0          Sports 10      123
...

所以,我们需要的是 c.catid AS catid, c.parentid AS parentid, c.name AS name(注意我们可以用“AS newname”重命名列名,所以我们不需要改变我们的程序)。此外,我们需要“(l.catid IS NOT NULL)AS like”,它为所有具有!= NULL和“false”(值:“0”)的l.catid提供“true”(值:“1”)如果l.catid == NULL。

在我们的 SQL 命令中,如下所示:

$userid = 123; 
$sql = "SELECT
          c.catid AS catid,
          c.parentid AS parentid,
          c.name AS name,
          (l.catid IS NOT NULL) as liked
        FROM category c RIGHT OUTER JOIN likes l ON c.catid=l.catid WHERE l.userid=$userid
        ORDER BY name";

好的,现在我们像以前一样实现 SQL-result-fetching。但是由于结果中的新“喜欢”字段而进行了一些修改。我们标记为每个节点添加一个“标记”值,该值最初是为每个“喜欢的”节点设置的。稍后我们还将标记“标记”节点的所有父节点。这些是我们要显示的节点...

$refs = array();
$list = array();

$userid = 1;
$sql = "SELECT c.catid, c.parentid, c.name, (l.catid IS NOT NULL) as liked FROM category c RIGHT OUTER JOIN likes l ON c.catid=l.catid WHERE l.userid=$userid ORDER BY name";
$result = mysql_query($sql);
while($data = mysql_fetch_assoc($result)) {
    $thisref = &$refs[ $data['catid'] ];

    $thisref['parentid'] = $data['parentid'];
    $thisref['name'] = $data['name'];
    $thisref['catid'] = $data['catid'];
    $thisref['liked'] = $data['liked']; // save information: user likes it
    $thisref['mark'] = $data['liked']; // initially mark all 'liked' nodes

    if ($data['parentid'] == 0) {
        $list[ $data['catid'] ] = &$thisref;
    } else {
        $refs[ $data['parentid'] ]['children'][ $data['catid'] ] = &$thisref;
    }
}
mysql_free_result($result);

现在您递归地标记需要显示的树的部分。因此,对于 toUL() 函数,我们从图的根开始。由于我们要显示任何需要显示的具有子节点的节点(=被用户喜欢),我们首先分析子子树。如果最后,其中一个子树有我们需要显示的节点,或者该节点本身需要显示(喜欢),我们标记它。它与 toUL() 的工作方式没有太大区别......但遍历顺序不同(“先是子节点,然后是当前节点”与“当前节点,然后是子节点”)。这是代码:

function markLikedSubtree($arr){
  $needs_display = false;

  foreach ($arr as $v){
    // recursively mark subtree of child $v
    $child_marked = markLikeSubtree( $v['children'] );

    // if mark this node, if it was marked already OR any child of $v needs displaying
    $v['mark'] = $v['mark'] || $child_marked;

    // if $v needs to be displayed, the parent needs to be displayed too
    if ($v['mark']) $needs_display = true;
  }

  // return true, if parent is needed to be displayed.
  return $needs_display;
}

现在我们需要修改 toUL() 函数以仅显示具有 $v["mark"] == true 的节点,如下所示:

function toUL($arr){
  $html = '<ul>';
  foreach ($arr as $v) {
    if ($v['mark']) {
      $html .= '<li><a href="category.php?id=' . $v['catid'] . '">' . $v['name'] . '</a>';
      if (array_key_exists('children', $v)){
          $html .= toUL($v['children']);
      }
      $html .= '</li>';
    }
  }
  $html .= '</ul>';
  return $html;
}

最后我们调用标记喜欢节点的所有父节点的函数并调用我们的新 toUL() 函数:

markLikedSubtree($list);
echo toUL($list);

此外,您可以将所有已设置 $v['mark'] 但未设置 $v['like'] 的节点变灰(=以不同方式显示)...或者您只需在类别后面添加一些“喜欢”符号名字,如果喜欢的话。这可能是这样的:

function toUL($arr){
  $html = '<ul>';
  foreach ($arr as $v) {
    if ($v['mark']) {
      $html .= '<li><a href="category.php?id=' . $v['catid'] . '">' . $v['name'] . '</a>';

      if ($v['liked']) $html .= " <img src='thumb_up.jpg'>";

      if (array_key_exists('children', $v)){
          $html .= toUL($v['children']);
      }
      $html .= '</li>';
    }
  }
  $html .= '</ul>';
  return $html;
}
于 2012-08-15T09:56:12.010 回答
1

这是一种可能适合您的方式:

  1. 创建一个查找特定节点的所有父节点的函数
  2. Foreach 节点,添加一个标志,表示用户是否“喜欢”该节点
  3. foreach 用户喜欢的节点,将该节点的所有父节点标记为“喜欢”

在您的情况下,“喜欢”相当于“可见”

于 2012-08-09T14:55:22.077 回答
0

加入喜欢表。

SELECT catid, parentid, name
FROM category
JOIN likes ON likes.catid = category.catid
WHERE likes.userid = $user_id
ORDER BY name
于 2012-08-12T03:43:46.790 回答