Private
Public Access
1
0
Files
MineSeeker/src/Migrations/2026/04/Version20260420130000.php

125 lines
5.5 KiB
PHP

<?php declare(strict_types=1);
/**
* This file is part of the SplendidBear Websites' projects.
*
* Copyright (c) 2026 @ www.splendidbear.org
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Migrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Class Version20260420130000
*
* Creates recent_battles materialized view for ProfileController optimization.
*
* @package App\Migrations
* @author Lang <https://www.splendidbear.org>
* @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');
}
}