Missing seasons added

This commit is contained in:
Ilya Rogozhin
2026-06-10 12:30:07 +02:00
parent a752b27bc9
commit 2810d3a809
12 changed files with 343 additions and 163 deletions
-98
View File
@@ -1,98 +0,0 @@
-- MySQL dump 10.13 Distrib 9.7.0, for Linux (x86_64)
--
-- Host: localhost Database: test_fhr
-- ------------------------------------------------------
-- Server version 9.7.0
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!50503 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
SET @MYSQLDUMP_TEMP_LOG_BIN = @@SESSION.SQL_LOG_BIN;
SET @@SESSION.SQL_LOG_BIN= 0;
--
-- GTID state at the beginning of the backup
--
SET @@GLOBAL.GTID_PURGED=/*!80000 '+'*/ '508e7ea7-63f5-11f1-9d13-5242ca7f4bf5:1-285';
--
-- Table structure for table `clubs`
--
DROP TABLE IF EXISTS `clubs`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `clubs` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name_ru` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name_en` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`city_ru` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`city_en` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `clubs`
--
LOCK TABLES `clubs` WRITE;
/*!40000 ALTER TABLE `clubs` DISABLE KEYS */;
INSERT INTO `clubs` VALUES (1,'Металлург','Metallurg','Магнитогорск','Magnitogorsk','2026-06-09 14:08:31','2026-06-09 14:08:31'),(2,'Северсталь','Severstal','Сочи','Sochi','2026-06-09 14:08:31','2026-06-09 14:08:31'),(3,'Сибирь','Sibir','Санкт-Петербург','St. Petersburg','2026-06-09 14:08:31','2026-06-09 14:08:31'),(4,'Спартак','Spartak','Тольятти','Tolyatti','2026-06-09 14:08:31','2026-06-09 14:08:31'),(5,'Сочи','Sochi','Астана','Astana','2026-06-09 14:08:31','2026-06-09 14:08:31'),(6,'Салават Юлаев','Salavat Yulaev','Уфа','Ufa','2026-06-09 14:08:31','2026-06-09 14:08:31'),(7,'Металлург','Metallurg','Новосибирск','Novosibirsk','2026-06-09 14:08:31','2026-06-09 14:08:31'),(8,'Салават Юлаев','Salavat Yulaev','Хабаровск','Khabarovsk','2026-06-09 14:08:31','2026-06-09 14:08:31'),(9,'Металлург','Metallurg','Тольятти','Tolyatti','2026-06-09 14:08:31','2026-06-09 14:08:31'),(10,'СКА','SKA','Владивосток','Vladivostok','2026-06-09 14:08:31','2026-06-09 14:08:31'),(11,'Динамо','Dynamo','Владивосток','Vladivostok','2026-06-09 14:08:31','2026-06-09 14:08:31'),(12,'Ак Барс','Ak Bars','Санкт-Петербург','St. Petersburg','2026-06-09 14:08:31','2026-06-09 14:08:31');
/*!40000 ALTER TABLE `clubs` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `players`
--
DROP TABLE IF EXISTS `players`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!50503 SET character_set_client = utf8mb4 */;
CREATE TABLE `players` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`full_name_ru` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`full_name_en` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`weight` int unsigned NOT NULL,
`height` int unsigned NOT NULL,
`squad_number` tinyint NOT NULL,
`club_id` bigint unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `players_club_id_squad_number_unique` (`club_id`,`squad_number`),
CONSTRAINT `players_club_id_foreign` FOREIGN KEY (`club_id`) REFERENCES `clubs` (`id`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `players`
--
LOCK TABLES `players` WRITE;
/*!40000 ALTER TABLE `players` DISABLE KEYS */;
INSERT INTO `players` VALUES (1,'Данис Зарипов','Danis Zaripov',72,172,45,9,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(2,'Илья Брызгалов','Ilya Bryzgalov',73,168,89,3,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(3,'Александр Семин','Alexander Semin',102,163,43,3,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(4,'Василий Подколзин','Vasily Podkolzin',96,173,94,11,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(5,'Егор Чинахов','Yegor Chinakhov',85,187,13,2,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(6,'Александр Георгиев','Alexandar Georgiev',97,172,97,8,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(7,'Александр Еременко','Alexander Yeryomenko',70,189,35,12,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(8,'Павел Бучневич','Pavel Buchnevich',96,190,61,10,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(9,'Антон Худобин','Anton Khudobin',85,199,5,4,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(10,'Александр Еременко','Alexander Yeryomenko',82,173,91,12,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(11,'Даниил Мироманов','Daniil Miromanov',100,177,22,3,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(12,'Александр Степанов','Alexander Stepanov',89,208,83,2,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(13,'Евгений Малкин','Evgeni Malkin',86,179,12,10,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(14,'Николай Хабибулин','Nikolai Khabibulin',94,203,71,10,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(15,'Александр Могильный','Alexander Mogilny',101,166,91,5,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(16,'Михаил Сергачев','Mikhail Sergachev',86,183,37,1,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(17,'Илья Ковальчук','Ilya Kovalchuk',68,171,10,2,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(18,'Николай Кулемин','Nikolay Kulemin',78,205,58,2,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(19,'Максим Сушинский','Maxim Sushinsky',70,164,66,10,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(20,'Евгений Кузнецов','Evgeny Kuznetsov',79,196,94,12,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(21,'Иван Проворов','Ivan Provorov',101,209,93,1,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(22,'Даниил Тарасов','Daniil Tarasov',96,181,78,12,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(23,'Сергей Светлов','Sergei Svetlov',85,163,69,6,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(24,'Илья Никулин','Ilya Nikulin',67,172,51,9,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(25,'Павел Бучневич','Pavel Buchnevich',90,170,68,3,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(26,'Семен Варламов','Semyon Varlamov',70,173,78,5,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(27,'Александр Гусев','Alexander Gusev',82,177,93,12,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(28,'Андрей Свечников','Andrei Svechnikov',67,194,5,7,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(29,'Валерий Васильев','Valeri Vasiliev',81,175,48,9,'2026-06-09 14:08:35','2026-06-09 14:08:35'),(30,'Александр Овечкин','Alexander Ovechkin',91,198,2,4,'2026-06-09 14:08:35','2026-06-09 14:08:35');
/*!40000 ALTER TABLE `players` ENABLE KEYS */;
UNLOCK TABLES;
SET @@SESSION.SQL_LOG_BIN = @MYSQLDUMP_TEMP_LOG_BIN;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2026-06-09 14:09:19
File diff suppressed because one or more lines are too long
+9 -9
View File
@@ -1,5 +1,5 @@
## Тестовое задание ФХР
## Тестовое задание ФХР + upd
- OS: Linux, AltLinux 11
- Editor: PhpStorm, MS Code
@@ -67,23 +67,21 @@ php artisan app:test-fhr-sort
```bash
fhr-php
# Клубы
php artisan db:seed --class=ClubSeeder
# Игроки, здесь может потребоваться пара запусков,
# я не полировал проверку уникальности номеров в клубе
# под капотом обычного сидирования
php artisan db:seed --class=PlayerSeeder
# Сидируем клубы, игроков, связь с сезонами
php artisan db:seed
```
Выгрузка данных по игрокам и клубам вместе со структурой
```bash
fhr-mysql
cd /var/lib/mysql-files
mysqldump -u root -psecret test_fhr clubs players > clubs_and_players_dump.sql
mysqldump -u root -psecret test_fhr clubs players seasons player_season > clubs_players_season_dump.sql
```
После такой выгрузки, дамп появится в папке MySQL-files
Тестовый дамп лежит в корне проекта **clubs_and_players_dump.sql**
Тестовый дамп лежит в корне проекта **clubs_players_season_dump.sql**
**Время: 1 час 25 минут.**
@@ -91,6 +89,8 @@ mysqldump -u root -psecret test_fhr clubs players > clubs_and_players_dump.sql
Сидируем БД
```bash
fhr-php
# Долгий сид достаточно, минут 15-20 возможно
php artisan db:seed --class=UserSeeder
```
+4 -1
View File
@@ -9,5 +9,8 @@ class Club extends Model
{
use HasFactory;
//
public function players()
{
return $this->hasMany(Player::class);
}
}
+4 -4
View File
@@ -2,12 +2,12 @@
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Player extends Model
{
use HasFactory;
//
public function club()
{
return $this->belongsTo(Club::class);
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Season extends Model
{
protected $fillable = ['title_ru', 'title_en'];
public function players()
{
return $this->belongsToMany(Player::class);
}
}
@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\Season;
use App\Models\Player;
use App\Models\Club;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('seasons', function (Blueprint $table) {
$table->id();
$table->string('title_ru');
$table->string('title_en');
$table->timestamps();
});
// test seed
for ($i = 2020; $i < 2026; $i++) {
Season::create([
'title_ru' => $i . ' год',
'title_en' => $i . ' year',
]);
}
Schema::create('player_season', function (Blueprint $table) {
$table->id();
$table->foreignIdFor(Season::class)
->constrained()
->onUpdate('cascade')
->onDelete('restrict');
$table->foreignIdFor(Player::class)
->constrained()
->onUpdate('cascade')
->onDelete('restrict');
$table->unique(['season_id', 'player_id']);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('player_season');
Schema::dropIfExists('seasons');
}
};
+1 -1
View File
@@ -14,7 +14,7 @@ class ClubSeeder extends Seeder
public function run(): void
{
Club::factory()
->count(12)
->count(10)
->create();
}
}
@@ -2,7 +2,6 @@
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
@@ -15,6 +14,14 @@ class DatabaseSeeder extends Seeder
*/
public function run(): void
{
//
$this->command->info('Starting database seeding...');
$this->call([
ClubSeeder::class,
PlayerSeeder::class,
SeasonPlayerSeeder::class,
]);
$this->command->info('Database seeding finished successfully.');
}
}
+45 -4
View File
@@ -2,19 +2,60 @@
namespace Database\Seeders;
use App\Models\Club;
use App\Models\Player;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Database\Seeders\Traits\HasPlayerNames;
class PlayerSeeder extends Seeder
{
use HasPlayerNames;
/**
* Run the database seeds.
*/
public function run(): void
{
Player::factory()
->count(30)
->create();
$this->command->info('Seeding players for each club with unique squad numbers and names...');
$clubs = Club::all();
$playerNames = $this->getPlayerNames();
$totalNames = count($playerNames);
$nameIndex = 0;
if ($clubs->isEmpty()) {
$this->command->warn('No clubs found. Skipping player seeding.');
return;
}
$this->command->getOutput()->progressStart($clubs->count());
foreach ($clubs as $club) {
$availableNumbers = range(1, 99);
shuffle($availableNumbers);
for ($i = 0; $i < 10; $i++) {
$squadNumber = array_pop($availableNumbers);
if (is_null($squadNumber)) {
break;
}
$name = $playerNames[$nameIndex % $totalNames];
$nameIndex++;
Player::create([
'club_id' => $club->id,
'squad_number' => $squadNumber,
'full_name_ru' => $name['ru'],
'full_name_en' => $name['en'],
'weight' => rand(65, 105),
'height' => rand(160, 210),
]);
}
$this->command->getOutput()->progressAdvance();
}
$this->command->getOutput()->progressFinish();
$this->command->info("\nFinished seeding players.");
}
}
@@ -0,0 +1,40 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Season;
use App\Models\Player;
class SeasonPlayerSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
$this->command->info('Seeding season-player relationships...');
$seasons = Season::all();
$allPlayerIds = Player::pluck('id');
if ($allPlayerIds->isEmpty()) {
$this->command->warn('No players found. Skipping season-player seeding.');
return;
}
$playersToAttachCount = min(5, $allPlayerIds->count());
foreach ($seasons as $season) {
$randomPlayerIds = $allPlayerIds->random($playersToAttachCount);
// syncWithoutDetaching гарантирует уникальность связи.
// Если такая пара (season_id, player_id) уже есть, она не будет добавлена.
$season->players()->syncWithoutDetaching($randomPlayerIds);
}
$this->command->info('Finished seeding season-player relationships.');
}
}
@@ -1,25 +1,12 @@
<?php
namespace Database\Factories;
namespace Database\Seeders\Traits;
use App\Models\Player;
use App\Models\Club;
use Illuminate\Database\Eloquent\Factories\Factory;
use Exception;
/**
* @extends Factory<Player>
*/
class PlayerFactory extends Factory
trait HasPlayerNames
{
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
protected function getPlayerNames(): array
{
$names = [
return [
['ru' => 'Александр Овечкин', 'en' => 'Alexander Ovechkin'],
['ru' => 'Евгений Малкин', 'en' => 'Evgeni Malkin'],
['ru' => 'Никита Кучеров', 'en' => 'Nikita Kucherov'],
@@ -121,31 +108,5 @@ class PlayerFactory extends Factory
['ru' => 'Анатолий Семенов', 'en' => 'Anatoly Semenov'],
['ru' => 'Андрей Ломакин', 'en' => 'Andrei Lomakin'],
];
$name = $names[array_rand($names)];
$club = Club::inRandomOrder()->first();
if (!$club) {
throw new Exception('No clubs found in the database. Please seed the clubs table first.');
}
$usedNumbers = Player::where('club_id', $club->id)->pluck('squad_number')->toArray();
$availableNumbers = array_diff(range(1, 99), $usedNumbers);
if (empty($availableNumbers)) {
throw new Exception("No available squad numbers for club ID: {$club->id}.");
}
$squadNumber = $availableNumbers[array_rand($availableNumbers)];
return [
'full_name_ru' => $name['ru'],
'full_name_en' => $name['en'],
'weight' => rand(65, 105),
'height' => rand(160, 210),
'squad_number' => $squadNumber,
'club_id' => $club->id,
];
}
}