Ellrion
4/15/2016 - 10:19 AM

Macroable global scope with caching pagination

Macroable global scope with caching pagination

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model as BaseModel;
use App\Support\Eloquent\MacroableQueryTrait;

class Model extends BaseModel
{
    use MacroableQueryTrait;

    protected $macros = ['uniqid', 'paginateOrCache'];

    public function macroUniqid(Builder $builder, $salt) {
        $name = $builder->getModel()->getConnection()->getName();

        return md5($salt . $name . $builder->toSql() . serialize($builder->getQuery()->getBindings()));
    }

    public function macroPaginateOrCache(Builder $builder, \Illuminate\Contracts\Cache\Repository $cache, $minutes = 1, $perPage = null)
    {
        $page = \Illuminate\Pagination\Paginator::resolveCurrentPage();
        $perPage = $perPage ?: $builder->getModel()->getPerPage();

        $key = $builder->uniqid($perPage);

        $total = $cache->remember("{$key}-total", $minutes, function () use ($builder) {
            return $builder->getQuery()->getCountForPagination();
        });

        $results = $cache->remember("{$key}-page-{$page}", $minutes, function () use ($builder, $page, $perPage) {
            return $builder->getQuery()->forPage($page, $perPage)->get();
        });

        return new \Illuminate\Pagination\LengthAwarePaginator($results, $total, $perPage, $page, [
            'path' => \Illuminate\Pagination\Paginator::resolveCurrentPath(),
        ]);
    }
}
<?php 

namespace App\Support\Eloquent;

trait MacroableQueryTrait
{
    public static function bootMacroableQueryTrait()
    {
        static::addGlobalScope(new MacroableQueryScope());
    }

    public function getMacroses()
    {
        return isset($this->macros) ? $this->macros : [];
    }
}
<?php 

namespace App\Support\Eloquent;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ScopeInterface;

class MacroableQueryScope implements ScopeInterface
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param Builder $builder
     * @param Model   $model
     */
    public function apply(Builder $builder, Model $model)
    {
        foreach ($model->getMacroses() as $macro) {
            $method = 'macro' . studly_case($macro);
            $builder->macro($macro, function(Builder $builder) use ($model, $method) {
                $parameters = func_get_args();
                return call_user_func_array([$model, $method], $parameters) ?: $this;
            });
        }
    }

    /**
     * Remove the scope from the given Eloquent query builder.
     *
     * @param Builder $builder
     * @param Model   $model
     */
    public function remove(Builder $builder, Model $model)
    {
        // do nothing
    }
}