Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions app/Support/DatabaseView.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace App\Support;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class DatabaseView
{
public static function createOrReplace(string $viewName): void
{
$sql = file_get_contents(database_path("views/{$viewName}.sql"));
DB::statement("CREATE OR REPLACE VIEW {$viewName} AS\n{$sql}");
}

public static function warnOnRollback(string $viewName): void
{
Log::warning("View migration rollback is a no-op for '{$viewName}'. Run migrate:fresh to rebuild.");
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<?php

use App\Support\DatabaseView;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
Expand All @@ -17,48 +17,12 @@ public function up(): void
);
});

DB::statement(<<<'SQL'
CREATE VIEW media_tracking_summary AS
SELECT
m.id AS media_id,
m.title,
m.year,
mt.name AS media_type,
c.name AS creator,
COALESCE(
(
SELECT met.name
FROM media_events me
JOIN media_event_types met ON me.media_event_type_id = met.id
WHERE me.media_id = m.id
AND met.name != 'comment'
ORDER BY me.occurred_at DESC
LIMIT 1
),
'backlog'
) AS current_status,
agg.started_at,
agg.finished_at,
agg.abandoned_at
FROM media m
LEFT JOIN media_types mt ON m.media_type_id = mt.id
LEFT JOIN creators c ON m.creator_id = c.id
LEFT JOIN LATERAL (
SELECT
-- ASC: we want the earliest start date, not the most recent restart
MIN(me.occurred_at) FILTER (WHERE met.name = 'started') AS started_at,
MAX(me.occurred_at) FILTER (WHERE met.name = 'finished') AS finished_at,
MAX(me.occurred_at) FILTER (WHERE met.name = 'abandoned') AS abandoned_at
FROM media_events me
JOIN media_event_types met ON me.media_event_type_id = met.id
WHERE me.media_id = m.id
) agg ON TRUE
SQL);
DatabaseView::createOrReplace('media_tracking_summary');
}

public function down(): void
{
DB::statement('DROP VIEW IF EXISTS media_tracking_summary');
DatabaseView::warnOnRollback('media_tracking_summary');

Schema::table('media_events', function (Blueprint $table) {
$table->dropIndex('media_events_tracking_summary_idx');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

use App\Support\DatabaseView;
use Illuminate\Database\Migrations\Migration;

return new class extends Migration
{
public function up(): void
{
DatabaseView::createOrReplace('media_tracking_summary');
}

public function down(): void
{
DatabaseView::warnOnRollback('media_tracking_summary');
}
};
34 changes: 34 additions & 0 deletions database/views/media_tracking_summary.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
SELECT
m.id AS media_id,
m.title,
m.year,
mt.name AS media_type,
c.name AS creator,
COALESCE(
(
SELECT met.name
FROM media_events me
JOIN media_event_types met ON me.media_event_type_id = met.id
WHERE me.media_id = m.id
AND met.name != 'comment'
ORDER BY me.occurred_at DESC
LIMIT 1
),
'backlog'
) AS current_status,
agg.started_at,
agg.finished_at,
agg.abandoned_at
FROM media m
LEFT JOIN media_types mt ON m.media_type_id = mt.id
LEFT JOIN creators c ON m.creator_id = c.id
LEFT JOIN LATERAL (
SELECT
-- ASC: we want the earliest start date, not the most recent restart
MIN(me.occurred_at) FILTER (WHERE met.name = 'started') AS started_at,
MAX(me.occurred_at) FILTER (WHERE met.name = 'finished') AS finished_at,
MAX(me.occurred_at) FILTER (WHERE met.name = 'abandoned') AS abandoned_at
FROM media_events me
JOIN media_event_types met ON me.media_event_type_id = met.id
WHERE me.media_id = m.id
) agg ON TRUE
Loading