Ellrion
3/15/2017 - 7:51 AM

Additional helper scopes for Laravel Eloquent Models: `->orderByRelation('author', 'name')`; `->orderByRelationCount('posts')`; `->withJoinn

Additional helper scopes for Laravel Eloquent Models: ->orderByRelation('author', 'name'); ->orderByRelationCount('posts'); ->withJoinnedRelated('author', 'name');

<?php

namespace App;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Database\Query\Expression;

abstract class BaseModel extends Model
{
    /**
     * Сортировка выборки по полю из связанной модели.
     *
     * :WARNING: only for hasOne relation.
     * :FIXME: check query->column before reset select.
     *
     * @param Builder|QueryBuilder $query
     * @param string $relation
     * @param string|string[] $column
     * @param string $direction
     * @return Builder|QueryBuilder
     */
    public function scopeOrderByRelation($query, $relation, $column, $direction = 'asc')
    {
        if (null === $query->getQuery()->columns) {
            $query->select([$this->getTable() . '.*']);
        }

        $relation = $query->getRelation($relation);
        $related_table = $relation->getRelated()->getTable();

        //for laravel "< 5.4"
        //$query->leftJoin($related_table, $relation->getForeignKey(), '=', $relation->getQualifiedParentKeyName());
        $query->leftJoin($related_table, $relation->getQualifiedForeignKeyName(), '=', $relation->getQualifiedParentKeyName());

        foreach ((array) $column as $order) {
            $query->orderBy($related_table . '.' . $order, $direction);
        }

        return $query;
    }

    /**
     * Сортировка выборки по кол-ву связанных записей.
     *
     * :INFO: для связей hasMany и belongsToMany
     *
     * @param Builder|QueryBuilder $query
     * @param string $relation
     * @param string $direction
     * @return Builder|QueryBuilder
     */
    public function scopeOrderByRelationCount($query, $relation, $direction = 'asc')
    {
        return $query->withCount($relation)->orderBy(snake_case($relation) . '_count', $direction);
    }

    /**
     * Выборка полей из связанной модели с помощью джоина.
     *
     * :WARNING: only for hasOne relation.
     *
     * @param Builder|QueryBuilder $query
     * @param string $relation
     * @param array|string $column
     * @return Builder|QueryBuilder
     */
    public function scopeWithJoinedRelated($query, $relation, $column)
    {
        if (null === $query->getQuery()->columns) {
            $query->select([$this->getTable() . '.*']);
        }

        $relation_name = snake_case($relation);
        $relation = $query->getRelation($relation);
        $related_table = $relation->getRelated()->getTable();

        //for laravel "< 5.4"
        //$query->leftJoin($related_table, $relation->getForeignKey(), '=', $relation->getQualifiedParentKeyName());
        $query->leftJoin($related_table, $relation->getQualifiedForeignKeyName(), '=', $relation->getQualifiedParentKeyName());

        foreach ((array) $column as $name) {
            $segments = explode(' ', $name);

            $alias = "{$relation_name}_{$name}";

            if (count($segments) === 3 && Str::lower($segments[1]) === 'as') {
                list($name, $alias) = [$segments[0], $segments[2]];
            }

            $query->addSelect("{$related_table}.{$name} as {$alias}");
        }

        return $query;
    }
}