Laravel标记搜索以返回包含所有标记的图像

时间:2014-07-11 18:23:58

标签: join laravel laravel-4 eloquent query-builder

我正在开发一个列出图像的应用程序,并为每个图像分配了多个标签。我希望能够找到标记为所有所搜索标记的图片。


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像素宽

2 个答案:

答案 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列表中的所有图像。