Borrado Programado de Modelos en Laravel

Spread the love

¿Necesitas limpiar registros de tu base de datos?

Es muy común que quieras hacer limpieza regular de registros que ya no necesitas en tu base de datos.

Por ejemplo es posible que quieras borrar todos los usuarios que no han verificado su cuenta de correo después de un tiempo. y esto por lo general lo haces programando un comando para usar con el schedule de Laravel.

Pero afortunadamente en Laravel 8 esto es muy, pero muy sencillo de realizar ya que ahora cuentas con los traits Illuminate\Database\Eloquent\Prunable y Illuminate\Database\Eloquent\MassPrunable que nos permiten indicar que modelos queremos borrar de forma periódica.

pero ¿por qué dos traits?

Para responder esto vamos a ver cada uno con un ejemplo:

Prunable

Digamos que quieres borrar de forma periódica todos los usuarios que no han verificado su cuenta de correo después de un mes, así que lo único que tienes que hacer es agregar el trait Prunable a tu modelo User y luego implementar el método prunable como se ve en el código:

<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

/**
 * Class User
 *
 * @property mixed credit_card
 * @property mixed balance
 * @package App\Models
 */
class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable, Prunable;

    //..

    public function prunable()
    {
        return static::notVerified()->afterAt(now()->subMonth());
    }

    //..
}

el método prunable funciona como la condición que determina que registros serán borrados, esta condición puede ser mas compleja;

<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

/**
 * Class User
 *
 * @property mixed credit_card
 * @property mixed balance
 * @package App\Models
 */
class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable, Prunable;

    //..

    public function prunable()
    {
        return static::afterAt(now()->subMonths(6))
            ->whereDoesntHave('posts', function(Builder $query){
                $query->where('status', 'publish');
            });
    }

    //..
}

En el ejemplo se borran todos los usuario con una antigüedad de 6 meses o mas y que no tengan ningún post publicado.

Obviamente si tienes bien configurado tus relaciones con el onDelete en cascada, el usuario se borrara junto con todos los post, si no tienes configurada esta opción, puedes indicar que se borren de forma adicional usando el método pruning de la siguiente forma.

    public function pruning()
    {
        $this->posts()->delete();
        echo 'Pruning user: ' . $this->name . "\n";
    }

El método prunning se ejecuta antes de que el modelo sea borrado, así que para nuestro caso nos ayuda para borrar los posts relacionados al usuario, pero además puedes usar este método para hacer tareas adicionales, como borrar imágenes o archivos asociados al usuario o solo mostrar el nombre como en nuestro ejemplo.

¿Cómo ejecuto todo esto?

Para borrar los modelos solo tienes que ejecutar el comando artisan model:prune que de forma interna busca todos los modelos en app/Models que se indicaron como prunables y los borra de acuerdo a la condición que indicamos en el método prunable en la clase User.

Si ejecutas el comando usando el ejemplo, verías algo similar a esto:

λ artisan model:prune
Pruning user: Jody Ledner Jr.
Pruning user: Marques Vandervort
Pruning user: Prof. Unique Bayer PhD
Pruning user: Fern Bahringer
Pruning user: Ms. Kimberly Hane
Pruning user: Hobart Harber
Pruning user: Samanta Green
Pruning user: Linwood Romaguera Jr.
Pruning user: Elaina Grant
Pruning user: Dr. Frankie Sporer PhD
10 [App\Models\User] records have been pruned.

También puedes programar la limpieza de registros usando el Schedule de Laravel

/**
 * Define the application's command schedule.
 *
 * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
 * @return void
 */
protected function schedule(Schedule $schedule)
{
    $schedule->command('model:prune')->daily();
}

Si por alguna razón tus modelos no están en el directorio Models, puede indicar los modelos de forma manual.

$schedule->command('model:prune', [
    '--model' => [User::class, Post::class],
])->daily();

Mass Prunable

Este Trait se utiliza igual que el Prunable pero esta pensado para ser mas rápido ya que este trait no llama al método prunning y tampoco dispara los eventos deleting y deleted del modelo.

<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Prunable;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

/**
 * Class User
 *
 * @property mixed credit_card
 * @property mixed balance
 * @package App\Models
 */
class User extends Authenticatable implements MustVerifyEmail
{
    use HasFactory, Notifiable, MassPrunable;

    //..

    public function prunable()
    {
        return static::afterAt(now()->subMonths(6))
            ->whereDoesntHave('posts', function(Builder $query){
                $query->where('status', 'publish');
            });
    }

    //..
}

Pues bien ya solo queda que aproveches esta nueva funcionalidad en tus proyectos.

Si tienes alguna duda no olvides dejar tus comentarios 👍🏼