diff --git a/docs/book/v1/tutorials/doctrine-installation.md b/docs/book/v1/tutorials/doctrine-installation.md new file mode 100644 index 0000000..c6cb65f --- /dev/null +++ b/docs/book/v1/tutorials/doctrine-installation.md @@ -0,0 +1,312 @@ +# Installing Doctrine + +## Composer Requirements + +The first step is to add alongside your current packages the required entries for our Doctrine installation. We would add the following to our `composer.json` file located in our root folder: + +```json +{ + "require": { + "dotkernel/dot-cache": "^4.0", + "ramsey/uuid": "^4.5.0", + "ramsey/uuid-doctrine": "^2.1.0", + "roave/psr-container-doctrine": "^5.2.2" + }, + "require-dev": { + "phpstan/phpstan-doctrine": "^2.0.3" + } +} +``` + +`dotkernel/dot-cache` + +Provides caching support for DotKernel applications. +It offers a PSR-6 and PSR-16 compatible caching system that integrates smoothly with DotKernel's service container. + +`ramsey/uuid` + +A widely used PHP library for generating and working with UUIDs (Universally Unique Identifiers). +It supports multiple UUID versions. + +`ramsey/uuid-doctrine` + +Adds UUID support to Doctrine ORM using ramsey/uuid. +It allows Doctrine to store and retrieve UUIDs as proper value objects instead of plain strings, improving type safety. + +`roave/psr-container-doctrine` + +Provides a set of factory classes that integrate Doctrine ORM with any PSR-11 container. +It simplifies wiring Doctrine EntityManager, DBAL, configuration, and related services in frameworks like DotKernel. + +`phpstan/phpstan-doctrine (dev requirement)` + +An extension for PHPStan that improves static analysis for Doctrine. +It understands entity metadata, repositories, and common Doctrine patterns, helping catch errors during development. + +## Setting Up Doctrine + +After successfully installing our dependencies we now need to configure our Doctrine instance. + +### Declare your database + +In the file `config/autoload/local.php`: + +```php +$databases = [ + 'default' => [ + 'host' => 'localhost', + 'dbname' => 'light', + 'user' => 'root', + 'password' => '123', + 'port' => 3306, + 'driver' => 'pdo_mysql', + 'charset' => 'utf8mb4', + 'collate' => 'utf8mb4_general_ci', + ], + // you can add more database connections into this array +]; + +return [ + 'databases' => $databases, + //the rest of your configuration variables +]; +``` + +### Declare the Doctrine Drivers and Migrations Location + +With the very nice utility of the package `laminas/laminas-config-aggregator` we can declare our doctrine settings in the `src/App/src/ConfigProvider.php` file. +This package takes all the provided configs from the `config/config.php` file and merges them into one. + +Our new `src/App/src/ConfigProvider.php` class would look like this now: + +```php +, + * templates: array, + * } + */ + public function __invoke(): array + { + return [ + 'dependencies' => $this->getDependencies(), + 'doctrine' => $this->getDoctrineConfig(), + 'templates' => $this->getTemplates(), + ]; + } + + /** + * @return array{ + * delegators: array>, + * factories: array, + * } + */ + public function getDependencies(): array + { + return [ + 'delegators' => [ + Application::class => [ + RoutesDelegator::class, + ], + ], + 'factories' => [ + 'doctrine.entity_manager.orm_default' => EntityManagerFactory::class, + GetIndexViewHandler::class => GetIndexViewHandlerFactory::class, + ], + 'aliases' => [ + EntityManager::class => 'doctrine.entity_manager.orm_default', + EntityManagerInterface::class => 'doctrine.entity_manager.orm_default', + ], + ]; + } + + /** + * @return array{ + * paths: array{ + * app: array{literal-string&non-falsy-string}, + * error: array{literal-string&non-falsy-string}, + * layout: array{literal-string&non-falsy-string}, + * partial: array{literal-string&non-falsy-string}, + * } + * } + */ + public function getTemplates(): array + { + return [ + 'paths' => [ + 'app' => [__DIR__ . '/../templates/app'], + 'error' => [__DIR__ . '/../templates/error'], + 'layout' => [__DIR__ . '/../templates/layout'], + 'partial' => [__DIR__ . '/../templates/partial'], + ], + ]; + } + + private function getDoctrineConfig(): array + { + return [ + 'cache' => [ + 'array' => [ + 'class' => ArrayAdapter::class, + ], + 'filesystem' => [ + 'class' => FilesystemAdapter::class, + 'directory' => getcwd() . '/data/cache', + 'namespace' => 'doctrine', + ], + ], + 'configuration' => [ + 'orm_default' => [ + 'result_cache' => 'filesystem', + 'metadata_cache' => 'filesystem', + 'query_cache' => 'filesystem', + 'hydration_cache' => 'array', + 'typed_field_mapper' => null, + 'second_level_cache' => [ + 'enabled' => true, + 'default_lifetime' => 3600, + 'default_lock_lifetime' => 60, + 'file_lock_region_directory' => '', + 'regions' => [], + ], + ], + ], + 'connection' => [ + 'orm_default' => [ + 'doctrine_mapping_types' => [ + UuidBinaryType::NAME => 'binary', + UuidBinaryOrderedTimeType::NAME => 'binary', + ], + ], + ], + 'driver' => [ + // The default metadata driver aggregates all other drivers into a single one. + // Override `orm_default` only if you know what you're doing. + 'orm_default' => [ + 'class' => MappingDriverChain::class, + ], + ], + 'migrations' => [ + // Modify this line based on where you would like to have you migrations + 'migrations_paths' => [ + 'Migrations' => 'src/Migrations', + ], + 'all_or_nothing' => true, + 'check_database_platform' => true, + ], + 'types' => [ + UuidType::NAME => UuidType::class, + UuidBinaryType::NAME => UuidBinaryType::class, + UuidBinaryOrderedTimeType::NAME => UuidBinaryOrderedTimeType::class, + ], + ]; + } +} + +``` + +We also require a new file `config/cli-config.php`. +It initializes and returns a `DependencyFactory` that Doctrine Migrations uses to run migrations. + +```php +get(EntityManager::class); +$entityManager->getEventManager(); + +return DependencyFactory::fromEntityManager( + new ConfigurationArray($container->get('config')['doctrine']['migrations']), + new ExistingEntityManager($entityManager) +); +``` + +## Running doctrine + +Now that everything has been configured we only need to do one last thing, to create an executable for the Doctrine CLI. +In our case we will create it as `/bin/doctrine` + +```php +#!/usr/bin/env php +get(EntityManager::class); +$entityManager->getEventManager(); + +ConsoleRunner::run(new SingleManagerProvider($entityManager)); +``` + +(Optional) To keep things tidy we recommend to make an executable for the migrations of Doctrine as well for example `/bin/doctrine-migrations`: + +```php +#!/usr/bin/env php +