diff --git a/module/Core/migrations/Version20240226214216.php b/module/Core/migrations/Version20240226214216.php new file mode 100644 index 00000000..fadea110 --- /dev/null +++ b/module/Core/migrations/Version20240226214216.php @@ -0,0 +1,126 @@ +skipIf(! $schema->hasTable('device_long_urls')); + + // First create redirect conditions for all device types + $qb = $this->connection->createQueryBuilder(); + $devices = $qb->select('device_type') + ->distinct() + ->from('device_long_urls') + ->executeQuery(); + + $conditionIds = []; + while ($deviceRow = $devices->fetchAssociative()) { + $deviceType = $deviceRow['device_type']; + $conditionQb = $this->connection->createQueryBuilder(); + $conditionQb->insert('redirect_conditions') + ->values([ + 'name' => ':name', + 'type' => ':type', + 'match_value' => ':match_value', + 'match_key' => ':match_key', + ]) + ->setParameters([ + 'name' => 'device-' . $deviceType, + 'type' => 'device', + 'match_value' => $deviceType, + 'match_key' => null, + ]) + ->executeStatement(); + $id = $this->connection->lastInsertId(); + $conditionIds[$deviceType] = $id; + } + + // Then insert a rule per every device_long_url, and link it to the corresponding condition + $qb = $this->connection->createQueryBuilder(); + $rules = $qb->select('short_url_id', 'device_type', 'long_url') + ->from('device_long_urls') + ->executeQuery(); + + $priorities = []; + while ($ruleRow = $rules->fetchAssociative()) { + $shortUrlId = $ruleRow['short_url_id']; + $priority = $priorities[$shortUrlId] ?? 1; + + $ruleQb = $this->connection->createQueryBuilder(); + $ruleQb->insert('short_url_redirect_rules') + ->values([ + 'priority' => ':priority', + 'long_url' => ':long_url', + 'short_url_id' => ':short_url_id', + ]) + ->setParameters([ + 'priority' => $priority, + 'long_url' => $ruleRow['long_url'], + 'short_url_id' => $shortUrlId, + ]) + ->executeStatement(); + $ruleId = $this->connection->lastInsertId(); + + $relationQb = $this->connection->createQueryBuilder(); + $relationQb->insert('redirect_conditions_in_short_url_redirect_rules') + ->values([ + 'redirect_condition_id' => ':redirect_condition_id', + 'short_url_redirect_rule_id' => ':short_url_redirect_rule_id', + ]) + ->setParameters([ + 'redirect_condition_id' => $conditionIds[$ruleRow['device_type']], + 'short_url_redirect_rule_id' => $ruleId, + ]) + ->executeStatement(); + + $priorities[$shortUrlId] = $priority + 1; + } + } + + public function postUp(Schema $schema): void + { + $this->skipIf(! $schema->hasTable('device_long_urls')); + $schema->dropTable('device_long_urls'); + } + + public function down(Schema $schema): void + { + $this->skipIf($schema->hasTable('device_long_urls')); + + $table = $schema->createTable('device_long_urls'); + $table->addColumn('id', Types::BIGINT, [ + 'unsigned' => true, + 'autoincrement' => true, + 'notnull' => true, + ]); + $table->setPrimaryKey(['id']); + + $table->addColumn('device_type', Types::STRING, ['length' => 255]); + $table->addColumn('long_url', Types::TEXT, ['length' => 2048]); + $table->addColumn('short_url_id', Types::BIGINT, [ + 'unsigned' => true, + 'notnull' => true, + ]); + + $table->addForeignKeyConstraint('short_urls', ['short_url_id'], ['id'], [ + 'onDelete' => 'CASCADE', + 'onUpdate' => 'RESTRICT', + ]); + + $table->addUniqueIndex(['device_type', 'short_url_id'], 'UQ_device_type_per_short_url'); + } + + public function isTransactional(): bool + { + return ! ($this->connection->getDatabasePlatform() instanceof MySQLPlatform); + } +} diff --git a/module/Core/src/RedirectRule/Model/RedirectConditionType.php b/module/Core/src/RedirectRule/Model/RedirectConditionType.php index 764c5a2b..51076068 100644 --- a/module/Core/src/RedirectRule/Model/RedirectConditionType.php +++ b/module/Core/src/RedirectRule/Model/RedirectConditionType.php @@ -4,7 +4,7 @@ namespace Shlinkio\Shlink\Core\RedirectRule\Model; enum RedirectConditionType: string { -// case DEVICE = 'device'; + case DEVICE = 'device'; case LANGUAGE = 'language'; case QUERY_PARAM = 'query'; }