125 lines
5.5 KiB
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');
|
|
}
|
|
}
|