Laravel.io
/******************************************
The Base Model
******************************************/
<?php
namespace App;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;
class BaseModel extends Model {
/**
* query scope nPerGroup
*
* @return void
*/
public function scopeNPerGroup($query, $group, $n = 10)
{
// queried table
$table = ($this->getTable());
// initialize MySQL variables inline
$query->from( DB::raw("(SELECT @rank:=0, @group:=0) as vars, {$table}") );
// if no columns already selected, let's select *
if ( ! $query->getQuery()->columns)
{
$query->select("{$table}.*");
}
// make sure column aliases are unique
$groupAlias = 'group_'.md5(time());
$rankAlias = 'rank_'.md5(time());
// apply mysql variables
$query->addSelect(DB::raw(
"@rank := IF(@group = {$group}, @rank+1, 1) as {$rankAlias}, @group := {$group} as {$groupAlias}"
));
// make sure first order clause is the group order
$query->getQuery()->orders = (array) $query->getQuery()->orders;
array_unshift($query->getQuery()->orders, ['column' => $group, 'direction' => 'asc']);
// prepare subquery
$subQuery = $query->toSql();
// prepare new main base Query\Builder
$newBase = $this->newQuery()
->from(DB::raw("({$subQuery}) as {$table}"))
->mergeBindings($query->getQuery())
->where($rankAlias, '<=', $n)
->getQuery();
// replace underlying builder to get rid of previous clauses
$query->setQuery($newBase);
}
}
/***********************************************************
The Board Model extending it
***********************************************************/
<?php
namespace App;
use App\BaseModel;
class Board extends BaseModel
{
public function pins()
{
return $this->hasMany('App\Pin');
}
public function recentPins()
{
return $this->pins()->latest()->nPerGroup('board_id', 5);
}
}
/***********************************************************
Calling the method in Tinker
***********************************************************/
App\Board::with('latestComments')->get();