From: LeonardoBizzoni Date: Mon, 18 Apr 2022 08:50:23 +0000 (+0200) Subject: Connecting to db + migrations X-Git-Url: http://git.leonardobizzoni.com/?a=commitdiff_plain;h=7324eb0e60e853d4383529414a629c2d9e17d30c;p=highschool-graduation-project Connecting to db + migrations --- diff --git a/README.org b/README.org index 778e714..0517831 100644 --- a/README.org +++ b/README.org @@ -11,6 +11,7 @@ - [[#realizzazione][Realizzazione]] - [[#entry-point---indexphp][Entry point - index.php]] - [[#core][Core]] + - [[#migrations][Migrations]] - [[#models][Models]] - [[#controllers][Controllers]] @@ -208,6 +209,20 @@ if ($ruleName == self::RULE_MATCH && $value != $this->{$rule["match"]}) { } #+end_src +*** Database class +Utilizza PDO per effettuare la connessione al server mariadb in esecuzione nel docker container. +Sia le credenziali di accesso al database che il domain service name (DSN) sono salvate nel file "www/.env" (vedi [[./www/.env.example][.env.example]]) e per leggerle si utilizza il package [[https://github.com/vlucas/phpdotenv][phpdotenv]]. + +Installazione: +#+begin_src bash +cd www +composer require vlucas/phpdotenv +#+end_src + +** Migrations +Contengono i cambiamenti efftuati in ordine sul database. +/Formato migration: touch "m_$(date +%s)_name.php"/ + ** Models Le classi "Model" gestiscono i dati presenti nel database e controlla che i dati seguano delle determinate regole. Tutte le classi Model derivano dalla classe "BaseModel". diff --git a/dockerfiles/web/Dockerfile b/dockerfiles/web/Dockerfile index 0e1ccc9..480a6ca 100644 --- a/dockerfiles/web/Dockerfile +++ b/dockerfiles/web/Dockerfile @@ -5,6 +5,7 @@ RUN apk update && apk add --no-cache \ php8 \ php8-fpm \ php8-mysqli \ + php8-pdo \ nginx RUN docker-php-ext-install pdo pdo_mysql mysqli diff --git a/www/.env.example b/www/.env.example new file mode 100644 index 0000000..f1a3ca7 --- /dev/null +++ b/www/.env.example @@ -0,0 +1,3 @@ +DB_DSN = mysql:host=containerAddress;port=3306;dbname=dbName +DB_USER = root +DB_PASSWORD = root diff --git a/www/.gitignore b/www/.gitignore index 57872d0..3d5163b 100644 --- a/www/.gitignore +++ b/www/.gitignore @@ -1 +1,2 @@ /vendor/ +.env diff --git a/www/Migrations/m_1650263661_Initial.php b/www/Migrations/m_1650263661_Initial.php new file mode 100644 index 0000000..88b7c31 --- /dev/null +++ b/www/Migrations/m_1650263661_Initial.php @@ -0,0 +1,29 @@ +db; + + $sql = "CREATE TABLE users ( + id INT AUTO_INCREMENT PRIMARY KEY, + email VARCHAR(255) NOT NULL, + firstname VARCHAR(255) NOT NULL, + lastname VARCHAR(255) NOT NULL, + status TINYINT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) ENGINE=INNODB;"; + + $db->pdo->exec($sql); + } + + public function down() { + $db = Application::$app->db; + + $sql = "DROP TABLE users;"; + + $db->pdo->exec($sql); + } +} +?> diff --git a/www/Migrations/m_1650271236_addPasswordToUser.php b/www/Migrations/m_1650271236_addPasswordToUser.php new file mode 100644 index 0000000..4069115 --- /dev/null +++ b/www/Migrations/m_1650271236_addPasswordToUser.php @@ -0,0 +1,22 @@ +db; + + $sql = "ALTER TABLE users ADD COLUMN password VARCHAR(512) NOT NULL;"; + + $db->pdo->exec($sql); + } + + public function down() { + $db = Application::$app->db; + + $sql = "ALTER TABLE users DROP COLUMN password;"; + + $db->pdo->exec($sql); + } +} +?> diff --git a/www/composer.json b/www/composer.json index d7efd5b..c7ea11a 100644 --- a/www/composer.json +++ b/www/composer.json @@ -11,5 +11,7 @@ "app\\": "./" } }, - "require": {} + "require": { + "vlucas/phpdotenv": "^5.4" + } } diff --git a/www/composer.lock b/www/composer.lock index b9bf3ef..b0e7f04 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -4,8 +4,470 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9cda257e3e012f147b9543a67efa2819", - "packages": [], + "content-hash": "969f7975a8700b8eec51b07397e5007c", + "packages": [ + { + "name": "graham-campbell/result-type", + "version": "v1.0.4", + "source": { + "type": "git", + "url": "https://github.com/GrahamCampbell/Result-Type.git", + "reference": "0690bde05318336c7221785f2a932467f98b64ca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/0690bde05318336c7221785f2a932467f98b64ca", + "reference": "0690bde05318336c7221785f2a932467f98b64ca", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "phpoption/phpoption": "^1.8" + }, + "require-dev": { + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "GrahamCampbell\\ResultType\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "An Implementation Of The Result Type", + "keywords": [ + "Graham Campbell", + "GrahamCampbell", + "Result Type", + "Result-Type", + "result" + ], + "support": { + "issues": "https://github.com/GrahamCampbell/Result-Type/issues", + "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.0.4" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", + "type": "tidelift" + } + ], + "time": "2021-11-21T21:41:47+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.8.1", + "source": { + "type": "git", + "url": "https://github.com/schmittjoh/php-option.git", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "reference": "eab7a0df01fe2344d172bff4cd6dbd3f8b84ad15", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "phpunit/phpunit": "^6.5.14 || ^7.5.20 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.8-dev" + } + }, + "autoload": { + "psr-4": { + "PhpOption\\": "src/PhpOption/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com", + "homepage": "https://github.com/schmittjoh" + }, + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + } + ], + "description": "Option Type for PHP", + "keywords": [ + "language", + "option", + "php", + "type" + ], + "support": { + "issues": "https://github.com/schmittjoh/php-option/issues", + "source": "https://github.com/schmittjoh/php-option/tree/1.8.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", + "type": "tidelift" + } + ], + "time": "2021-12-04T23:24:31+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "30885182c981ab175d4d034db0f6f469898070ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab", + "reference": "30885182c981ab175d4d034db0f6f469898070ab", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-10-20T20:35:02+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825", + "reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2021-11-30T18:21:41+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.25.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.23-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-03-04T08:16:47+00:00" + }, + { + "name": "vlucas/phpdotenv", + "version": "v5.4.1", + "source": { + "type": "git", + "url": "https://github.com/vlucas/phpdotenv.git", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/264dce589e7ce37a7ba99cb901eed8249fbec92f", + "reference": "264dce589e7ce37a7ba99cb901eed8249fbec92f", + "shasum": "" + }, + "require": { + "ext-pcre": "*", + "graham-campbell/result-type": "^1.0.2", + "php": "^7.1.3 || ^8.0", + "phpoption/phpoption": "^1.8", + "symfony/polyfill-ctype": "^1.23", + "symfony/polyfill-mbstring": "^1.23.1", + "symfony/polyfill-php80": "^1.23.1" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-filter": "*", + "phpunit/phpunit": "^7.5.20 || ^8.5.21 || ^9.5.10" + }, + "suggest": { + "ext-filter": "Required to use the boolean validator." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.4-dev" + } + }, + "autoload": { + "psr-4": { + "Dotenv\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Vance Lucas", + "email": "vance@vancelucas.com", + "homepage": "https://github.com/vlucas" + } + ], + "description": "Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.", + "keywords": [ + "dotenv", + "env", + "environment" + ], + "support": { + "issues": "https://github.com/vlucas/phpdotenv/issues", + "source": "https://github.com/vlucas/phpdotenv/tree/v5.4.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", + "type": "tidelift" + } + ], + "time": "2021-12-12T23:22:04+00:00" + } + ], "packages-dev": [], "aliases": [], "minimum-stability": "stable", diff --git a/www/core/Application.php b/www/core/Application.php index 9ffc416..a0abfe3 100644 --- a/www/core/Application.php +++ b/www/core/Application.php @@ -7,17 +7,19 @@ class Application { public Router $router; public Request $req; public Response $res; + public Database $db; public static Application $app; public static string $ROOT_DIR; - public function __construct(string $root) { + public function __construct(string $root, array $config) { self::$ROOT_DIR = $root; self::$app = $this; $this->req = new Request(); $this->res = new Response(); $this->router = new Router($this->req, $this->res); + $this->db = new Database($config["db"]); } public function run() { diff --git a/www/core/Database.php b/www/core/Database.php new file mode 100644 index 0000000..8c07075 --- /dev/null +++ b/www/core/Database.php @@ -0,0 +1,81 @@ +pdo = new PDO($dsn, $user, $password); + $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + } + + public function applyMigrations() + { + $this->createMigrationsTable(); + $applied = $this->getAppliedMigrations(); + + $newMigrations = []; + $files = scandir(Application::$ROOT_DIR . "/Migrations"); + $toApply = array_diff($files, $applied); + + foreach ($toApply as $migration) { + if ($migration == "." || $migration == "..") { + continue; + } + + require_once Application::$ROOT_DIR . "/Migrations/$migration"; + $migrationClassName = pathinfo($migration, PATHINFO_FILENAME); + + $this->log("Applying migration: $migration"); + $migrationInstance = new $migrationClassName; + $migrationInstance->up(); + $this->log("Applied migration: $migration"); + + $newMigrations[] = $migration; + } + + if (!empty($newMigrations)) { + $this->saveMigration($newMigrations); + } else { + $this->log("All migrations are applied!"); + } + } + + public function createMigrationsTable() + { + $this->pdo->exec(" +CREATE TABLE IF NOT EXISTS migrations ( + id INT AUTO_INCREMENT PRIMARY KEY, + migration VARCHAR(255), + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=INNODB; +"); + } + + public function getAppliedMigrations() + { + $statment = $this->pdo->prepare("SELECT migration FROM migrations"); + $statment->execute(); + + return $statment->fetchAll(PDO::FETCH_COLUMN); + } + + public function saveMigration(array $migrations) + { + $str = implode(",", array_map(fn($m) => "('$m')", $migrations)); + $this->pdo->prepare("INSERT INTO migrations(migration) VALUES $str")->execute(); + } + + protected function log(string $message) { + echo "[".date("Y/m/d H:m:s")."] - $message".PHP_EOL; + } +} diff --git a/www/migrationScript.php b/www/migrationScript.php new file mode 100644 index 0000000..c9a0432 --- /dev/null +++ b/www/migrationScript.php @@ -0,0 +1,20 @@ +load(); + +$config = [ + "db" => [ + "dsn" => $_ENV["DB_DSN"], + "user" => $_ENV["DB_USER"], + "password" => $_ENV["DB_PASSWORD"] + ] +]; + +$app = new Application(__DIR__, $config); +$app->db->applyMigrations(); +?> diff --git a/www/pub/index.php b/www/pub/index.php index e2ef83f..57aa14d 100644 --- a/www/pub/index.php +++ b/www/pub/index.php @@ -1,11 +1,21 @@ load(); + +$config = [ + "db" => [ + "dsn" => $_ENV["DB_DSN"], + "user" => $_ENV["DB_USER"], + "password" => $_ENV["DB_PASSWORD"] + ] +]; + +$app = new Application(dirname(__DIR__), $config); $app->router->get("/", [SiteController::class, "home"]);