首先,您需要将用户的所有匹配喜欢及其类别连接在一起,然后选择 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;
}