【问题标题】:Laravel tag search to return images that contain all tagsLaravel 标签搜索返回包含所有标签的图片
【发布时间】:2014-09-02 10:56:55
【问题描述】:

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


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 和标签 skyscraper 的图像。

我尝试将$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' = ?",绑定 nycskyscraper

我想知道是否单独为每个标签进行图像搜索(在上面的示例中,它将返回带有标签 nyc 的图像,然后返回带有标签 skyscraper 的图像),然后挑选出两个结果中出现的图像,但我不确定实现它的最佳方法是什么,以及它是否是最有效的方法。

我怀疑有一种简单的方法可以使用我目前缺少的查询生成器来执行此操作!


使它更复杂一点(并且目前不起作用)的是,除了标签搜索之外,我还想显示具有(例如)一定宽度的图像。所以在标签代码之后,我已经包含了

    $query->where('image_width', '>', 1000);

它返回所有宽度超过 1000 像素的图像,但仅当我不包含标签搜索代码时。如果我确实包含了标签搜索代码,它会忽略搜索的图像宽度部分。

因此,理想情况下,我想返回标记为 nyc 和标记为 skyscraper 并且 宽度超过 1000 像素 的图像>.

【问题讨论】:

  • 我认为您与$query->where('tags.name', '=', $tag); 关系密切。运行完成后您可以执行的操作是 echo $query->toSql();,它应该向您显示它正在尝试运行的 SQL,它会让您更好地了解它的问题所在。
  • 谢谢——我知道我忘了包括一些东西。我现在已经添加了查询。
  • 现在我看到了查询,我可以很明显地看到为什么它不起作用。看来我们需要更有创意了,等我搞定后我会发布答案。

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


【解决方案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 上的图像表连接起来(如果有任何意义) .

或者只是使用->lists('images.id') 来获取 id 的数组,然后对 images 表进行另一个查询,以获取该 id 列表中的所有图像。

【讨论】:

  • 很好的答案,虽然我确实得到了一个错误。 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 '2 ?' at line 1 (SQL: select COUNT(*) AS 'TheCount', images.download_url 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'.'id' in (nyc, skyscraper) group by 'images'.'download_url' having 'TheCount' 2 ) 知道可能是什么问题吗?
【解决方案2】:

手动计数和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 的解决方案只有在数据透视表上没有重复项时才有效。在BelongsToMany 关系上使用Eloquent 的attach 方法可能会导致这种情况。

【讨论】:

  • 我喜欢这个答案的简单性——而且它有效!我想知道如何向查询添加其他(可选)部分。将->where('image_width', '<', 3000)->get(); 添加到该代码的末尾会给我与标签和宽度匹配的图像,但由于宽度(和其他)是可选的,我想稍后使用if 语句添加它们。我尝试删除get(),然后添加$images->where('image_width', '<', 3000); $images->get();,但我收到错误无法将类Illuminate\Database\Eloquent\Builder 的对象转换为字符串。可以像查询生成器一样添加它们吗?
  • 我不完全确定它是如何工作的,但它现在正在按照我想要的方式工作!谢谢。
  • 很高兴你解决了。可以像你想要的那样链接它,所以$query = ...; $query->where...; $query->orderBy...; 然后在其他地方调用$query->get()。您可能会收到该错误,因为您最后忘记了get()
  • @JarekTkaczyk 谢谢,为我工作。你拯救了我的一天。
猜你喜欢
  • 1970-01-01
  • 2018-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多