回应用户喜欢的分类MySQL表

时间:2012-08-09 01:33:18

标签: php mysql sql

我有一个名为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"的父主题会怎么样?

如何输出用户可能喜欢的类别列表?

3 个答案:

答案 0 :(得分: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会给你一个错误信息(它不能在类别之间决定catid或者喜欢连接的表格;我们知道它不是相关的,因为它具有相同的值,但是mysql没有'实现'那个)。我给别名别名“c”并且喜欢别名“l”,所以你可以写c.catid而不是category.catid。

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

最简单的方法是选择表'类别'和'喜欢'的'外部'连接结果; 'outer join'表示如果对于一个表,其他连接的信息不可用(例如,该用户没有类似行的类别行),那么该表的这一行(例如类别)也会添加到结果中但是该部分不可用表(此处为'likes')设置为NULL。

更为开心:我们想要一个“类别LEFT OUTER JOIN喜欢”,这意味着:类别(= 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喜欢”,这给了我们所有带有!= 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结果提取。但由于结果中新的“喜欢”字段,有点修改。我们标记为每个节点添加一个“标记”值,最初为每个“喜欢”节点设置。稍后我们还将标记所有“标记”节点的父节点。这些是我们想要显示的节点......

$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()的工作原理没什么不同......但是遍历顺序是不同的(“孩子优先,然后当前节点”vs“当前节点,然后是孩子”)。这是代码:

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'] ...或者您只需添加一些“is-likes”符号如果喜欢,则在类别名称后面。 这可能是这样的:

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;
}

答案 1 :(得分:1)

这是一种适合您的方式:

  1. 创建一个查找特定节点的所有父节点的函数
  2. Foreach节点,添加一个标志,表示用户是否“喜欢”该节点
  3. 用户喜欢的Foreach节点,标记为“喜欢”节点的所有父节点
  4. “like”相当于你案例中的“可见”

答案 2 :(得分:0)

加入喜欢的桌子。

SELECT catid, parentid, name
FROM category
JOIN likes ON likes.catid = category.catid
WHERE likes.userid = $user_id
ORDER BY name