Add database migrations, start fleshing out a specific database connection.

This commit is contained in:
Dave Smith-Hayes 2024-06-26 21:10:48 -04:00
parent 2d6dee2f0c
commit d05faaead7
8 changed files with 83 additions and 25 deletions

View File

@ -19,7 +19,7 @@ CREATE TABLE channels (
image_id INT(11) UNSIGNED NOT NULL, image_id INT(11) UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, 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`), PRIMARY KEY(`id`),
FOREIGN KEY(`owner_id`) REFERENCES users(`id`), FOREIGN KEY(`owner_id`) REFERENCES users(`id`),
@ -56,6 +56,7 @@ final class CreateChannelsTable extends AbstractMigration
->addColumn('image_id', 'integer') ->addColumn('image_id', 'integer')
->addIndex([ 'name' ], [ 'unique' => true ]) ->addIndex([ 'name' ], [ 'unique' => true ])
->addForeignKey('owner_id', 'users') ->addForeignKey('owner_id', 'users')
->addForeignKey('image_id', 'images')
->create(); ->create();
} }
} }

View File

@ -2,6 +2,8 @@
namespace Slovocast; namespace Slovocast;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
use Slim\App; use Slim\App;
use Slim\Factory\AppFactory; use Slim\Factory\AppFactory;
@ -22,12 +24,14 @@ use Slovocast\Configuration\{
DatabaseConnectionSchema, DatabaseConnectionSchema,
SessionSchema SessionSchema
}; };
use Twig\Error\LoaderError;
use Slovocast\Domain\Repository\{ use Slovocast\Domain\Repository\{
UserRepositoryInterface, UserRepositoryInterface,
UserRepository UserRepository
}; };
use Slovocast\Infrastructure\{ use Slovocast\Infrastructure\{
DatabaseConnectionInterface, DatabaseConnectionInterface,
PdoDatabaseConnection,
User\UserAuthorizationInterface, User\UserAuthorizationInterface,
User\BasicUserAuthorization User\BasicUserAuthorization
}; };
@ -38,7 +42,7 @@ use Monolog\Handler\StreamHandler;
use Monolog\Level; use Monolog\Level;
use Odan\Session\PhpSession; use Odan\Session\PhpSession;
use Odan\Session\SessionInterface; use Odan\Session\SessionInterface;
use Odan\Session\SessionManagerInterface; use Odan\Session\SessionManagerInterface;
/** /**
* Defines here are used globally * Defines here are used globally
@ -53,7 +57,7 @@ define('APP_TEMP_DIR', __DIR__ . '/../var/temp');
class Bootstrap class Bootstrap
{ {
/** /**
* Pulls out all the configuration schemas and configuration values. * Pulls out all the configuration schemas and configuration values.
* *
* @return Configuration * @return Configuration
*/ */
@ -77,6 +81,12 @@ class Bootstrap
], ],
'session' => [ 'session' => [
'name' => 'slovocast' 'name' => 'slovocast'
],
'database' => [
'host' => '127.0.0.1',
'database' => 'slovocast',
'username' => 'slovocast',
'password' => 'Password01',
] ]
]); ]);
@ -88,8 +98,9 @@ class Bootstrap
* initialization of configuration. * initialization of configuration.
* *
* @return Container * @return Container
* @throws \Exception
*/ */
protected static function initContainer(): Container protected static function initContainer(): Container
{ {
$containerBuilder = new ContainerBuilder(); $containerBuilder = new ContainerBuilder();
@ -117,7 +128,7 @@ class Bootstrap
'flash' => function (ContainerInterface $container) { 'flash' => function (ContainerInterface $container) {
return $container->get(SessionInterface::class)->getFlash(); return $container->get(SessionInterface::class)->getFlash();
}, },
/** /**
* Application DI * Application DI
*/ */
@ -128,6 +139,20 @@ class Bootstrap
/** /**
* Database Connections * 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 * 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. * application.
* *
* @param App $app * @param App $app
@ -169,6 +194,9 @@ class Bootstrap
* method. * method.
* *
* @param App $app * @param App $app
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
* @throws LoaderError
*/ */
protected static function establishMiddleware(App $app): void protected static function establishMiddleware(App $app): void
{ {
@ -176,7 +204,7 @@ class Bootstrap
$container = $app->getContainer(); $container = $app->getContainer();
/** /**
* @var Configuration * @var Configuration $config
*/ */
$config = $container->get('config'); $config = $container->get('config');
@ -202,6 +230,10 @@ class Bootstrap
* Instantiates the application. * Instantiates the application.
* *
* @return App * @return App
* @throws ContainerExceptionInterface
* @throws LoaderError
* @throws NotFoundExceptionInterface
* @throws \Exception
*/ */
public static function init(): App public static function init(): App
{ {

View File

@ -10,9 +10,9 @@ class DatabaseConnectionSchema
public static function getSchema(): Schema public static function getSchema(): Schema
{ {
return Expect::structure([ return Expect::structure([
'driver' => Expect::anyOf('mysql', 'sqlite')->required(), 'driver' => Expect::anyOf('mysql', 'sqlite')->default('mysql'),
'host' => Expect::string()->default('localhost'), '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(), 'database' => Expect::string()->required(),
'username' => Expect::string()->required(), 'username' => Expect::string()->required(),
'password' => Expect::string()->nullable() 'password' => Expect::string()->nullable()

View File

@ -0,0 +1,28 @@
<?php
namespace Slovocast\Controller;
use Slovocast\Controller\Controller;
use Slovocast\Infrastructure\DatabaseConnectionInterface;
use Psr\Http\Message\ResponseInterface as Response;
class HealthCheck extends Controller
{
public function __construct(
protected DatabaseConnectionInterface $connection
) { }
public function handle(): Response
{
/**
* @var PdoDatabaseConnection $this->connection
*/
$stmt = $this->connection->query("SELECT 1");
$dbResult = $stmt->fetch();
return $this->json([
'http' => true,
'database' => (bool) $dbResult[0]
]);
}
}

View File

@ -34,7 +34,7 @@ class UserRepository implements UserRepositoryInterface
/** /**
* @param string $query The Query for getting a User * @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 * @param PDO $connection The PDO connection
* @return array The column data from the Database * @return array The column data from the Database
* @throws EntityNotFoundException The User does no exist * @throws EntityNotFoundException The User does no exist
@ -83,8 +83,8 @@ class UserRepository implements UserRepositoryInterface
{ {
$query = "SELECT * FROM users WHERE email = :email LIMIT 1"; $query = "SELECT * FROM users WHERE email = :email LIMIT 1";
$userData = $this->queryForUser( $userData = $this->queryForUser(
$query, $query,
[ 'email' => $email ], [ 'email' => $email ],
$this->database->getConnection() $this->database->getConnection()
); );

View File

@ -2,14 +2,11 @@
namespace Slovocast\Infrastructure; namespace Slovocast\Infrastructure;
use PDO;
/** /**
* Represents an active connection to the Database through the PDO. Unlike a * Represents an active connection to the Database through the PDO. Unlike a
* pool, these connections don't need to be returned. * pool, these connections don't need to be returned.
*/ */
interface DatabaseConnectionInterface interface DatabaseConnectionInterface
{ {
public function getConnection(): PDO;
public function getName(): string; public function getName(): string;
} }

View File

@ -8,20 +8,18 @@ use Slovocast\Infrastructure\DatabaseConnectionInterface;
/** /**
* Represents an active connection to a database * Represents an active connection to a database
*/ */
class DatabaseConnection implements DatabaseConnectionInterface class PdoDatabaseConnection extends PDO
implements DatabaseConnectionInterface
{ {
public function __construct( protected string $name;
private string $name,
private PDO $pdo
) { }
public function getConnection(): PDO
{
return $this->pdo;
}
public function getName(): string public function getName(): string
{ {
return $this->name; return $this->name;
} }
public function setName(string $name): void
{
$this->name = $name;
}
} }

View File

@ -4,6 +4,7 @@ namespace Slovocast;
use Slim\App; use Slim\App;
use Slovocast\Controller\HomePage; use Slovocast\Controller\HomePage;
use Slovocast\Controller\HealthCheck;
use Slovocast\Controller\User\{ use Slovocast\Controller\User\{
RegisterUserPage, RegisterUserPage,
RegisterUserAction, RegisterUserAction,
@ -20,6 +21,7 @@ class Routes
public static function init(App $app): void public static function init(App $app): void
{ {
$app->get('/', HomePage::class); $app->get('/', HomePage::class);
$app->get('/healthcheck', HealthCheck::class);
// User Routes // User Routes
self::users($app); self::users($app);
} }