如何获取标记为 Tag1 AND Tag2 AND ... 的所有项目,用于 3-table 标签解决方案
How to get all items that are tagged Tag1 AND Tag2 AND ... for a 3-table tag solution
所以我实施了 3 table 标签解决方案,如 this classic post 中所示(所谓的“Toxi”解决方案)。一切都很好,我唯一遇到的困难是获取所有标记有多个标签的项目。
我的第一个解决方案使用 IN 并且工作正常,但当然给了我所有具有任何提供的标签的项目。但我真正想要的是所有具有每个提供的标签的项目。
所以我尝试:
/**
* GALLERY::filter_by_tags()
* @param array $tags - an array of strings
*/
public function filter_by_tags($tags){
if (count($tags) > 1){
//do the string building of the query
$query = "SELECT DISTINCT m.* FROM fm_maps m
INNER JOIN fm_map_tags mt ON mt.map_id = m.id
INNER JOIN fm_tags t
ON t.id = mt.tag_id WHERE t.tag = ";
$current_tag_string = "";
$sql_values = [];
$i = 1;
foreach ($tags as $tag){
$current_tag_string = 'tag' . $i;
$sql_values[$current_tag_string] = $tag;
if ($i == 1) {
$query .= ':' . $current_tag_string;
} else {
$query .= ' INNER JOIN fm_map_tags mt ON mt.map_id = m.id INNER JOIN fm_tags t
ON t.id = mt.tag_id WHERE t.tag = :' . $current_tag_string;
}
$i++;
}
$stmt = $this->map_db_connector->conn->prepare($query);
$stmt->execute($sql_values);
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
} else {
//just use tags[0]
$stmt = $this->map_db_connector->conn->prepare("SELECT DISTINCT m.* FROM fm_maps m
INNER JOIN fm_map_tags mt ON mt.map_id = m.id
INNER JOIN fm_tags t ON t.id = mt.tag_id WHERE t.tag IN (:tag)");
$stmt->bindValue(':tag', $tags[0]);
$stmt->execute();
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
}
这可能不必要地复杂,但原则上整个功能都有效。只是第一个 if
子句中的 SQl 语句本身被破坏了。 Returns Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN fm_map_tags mt ON mt.map_id = m.id INNER JOIN fm_tags t ON'
else
部分工作正常。我也试过了
foreach ($tags as $tag){
$current_tag_string = 'tag' . $i;
$sql_values[$current_tag_string] = $tag;
if ($i == 1) {
$query .= ':' . $current_tag_string;
} else {
$query .= ' AND t.tag = :' . $current_tag_string;
}
我认为这更简单,但这也不起作用。我没有收到任何错误,但也没有结果。
作为 SQL 和 PHP 初学者,我被困住了。谁能指出我正确的方向?
此查询将为您提供 fm_maps
,其中包含所有标签 A
、B
和 C
:
SELECT * FROM fm_maps
WHERE EXISTS (
SELECT 1 FROM fm_map_tags mt
JOIN fm_tags t ON mt.tag_id = t.id AND t.tag IN ('A', 'B', 'C')
WHERE mt.map_id = fm_maps.id
GROUP BY mt.map_id
HAVING COUNT(*) = 3
)
使用此查询实现 filter_by_tags
方法可能是这样的:
/**
* GALLERY::filter_by_tags()
* @param array $tags - an array of strings
*/
public function filter_by_tags($tags)
{
if (empty($tags)) {
// Do something if there are no tags
} else {
$placeholders = '?' . str_repeat(', ?', count($tags) - 1);
$query =
"SELECT * FROM fm_maps WHERE EXISTS ("
. "SELECT 1 FROM fm_map_tags mt "
. "JOIN fm_tags t ON mt.tag_id = t.id AND t.tag IN ($placeholders) "
. "WHERE mt.map_id = fm_maps.id GROUP BY mt.map_id HAVING COUNT(*) = ?)";
$params = $tags;
$params[] = count($tags);
$stmt = $this->map_db_connector->conn->prepare($query);
$stmt->execute($params);
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
}
}
所以我实施了 3 table 标签解决方案,如 this classic post 中所示(所谓的“Toxi”解决方案)。一切都很好,我唯一遇到的困难是获取所有标记有多个标签的项目。
我的第一个解决方案使用 IN 并且工作正常,但当然给了我所有具有任何提供的标签的项目。但我真正想要的是所有具有每个提供的标签的项目。
所以我尝试:
/**
* GALLERY::filter_by_tags()
* @param array $tags - an array of strings
*/
public function filter_by_tags($tags){
if (count($tags) > 1){
//do the string building of the query
$query = "SELECT DISTINCT m.* FROM fm_maps m
INNER JOIN fm_map_tags mt ON mt.map_id = m.id
INNER JOIN fm_tags t
ON t.id = mt.tag_id WHERE t.tag = ";
$current_tag_string = "";
$sql_values = [];
$i = 1;
foreach ($tags as $tag){
$current_tag_string = 'tag' . $i;
$sql_values[$current_tag_string] = $tag;
if ($i == 1) {
$query .= ':' . $current_tag_string;
} else {
$query .= ' INNER JOIN fm_map_tags mt ON mt.map_id = m.id INNER JOIN fm_tags t
ON t.id = mt.tag_id WHERE t.tag = :' . $current_tag_string;
}
$i++;
}
$stmt = $this->map_db_connector->conn->prepare($query);
$stmt->execute($sql_values);
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
} else {
//just use tags[0]
$stmt = $this->map_db_connector->conn->prepare("SELECT DISTINCT m.* FROM fm_maps m
INNER JOIN fm_map_tags mt ON mt.map_id = m.id
INNER JOIN fm_tags t ON t.id = mt.tag_id WHERE t.tag IN (:tag)");
$stmt->bindValue(':tag', $tags[0]);
$stmt->execute();
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
}
这可能不必要地复杂,但原则上整个功能都有效。只是第一个 if
子句中的 SQl 语句本身被破坏了。 Returns Uncaught PDOException: SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INNER JOIN fm_map_tags mt ON mt.map_id = m.id INNER JOIN fm_tags t ON'
else
部分工作正常。我也试过了
foreach ($tags as $tag){
$current_tag_string = 'tag' . $i;
$sql_values[$current_tag_string] = $tag;
if ($i == 1) {
$query .= ':' . $current_tag_string;
} else {
$query .= ' AND t.tag = :' . $current_tag_string;
}
我认为这更简单,但这也不起作用。我没有收到任何错误,但也没有结果。
作为 SQL 和 PHP 初学者,我被困住了。谁能指出我正确的方向?
此查询将为您提供 fm_maps
,其中包含所有标签 A
、B
和 C
:
SELECT * FROM fm_maps
WHERE EXISTS (
SELECT 1 FROM fm_map_tags mt
JOIN fm_tags t ON mt.tag_id = t.id AND t.tag IN ('A', 'B', 'C')
WHERE mt.map_id = fm_maps.id
GROUP BY mt.map_id
HAVING COUNT(*) = 3
)
使用此查询实现 filter_by_tags
方法可能是这样的:
/**
* GALLERY::filter_by_tags()
* @param array $tags - an array of strings
*/
public function filter_by_tags($tags)
{
if (empty($tags)) {
// Do something if there are no tags
} else {
$placeholders = '?' . str_repeat(', ?', count($tags) - 1);
$query =
"SELECT * FROM fm_maps WHERE EXISTS ("
. "SELECT 1 FROM fm_map_tags mt "
. "JOIN fm_tags t ON mt.tag_id = t.id AND t.tag IN ($placeholders) "
. "WHERE mt.map_id = fm_maps.id GROUP BY mt.map_id HAVING COUNT(*) = ?)";
$params = $tags;
$params[] = count($tags);
$stmt = $this->map_db_connector->conn->prepare($query);
$stmt->execute($params);
$maps = $stmt->fetchAll(PDO::FETCH_CLASS, 'MAP');
foreach ($maps as $current_map){
$this->create_gallery_entry($current_map);
}
}
}