DanVeira DanVeira - 17 days ago 9
PHP Question

Delete hasManyThrough relatioship rows using Laravel's Eloquent

I have three models,

Advertiser
,
PtcAd
, and
PtcCampaign
. When deleting a Advertiser I want to delete all related PtcAds and PtcCampaigns. The Advertiser has many PtcCampaigns through PtcAds.

Advertiser Model

use SoftDeletes;

protected $dates = ['deleted_at'];

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

public function ptcCampaigns()
{
return $this->hasManyThrough('App\PtcCampaign', 'App\PtcAd');
}

public function delete()
{
$this->ptcAds()->delete();
// I'VE TRIED WITH AND WITHOUT THIS
$this->ptcCampaigns()->delete();

return parent::delete();
}


PtcAd Model

use SoftDeletes;

protected $fillable = ['advertiser_id', 'title'];

protected $dates = ['deleted_at'];

public function advertiser()
{
return $this->belongsTo('App\Advertiser');
}

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

public function delete()
{
$this->ptcCampaigns()->delete();

return parent::delete();
}


PtcCampaign Model

use SoftDeletes;

public $timestamps = false;

protected $fillable = ['ptc_ad_id', 'clicks'];

protected $dates = ['paused_at', 'deleted_at'];

public function ptcAd()
{
return $this->belongsTo('App\PtcAd');
}


My tests:

public function test_delete_advertiser()
{
$advertiser = factory(Advertiser::class)->create();

$ptcAd = factory(PtcAd::class)->create(['advertiser_id' => $advertiser->id]);

$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);

$this->assertTrue($advertiser->delete());
$this->assertFalse(Advertiser::all()->contains($advertiser));
$this->assertFalse(PtcAd::all()->contains($ptcAd));

// THE FOLLOWING TEST DOESN'T WORK!
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}

// ALL OF THE FOLLOWING TESTS WORK!
public function test_delete_ad()
{
$ptcAd = factory(PtcAd::class)->create();

$ptcCampaign = factory(PtcCampaign::class)->create(['ptc_ad_id' => $ptcAd->id]);

$this->assertTrue($ptcAd->delete());
$this->assertFalse(PtcAd::all()->contains($ptcAd));
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign));
}


The
$this->assertFalse(PtcCampaign::all()->contains($ptcCampaign))
in the
test_delete_advertiser()
test fails, why?

I have more tests to make sure all the relationships work so I really don't know what could possibly be wrong. My next attempt would be to make
foreach
in the Advertiser's delete() method but maybe there's something simpler and I want to understand why this doesn't work.

Answer

It looks the problem is with the sequence of delete statement.

Try by changing the sequence like below:

public function delete()
{
    $this->ptcCampaigns()->delete();

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

    return parent::delete();
}
Comments