Denislav Karagiozov
Denislav Karagiozov

Reputation: 393

Laravel - Collection::delete method does not exist

I am trying to test the boot() static::deleting method, which should fire when a model is deleted through Eloquent.

The command in tinker App\User::find(6)->delete(); returns a 'method [...]Collection::delete does not exist'.

If I try to use App\User::where('id', 6)->delete(); then the static::deleting method does not get triggered since Eloquent is not loaded. If I load Eloquent with ->first() then I get the same error that states method does not exist.

Here is the entire user model

 <?php

namespace App;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;

class User extends Authenticatable
{
    use Notifiable;

    public function profile() {
        return $this->hasOne(Profile::class);
    }

    public function posts() {
        return $this->hasMany(Post::class);
    }

    public function tempUploads() {
        return $this->hasMany(TempUploads::class);
    }
    
    protected static function boot() {
        parent::boot();
        
        static::created(function ($user) {
            $user->profile()->create(['id' => $user->username, 'avatar' => '/storage/avatars/edit-profile.png']);
            mkdir(public_path() . "/storage/images/" . $user->username , 0755);

            // $data = [
            //  'user_id' => $user->username
            // ];
            // Mail::to($user->email)->send(new WelcomeMail($data));
        });

        static::deleting(function ($user) {
            $user->posts->delete();
            if ($user->profile->avatar != '/storage/avatars/edit-profile.png') {
                if ($user->profile->cover != NULL && $user->profile->cover != '') {
                    $oldAvatar = $_SERVER['DOCUMENT_ROOT'] . $user->profile->avatar;
                    $oldCover = $_SERVER['DOCUMENT_ROOT'] . $user->profile->cover;
                    if (is_file($oldAvatar) && is_file($oldCover)) {
                        unlink($oldAvatar);
                        unlink($oldCover);
                    } else {
                        die("Грешка при изтриване на стария файл. File does not exist in profile deleting method.");
                    }
                }
            }
            $user->profile->delete();
        });
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name', 'username', 'email', 'password',
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password', 'remember_token',
    ];

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}

I have spent hours now looking through google for possible solutions but nothing has yet.

How should I properly delete a User model while triggering the boot deleting method ?

Upvotes: 4

Views: 10189

Answers (6)

khateeb
khateeb

Reputation: 486

$user->posts()->delete()   will work

$user->posts->delete()   will not work

Because if you not put '()' then it becomes collection instead of query. delete() works on query

Upvotes: 1

0gravity000
0gravity000

Reputation: 149

You can use higher order messages as well:

$user->posts->each->delete();

Upvotes: 2

Dipesh C
Dipesh C

Reputation: 33

$user->posts->map->delete()

Upvotes: 1

Daniel Juric
Daniel Juric

Reputation: 148

I used this in my Controller File to delete the Database Entry:

public function destroy(Item $id) {
        $id->destroy($id->id);
        //return view('inv.delete', compact('id'));
        return redirect('/inv');
    }

Upvotes: 0

lagbox
lagbox

Reputation: 50491

In your deleting listener you are trying to delete something else, which is a Collection which is causing the error.

$user->posts is a relationship to Posts which is a plural which is a hasMany relationship (most likely) so it returns a Collection always. Collections do not have a delete method. You will have to iterate through the collection and call delete on each Post

// calling `delete()` on a Collection not a Model
// will throw the error you see
$user->posts->delete();

// iterate through the Collection
foreach ($user->posts as $post) {
    $post->delete();
}

Side Note: you can not do any action in bulk with Models and queries and have the events be fired. All Model events are based on single instances of the Models. A direct query bypasses the Model.

Upvotes: 7

Dan
Dan

Reputation: 5358

You can optimise lagbox's answer by using only one query to delete all of the posts. In his example he's executing a delete query for every post attached to the user.

For a single delete query either use the query builder of the relationship directly:

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

or use the pluck method of the collection and a separate query:

Post::where('id', $user->posts->pluck('id'))->delete();

Upvotes: 6

Related Questions