我有一个名为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"
的父主题会怎么样?
如何输出用户可能喜欢的类别列表?
答案 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)
这是一种适合您的方式:
“like”相当于你案例中的“可见”
答案 2 :(得分:0)
加入喜欢的桌子。
SELECT catid, parentid, name
FROM category
JOIN likes ON likes.catid = category.catid
WHERE likes.userid = $user_id
ORDER BY name