* @category Class * @license https://www.gnu.org/licenses/lgpl-3.0.en.html GNU Lesser General Public License * @link www.splendidbear.org * @since 2026. 04. 20. */ final class Version20260420130000 extends AbstractMigration { public function getDescription(): string { return 'Create recent_battles materialized view for profile recent games list.'; } public function up(Schema $schema): void { $this->addSql(' CREATE MATERIALIZED VIEW recent_battles AS SELECT pg.id AS game_id, pg.uuid::text AS uuid, u.id AS user_id, CASE WHEN pg.red_id = u.id THEN true ELSE false END AS is_red, COALESCE(ru.username, ra.user_name, \'Guest\') AS red_name, COALESCE(bu.username, ba.user_name, \'Guest\') AS blue_name, ru.avatar_path AS red_avatar_path, bu.avatar_path AS blue_avatar_path, pg.red_points, pg.blue_points, pg.red_exploded_bomb, pg.blue_exploded_bomb, pg.resign, COALESCE(pg.red_bonus_points, 0.0) AS red_bonus_points, COALESCE(pg.blue_bonus_points, 0.0) AS blue_bonus_points, COALESCE(pg.red_bonus_stats, \'[]\'::json) AS red_bonus_stats, COALESCE(pg.blue_bonus_stats, \'[]\'::json) AS blue_bonus_stats, CASE WHEN pg.red_id = u.id AND pg.resign = \'red\' THEN \'loss\' WHEN pg.blue_id = u.id AND pg.resign = \'blue\' THEN \'loss\' WHEN pg.red_id = u.id AND pg.resign = \'blue\' THEN \'win\' WHEN pg.blue_id = u.id AND pg.resign = \'red\' THEN \'win\' WHEN pg.red_id = u.id AND pg.red_points IS NOT NULL AND pg.blue_points IS NOT NULL AND pg.red_points > pg.blue_points THEN \'win\' WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points IS NOT NULL AND pg.blue_points > pg.red_points THEN \'win\' WHEN pg.red_id = u.id AND pg.red_points IS NOT NULL AND pg.blue_points IS NOT NULL AND pg.red_points < pg.blue_points THEN \'loss\' WHEN pg.blue_id = u.id AND pg.blue_points IS NOT NULL AND pg.red_points IS NOT NULL AND pg.blue_points < pg.red_points THEN \'loss\' ELSE \'draw\' END AS result, -- Whether the opponent in this game is an anonymous guest (no app_user account) CASE WHEN pg.red_id = u.id AND pg.blue_id IS NULL THEN true WHEN pg.blue_id = u.id AND pg.red_id IS NULL THEN true ELSE false END AS opp_is_guest, pg.created, pg.updated FROM app_user u JOIN played_game pg ON pg.red_id = u.id OR pg.blue_id = u.id LEFT JOIN app_user ru ON ru.id = pg.red_id LEFT JOIN app_user bu ON bu.id = pg.blue_id LEFT JOIN gamer ra ON ra.id = pg.red_anon LEFT JOIN gamer ba ON ba.id = pg.blue_anon '); $this->addSql('CREATE UNIQUE INDEX idx_recent_battles_pk ON recent_battles (user_id, game_id)'); $this->addSql('CREATE INDEX idx_recent_battles_user_upd ON recent_battles (user_id, updated DESC)'); $this->addSql(' CREATE OR REPLACE FUNCTION refresh_recent_battles() RETURNS TRIGGER AS $$ BEGIN REFRESH MATERIALIZED VIEW CONCURRENTLY recent_battles; RETURN NULL; END; $$ LANGUAGE plpgsql '); $this->addSql(' CREATE TRIGGER trigger_refresh_recent_battles AFTER INSERT OR UPDATE OR DELETE ON played_game FOR EACH STATEMENT EXECUTE FUNCTION refresh_recent_battles() '); } public function down(Schema $schema): void { $this->addSql('DROP TRIGGER IF EXISTS trigger_refresh_recent_battles ON played_game'); $this->addSql('DROP FUNCTION IF EXISTS refresh_recent_battles()'); $this->addSql('DROP MATERIALIZED VIEW IF EXISTS recent_battles'); } }