Pacuna's Blog

Utilizando scopes en Laravel 4

Un problema típico que se da en nuestros modelos es el uso de búsquedas complejas. Una manera de eliminar la complejidad en estas búsquedas es hacer uso de scopes en Laravel 4.

Supongamos que tenemos un modelo llamado Process el cual contiene los siguientes métodos para realizar búsquedas:

class Process extends Eloquent{
    protected $guarded = [];

    //encontrar los 5 procesos en ejecución que consumen más memoria
    public static function findTopRunningProcesses($limit=5)
    {
        return self::where('state', '=', 'running')
            ->orderBy('percent_cpu', 'desc')
            ->take($limit)
            ->get();
    }

   //encontrar los 5 procesos de sistema en ejecución que consumen más memoria
    public static function findTopRunningSystemProcesses($limit=5)
    {
        return self::where('state', '=', 'running')
            ->whereIn('owner', ['root', 'mysql'])
            ->orderBy('percent_cpu', 'desc')
            ->take($limit)
            ->get();
    }
}

Luego para realizar una búsqueda usamos:

$processes = Process::findTopRunningProcesses();
$processes = Process::findTopRunningSystemProcesses();

Este tipo de búsquedas se vuelven problemáticas con el tiempo ya que pueden surgir cada vez más y más combinaciones posibles, y nuestro modelo estará lleno de búsquedas repetidas y que no serán reutilizables.

Para limpiar estos métodos y además crear otros reutilizables podemos hacer uso de la funcionalidad scopes en Laravel 4. Para definir un scope, solo basta con definir una función y anteponerle la palabra scope antes del nombre real. A continuación puedes ver el código con el cual creamos scopes para cada uno de los elementos que componen las búsquedas anteriores:

class Process extends Eloquent{
    protected $guarded = [];

    public function scopeRunning($query){
        return $query->where('state', '=', 'running');
    }
    public function scopeSystem($query){

        return $query->whereIn('owner', ['root', 'mysql']);
    }
    public function scopeSorted($query){
        return $query->orderBy('percent_cpu', 'desc');
    }
    public function scopeTop($query, $limit){
        return $query->take($limit);
    }
}

Luego cuando queremos utilizar estos scopes podemos llamarlos usando:

$processes = Process::running()->get()
$processes = Process::system()->get()
$processes = Process::sorted()->get()
$processes = Process::top(5)->get()

Quizás ahora lo ganado no sea muy notorio, pero a medida que el modelo requiera búsquedas más complejas, verás como la reutilización de estas funciones va mostrando el verdadero valor de esta refactorización. Estos scopes pueden ser encadenados para formar otros scopes, lo cual permite su reutilización para ir armando consultas más complejas. Por ejemplo, si queremos reproducir las funciones con las que empezamos, podemos escribirlas así:

    public function scopeFindTopRunningSystemProcesses($query, $limit=5){
        return $query->running()->system()->sorted()->top($limit);
    }

    public function scopeFindTopRunningProcesses($query, $limit=5){
        return $query->running()->sorted()->top($limit);
    }

Y luego las utilizamos como cualquier otro scope:

$processes = Process::findTopRunningProcesses()->get();
$processes = Process::findTopRunningProcesses()->get();

Eso es todo!

Nota: el ejemplo utilizado en este tutorial, se obtuvo del libro "Rails AntiPatterns: Best Practice Ruby on Rails Refactoring" y el código fue adaptado para PHP y Laravel 4.

View original

#php #laravel

- 1 toasts