我正在开发一个列出图像的应用程序,并为每个图像分配了多个标签。我希望能够找到标记为所有所搜索标记的图片。
images
表- id
- download_url
- image_width
- image_height
tags
表- id
- name
image_tag
表- id
- image_id
- tag_id
在Image
的模型中,我有belongsToMany('Tag')
,而在Tags
的模型中,我有belongsToMany('Image')
。到目前为止,这一切都按照我的希望进行。
但是,当我尝试通过标签搜索图像时,会出现问题。我似乎可以使用以下代码进行或搜索。
$tags = explode(' ', $tag_text);
$query = DB::table('images');
$query->join('image_tag', 'images.id', '=', 'image_tag.image_id');
$query->join('tags', 'tags.id', '=', 'image_tag.tag_id');
foreach ($tags as $tag)
{
$query->orWhere('tags.name', '=', $tag);
}
$result = $query->get();
如果我搜索(例如)nyc skyscraper
,我会得到一个包含 nyc 标签或标签 skyscraper 的图片列表。但是,我只想显示带有 nyc 标签和摩天大楼标签的图片。
我尝试将$query->orWhere('tags.name', '=', $tag);
更改为$query->where('tags.name', '=', $tag);
,但这不会返回任何结果(大概是因为它搞乱了标记名称搜索)。查询Laravel正在使用绑定"select * from 'images' inner join 'image_tag' on 'images'.'id' = 'image_tag'.'image_id' inner join 'tags' on 'tags'.'id' = 'image_tag'.'tag_id' where 'tags'.'name' = ? and 'tags'.'name' = ?"
和nyc
运行skyscraper
。
我想知道是否单独对每个标签进行图像搜索(在上面的例子中会返回带有 nyc 标签的图像,然后返回标签 skyscraper 的图像),然后挑出两个结果中出现的图像,但我不确定实现这个的最佳方法是什么,以及它是否是最有效的方式。
我怀疑使用我目前缺少的查询构建器可以轻松实现此目的!
让它变得更复杂(当前不起作用)的是,除了标签搜索之外,我还想用(例如)一定宽度来显示图像。所以在标记代码之后,我已经包含了
$query->where('image_width', '>', 1000);
返回宽度超过1000像素的所有图像,但仅当我不包含标记搜索代码时。如果我确实包含标签搜索代码,它将忽略搜索的图像宽度部分。
所以,理想情况下,我想要返回标记为 nyc 且标记为摩天大楼的图像,超过1000像素宽
答案 0 :(得分:7)
手动计数和having
可行,但您可以使用简单的whereHas
代替:
// let it be array of tag ids:
$tagsIds;
$images = Image::whereHas('tags', function ($q) use ($tagsIds) {
$q->whereIn('tags.id', $tagsIds);
}, '=', count($tagsIds))->get();
但是请注意,当您在数据透视表上没有重复项时,我和@ user3158900的解决方案都将起作用。在attach
关系上使用Eloquent的BelongsToMany
方法可能会导致这种情况。
答案 1 :(得分:1)
查找包含所有标记的图像,我们可能需要在进行必要的连接后计算结果并按image_url进行分组。通过这种方式,我们可以看到每个图像匹配多少个标签,并且只需使用计数就可以获得与所有标签匹配的图像,因为我们知道我们有多少个标签。
$tags = explode(' ', $tag_text);
$images = DB::table('images')
->select(DB::raw('COUNT(*) AS `TheCount`, images.download_url'))
->join('image_tag', 'images.id', '=', 'image_tag.image_id')
->join('tags', 'tags.id', '=', 'image_tag.tag_id')
->whereIn('tags.id', $tags)
->groupBy('images.download_url')
->having('TheCount', count($tags))
->get();
如果您需要的不仅仅是每个图片的download_url
,您可能需要稍微更改一下。我建议不要按download_url
选择和分组,而是按照图片的id
进行分组,然后将其设置为子选择,并将其与匹配ID上的图像表连接起来if if这是有道理的。
或者只是使用->lists('images.id')
来获取id的数组,并在images表上进行另一个查询,以获取id列表中的所有图像。