2013-07-31 12 views
12

Provato a usare chiavi esterne con cancelli in cascata e softDelet senza molta fortuna.Come eseguire una cascata su softdelet in Laravel4?

Ho 2 tabelle: Utenti, Eventi. Entrambe le tabelle hanno softDeletes.

Gli utenti possono avere 0..n Eventi.
eventi hanno un id_utente, utilizzato come chiave esterna sugli utenti, in questo modo:

$table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE')->onUpdate('CASCADE'); 

Il problema è che, quando elimino un utente, si arriva soft-eliminato, ma i suoi eventi non - o la cancellazione morbido o fisica eliminazione.

Sto facendo qualcosa di sbagliato, o è questo il comportamento Eloquent corretto?

In secondo luogo, se questo è il comportamento corretto, come implementare al meglio la cancellazione in cascata? forse sovrascrivendo il metodo delete() nei miei Modelli come questo ...

public function delete() 
{ 
    //delete all events... 
    __parent::delete() 
} 

?

risposta

11

La chiave esterna del DB non farà nulla perché non è stata modificata la chiave primaria in questione. Solo se si aggiorna o si elimina la chiave primaria, le righe correlate verranno modificate.

Da tutto ciò che posso trovare su questo argomento, la soluzione è utilizzare Eloquent's Model Events per ascoltare un evento di eliminazione e aggiornare le tabelle correlate.

Here's one StackOverflow question about it.

In alternativa, è possibile "estendere" il metodo delete() e comprendono la funzionalità direttamente pure. Here's an example.

+0

Cryode, ho provato il modello Eventi e funziona - ma parzialmente. Lasciatemi spiegare. La situazione reale è di 3 tabelle: utenti, eventi, commenti. Gli eventi hanno 0..n Commenti. Seguendo [link] (http://stackoverflow.com/questions/17243637/laravel-4-cascading-soft-deletes), ho aggiunto la logica nel metodo boot() a utenti ed eventi. Ora: evento SoftDelete, Previsto: softDelete Commenti, Risultato: OK; softDelete Utente, Previsto: softDelete Eventi, Risultato: OK, softDelete Commenti a questi eventi, risultato: KO. Sembra che le delezioni non si propaghino oltre il primo "livello" .. – RedSash

+1

Questa è la soluzione che ho implementato personalmente. Prima estraggo una mappa delle relazioni che tutti i miei modelli hanno tra loro e poi scrivo i metodi di cancellazione per estendere l'eloquenza all'interno dei miei modelli che dovrebbero eliminare a cascata le eliminazioni. Infine, dato che si tratta di un compito facilmente potenziabile, scrivo test unitari per dimostrare che l'effetto a cascata ha effetto solo su ciò che dovrebbe. In questo modo gli utenti eliminano le chiamate UserRecords Delete che poi chiama UserRecordsMeta delete e così via. – carbontwelve

0

Se ho capito bene, stai provando a mettere in cascata i softdelet in entrambe le tabelle?

Credo di farlo con ON UPDATE CASCADE non è l'approccio corretto. Proverò a spiegare perché ...

Per tentare di fare ciò è necessario creare una relazione tra chiave esterna e chiave composta.

vale a dire che è necessario collegare (events.user_id e deleted_at) a (user.id e delete_at). Ne cambi uno, aggiornerà l'altro.

Per prima cosa è necessario aggiungere una regola predefinita alle colonne deleted_at, poiché non è possibile collegare valori null.

Quindi aggiungere ai vostri migrazioni per entrambe le tabelle ... $table->softDeletes()->default('0000-00-00 00:00:00');

Aggiungi alla tua tabella utente utilizzando una chiave univoca 'id' e 'deleted_at'

Schema::table('users; function($table) { $table->unique(array('id','deleted_at')) });

Poi negli eventi tavolo creare una chiave esterna in questo modo (link alla chiave univoca)

Schema::table('events; function($table) { $table->foreign(array('user_id','deleted_at'),'events_deleted_at_foreign_key')-> }->references(array('id','deleted_at'))->on('users')->onUpdate('CASCADE'));

Esegui questo, ora dovresti trovare se elimini il tuo utente, eliminerà i suoi "eventi".

Tuttavia, se si tenta di eliminare un evento, il test della chiave esterna non verrà eseguito correttamente. Perché potresti chiedere !?

Bene, quello che stai facendo è creare una relazione Parent Child usando id, deleted_at in entrambe le tabelle. L'aggiornamento del genitore aggiornerà il bambino. E la relazione è ininterrotta. Tuttavia, se si aggiorna il figlio, la relazione è ora interrotta, lasciando il bambino come un orfano nella tabella. Ciò fallisce la restrizione della chiave esterna.

Sooo una risposta a lungo termine, ma si spera che una buona spiegazione del motivo per cui ciò che si sta tentando di fare non funzionerà e risparmiare un sacco di tempo cercando di farlo con ON AGGIORNAMENTO CASCADE. Entrate nei TRIGGER, e TRIGGER una funzione per gestire ciò che state cercando di fare, o gestirlo nella vostra applicazione. Personalmente lo farei con TRIGGERS in modo che il database rimanga la sua entità e non dover fare affidamento su nulla per mantenere l'integrità dei dati.

delimiter // 

CREATE TRIGGER soft_delete_child DOPO L'AGGIORNAMENTO SULLA db.users per ogni riga BEGIN SE NEW.deleted_at <> OLD.deleted_at ALLORA eventi UPDATE SET deleted_at = NEW.deleted_at DOVE events.user_id = NEW.id; FINE IF; END;

// delimitatore;

2

Stai pensando troppo a questo.

O semplicemente eliminare gli eventi a destra prima di eliminare gli utenti:

$user->events()->delete(); 
$user->delete(); 

o si crea un cliente funzione di cancellazione del modello di utente:

public function customDelete(){ 
    $this->events()->delete(); 
    return $this->delete(); 
} 

Si potrebbe anche aggiungere un modello di osservatore e orologio per l'eliminazione o l'eliminazione dell'evento, ma nello scenario che hai menzionato sopra, i due metodi precedenti sarebbero una soluzione più semplice.

http://laravel.com/docs/4.2/eloquent#model-observers

Problemi correlati