Automatic deletion of related rows in Laravel (Eloquent ORM)

When I delete a line using this syntax:

$user->delete(); 

Is there a way to bind the sorts callback so that, for example, do it automatically:

 $this->photo()->delete(); 

Preferably inside the model class.

+126
php laravel
Jan 05 '13 at 16:55
source share
13 answers

I believe this is an ideal use case for Eloquent events ( http://laravel.com/docs/eloquent#model-events ). You can use the "delete" event to clear:

 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... }); } } 

You probably should also put all this in a transaction to ensure referential integrity.

+167
Nov 20 '13 at 22:05
source share

In fact, you can set this in your migrations:

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

Source: http://laravel.com/docs/5.1/migrations#foreign-key-constraints

You can also specify the desired action for the “on delete” and “on update” restriction properties:

 $table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade'); 
+174
Jan 05 '13 at
source share

Note This answer was written for Laravel 3 . Therefore, it may or may not work well in a later version of Laravel.

You can delete all related photos before deleting the user.

 <?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 an uglier alternative, but faster // Photo::where("user_id", $this->id)->delete() // delete the user return parent::delete(); } } 

Hope this helps.

+45
Jan 05 '13 at 17:23
source share

Relationship in user model:

 public function photos() { return $this->hasMany('Photo'); } 

Delete record and link:

 $user = User::find($id); // delete related $user->photos()->delete(); $user->delete(); 
+23
Jan 06 '15 at 19:13
source share

As in Laravel 5.2, the documentation states that these types of event handlers must be registered with AppServiceProvider:

 <?php class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { User::deleting(function ($user) { $user->photos()->delete(); }); } 

I even suggest moving them to separate classes instead of closing for a better application structure.

+12
May 18 '16 at 18:15
source share

There are 3 approaches to solving this problem:

1. The use of eloquent events when loading the model (link: 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. Using the Eloquent Event Observers (link: https://laravel.com/docs/5.7/eloquent#observers )

In your AppServiceProvider, register an observer as follows:

 public function boot() { User::observe(UserObserver::class); } 

Then add the Observer class as follows:

 class UserObserver { public function deleting(User $user) { $user->photos()->delete(); } } 

3. Use of foreign key restrictions (link: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )

 $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 
+8
Sep 14 '18 at 23:02
source share

In my case, it was pretty simple, because my database tables are InnoDB with foreign keys with Cascade on Delete.

So, in this case, if your photo table contains a link to a foreign key for the user, than all you need to do is delete the hotel, and the database will clear it, the database will delete all photo entries from the database.

+1
Jan 07 '15 at 8:11
source share

I would iterate over the entire collection, disconnecting everything before deleting the object itself.

here is an example:

 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); } 

I know that this is not automatic, but it is very simple.

Another simple approach is to provide the model with a method. Like this:

 public function detach(){ try { if ($this->has('photos')) { foreach ($this->photos as $photo) { $this->photos()->detach($photo); } } } catch (Exception $e) { dd($e); } } 

Then you can just call where you need to:

 $user->detach(); $user->delete(); 
+1
Feb 21 '18 at 20:40
source share

To do this, it is better to override the delete method. Thus, you can include database transactions in the delete method itself. If you use the event method, you will have to cover the call to the delete method with a database transaction every time you call it.

In your User model.

 public function delete() { \DB::beginTransaction(); $this ->photo() ->delete() ; $result = parent::delete(); \DB::commit(); return $result; } 
+1
Jul 04 '19 at 5:48
source share

To clarify the selected answer, if your relationships also have child relationships that you want to delete, you must first retrieve all the records of the child relationships and then call the delete() method so that their delete events also fire correctly.

You can do this easily with higher order messages .

 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(); }); } } 

You can also improve performance by querying only the relationship ID column:

 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(); }); } } 
0
Jan 16 '19 at 20:06
source share

You can use this method as an alternative.

What will happen is that we take all the tables associated with the user table and delete the related data using a loop

 $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]); } 
0
Apr 17 '19 at 10:53 on
source share

Or you can do this if you want, another 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); } 

Note that if you are not using the default larvel db connection, you need to do the following:

 DB::connection('connection_name')->pdo->beginTransaction(); DB::connection('connection_name')->pdo->commit(); DB::connection('connection_name')->pdo->rollBack(); 
-one
Feb 22 '13 at 7:47
source share

yes, but as @supersan pointed out in the comment above, if you delete () in QueryBuilder, the model event will not be fired because we do not load the model itself and then call delete () for this model.

Events are triggered only if we use the delete function in the model instance.

So this being said:

 if user->hasMany(post) and if post->hasMany(tags) 

to remove the tags $user->posts when deleting a user, we will need to iterate over $user->posts and call $post->delete()

foreach($user->posts as $post) { $post->delete(); } foreach($user->posts as $post) { $post->delete(); } foreach($user->posts as $post) { $post->delete(); } foreach($user->posts as $post) { $post->delete(); } → this will trigger a delete event in the post

V.S.

$user->posts()->delete() → this will not trigger a message delete event because we are not actually loading the message model (we only run SQL- DELETE * from posts where user_id = $user->id : DELETE * from posts where user_id = $user->id and thus the Post model doesn’t even load)

-one
Sep 24 '18 at 12:38
source share



All Articles