Tengo esta tabla de facturas que tiene la siguiente estructura
id | name | amount | deleted_at
2 iMac 1500 | NULL
y una tabla de pagos con la siguiente estructura
id | invoice_id | amount | deleted_at
2 2 1000 | NULL
Modelo de factura
class Invoice extends Model {
use SoftDeletes;
}
aquí está el código para eliminar la factura
public function cance(Request $request,$id)
{
$record = Invoice::findOrFail($id);
$record->delete();
return response()->json([
'success' => 'OK',
]);
}
Modelo de pagos
class Payment extends Model {
use SoftDeletes;
}
La tabla softDelete on Invoice funciona perfectamente, pero sus registros relacionados (pagos) todavía existen. ¿Cómo los elimino usando softDelete?
jedrzej.kurylo
Elocuente no proporciona la eliminación automática de objetos relacionados, por lo tanto, deberá escribir algo de código usted mismo. Afortunadamente, es bastante simple.
modelos elocuentes disparar diferentes eventos en diferentes etapas del ciclo de vida del modelo, como crear, crear, eliminar, eliminar, etc. Puede leer más sobre esto aquí: http://laravel.com/docs/5.1/eloquent#events. Lo que necesita es un oyente que se ejecutará cuando eliminado se activa el evento: este detector debe eliminar todos los objetos relacionados.
Puede registrar oyentes modelo en su modelo bota() método. El oyente debe recorrer todos los pagos de la factura que se está eliminando y debe eliminarlos uno por uno. La eliminación masiva no funcionará aquí, ya que ejecutaría la consulta SQL directamente sin pasar por los eventos del modelo.
Esto hará el truco:
class MyModel extends Model {
protected static function boot() {
parent::boot();
static::deleted(function ($invoice) {
$invoice->payments()->delete();
});
}
}
-
¡No funciona! FatalErrorException en Invoice.php línea 18: No se puede hacer que el método estático Illuminate\Database\Eloquent\Model::boot() no sea estático en la clase App\Models\Invoice
– usuario3407278
23 de agosto de 2015 a las 7:07
-
Corregido, a la función le faltaba el modificador estático
– jedrzej.kurylo
23 de agosto de 2015 a las 7:10
-
¡Eso funcionó maravillosamente! ¡Muchas gracias! ¿Esto causa algún problema de rendimiento cuando se eliminan suavemente unos 100 registros?
– usuario3407278
23 de agosto de 2015 a las 7:18
-
Tienes que buscarlos todos y luego guardarlos, por lo que son 101 consultas adicionales… En tal caso, podrías configurar delete_at manualmente para modelos relacionados, esto será menos limpio pero ejecutará solo una consulta SQL. Actualizaré la respuesta en un segundo.
– jedrzej.kurylo
23 de agosto de 2015 a las 7:20
-
Sé que esto es antiguo, pero realmente recomendaría el uso de observadores para lograrlo.
– stetim94
11 de noviembre de 2019 a las 14:01
Rwd
Puedes ir de 2 maneras con esto.
La forma más sencilla sería anular Eloquents delete()
método e incluir los modelos relacionados también, por ejemplo:
public function delete()
{
$this->payments()->delete();
return parent::delete();
}
El método anterior debería funcionar, pero parece un poco sucio y diría que no es el método preferido dentro de la comunidad.
La forma más limpia (IMO) sería aprovechar los eventos de Eloquents, por ejemplo:
public static function boot()
{
parent::boot();
static::deleting(function($invoice) {
$invoice->payments()->delete();
});
}
Cualquiera (pero no ambos) de los métodos anteriores entraría en su Invoice
modelo. Además, asumo que tiene sus relaciones configuradas en su modelo, sin embargo, no estoy seguro si permite pagos múltiples para una factura. De cualquier manera, es posible que deba cambiar el payments()
en los ejemplos a lo que sea que haya llamado la relación en su modelo de factura.
¡Espero que esto ayude!
-
Llamar directamente a delete() en la relación de pagos omitirá el modelo y no activará SoftDelete en los modelos relacionados. Se eliminarán de la base de datos. Para hacer que thek se elimine temporalmente, debe llamar a delete () en cada uno de los modelos relacionados.
– jedrzej.kurylo
23 de agosto de 2015 a las 6:38
-
También le falta la declaración de devolución en su método de eliminación anulado: debería hacer “return parent::delete();”, de lo contrario, perderá el valor que se devolvería de delete() si no lo hubiera sobrescrito.
– jedrzej.kurylo
23 de agosto de 2015 a las 6:54
-
@ jedrzej.kurylo, acabo de probar para asegurarme de que SÍ puedes usar la eliminación temporal en una relación.
– Rwd
23 de agosto de 2015 a las 7:30
-
Cierto, acabo de comprobar el código. Es bueno saberlo para el futuro 🙂
– jedrzej.kurylo
23 de agosto de 2015 a las 7:43
-
¿garantiza la transacción @RossWilson?
–Tomonso Ejang
20 de julio de 2018 a las 8:07
salar bahador
Sé que hiciste esta pregunta hace mucho tiempo, pero encontré este paquete ser muy simple y directo.
O puedes usar este paquete también es útil.
Recuerda para instalar la versión correcta dependiendo de su versión de laravel.
Debes instalarlo a través de composer:
composer require askedio/laravel5-soft-cascade ^version
En segundo paquete:
composer require iatstuti/laravel-cascade-soft-deletes
Registre el proveedor de servicios en su config/app.php.
puede leer los documentos en la página de GitHub.
Si elimina un registro, este paquete reconoce todos sus elementos secundarios y también los elimina temporalmente.
Si tiene otra relación en su modelo hijo, use su rasgo en ese modelo también. es mucho más fácil que hacerlo manualmente.
El segundo paquete tiene la ventaja de eliminar a los nietos del modelo. en algunos casos, digo que es un mejor enfoque.
-
Usé el segundo paquete y funcionó muy bien para eliminar (), pero ¿puedo hacer que funcione también para restaurar?
– hassanrazadev
26 de marzo de 2020 a las 13:03
Si la relación de su base de datos no va más allá de una sola capa, entonces simplemente podría usar eventos de Laravel para manejar sus eliminaciones suaves dentro del Modelo boot()
método de la siguiente manera:
<?php
//...
protected static boot() {
parent::boot();
static::deleting(function($invoice) {
$invoice->payments()->delete();
});
}
Sin embargo, si su estructura es más profunda que una sola capatendrá que modificar ese fragmento de código.
Digamos, por ejemplo, que no desea eliminar los pagos de una factura, sino todo el historial de pagos de un usuario determinado.
<?php
// ...
class Invoice extends Model
{
// ...
/**
* Holds the methods names of Eloquent Relations
* to fall on delete cascade or on restoring
*
* @var array
*/
protected static $relations_to_cascade = ['payments'];
protected static boot()
{
parent::boot();
static::deleting(function($resource) {
foreach (static::$relations_to_cascade as $relation) {
foreach ($resource->{$relation}()->get() as $item) {
$item->delete();
}
}
});
static::restoring(function($resource) {
foreach (static::$relations_to_cascade as $relation) {
foreach ($resource->{$relation}()->get() as $item) {
$item->withTrashed()->restore();
}
}
});
}
public function payments()
{
return $this->hasMany(Payment::class);
}
}
<?php
// ...
class User extends Model
{
// ...
/**
* Holds the methods names of Eloquent Relations
* to fall on delete cascade or on restoring
*
* @var array
*/
protected static $relations_to_cascade = ['invoices'];
protected static boot()
{
parent::boot();
static::deleting(function($resource) {
foreach (static::$relations_to_cascade as $relation) {
foreach ($resource->{$relation}()->get() as $item) {
$item->delete();
}
}
});
static::restoring(function($resource) {
foreach (static::$relations_to_cascade as $relation) {
foreach ($resource->{$relation}()->get() as $item) {
$item->withTrashed()->restore();
}
}
});
}
public function invoices()
{
return $this->hasMany(Invoice::class);
}
}
Este paradigma asegura que Laravel siga la madriguera del conejo sin importar qué tan profundo sea.
También puede utilizar Observadores de modelos:
php artisan make:Observer InvoiceOberser --model=Invoice
Creará un nuevo archivo en /app/Observers/InvoiceObserver.php con los siguientes métodos:
- creado
- actualizado
- eliminado
- restaurado
- forzadoEliminado
Solo necesita actualizar el método eliminado a esto:
public function deleted(Invoice $invoice)
{
$invoice->payments()->delete();
}
Y finalmente en /app/Providers/EventServiceProvider.php agregue estas líneas:
// On the top
use App\Models\Invoice;
use App\Observers\InvoiceObserver;
// On boot method
Invoice::observe(InvoiceObserver::class);