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
17 changes: 17 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "baremetalphp-src",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"react": "^18.2.0",
"react-dom": "^18.2.0"
},
"devDependencies": {
"vite": "^5.0.0",
"@vitejs/plugin-react": "^4.2.0"
}
}
12 changes: 12 additions & 0 deletions resources/js/App.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App';

const container = document.getElementById('app');
if (container) {
const component = container.dataset.component || 'App';
const props = container.dataset.props ? JSON.parse(container.dataset.props) : {};

const root = createRoot(container);
root.render(<App {...props} />);
}
1 change: 1 addition & 0 deletions src/Console/Application.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public function __construct(Container $app)
'frontend:install' => Commands\InstallFrontendCommand::class,
'go:install' => Commands\InstallGoAppServerCommand::class,
'go:serve' => Commands\GoServeCommand::class,
'seed' => Commands\SeedDatabaseCommand::class,
];

}
Expand Down
20 changes: 20 additions & 0 deletions src/Console/Commands/SeedDatabaseCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace BareMetalPHP\Console\Commands;

class SeedDatabaseCommand
{
public function handle(array $args = []): void
{
if ($argv[1] === 'seed') {
$seeder = $argv[2] ?? 'DatabaseSeeder';

$runner = new \BareMetalPHP\Database\Seeder\SeederRunner();
$runner->run($seeder);

echo "Seeded using {$seeder}\n";

exit;
}
}
}
16 changes: 14 additions & 2 deletions src/Database/Seeder/Seeder.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,20 @@ abstract class Seeder
{
abstract public function run(): void;

protected function call(string $seederClass): void
protected function call(string $seederClass, ?string $seederDirectory = null): void
{
(new $seederClass())->run();
// If class doesn't exist, try to load it via SeederRunner
if (!class_exists($seederClass)) {
$seederDirectory = $seederDirectory ?? base_path('database/seeders');
$runner = new SeederRunner($seederDirectory);
$runner->run($seederClass);
return;
}

$seeder = new $seederClass();
if (!$seeder instanceof Seeder) {
throw new \RuntimeException("{$seederClass} must extend Seeder");
}
$seeder->run();
}
}
33 changes: 33 additions & 0 deletions src/Database/Seeder/SeederRunner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace BareMetalPHP\Database\Seeder;

use RuntimeException;
class SeederRunner
{
protected string $seederDirectory;

public function __construct(string $path = null)
{
$this->seederDirectory = $path ?? base_path('database/seeders');
}

public function run(string $seederClass = 'DatabaseSeeder'): void
{
$file = $this->seederDirectory . '/' . $seederClass . '.php';

if (! file_exists($file)) {
throw new RuntimeException("Seeder file not found: {$file}");
}

require_once $file;

$seeder = new $seederClass();

if (! $seeder instanceof Seeder) {
throw new RuntimeException("{$seederClass} must extend Seeder");
}

$seeder->run();
}
}
162 changes: 162 additions & 0 deletions tests/Unit/Database/SeederRunnerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
<?php

declare(strict_types=1);

namespace Tests\Unit\Database;

use BareMetalPHP\Database\Seeder\SeederRunner;
use PHPUnit\Framework\TestCase;
use RuntimeException;

final class SeederRunnerTest extends TestCase
{
private string $seederDir;

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

$this->seederDir = sys_get_temp_dir() . '/baremetalphp-seeders-' . uniqid('', true);

if (! is_dir($this->seederDir)) {
mkdir($this->seederDir, 0777, true);
}
}

protected function tearDown(): void
{
if (is_dir($this->seederDir)) {
foreach (glob($this->seederDir . '/*.php') ?: [] as $file) {
@unlink($file);
}

@rmdir($this->seederDir);
}

parent::tearDown();
}

public function testSeederRunsTheGivenSeederClass(): void
{
// Arrange: fake DatabaseSeeder that tracks execution.
$className = 'TestSeeder' . uniqid();
$this->writeSeederFile($className . '.php', <<<PHP
<?php

use BareMetalPHP\Database\Seeder\Seeder as BaseSeeder;

class {$className} extends BaseSeeder
{
public static bool \$ran = false;

public function run(): void
{
self::\$ran = true;
}
}
PHP
);

$runner = new SeederRunner($this->seederDir);

// Act
$runner->run($className);

// Assert
$this->assertTrue($className::$ran);
}

public function testItCanChainOtherSeedersViaCall(): void
{
// Use unique class names to avoid redeclaration errors
$postSeederClass = 'PostSeeder' . uniqid();
$dbSeederClass = 'DatabaseSeeder' . uniqid();

// Child seeder that will be called by DatabaseSeeder
$this->writeSeederFile($postSeederClass . '.php', <<<PHP
<?php

use BareMetalPHP\Database\Seeder\Seeder as BaseSeeder;

class {$postSeederClass} extends BaseSeeder
{
public static bool \$ran = false;

public function run(): void
{
self::\$ran = true;
}
}
PHP);

// DatabaseSeeder that calls PostSeeder
$seederDir = $this->seederDir;
$this->writeSeederFile($dbSeederClass . '.php', <<<PHP
<?php

use BareMetalPHP\Database\Seeder\Seeder as BaseSeeder;

class {$dbSeederClass} extends BaseSeeder
{
public static bool \$ran = false;

public function run(): void
{
self::\$ran = true;

\$this->call('{$postSeederClass}', '{$seederDir}');
}
}
PHP
);

$runner = new SeederRunner($this->seederDir);

// Act
$runner->run($dbSeederClass);

// Assert
$this->assertTrue($dbSeederClass::$ran, 'DatabaseSeeder::run should be executed');
$this->assertTrue($postSeederClass::$ran, 'PostSeeder::run should be executed via call()');
}

public function testItThrowsWhenSeederFileDoesNotExist(): void
{
$runner = new SeederRunner($this->seederDir);

$this->expectException(RuntimeException::class);

$runner->run('MissingSeeder');
}

public function testItThrowsWhenClassDoesNotExtendSeeder(): void
{
// File exists, but class does not extend the base Seeder
$className = 'InvalidSeeder' . uniqid();
$this->writeSeederFile($className . '.php', <<<PHP
<?php

class {$className}
{
public function run(): void
{
// noop
}
}
PHP);

$runner = new SeederRunner($this->seederDir);

$this->expectException(RuntimeException::class);

$runner->run($className);
}

/**
* Helper to write a seeder file into the temporary seeder directory.
*/
private function writeSeederFile(string $filename, string $contents): void
{
file_put_contents($this->seederDir . '/' . $filename, $contents);
}
}
Loading