141 - Add a cascading foreign key to article publications
This commit is contained in:
parent
dc64dd83b8
commit
6270c97c29
2 changed files with 157 additions and 0 deletions
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
// The column never had a constraint, so pre-existing rows may point at deleted channels.
|
||||
$orphans = DB::table('article_publications')
|
||||
->whereNotIn('platform_channel_id', DB::table('platform_channels')->select('id'));
|
||||
|
||||
if (($count = $orphans->count()) > 0) {
|
||||
Log::warning("Deleting {$count} orphaned article_publications rows before adding the channel foreign key.");
|
||||
$orphans->delete();
|
||||
}
|
||||
|
||||
Schema::table('article_publications', function (Blueprint $table) {
|
||||
$table->foreign('platform_channel_id')
|
||||
->references('id')
|
||||
->on('platform_channels')
|
||||
->onDelete('cascade');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
// Orphan rows deleted in up() are not restored.
|
||||
Schema::table('article_publications', function (Blueprint $table) {
|
||||
$table->dropForeign(['platform_channel_id']);
|
||||
});
|
||||
}
|
||||
};
|
||||
120
tests/Feature/ChannelDeletionCascadeTest.php
Normal file
120
tests/Feature/ChannelDeletionCascadeTest.php
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\Article;
|
||||
use App\Models\ArticlePublication;
|
||||
use App\Models\Feed;
|
||||
use App\Models\Keyword;
|
||||
use App\Models\PlatformAccount;
|
||||
use App\Models\PlatformChannel;
|
||||
use App\Models\Route;
|
||||
use App\Models\RouteArticle;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ChannelDeletionCascadeTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* @return array{PlatformChannel, Feed}
|
||||
*/
|
||||
private function channelWithEverything(): array
|
||||
{
|
||||
$feed = Feed::factory()->create();
|
||||
/** @var PlatformChannel $channel */
|
||||
$channel = PlatformChannel::factory()->create();
|
||||
|
||||
Route::create([
|
||||
'feed_id' => $feed->id,
|
||||
'platform_channel_id' => $channel->id,
|
||||
'priority' => 50,
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
Keyword::create([
|
||||
'feed_id' => $feed->id,
|
||||
'platform_channel_id' => $channel->id,
|
||||
'keyword' => 'brussels',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$article = Article::factory()->create(['feed_id' => $feed->id]);
|
||||
|
||||
RouteArticle::factory()->create([
|
||||
'feed_id' => $feed->id,
|
||||
'platform_channel_id' => $channel->id,
|
||||
'article_id' => $article->id,
|
||||
]);
|
||||
|
||||
ArticlePublication::factory()->create([
|
||||
'article_id' => $article->id,
|
||||
'platform_channel_id' => $channel->id,
|
||||
]);
|
||||
|
||||
$account = PlatformAccount::factory()->create();
|
||||
$channel->platformAccounts()->attach($account->id, [
|
||||
'is_active' => true,
|
||||
'priority' => 1,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return [$channel, $feed];
|
||||
}
|
||||
|
||||
public function test_deleting_a_channel_cascades_to_every_dependent_table(): void
|
||||
{
|
||||
[$channel] = $this->channelWithEverything();
|
||||
|
||||
$this->assertSame(1, Route::count());
|
||||
$this->assertSame(1, Keyword::count());
|
||||
$this->assertSame(1, RouteArticle::count());
|
||||
$this->assertSame(1, ArticlePublication::count());
|
||||
|
||||
$channel->delete();
|
||||
|
||||
$this->assertSame(0, Route::count());
|
||||
$this->assertSame(0, Keyword::count());
|
||||
$this->assertSame(0, RouteArticle::count());
|
||||
$this->assertSame(0, ArticlePublication::count());
|
||||
$this->assertDatabaseCount('platform_account_channels', 0);
|
||||
}
|
||||
|
||||
public function test_deleting_a_channel_leaves_the_feed_and_article_intact(): void
|
||||
{
|
||||
[$channel, $feed] = $this->channelWithEverything();
|
||||
|
||||
$channel->delete();
|
||||
|
||||
$this->assertDatabaseHas('feeds', ['id' => $feed->id]);
|
||||
$this->assertSame(1, Article::count());
|
||||
}
|
||||
|
||||
public function test_deleting_a_channel_leaves_other_channels_untouched(): void
|
||||
{
|
||||
[$channel] = $this->channelWithEverything();
|
||||
$other = PlatformChannel::factory()->create();
|
||||
|
||||
$channel->delete();
|
||||
|
||||
$this->assertDatabaseHas('platform_channels', ['id' => $other->id]);
|
||||
}
|
||||
|
||||
public function test_publications_for_another_channel_survive(): void
|
||||
{
|
||||
[$channel] = $this->channelWithEverything();
|
||||
|
||||
$other = PlatformChannel::factory()->create();
|
||||
$article = Article::factory()->create();
|
||||
ArticlePublication::factory()->create([
|
||||
'article_id' => $article->id,
|
||||
'platform_channel_id' => $other->id,
|
||||
]);
|
||||
|
||||
$channel->delete();
|
||||
|
||||
$this->assertSame(1, ArticlePublication::count());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue