Created
August 24, 2022 13:57
-
-
Save sebastiaanluca/e4ee1659ff82884733f80372b0d34620 to your computer and use it in GitHub Desktop.
Simple Laravel database state system to ensure database content is up-to-date (in addition to its schema using migrations).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
use App\Domain\Users\User; | |
use App\State; | |
use Illuminate\Support\Facades\Hash; | |
return new class extends State { | |
public function up(): void | |
{ | |
$attributes = array_merge( | |
['email_verified_at' => now()], | |
config('app.owner'), | |
); | |
$attributes['password'] = Hash::make($attributes['password']); | |
User::create($attributes); | |
} | |
public function skipIf(): bool | |
{ | |
return User::query() | |
->where('email', config('app.owner.email')) | |
->exists(); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
use App\Domain\Elasticsearch\Elasticsearch; | |
use App\State; | |
return new class extends State { | |
public function up(): bool | |
{ | |
return Elasticsearch::indices() | |
->deleteIndexTemplate(['name' => 'map_numerics_to_doubles']) | |
->asBool(); | |
} | |
public function skipIf(): bool | |
{ | |
return ! Elasticsearch::indices() | |
->existsIndexTemplate(['name' => 'map_numerics_to_doubles']) | |
->asBool(); | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
namespace App\Console\Commands; | |
use App\State; | |
use Illuminate\Console\Command; | |
use Illuminate\Filesystem\Filesystem; | |
use RuntimeException; | |
class EnsureDatabaseState extends Command | |
{ | |
/** | |
* @var string | |
*/ | |
protected $signature = 'db:ensure-state'; | |
/** | |
* @var string | |
*/ | |
protected $description = 'Ensure the database is in the correct state'; | |
public function __construct(private readonly Filesystem $filesystem) | |
{ | |
parent::__construct(); | |
} | |
public function handle(): int | |
{ | |
$this->components->info('Preparing state of database.'); | |
$files = $this->filesystem->files(database_path('states')); | |
collect($files) | |
->map(fn (string $path): array => [ | |
'path' => basename($path, '.php'), | |
'state' => $this->filesystem->getRequire($path), | |
]) | |
->each($this->verify(...)) | |
->each($this->runUp(...)); | |
return static::SUCCESS; | |
} | |
private function verify(array $file): void | |
{ | |
['state' => $state] = $file; | |
if ($state instanceof State) { | |
return; | |
} | |
throw new RuntimeException(sprintf('State "%s" must be an instance of "App\State"', $state)); | |
} | |
private function runUp(array $file): void | |
{ | |
['path' => $path, 'state' => $state] = $file; | |
if (method_exists($state, 'skipIf') && $state->skipIf()) { | |
$success = true; | |
} else { | |
$success = $state->up() ?? true; | |
} | |
$this->components->task($path, static fn (): bool => $success); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment