From d05faaead7e53e19d2770af0ae4fb62ca36de323 Mon Sep 17 00:00:00 2001 From: Dave Smith-Hayes Date: Wed, 26 Jun 2024 21:10:48 -0400 Subject: [PATCH] Add database migrations, start fleshing out a specific database connection. --- .../20240624023258_create_channels_table.php | 3 +- app/src/Bootstrap.php | 44 ++++++++++++++++--- .../DatabaseConnectionSchema.php | 4 +- app/src/Controller/HealthCheck.php | 28 ++++++++++++ app/src/Domain/Repository/UserRepository.php | 6 +-- .../DatabaseConnectionInterface.php | 3 -- ...nnection.php => PdoDatabaseConnection.php} | 18 ++++---- app/src/Routes.php | 2 + 8 files changed, 83 insertions(+), 25 deletions(-) create mode 100644 app/src/Controller/HealthCheck.php rename app/src/Infrastructure/{DatabaseConnection.php => PdoDatabaseConnection.php} (53%) diff --git a/app/db/migrations/20240624023258_create_channels_table.php b/app/db/migrations/20240624023258_create_channels_table.php index 55a7af6..41d57a7 100644 --- a/app/db/migrations/20240624023258_create_channels_table.php +++ b/app/db/migrations/20240624023258_create_channels_table.php @@ -19,7 +19,7 @@ CREATE TABLE channels ( image_id INT(11) UNSIGNED NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY(`id`), FOREIGN KEY(`owner_id`) REFERENCES users(`id`), @@ -56,6 +56,7 @@ final class CreateChannelsTable extends AbstractMigration ->addColumn('image_id', 'integer') ->addIndex([ 'name' ], [ 'unique' => true ]) ->addForeignKey('owner_id', 'users') + ->addForeignKey('image_id', 'images') ->create(); } } diff --git a/app/src/Bootstrap.php b/app/src/Bootstrap.php index 817f6ab..eea2821 100644 --- a/app/src/Bootstrap.php +++ b/app/src/Bootstrap.php @@ -2,6 +2,8 @@ namespace Slovocast; +use Psr\Container\ContainerExceptionInterface; +use Psr\Container\NotFoundExceptionInterface; use Slim\App; use Slim\Factory\AppFactory; @@ -22,12 +24,14 @@ use Slovocast\Configuration\{ DatabaseConnectionSchema, SessionSchema }; +use Twig\Error\LoaderError; use Slovocast\Domain\Repository\{ UserRepositoryInterface, UserRepository }; use Slovocast\Infrastructure\{ DatabaseConnectionInterface, + PdoDatabaseConnection, User\UserAuthorizationInterface, User\BasicUserAuthorization }; @@ -38,7 +42,7 @@ use Monolog\Handler\StreamHandler; use Monolog\Level; use Odan\Session\PhpSession; use Odan\Session\SessionInterface; -use Odan\Session\SessionManagerInterface; +use Odan\Session\SessionManagerInterface; /** * Defines here are used globally @@ -53,7 +57,7 @@ define('APP_TEMP_DIR', __DIR__ . '/../var/temp'); class Bootstrap { /** - * Pulls out all the configuration schemas and configuration values. + * Pulls out all the configuration schemas and configuration values. * * @return Configuration */ @@ -77,6 +81,12 @@ class Bootstrap ], 'session' => [ 'name' => 'slovocast' + ], + 'database' => [ + 'host' => '127.0.0.1', + 'database' => 'slovocast', + 'username' => 'slovocast', + 'password' => 'Password01', ] ]); @@ -88,8 +98,9 @@ class Bootstrap * initialization of configuration. * * @return Container + * @throws \Exception */ - protected static function initContainer(): Container + protected static function initContainer(): Container { $containerBuilder = new ContainerBuilder(); @@ -117,7 +128,7 @@ class Bootstrap 'flash' => function (ContainerInterface $container) { return $container->get(SessionInterface::class)->getFlash(); }, - + /** * Application DI */ @@ -128,6 +139,20 @@ class Bootstrap /** * Database Connections */ + DatabaseConnectionInterface::class => function(ContainerInterface $container) { + $databaseConfig = $container->get('config')->get('database'); + $dsn = sprintf( + "%s:dbname=%s;host=%s", + $databaseConfig['driver'], + $databaseConfig['database'], + $databaseConfig['host'] + ); + $username = $databaseConfig['username']; + $password = $databaseConfig['password']; + + $connection = new PdoDatabaseConnection($dsn, $username, $password); + return $connection; + }, /** * Utility classes @@ -151,7 +176,7 @@ class Bootstrap } /** - * Tasking the instaniated Application, sets up all the routes for the + * Tasking the instantiated Application, sets up all the routes for the * application. * * @param App $app @@ -169,6 +194,9 @@ class Bootstrap * method. * * @param App $app + * @throws ContainerExceptionInterface + * @throws NotFoundExceptionInterface + * @throws LoaderError */ protected static function establishMiddleware(App $app): void { @@ -176,7 +204,7 @@ class Bootstrap $container = $app->getContainer(); /** - * @var Configuration + * @var Configuration $config */ $config = $container->get('config'); @@ -202,6 +230,10 @@ class Bootstrap * Instantiates the application. * * @return App + * @throws ContainerExceptionInterface + * @throws LoaderError + * @throws NotFoundExceptionInterface + * @throws \Exception */ public static function init(): App { diff --git a/app/src/Configuration/DatabaseConnectionSchema.php b/app/src/Configuration/DatabaseConnectionSchema.php index 757d1eb..6fedd60 100644 --- a/app/src/Configuration/DatabaseConnectionSchema.php +++ b/app/src/Configuration/DatabaseConnectionSchema.php @@ -10,9 +10,9 @@ class DatabaseConnectionSchema public static function getSchema(): Schema { return Expect::structure([ - 'driver' => Expect::anyOf('mysql', 'sqlite')->required(), + 'driver' => Expect::anyOf('mysql', 'sqlite')->default('mysql'), 'host' => Expect::string()->default('localhost'), - 'post' => Expect::int()->min(1)->max(65535), + 'port' => Expect::int()->min(1)->max(65535)->default(3306), 'database' => Expect::string()->required(), 'username' => Expect::string()->required(), 'password' => Expect::string()->nullable() diff --git a/app/src/Controller/HealthCheck.php b/app/src/Controller/HealthCheck.php new file mode 100644 index 0000000..9ce8dc9 --- /dev/null +++ b/app/src/Controller/HealthCheck.php @@ -0,0 +1,28 @@ +connection + */ + $stmt = $this->connection->query("SELECT 1"); + $dbResult = $stmt->fetch(); + + return $this->json([ + 'http' => true, + 'database' => (bool) $dbResult[0] + ]); + } +} diff --git a/app/src/Domain/Repository/UserRepository.php b/app/src/Domain/Repository/UserRepository.php index 99e4996..08c3f80 100644 --- a/app/src/Domain/Repository/UserRepository.php +++ b/app/src/Domain/Repository/UserRepository.php @@ -34,7 +34,7 @@ class UserRepository implements UserRepositoryInterface /** * @param string $query The Query for getting a User - * @param arary $params The parameters in the query + * @param array $params The parameters in the query * @param PDO $connection The PDO connection * @return array The column data from the Database * @throws EntityNotFoundException The User does no exist @@ -83,8 +83,8 @@ class UserRepository implements UserRepositoryInterface { $query = "SELECT * FROM users WHERE email = :email LIMIT 1"; $userData = $this->queryForUser( - $query, - [ 'email' => $email ], + $query, + [ 'email' => $email ], $this->database->getConnection() ); diff --git a/app/src/Infrastructure/DatabaseConnectionInterface.php b/app/src/Infrastructure/DatabaseConnectionInterface.php index 00f6a05..edc1b1e 100644 --- a/app/src/Infrastructure/DatabaseConnectionInterface.php +++ b/app/src/Infrastructure/DatabaseConnectionInterface.php @@ -2,14 +2,11 @@ namespace Slovocast\Infrastructure; -use PDO; - /** * Represents an active connection to the Database through the PDO. Unlike a * pool, these connections don't need to be returned. */ interface DatabaseConnectionInterface { - public function getConnection(): PDO; public function getName(): string; } diff --git a/app/src/Infrastructure/DatabaseConnection.php b/app/src/Infrastructure/PdoDatabaseConnection.php similarity index 53% rename from app/src/Infrastructure/DatabaseConnection.php rename to app/src/Infrastructure/PdoDatabaseConnection.php index e0e0d2f..c2d9418 100644 --- a/app/src/Infrastructure/DatabaseConnection.php +++ b/app/src/Infrastructure/PdoDatabaseConnection.php @@ -8,20 +8,18 @@ use Slovocast\Infrastructure\DatabaseConnectionInterface; /** * Represents an active connection to a database */ -class DatabaseConnection implements DatabaseConnectionInterface +class PdoDatabaseConnection extends PDO + implements DatabaseConnectionInterface { - public function __construct( - private string $name, - private PDO $pdo - ) { } - - public function getConnection(): PDO - { - return $this->pdo; - } + protected string $name; public function getName(): string { return $this->name; } + + public function setName(string $name): void + { + $this->name = $name; + } } diff --git a/app/src/Routes.php b/app/src/Routes.php index 2f1fe30..bbbd717 100644 --- a/app/src/Routes.php +++ b/app/src/Routes.php @@ -4,6 +4,7 @@ namespace Slovocast; use Slim\App; use Slovocast\Controller\HomePage; +use Slovocast\Controller\HealthCheck; use Slovocast\Controller\User\{ RegisterUserPage, RegisterUserAction, @@ -20,6 +21,7 @@ class Routes public static function init(App $app): void { $app->get('/', HomePage::class); + $app->get('/healthcheck', HealthCheck::class); // User Routes self::users($app); }