排序标签最常用于Laravel多对多的多态

时间:2018-02-27 02:55:04

标签: php laravel eloquent laravel-query-builder

我正在开发一个Laravel项目,我需要为多个Eloquent模型实现标记功能,因此我为此任务选择了多对多的多态关系。我需要显示相关的标签,并按照侧边栏部分中用于特定模型的大多数进行排序。

目前,这些模型(表格)需要标记功能:

  • 图片(id,文件名,...)
  • 视频(id,filename,...)
  • 文章(id,slug,title,summary,content ...)

我用taggables数据透视表制作了Tag模型。

  • 标签(id,名称,描述)
  • taggables(id,tag_id,taggable_type,taggable_id)

所有这些模型都通过morphToMany与Tag模型和Tag模型按惯例通过morphedByMany关系连接,与Laravel 5.6 docs中的方式相同,并且一切正常。

这是taggables pivot schema:

<?php

class CreateTaggablesTable extends Migration {
    public function up() {
        Schema::create('taggables', function (Blueprint $table) {
            $table->increments('id');
            $table->unsignedInteger('tag_id');
            $table->morphs('taggable');
            $table->unique(['tag_id', 'taggable_type', 'taggable_id']); // Only unique
        });
    }
}

第一时间,客户要求只在边栏上显示它们,所以我这样做了:

<?php

namespace App; 
use Illuminate\Database\Eloquent\Model;

class Tag extends Model {
    public static function fetchLatest(int $limit) {
        return static::has('articles')->latest()->take($limit)->pluck('name');        
    }
}

之后,客户希望按照最常用的方式对它们进行排序,因此我决定以某种方式计算它们在taggables数据透视表中出现的次数。我最终得到了这个:

<?php

namespace App;
use Illuminate\Database\Eloquent\Model;

class Tag extends Model {
    public static function fetchByMostUsed(int $limit = 15, string $model = 'App\Article') 
    {
        return static::select('tags.id', 'tags.name')
            ->join('taggables', 'tags.id', '=', 'taggables.tag_id')
            ->selectRaw('count(taggables.tag_id) as aggregate')
            ->where('taggables.taggable_type', '=', $model)
            ->groupBy('tags.id', 'tags.name')
            ->orderBy('aggregate', 'desc')
            ->limit($limit)
           ->pluck('name');
   }
}

如果您想要更纯粹的SQL查询,它看起来像:

select `tags`.`id`, `tags`.`name`, count(taggables.tag_id) as aggregate from `tags` inner join `taggables` on `tags`.`id` = `taggables`.`tag_id` where `taggables`.`taggable_type` = 'App\Article' and `tags`.`deleted_at` is null group by `tags`.`id`, `tags`.`name` order by `aggregate` desc limit 15

这样可行,但有没有更简单,更优雅的方法呢?至少,要摆脱我需要通过变量硬编码或传递特定模型(taggable_type)的事实?我宁愿制作3种不同的方法(对于每种模型而不是在代码中的任何地方进行硬编码。

1 个答案:

答案 0 :(得分:-1)

withCount('relationship')为附加列添加了一个选择。

Article::withCount('tags')->orderBy('tags_count', 'desc')->get();
Picture::withCount('tags')->orderBy('tags_count', 'desc')->get();
Video::withCount('tags')->orderBy('tags_count', 'desc')->get();

Laravel文档:https://laravel.com/docs/5.6/eloquent-relationships#counting-related-models