Skip to content

Commit

Permalink
[Feat] Support Integrate Seeder (#217)
Browse files Browse the repository at this point in the history
* feat: add integrate seeder

- add seeder abstract, to run seeder task
 - `run`, basic method to run seeder
 - `call`, call another seeder class
 - `create`, shorthand to create/insert data to database
- add path for seeder path
- add command to run seeder
- add commnad to make seeder

* change group test for seeder command

* add make seeder command

* add null property type

* add test

* optional force create exist seeder class
  • Loading branch information
SonyPradana authored Oct 3, 2023
1 parent b1fe68d commit 2563efa
Show file tree
Hide file tree
Showing 13 changed files with 474 additions and 2 deletions.
40 changes: 40 additions & 0 deletions src/System/Database/Seeder/Seeder.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

namespace System\Database\Seeder;

use System\Database\MyPDO;
use System\Database\MyQuery\Insert;
use System\Integrate\Application;

abstract class Seeder
{
protected Application $app;

public function __construct(Application $app)
{
$this->app = $app;
}

/**
* @param class-string $class_name
*/
public function call(string $class_name): void
{
$this->app->call([$class_name, 'run']);
}

public function create(string $table_name): Insert
{
/** @var MyPDO */
$pdo = $this->app->get(MyPDO::class);

return new Insert($table_name, $pdo);
}

/**
* Run seeder.
*/
abstract public function run(): void;
}
28 changes: 27 additions & 1 deletion src/System/Integrate/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ final class Application extends Container
*/
private string $migraton_path;

/**
* Seeder path.
*/
private string $seeder_path;

/**
* Public path.
*/
Expand Down Expand Up @@ -226,6 +231,7 @@ public function loadConfig(string $base_path)
$this->setProviderPath($configs['SERVICE_PROVIDER']);
$this->setMigrationPath($configs['MIGRATION_PATH']);
$this->setPublicPath($configs['PUBLIC_PATH']);
$this->setSeederPath($configs['SEEDER_PATH']);
// pusher config
$this->set('config.pusher_id', $configs['PUSHER_APP_ID']);
$this->set('config.pusher_key', $configs['PUSHER_APP_KEY']);
Expand Down Expand Up @@ -262,8 +268,9 @@ private function defaultConfigs()
'CONFIG' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR,
'MIDDLEWARE' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'middleware' . DIRECTORY_SEPARATOR,
'SERVICE_PROVIDER' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR,
'MIGRATION_PATH' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR,
'MIGRATION_PATH' => DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR,
'PUBLIC_PATH' => DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR,
'SEEDER_PATH' => DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'seeders' . DIRECTORY_SEPARATOR,

'PROVIDERS' => [
// provider class name
Expand Down Expand Up @@ -503,6 +510,17 @@ public function setMigrationPath(string $path): self
return $this;
}

/**
* Set seeder path.
*/
public function setSeederPath(string $path): self
{
$this->seeder_path = $this->base_path . $path;
$this->set('path.seeder', $this->seeder_path);

return $this;
}

/**
* Set public path.
*/
Expand Down Expand Up @@ -644,6 +662,14 @@ public function migration_path(): string
return $this->get('path.migration');
}

/**
* Get seeder path.
*/
public function seeder_path(): string
{
return $this->get('path.seeder');
}

/**
* Get public path.
*/
Expand Down
161 changes: 161 additions & 0 deletions src/System/Integrate/Console/SeedCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
<?php

declare(strict_types=1);

namespace System\Integrate\Console;

use System\Console\Command;
use System\Console\Prompt;
use System\Console\Traits\PrintHelpTrait;
use System\Template\Generate;
use System\Template\Method;
use System\Text\Str;

use function System\Console\fail;
use function System\Console\info;
use function System\Console\ok;
use function System\Console\style;
use function System\Console\warn;

/**
* @property string|null $class
* @property bool|null $force
*/
class SeedCommand extends Command
{
use PrintHelpTrait;

/**
* Register command.
*
* @var array<int, array<string, mixed>>
*/
public static array $command = [
[
'cmd' => 'db:seed',
'mode' => 'full',
'class' => self::class,
'fn' => 'main',
],
[
'cmd' => 'make:seed',
'mode' => 'full',
'class' => self::class,
'fn' => 'make',
],
];

/**
* @return array<string, array<string, string|string[]>>
*/
public function printHelp(): array
{
return [
'commands' => [
'db:seed' => 'Run seeding',
'make:seed' => 'Create new seeder class',
],
'options' => [
'--class' => 'Target class',
],
'relation' => [
'db:seed' => ['--class'],
],
];
}

private function runInDev(): bool
{
if (app()->isDev() || $this->force) {
return true;
}

/* @var bool */
return (new Prompt(style('Runing seeder in production?')->textRed(), [
'yes' => fn () => true,
'no' => fn () => false,
], 'no'))
->selection([
style('yes')->textDim(),
' no',
])
->option();
}

public function main(): int
{
$class = $this->class;
$exit = 0;

if (false === $this->runInDev()) {
return 2;
}

if (null === $class) {
warn('command db:seed require --class flag follow by class name.')->out(false);

return 1;
}

if (!Str::startsWith($class, 'Database\Seeders')) {
$class = 'Database\\Seeders\\' . $class;
}

if (false === class_exists($class)) {
warn("Class '{$class}::class' doest exist.")->out(false);

return 1;
}

info('Running seeders...')->out(false);
try {
app()->call([$class, 'run']);

ok('Success run seeder')->out(false);
} catch (\Throwable $th) {
warn($th->getMessage())->out(false);
$exit = 1;
}

return $exit;
}

public function make(): int
{
$class = $this->OPTION[0] ?? null;

if (null === $class) {
warn('command make:seed require class name')->out(false);

return 1;
}

if (file_exists(seeder_path() . $class . '.php') && !$this->force) {
warn("Class '{$class}::class' already exist.")->out(false);

return 1;
}

$make = new Generate($class);
$make->tabIndent(' ');
$make->tabSize(4);
$make->namespace('Database\Seeders');
$make->use('System\Database\Seeder\Seeder');
$make->extend('Seeder');
$make->setEndWithNewLine();
$make->addMethod('run')
->visibility(Method::PUBLIC_)
->setReturnType('void')
->body('// run some insert db');

if (file_put_contents(seeder_path() . $class . '.php', $make->__toString())) {
ok('Success create seeder')->out();

return 0;
}

fail('Fail to create seeder')->out();

return 1;
}
}
9 changes: 9 additions & 0 deletions src/System/Integrate/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,15 @@ function migration_path(string $surfix_path = ''): string
}
}

if (!function_exists('seeder_path')) {
function seeder_path(string $surfix_path = ''): string
{
$path = app()->seeder_path() . $surfix_path;

return $path;
}
}

if (!function_exists('base_path')) {
/**
* Get base path.
Expand Down
3 changes: 2 additions & 1 deletion tests/Integrate/ApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ private function defaultConfigs()
'CONFIG' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'config' . DIRECTORY_SEPARATOR,
'MIDDLEWARE' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'middleware' . DIRECTORY_SEPARATOR,
'SERVICE_PROVIDER' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'Providers' . DIRECTORY_SEPARATOR,
'MIGRATION_PATH' => DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR,
'MIGRATION_PATH' => DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'migrations' . DIRECTORY_SEPARATOR,
'PUBLIC_PATH' => DIRECTORY_SEPARATOR . 'public' . DIRECTORY_SEPARATOR,
'SEEDER_PATH' => DIRECTORY_SEPARATOR . 'database' . DIRECTORY_SEPARATOR . 'seeders' . DIRECTORY_SEPARATOR,

'PROVIDERS' => [
// provider class name
Expand Down
1 change: 1 addition & 0 deletions tests/Integrate/Commands/CommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ protected function setUp(): void
$this->app->setCommandPath(__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR);
$this->app->setConfigPath(__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR);
$this->app->setMigrationPath(__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'migration' . DIRECTORY_SEPARATOR);
$this->app->setSeederPath(__DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'seeders' . DIRECTORY_SEPARATOR);
}

protected function tearDown(): void
Expand Down
91 changes: 91 additions & 0 deletions tests/Integrate/Commands/SeedCommandsTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
<?php

declare(strict_types=1);

namespace System\Test\Integrate\Commands;

use System\Integrate\Console\SeedCommand;

final class SeedCommandsTest extends CommandTest
{
protected function setUp(): void
{
parent::setUp();
}

protected function tearDown(): void
{
parent::tearDown();

$migration = __DIR__ . DIRECTORY_SEPARATOR . 'assets' . DIRECTORY_SEPARATOR . 'seeders' . DIRECTORY_SEPARATOR;
array_map('unlink', glob("{$migration}/*.php"));
}

/**
* @test
*/
public function itCanCallMakeCommandSeederWithSuccess()
{
$makeCommand = new SeedCommand($this->argv('cli make:seed BaseSeeder'));
ob_start();
$exit = $makeCommand->make();
ob_get_clean();

$this->assertSuccess($exit);

$file = __DIR__ . '/assets/seeders/BaseSeeder.php';
$this->assertTrue(file_exists($file));

$class = file_get_contents($file);
$this->assertContain('class BaseSeeder extends Seeder', $class, 'Stub test');
$this->assertContain('public function run(): void', $class, 'Stub test');
}

/**
* @test
*/
public function itCanCallMakeCommandSeedWithFails()
{
$makeCommand = new SeedCommand($this->argv('cli make:seed'));
ob_start();
$exit = $makeCommand->make();
ob_get_clean();

$this->assertFails($exit);
}

/**
* @test
*/
public function itCanCallMakeCommandSeedWithFailsFileExist()
{
app()->setSeederPath(__DIR__ . '//database//seeders//');
$makeCommand = new SeedCommand($this->argv('cli make:seed BasicSeeder'));
ob_start();
$exit = $makeCommand->make();
ob_get_clean();

$this->assertFails($exit);
}

/**
* @test
*/
public function itCanCallMakeExistCommandSeeder()
{
app()->setSeederPath(__DIR__ . '//database//seeders//');
$makeCommand = new SeedCommand($this->argv('cli make:seed ExistSeeder --force'));
ob_start();
$exit = $makeCommand->make();
ob_get_clean();

$this->assertSuccess($exit);

$file = __DIR__ . '//database//seeders//ExistSeeder.php';
$this->assertTrue(file_exists($file));

$class = file_get_contents($file);
$this->assertContain('class ExistSeeder extends Seeder', $class, 'Stub test');
$this->assertContain('public function run(): void', $class, 'Stub test');
}
}
Loading

0 comments on commit 2563efa

Please sign in to comment.