fredyounan
1/8/2017 - 3:29 AM

Enhanced version of https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/, tested on hasMany and belong

Enhanced version of https://softonsofa.com/tweaking-eloquent-relations-how-to-get-n-related-models-per-parent/, tested on hasMany and belongsToMany relationships

<?php
namespace App\Traits;

trait NPerGroup {

  /**
   * query scope nPerGroup
   *
   * @return void
   */

  public function scopeNPerGroup($query, $relatedTable = NULL, $group, $n = 10) {
    // queried table
    $table = ($this->getTable());

    $newQuery = $this->newQueryWithoutScopes();

    // initialize MySQL variables inline
    $newQuery->from(\DB::raw("(select @num:=0, @group:=0) as vars, {$table}"));
    $groupTable = $relatedTable ?: $table;

    // if no columns already selected, let's select *
    if (!$query->getQuery()->columns) {
      $newQuery->select("{$table}.*");
    }

    // make sure column aliases are unique
    $groupAlias = "{$table}_grp";//. md5(time());
    $numAlias = "{$table}_rn";// . md5(time());

    // apply mysql variables
    $newQuery->addSelect(\DB::raw(
      "@num := if(@group = {$groupTable}.{$group}, @num+1, 1) as {$numAlias}, @group := {$groupTable}.{$group} as {$groupAlias}"
    ));

    // make sure first order clause is the group order
    $newQuery->getQuery()->orders = (array) $query->getQuery()->orders;
    array_unshift($newQuery->getQuery()->orders, [
      'column' => "{$groupTable}.{$group}",
      'direction' => 'asc'
    ]);

    if ($relatedTable) {
      $newQuery->addSelect("{$groupTable}.{$group}");
      $newQuery->mergeBindings($query->getQuery());
      $newQuery->getQuery()->joins = (array) $query->getQuery()->joins;
      $query->whereRaw("{$table}.{$group} = {$groupTable}.{$group}");
    }

    // prepare subquery
    $subQuery = $query->toSql();
    $query->from(\DB::raw("({$newQuery->toSql()}) as {$table}"))
      ->where($numAlias, '<=', $n);

  }
}