Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
312 changes: 312 additions & 0 deletions docs/book/v1/tutorials/doctrine-installation.md
Original file line number Diff line number Diff line change
@@ -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
<?php

declare(strict_types=1);

namespace Light\App;

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\Persistence\Mapping\Driver\MappingDriverChain;
use Dot\Cache\Adapter\ArrayAdapter;
use Dot\Cache\Adapter\FilesystemAdapter;
use Light\App\Factory\GetIndexViewHandlerFactory;
use Light\App\Handler\GetIndexViewHandler;
use Mezzio\Application;
use Ramsey\Uuid\Doctrine\UuidBinaryOrderedTimeType;
use Ramsey\Uuid\Doctrine\UuidBinaryType;
use Ramsey\Uuid\Doctrine\UuidType;
use Roave\PsrContainerDoctrine\EntityManagerFactory;

use function getcwd;

class ConfigProvider
{
/**
@return array{
* dependencies: array<mixed>,
* templates: array<mixed>,
* }
*/
public function __invoke(): array
{
return [
'dependencies' => $this->getDependencies(),
'doctrine' => $this->getDoctrineConfig(),
'templates' => $this->getTemplates(),
];
}

/**
* @return array{
* delegators: array<class-string, array<class-string>>,
* factories: array<class-string, class-string>,
* }
*/
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
<?php

declare(strict_types=1);

use Doctrine\Migrations\Configuration\EntityManager\ExistingEntityManager;
use Doctrine\Migrations\Configuration\Migration\ConfigurationArray;
use Doctrine\Migrations\DependencyFactory;
use Doctrine\ORM\EntityManager;

$container = require 'config/container.php';

$entityManager = $container->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
<?php

declare(strict_types=1);

use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;

require_once 'vendor/autoload.php';

$container = require 'config/container.php';

$entityManager = $container->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
<?php

declare(strict_types=1);

namespace Doctrine\Migrations;

require __DIR__ . '/../vendor/doctrine/migrations/bin/doctrine-migrations.php';
```

Now by running the command bellow we should see the Doctrine CLI version alongside its available commands:

```shell
php bin/doctrine
```

Example (truncated) output:

```terminaloutput
Doctrine Command Line Interface 3.5.7.0

Options:
-h, --help Display help for the given command. When no command is given display help for the list command
--silent Do not output any message
-q, --quiet Only errors are displayed. All other output is suppressed
-V, --version Display this application version
--ansi|--no-ansi Force (or disable --no-ansi) ANSI output
-n, --no-interaction Do not ask any interactive question
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
```
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ nav:
- "Edit the Top Menu": v1/how-tos/edit-top-menu.md
- "Edit the Footer": v1/how-tos/edit-footer.md
- "Manage Assets": v1/how-tos/manage-assets.md
- Tutorials:
- "Installing Doctrine": v1/tutorials/doctrine-installation.md
site_name: light
site_description: "Dotkernel Light"
repo_url: "https://github.com/dotkernel/light"
Expand Down