php - 回显用户喜欢的类别的分层MySQL表

标签 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表“category”包含名为: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"的父主题怎么办?
如何输出用户可能喜欢的类别列表?

最佳答案

首先需要将一个用户的所有匹配的类与其类别一起联接,然后选择联接的类别部分的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";

请注意,您需要使用define从中选择哪个“catid”,否则mysql将给您一条错误消息(它无法从联接的catid或likes表中选择catid;我们知道它不相关,因为它具有相同的值,但mysql没有“意识到”这一点)。我给category取了别名“c”,并且喜欢别名“l”,因此您可以编写c.catid而不是category.catid。
关于第二个问题:如果用户不喜欢父类别之一。你应该显示父类别,即使他不喜欢它们,但不同(作为不同的颜色)。但您至少需要所有父节点,因为您需要知道在哪里附加喜欢的节点。如果没有中间节点,就无法实现这一点。
最简单的方法是选择表“categories”和“likes”的“outer”连接结果;“outer join”表示如果一个表的另一个连接信息不可用(例如,该用户没有like行的category行),然后,表的这一行(例如category)也被添加到结果中,但是不可用表的部分(这里是likes)被设置为空。
更详细地说:我们想要一个“类别左外连接喜欢”,这意味着:类别(=左)有或没有喜欢,但没有喜欢没有类别。这不应该发生。但是,如果由于任何原因,你删除了一个类别,但没有删除喜欢它,你会陷入麻烦。。。
匹配条件是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”表的匹配列(如果可用的话)。。。或设置为空(如果不可用):
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作为catid,c.parentid作为parentid,c.name作为name(注意,我们可以用“AS newname”重命名列名,所以不需要更改程序)。
此外,我们还需要“(l.catid不是空的)喜欢”,这给我们“真的”(值:“1”)所有的l.catid!=NULL和“false”(值:“0”),如果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结果获取。但是由于结果中新的“liked”字段而稍微修改了一下。我们给每个节点添加一个“mark”值,这个值最初是为每个“liked”节点设置的。稍后我们还将标记“标记”ed节点的所有父节点。这些是我们要显示的节点。。。
$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()的工作原理没有太大区别。。。但是遍历顺序不同(“childs first,then current node”vs“current node,then childs”)。代码如下:
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;
}

关于php - 回显用户喜欢的类别的分层MySQL表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11875587/

相关文章:

Mysql:自增主键出现重复键错误

MYSQL 仅选择同一类别中的 X 行

sql - 如何使用 SQL 创建开始日期和结束日期?

mysql - 为什么这个 SQL 查询会花很长时间?

MySQL 从表中获取出现次数最多的成员

php - Eclipse 问题不断打开文件的旧版本/副本

php - 使用 mysqli 调用连接数据库的方法

php - 在订单电子邮件和订单编辑页面中显示复选框字段数据

javascript - 从 JavaScript 代码创建多行文本文件

php - 一个困难的一对多mysql查询