Skip to content

Commit

Permalink
Merge pull request #8 from DasunNethsara-04/main
Browse files Browse the repository at this point in the history
Migrations implemented (make, run, rollback)
  • Loading branch information
DasunNethsara-04 authored Oct 26, 2024
2 parents de9eed8 + 89be921 commit 2fb5c04
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 69 deletions.
39 changes: 39 additions & 0 deletions Core/src/Database/Database.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace ZenithPHP\Core\Database;

use PDO;
use PDOException;

class Database
{
protected static ?PDO $connection = null;

public static function connect(): PDO
{
if (self::$connection === null) {
try {
// Load environment variables
$host = DB_HOST;
$dbname = DB_NAME;
$username = DB_USER;
$password = DB_PASS;

$dsn = "mysql:host=$host;dbname=$dbname;charset=utf8mb4";

// Initialize the PDO connection
self::$connection = new PDO($dsn, $username, $password);
self::$connection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die("Database connection failed: " . $e->getMessage());
}
}
return self::$connection;
}

public static function execute($sql): void
{
$connection = self::connect();
$connection->exec($sql);
}
}
9 changes: 9 additions & 0 deletions Core/src/Database/Migration.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

namespace ZenithPHP\Core\Database;

abstract class Migration
{
abstract public function up();
abstract public function down();
}
52 changes: 52 additions & 0 deletions Core/src/Database/Schema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace ZenithPHP\Core\Database;

class Schema
{
protected string $tableName;
protected array $columns = [];
protected string $primaryKey = 'id';

public static function create($tableName, callable $callback): void
{
$schema = new self();
$schema->tableName = $tableName;
$callback($schema);
$schema->executeCreate();
}

public static function drop($tableName): void
{
$sql = "DROP TABLE IF EXISTS `$tableName`;";
Database::execute($sql);
}

public function id(): void
{
$this->columns[] = "`{$this->primaryKey}` INT AUTO_INCREMENT PRIMARY KEY";
}

public function string($name, $length = 255): void
{
$this->columns[] = "`$name` VARCHAR($length)";
}

public function integer($name): void
{
$this->columns[] = "`$name` INT";
}

public function timestamps(): void
{
$this->columns[] = "`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP";
$this->columns[] = "`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP";
}

protected function executeCreate(): void
{
$columnsSql = implode(", ", $this->columns);
$sql = "CREATE TABLE IF NOT EXISTS `{$this->tableName}` ({$columnsSql});";
Database::execute($sql);
}
}
201 changes: 133 additions & 68 deletions cli
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@

namespace ZenithPHP;

use ZenithPHP\Core\Controller\Controller;
use ZenithPHP\Core\Model\Model;
use ZenithPHP\Core\Database\Migration;
use ZenithPHP\Core\Database\Schema;
use ZenithPHP\Core\Database\Database;
use ZenithPHP\Core\Http\InitEnv;

// Autoload necessary classes and environment configurations
require_once 'vendor/autoload.php';
require_once 'Core/src/Database/Migration.php';
require_once 'Core/src/Database/Schema.php';

class CLI
{
protected array $arguments;

public function __construct($argv)
public function __construct(array $argv)
{
$this->arguments = $argv;
$this->handle();
Expand All @@ -19,99 +26,157 @@ class CLI
{
$command = $this->arguments[1] ?? null;

switch ($command) {
case 'make:controller':
$this->makeController();
break;
case 'make:model':
$this->makeModel();
break;
case 'run':
$this->runProject();
break;
default:
echo "Invalid command. Use 'make:controller <ControllerName>', 'make:model <ModelName>', or 'run'.\n";
break;
$commands = [
'make:controller' => 'makeController',
'make:model' => 'makeModel',
'make:migration' => 'makeMigration',
'migrate' => 'runMigrations',
'migrate:rollback' => 'rollbackMigrations',
'migrate:fresh' => 'migrateFresh',
'run' => 'runProject',
];

if (array_key_exists($command, $commands)) {
$this->{$commands[$command]}();
} else {
echo "Invalid command. Use 'make:controller', 'make:model', 'make:migration', 'migrate', 'migrate:rollback', or 'run'.\n";
}
}

protected function makeController()
{
$controllerName = $this->arguments[2] ?? null;
if (!$controllerName) {
echo "Enter the name of the controller: ";
$controllerName = trim(fgets(STDIN));
}

$controllerTemplate = "<?php\n\nnamespace ZenithPHP\App\Controllers;\n\nuse ZenithPHP\Core\Controller\Controller;\n\nclass {$controllerName} extends Controller\n{\n\n}\n";

$controllerName = $this->arguments[2] ?? $this->prompt("Enter the name of the controller: ");
$path = "App/Controllers/{$controllerName}.php";
if (!file_exists($path)) {
file_put_contents($path, $controllerTemplate);
echo "Controller '{$controllerName}' created successfully.\n";
} else {

if (file_exists($path)) {
echo "Controller '{$controllerName}' already exists.\n";
return;
}

$template = "<?php\n\nnamespace ZenithPHP\App\Controllers;\n\nuse ZenithPHP\Core\Controller\Controller;\n\nclass {$controllerName} extends Controller\n{\n}\n";
file_put_contents($path, $template);
echo "Controller '{$controllerName}' created successfully.\n";
}

protected function makeModel()
{
$modelName = $this->arguments[2] ?? null;
if (!$modelName) {
echo "Enter the name of the model: ";
$modelName = trim(fgets(STDIN));
$modelName = $this->arguments[2] ?? $this->prompt("Enter the name of the model: ");
$path = "App/Models/{$modelName}.php";

if (file_exists($path)) {
echo "Model '{$modelName}' already exists.\n";
return;
}

$modelTemplate = "<?php\n\nnamespace ZenithPHP\App\Models;\n\nuse ZenithPHP\Core\Model\Model;\n\nclass {$modelName} extends Model\n{\n protected string \$table_name = '" . strtolower($modelName) . "s';\n}\n";
$template = "<?php\n\nnamespace ZenithPHP\App\Models;\n\nuse ZenithPHP\Core\Model\Model;\n\nclass {$modelName} extends Model\n{\n protected string \$table_name = '" . strtolower($modelName) . "s';\n}\n";
file_put_contents($path, $template);
echo "Model '{$modelName}' created successfully.\n";
}

$path = "App/Models/{$modelName}.php";
if (!file_exists($path)) {
file_put_contents($path, $modelTemplate);
echo "Model '{$modelName}' created successfully.\n";
} else {
echo "Model '{$modelName}' already exists.\n";
protected function makeMigration()
{
$migrationName = $this->arguments[2] ?? $this->prompt("Enter the name of the migration: ");
$timestamp = date('Y_m_d_His');
$file = "Migrations/_{$timestamp}_{$migrationName}.php";

if (!file_exists('Migrations')) mkdir('Migrations', 0755, true);

$template = "<?php\n\nnamespace ZenithPHP\Migrations;\n\nuse ZenithPHP\Core\Database\Migration;\nuse ZenithPHP\Core\Database\Schema;\n\nclass _{$timestamp}_{$migrationName} extends Migration\n{\n public function up()\n {\n Schema::create('{$migrationName}', function (Schema \$table) {\n \$table->id();\n // Add columns\n });\n }\n\n public function down()\n {\n Schema::drop('{$migrationName}');\n }\n}\n";

file_put_contents($file, $template);
echo "Migration '{$migrationName}' created successfully at '{$file}'.\n";
}

protected function runMigrations()
{
// $this->loadEnv();
$migrationFiles = glob('Migrations/*.php');

if (empty($migrationFiles)) {
echo "No migration files found.\n";
return;
}

foreach ($migrationFiles as $file) {
require_once $file;
$className = 'ZenithPHP\Migrations\\' . basename($file, '.php');

if (class_exists($className)) {
(new $className)->up();
echo "Migration '{$className}' executed successfully.\n";
} else {
echo "Class '{$className}' not found in '{$file}'.\n";
}
}
}

protected function runProject()
protected function rollbackMigrations()
{
$port = 8000;
$publicPath = __DIR__ . '/Public';
$routerPath = "$publicPath/router.php";
$this->loadEnv();
$migrationFiles = array_reverse(glob('Migrations/*.php'));

// Check if the Public directory exists
if (!is_dir($publicPath)) {
echo "Error: The Public directory does not exist at the expected path: $publicPath\n";
if (empty($migrationFiles)) {
echo "No migration files found.\n";
return;
}

echo "Starting server on http://localhost:$port\n";
chdir($publicPath); // Change to the Public directory

// Create the router script
$router = <<<PHP
<?php
// DO NOT DELETE THIS FILE...
if (php_sapi_name() === 'cli-server') {
// Serve the requested resource as-is if it exists
\$filePath = __DIR__ . parse_url(\$_SERVER['REQUEST_URI'], PHP_URL_PATH);
if (file_exists(\$filePath) && !is_dir(\$filePath)) {
return false;
foreach ($migrationFiles as $file) {
require_once $file;
$className = 'ZenithPHP\Migrations\\' . basename($file, '.php');

if (class_exists($className)) {
(new $className)->down();
echo "Rolled back: " . basename($file) . "\n";
} else {
echo "Class '{$className}' not found in '{$file}'.\n";
}
}
}

protected function migrateFresh()
{
// \ZenithPHP\Core\Http\InitEnv::load();
$this->loadEnv();
$this->dropAllTables();
$this->runMigrations();
}

protected function dropAllTables()
{
$db = Database::connect();
$tables = $db->query("SHOW TABLES")->fetchAll(\PDO::FETCH_COLUMN);

foreach ($tables as $table) {
$db->exec("DROP TABLE IF EXISTS `{$table}`");
echo "Dropped table: {$table}\n";
}
}
require 'index.php';
PHP;

file_put_contents($routerPath, $router);
protected function runProject()
{
$port = 8000;
$publicPath = __DIR__ . '/Public';
$routerPath = "$publicPath/router.php";

echo "Starting server on http://localhost:$port\n";
chdir($publicPath);

// Register shutdown function to delete the router.php
register_shutdown_function(function() use ($routerPath) {
@unlink($routerPath);
echo "Temporary router file deleted.\n";
});
file_put_contents($routerPath, "<?php if (php_sapi_name() === 'cli-server') { \$filePath = __DIR__ . parse_url(\$_SERVER['REQUEST_URI'], PHP_URL_PATH); if (file_exists(\$filePath) && !is_dir(\$filePath)) return false; } require 'index.php';");

// Start the PHP built-in server
exec("php -S localhost:$port router.php");
register_shutdown_function(fn() => unlink($routerPath) && print("Temporary router file deleted.\n"));

passthru("php -S localhost:$port router.php");
}

private function loadEnv()
{
InitEnv::load();
}

private function prompt(string $message): string
{
echo $message;
return trim(fgets(STDIN));
}
}

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
"ZenithPHP\\App\\Controllers\\": "App/Controllers/",
"ZenithPHP\\Model\\": "App/Models/",
"ZenithPHP\\Core\\": "Core/src/",
"ZenithPHP\\Config\\": "Config/"
"ZenithPHP\\Config\\": "Config/",
"ZenithPHP\\Migrations\\": "Migrations/"
}
}
}

0 comments on commit 2fb5c04

Please sign in to comment.