Wenn ich eine Zeile mit dieser Syntax lösche:
$user->delete();
Gibt es eine Möglichkeit, eine Art Rückruf anzuhängen, so dass dies z. B. automatisch geschieht:
$this->photo()->delete();
Am besten innerhalb der Modellklasse.
Wenn ich eine Zeile mit dieser Syntax lösche:
$user->delete();
Gibt es eine Möglichkeit, eine Art Rückruf anzuhängen, so dass dies z. B. automatisch geschieht:
$this->photo()->delete();
Am besten innerhalb der Modellklasse.
Antworten:
Ich glaube, dies ist ein perfekter Anwendungsfall für eloquente Ereignisse ( http://laravel.com/docs/eloquent#model-events ). Sie können das Ereignis "Löschen" verwenden, um die Bereinigung durchzuführen:
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
// this is a recommended way to declare event handlers
public static function boot() {
parent::boot();
static::deleting(function($user) { // before delete() method call this
$user->photos()->delete();
// do the rest of the cleanup...
});
}
}
Sie sollten wahrscheinlich auch das Ganze in eine Transaktion einfügen, um die referenzielle Integrität sicherzustellen.
foreach($user->photos as $photo)
, $photo->delete()
um sicherzustellen, dass jedes Kind seine Kinder auf allen Ebenen entfernt hat, anstatt nur eines, wie es aus irgendeinem Grund geschah.
Photos
has tags
und Sie dasselbe im Photos
Modell tun (dh bei deleting
method :), wird $photo->tags()->delete();
es nie ausgelöst. Aber wenn ich es zu einer for
Schleife mache und so etwas mache, werden for($user->photos as $photo) { $photo->delete(); }
die tags
auch gelöscht! nur zu
Sie können dies tatsächlich in Ihren Migrationen einrichten:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Quelle: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
Sie können auch die gewünschte Aktion für die Eigenschaften "Beim Löschen" und "Beim Aktualisieren" der Einschränkung angeben:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
Hinweis : Diese Antwort wurde für Laravel 3 geschrieben . So könnte oder könnte nicht gut in neueren Version von Laravel funktioniert.
Sie können alle zugehörigen Fotos löschen, bevor Sie den Benutzer tatsächlich löschen.
<?php
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
public function delete()
{
// delete all related photos
$this->photos()->delete();
// as suggested by Dirk in comment,
// it's an uglier alternative, but faster
// Photo::where("user_id", $this->id)->delete()
// delete the user
return parent::delete();
}
}
Ich hoffe es hilft.
$this->photos()->delete()
. Das photos()
gibt das Query Builder-Objekt zurück.
Beziehung im Benutzermodell:
public function photos()
{
return $this->hasMany('Photo');
}
Datensatz löschen und verwandte:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
Es gibt 3 Lösungsansätze:
1. Verwenden eloquenter Ereignisse beim Modellstart (siehe: https://laravel.com/docs/5.7/eloquent#events )
class User extends Eloquent
{
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->delete();
});
}
}
2. Verwenden von eloquenten Ereignisbeobachtern (siehe: https://laravel.com/docs/5.7/eloquent#observers )
Registrieren Sie den Beobachter in Ihrem AppServiceProvider wie folgt:
public function boot()
{
User::observe(UserObserver::class);
}
Fügen Sie als Nächstes eine Observer-Klasse wie folgt hinzu:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Verwenden von Fremdschlüsseleinschränkungen (siehe: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Ab Laravel 5.2 heißt es in der Dokumentation, dass diese Art von Ereignishandlern im AppServiceProvider registriert werden sollten:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
Ich nehme sogar an, sie für eine bessere Anwendungsstruktur in separate Klassen anstatt in Schließungen zu verschieben.
photos()
, müssen Sie auch vorsichtig sein - dieser Prozess löscht keine Enkelkinder, da Sie keine Modelle laden. Sie müssen eine Schleife durchführen photos
(beachten Sie, nicht photos()
) und die delete()
Methode auf ihnen als Modelle auslösen, um die löschbezogenen Ereignisse auszulösen.
Es ist besser, wenn Sie die delete
Methode dafür überschreiben . Auf diese Weise können Sie DB-Transaktionen in die delete
Methode selbst integrieren. Wenn Sie die Ereignismethode verwenden, müssen Sie Ihren delete
Methodenaufruf bei jedem Aufruf mit einer DB-Transaktion abdecken .
In Ihrem User
Modell.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
In meinem Fall war es ziemlich einfach, weil meine Datenbanktabellen InnoDB mit Fremdschlüsseln mit Cascade on Delete sind.
Wenn Ihre Fototabelle in diesem Fall eine Fremdschlüsselreferenz für den Benutzer enthält, müssen Sie lediglich das Hotel löschen und die Bereinigung durch die Datenbank durchführen. Die Datenbank löscht alle Fotodatensätze aus den Daten Base.
Ich würde die Sammlung durchlaufen und alles trennen, bevor ich das Objekt selbst lösche.
Hier ist ein Beispiel:
try {
$user = user::findOrFail($id);
if ($user->has('photos')) {
foreach ($user->photos as $photo) {
$user->photos()->detach($photo);
}
}
$user->delete();
return 'User deleted';
} catch (Exception $e) {
dd($e);
}
Ich weiß, dass es nicht automatisch ist, aber es ist sehr einfach.
Ein anderer einfacher Ansatz wäre, dem Modell eine Methode zur Verfügung zu stellen. So was:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
Dann können Sie dies einfach dort anrufen, wo Sie es brauchen:
$user->detach();
$user->delete();
Oder Sie können dies tun, wenn Sie möchten, nur eine weitere Option:
try {
DB::connection()->pdo->beginTransaction();
$photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user
$user = Geofence::where('id', '=', $user_id)->delete(); // Delete users
DB::connection()->pdo->commit();
}catch(\Laravel\Database\Exception $e) {
DB::connection()->pdo->rollBack();
Log::exception($e);
}
Hinweis: Wenn Sie nicht die Standard-Laravel-Datenbankverbindung verwenden, müssen Sie folgende Schritte ausführen:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
Um auf die ausgewählte Antwort einzugehen, müssen Sie, wenn Ihre Beziehungen auch untergeordnete Beziehungen haben, die gelöscht werden müssen, zuerst alle untergeordneten Beziehungsdatensätze abrufen und dann die delete()
Methode aufrufen , damit auch ihre Löschereignisse ordnungsgemäß ausgelöst werden.
Sie können dies problemlos mit Nachrichten höherer Ordnung tun .
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get()->each->delete();
});
}
}
Sie können die Leistung auch verbessern, indem Sie nur die Spalte Beziehungs-ID abfragen:
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get(['id'])->each->delete();
});
}
}
Ja, aber wie @supersan oben in einem Kommentar angegeben hat, wird das Modellereignis nicht ausgelöst, wenn Sie () in einem QueryBuilder löschen (), da wir das Modell selbst nicht laden und dann delete () für dieses Modell aufrufen.
Die Ereignisse werden nur ausgelöst, wenn wir die Löschfunktion für eine Modellinstanz verwenden.
Also sagte dieses Wesen:
if user->hasMany(post)
and if post->hasMany(tags)
Um die Post-Tags beim Löschen des Benutzers zu löschen, müssten wir iterieren $user->posts
und aufrufen$post->delete()
foreach($user->posts as $post) { $post->delete(); }
-> Dadurch wird das Löschereignis auf Post ausgelöst
VS
$user->posts()->delete()
-> Dadurch wird das Löschereignis beim Posten nicht ausgelöst, da das Post-Modell nicht tatsächlich geladen wird (wir führen nur eine SQL wie DELETE * from posts where user_id = $user->id
folgt aus : Daher wird das Post-Modell nicht einmal geladen).
Sie können diese Methode alternativ verwenden.
Was passieren wird, ist, dass wir alle mit der Benutzertabelle verknüpften Tabellen nehmen und die zugehörigen Daten mithilfe von Schleifen löschen
$tables = DB::select("
SELECT
TABLE_NAME,
COLUMN_NAME,
CONSTRAINT_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = 'users'
");
foreach($tables as $table){
$table_name = $table->TABLE_NAME;
$column_name = $table->COLUMN_NAME;
DB::delete("delete from $table_name where $column_name = ?", [$id]);
}
first()
der Abfrage hinzufügen, damit ich auf das Modellereignis zugreifen konnte, z. B.User::where('id', '=', $id)->first()->delete();
Quelle