Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/database/src/Config/SeederConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Tempest\Database\Config;

final class SeederConfig
{
public function __construct(
/** @var array<array-key, class-string<\Tempest\Database\DatabaseSeeder>> */
public array $seeders = [],
) {}
}
5 changes: 5 additions & 0 deletions packages/database/src/Config/seeder.config.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?php

use Tempest\Database\Config\SeederConfig;

return new SeederConfig();
10 changes: 10 additions & 0 deletions packages/database/src/DatabaseSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Tempest\Database;

use UnitEnum;

interface DatabaseSeeder
{
public function run(null|string|UnitEnum $database): void;
}
32 changes: 32 additions & 0 deletions packages/database/src/SeederDiscovery.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Tempest\Database;

use Tempest\Database\Config\SeederConfig;
use Tempest\Discovery\Discovery;
use Tempest\Discovery\DiscoveryLocation;
use Tempest\Discovery\IsDiscovery;
use Tempest\Reflection\ClassReflector;

final class SeederDiscovery implements Discovery
{
use IsDiscovery;

public function __construct(
private SeederConfig $seederConfig,
) {}

public function discover(DiscoveryLocation $location, ClassReflector $class): void
{
if ($class->implements(DatabaseSeeder::class)) {
$this->discoveryItems->add($location, $class->getName());
}
}

public function apply(): void
{
foreach ($this->discoveryItems as $discoveryItem) {
$this->seederConfig->seeders[] = $discoveryItem;
}
}
}
70 changes: 70 additions & 0 deletions src/Tempest/Framework/Commands/DatabaseSeedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Tempest\Framework\Commands;

use Tempest\Console\ConsoleArgument;
use Tempest\Console\ConsoleCommand;
use Tempest\Console\HasConsole;
use Tempest\Console\Middleware\CautionMiddleware;
use Tempest\Console\Middleware\ForceMiddleware;
use Tempest\Container\Container;
use Tempest\Database\Config\SeederConfig;

final class DatabaseSeedCommand
{
use HasConsole;

public function __construct(
private readonly Container $container,
private readonly SeederConfig $seederConfig,
) {}

#[ConsoleCommand(
aliases: ['db:seed'],
middleware: [ForceMiddleware::class, CautionMiddleware::class],
)]
public function __invoke(
#[ConsoleArgument(description: 'Use a specific database.')]
?string $database = null,
#[ConsoleArgument(description: 'Run all database seeders')]
bool $all = false,
#[ConsoleArgument(description: 'Select one specific seeder to run')]
?string $seeder = null,
): void {
if ($seeder !== null) {
$this->runSeeder($seeder, $database);
return;
}

if (count($this->seederConfig->seeders) === 1) {
$this->runSeeder($this->seederConfig->seeders[0], $database);
return;
}

if ($all) {
$seedersToRun = $this->seederConfig->seeders;
} else {
$seedersToRun = $this->ask(
question: 'Which seeders do you want to run?',
options: $this->seederConfig->seeders,
multiple: true,
);
}

foreach ($seedersToRun as $seederClass) {
$this->runSeeder($seederClass, $database);
}
}

private function runSeeder(string $seederClass, ?string $database): void
{
/** @var \Tempest\Database\DatabaseSeeder $seeder */
$seeder = $this->container->get($seederClass);
$seeder->run($database);

$this->console->keyValue(
key: "<style='fg-gray'>{$seederClass}</style>",
value: "<style='fg-green'>SEEDED</style>",
);
}
}
25 changes: 24 additions & 1 deletion src/Tempest/Framework/Commands/MigrateFreshCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public function __invoke(
bool $validate = true,
#[ConsoleArgument(description: 'Use a specific database.')]
?string $database = null,
#[ConsoleArgument(description: 'Run database seeders after the database has been migrated')]
bool $seed = false,
#[ConsoleArgument(description: 'Run all database seeders after the database has been migrated')]
bool $all = false,
#[ConsoleArgument(description: 'Select one specific seeder to run')]
?string $seeder = null,
): ExitCode {
if ($validate) {
$validationSuccess = $this->console->call(MigrateValidateCommand::class);
Expand All @@ -52,7 +58,24 @@ public function __invoke(
$this->console->info('There is no migration to drop.');
}

return $this->console->call(MigrateUpCommand::class, ['fresh' => false, 'validate' => false, 'database' => $database]);
$this->console->call(MigrateUpCommand::class, [
'fresh' => false,
'validate' => false,
'database' => $database,
]);

$seed = $seed || $seeder !== null;

if ($seed) {
$this->console->header('Seeding database');
$this->console->call(DatabaseSeedCommand::class, [
'database' => $database,
'all' => $all,
'seeder' => $seeder,
]);
}

return ExitCode::SUCCESS;
}

#[EventHandler]
Expand Down
1 change: 0 additions & 1 deletion src/Tempest/Framework/Commands/MigrateUpCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
use Tempest\Console\ConsoleArgument;
use Tempest\Console\ConsoleCommand;
use Tempest\Console\ExitCode;
use Tempest\Console\Input\ConsoleArgumentBag;
use Tempest\Console\Middleware\CautionMiddleware;
use Tempest\Console\Middleware\ForceMiddleware;
use Tempest\Container\Singleton;
Expand Down
22 changes: 22 additions & 0 deletions tests/Fixtures/SecondTestDatabaseSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace Tests\Tempest\Fixtures;

use Tempest\Database\DatabaseSeeder;
use Tests\Tempest\Fixtures\Modules\Books\Models\Book;
use UnitEnum;

use function Tempest\Database\query;

final class SecondTestDatabaseSeeder implements DatabaseSeeder
{
public function run(null|string|UnitEnum $database): void
{
query(Book::class)
->insert(
title: 'Timeline Taxi 2',
)
->onDatabase($database)
->execute();
}
}
23 changes: 23 additions & 0 deletions tests/Fixtures/TestDatabaseSeeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Tests\Tempest\Fixtures;

use Tempest\Database\DatabaseSeeder;
use Tempest\Discovery\SkipDiscovery;
use Tests\Tempest\Fixtures\Modules\Books\Models\Book;
use UnitEnum;

use function Tempest\Database\query;

final class TestDatabaseSeeder implements DatabaseSeeder
{
public function run(null|string|UnitEnum $database): void
{
query(Book::class)
->insert(
title: 'Timeline Taxi',
)
->onDatabase($database)
->execute();
}
}
66 changes: 65 additions & 1 deletion tests/Integration/Database/MultiDatabaseTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php

namespace Integration\Database;
namespace Tests\Tempest\Integration\Database;

use PDOException;
use Tempest\Container\Exceptions\TaggedDependencyCouldNotBeResolved;
Expand All @@ -13,7 +13,9 @@
use Tempest\Database\Migrations\CreateMigrationsTable;
use Tempest\Database\Migrations\Migration;
use Tempest\Database\Migrations\MigrationManager;
use Tests\Tempest\Fixtures\Migrations\CreateBookTable;
use Tests\Tempest\Fixtures\Migrations\CreatePublishersTable;
use Tests\Tempest\Fixtures\Modules\Books\Models\Book;
use Tests\Tempest\Fixtures\Modules\Books\Models\Publisher;
use Tests\Tempest\Integration\Database\Fixtures\MigrationForBackup;
use Tests\Tempest\Integration\Database\Fixtures\MigrationForMain;
Expand Down Expand Up @@ -271,6 +273,68 @@ public function test_should_migrate(): void
$this->assertTableDoesNotExist('main_table', 'backup');
}

public function test_database_seed_on_selected_database(): void
{
/** @var MigrationManager $migrationManager */
$migrationManager = $this->container->get(MigrationManager::class);

$migrationManager->onDatabase('main')->executeUp(new CreateMigrationsTable());
$migrationManager->onDatabase('main')->executeUp(new CreateBookTable());
$migrationManager->onDatabase('backup')->executeUp(new CreateMigrationsTable());
$migrationManager->onDatabase('backup')->executeUp(new CreateBookTable());

$this->console
->call('db:seed --database=main --all')
->assertSuccess();

$this->assertSame(
'Timeline Taxi',
query(Book::class)->select()->onDatabase('main')->first()->title,
);

$this->assertNull(
query(Book::class)->select()->onDatabase('backup')->first(),
);

$this->console
->call('db:seed --database=backup --all')
->assertSuccess();

/** @var Book $book */
/** @phpstan-ignore-next-line */
$book = query(Book::class)->select()->onDatabase('backup')->first();

$this->assertSame(
'Timeline Taxi',
$book->title,
);
}

public function test_migrate_fresh_seed_on_selected_database(): void
{
$this->console
->call('migrate:fresh --seed --database=main --all')
->assertSuccess();

$this->assertSame(
'Timeline Taxi',
query(Book::class)->select()->onDatabase('main')->first()->title,
);

$this->assertException(PDOException::class, function (): void {
query(Book::class)->select()->onDatabase('backup')->first();
});

$this->console
->call('migrate:fresh --seed --database=backup --all')
->assertSuccess();

$this->assertSame(
'Timeline Taxi',
query(Book::class)->select()->onDatabase('backup')->first()->title,
);
}

private function assertTableExists(string $tableName, string $onDatabase): void
{
$this->assertTrue(query($tableName)->count()->onDatabase($onDatabase)->execute() >= 0);
Expand Down
Loading