Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform an action based on Eloquent 'deleting' event when executing a mass delete statement in Laravel 5.4?

There are 2 Eloquent models in my project: App\StoredFile and App\StoredImageSize.

The StoredFile model is responsible for keeping uploaded files info. If the uploaded file would be an image, then 2 thumbnails will be stored in storage and their attributes in DB (StoredImageSize model).

Relationships are as below:

StoredFile.php:

public function sizes()
{
    return $this->hasMany('App\StoredImageSize');
}

StoredImageSize.php:

public function originalImage()
{
    return $this->belongTo('App\StoredFile');
}

StoredImageSize has an observer class (StoredImageSizeObserver) which has been registered through a ServiceProvider and works well.

The problem is that I want to delete the actual file from storage (in my case hard disk) when deleting the subsequent row from database.

StoredImageSizeObserver.php:

public function deleting(StoredImageSize $file)
{
    Storage::delete($file->server_url);
}

The logic is something like this:

$file = StoredFile::find(1);
$file->sizes()->delete();    // it does delete all related rows from 'StoredImageSize' but not files from storage
$file->delete()

actually it never hits deleting method from StoredImageSizeObserver class as Laravel documentation says:

When executing a mass delete statement via Eloquent, the deleting and deleted model events will not be fired for the deleted models. This is because the models are never actually retrieved when executing the delete statement.

So, How should I tackle this situation? Any solution or recommendation would be appreciated.

like image 951
ironcladgeek Avatar asked Jul 25 '17 14:07

ironcladgeek


People also ask

What is Cascade on Delete Laravel?

Laravel Soft Cascade is a package that makes it easy to perform soft cascade deletes and restores on related models using soft deleting.

How do I delete a record in Laravel?

Step 1: Create Controller UserController by executing this command. Step 2: We can delete records in two ways. Second Method: The second way is to delete using the Laravel delete Function and User Model (Easy one). ->name( 'users.


2 Answers

In the StoredFile model add the following method, you have two choices :

  • the first :

      protected static function boot() {
          parent::boot();
          static::deleting(function(StoredFile $fileToDelete) {
              foreach ($fileToDelete->sizes as $size)
              {
                  $size->delete();
              }
    
              $fileToDelete->delete()
          });
      }
    
  • The second :

      protected static function boot() {
          parent::boot();
          static::deleting(function(StoredFile $fileToDelete) {
              $size_ids = $fileToDelete->sizes()->lists('id');
              StoredImageSize::whereIn($size_ids)->delete();
    
              $fileToDelete->delete()
          });
      }
    

Or create an observer for the StoredFile model not for the StoredImageSize, because as you said about the doc the delete event will not be cascaded for StoredImageSize elements ! And do the same as above in the deleting method of StoredFileObserver :)

like image 83
Maraboc Avatar answered Oct 17 '22 18:10

Maraboc


Assuming that the App\StoredImageSizehas reference to the physical files on the disk

$file = StoredFile::find(1);
foreach($file->sizes as $size){
    Storage::delete($size->file_location);
}
$file->sizes()->delete();
$file->delete()
like image 43
YKalinde Avatar answered Oct 17 '22 17:10

YKalinde