Home > other >  How to get highest revision only in Eloquent
How to get highest revision only in Eloquent

Time:05-24

I have a table pages like

 ---- ----------- ---------- 
| id | slug      | revision |
 ---- ----------- ---------- 
|  1 | foo       |        0 |
|  2 | bar       |        0 |
|  3 | bar       |        1 |
 ---- ----------- ---------- 

The slug is unique (not as a key) but among all revisions. Thus, it could be used as a group by statement, if this is helpful.

Technically I have a solution for getting the latest revision only:

SELECT a.*
FROM `pages` a
  LEFT JOIN `pages` b
      ON a.slug = b.slug AND a.revision < b.revision
WHERE b.revision is NULL

In my approach I can use a select * to not take care about all the upcoming table cells.

How can I do that more elegant using Laravel Eloquent? Ideally I implement the logic directly in the Page model so that a simple Page::all() will give me the highest revisions only.

This is my model:

class Page extends Model
{
    use HasFactory, SoftDeletes, Sluggable;

    public function getRouteKeyName()
    {
        return 'slug';
    }

    protected $fillable = [
        'title',
        'slug',
        'revision',
    ];

    public function sluggable(): array
    {
        return [
            'slug' => [
                'source' => 'title',
            ],
        ];
    }

}
`` 

CodePudding user response:

This should work:

Page::select('slug', DB::raw('MAX(revision) as revision'))
    ->groupBy('slug')
    ->get();

CodePudding user response:

Using select * in this query can be archived by setting strict to false in config/database.php.

Then I needed to add a global scope:

class LatestScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        return $builder->select('*', DB::raw('MAX(revision) as revision'))
            ->groupBy('slug');
    }
}

and finally add the global scope to the Model:

class Page extends Model
{
    ...

    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new LatestScope);
    }

}
  • Related