Utiliser Doctrine dans le framework php Slim 4

La documentation de Slim est globalement bien faite, mais pour la version 4 il manque encore des parties. Comme je suis novice sur ces technologies j’ai mis un moment avant de comprendre comment transposer l’intégration de Doctrine ORM de Slim 3 (bien documenté) – vers Slim 4 (peu documenté).

Cet article est donc surtout un mémo pour le moi du futur. Cependant, s’il peut vous être utilise mais que mes explications ne sont pas assez claires, les commentaires sont ouverts.

Ajouter Doctrine à l’application

composer require doctrine/orm:^2

Paramétrer l’accès à la base de donnée

Le paramétrage de connexion à la base s’ajoute dans les settings de l’app.

En dur dans code

Ce n’est pas un bonne idée mais c’est simple à comprendre.

<?php

// settings.php

define('APP_ROOT', __DIR__);

return [
    'settings' => [
        'displayErrorDetails' => true,
        'determineRouteBeforeAppMiddleware' => false,

        'doctrine' => [
            // if true, metadata caching is forcefully disabled
            'dev_mode' => true,

            // path where the compiled metadata info will be cached
            // make sure the path exists and it is writable
            'cache_dir' => APP_ROOT . '/var/doctrine',

            // you should add any other path containing annotated entity classes
            'metadata_dirs' => [APP_ROOT . '/src/Domain'],

            'connection' => [
                'driver' => 'pdo_mysql',
                'host' => 'localhost',
                'port' => 3306,
                'dbname' => 'mydb',
                'user' => 'user',
                'password' => 'secret',
                'charset' => 'utf8'
            ]
        ]
    ]
];

En utilisant un .env

Pour ne pas avoir dans le code d’informations liés à l’environnement d’execution, une technique classique est d’utiliser un ficher .env qui n’est pas versionné. Il contiendra les données de paramètrage propre à l’environnement d’execution. Dans notre cas, les accès à la base de données.

Pour me simplifier la vie, j’utilise la librairie Dotenv de Vance Luacs.

composer require vlucas/phpdotenv

Si vous ne connaissez pas, reportez vous à la documentation, la librairie est très simple à utiliser.

Définir le service EntityManager

Dans la documentation de Slim V3, la definition de l’EntityManager est faite dans un ficher bootstrap.php placé à la racine du projet. Pour Slim V4, je préfère définir le service directement dans le containerBuilder. Je place le code dans /app/dependencies.php

<?php
declare(strict_types=1);

use DI\ContainerBuilder;
use Monolog\Handler\StreamHandler;
use Monolog\Logger;
use Monolog\Processor\UidProcessor;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;

use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Cache\FilesystemCache;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
use Doctrine\ORM\Tools\Setup;

return function (ContainerBuilder $containerBuilder) {
    $containerBuilder->addDefinitions([
        LoggerInterface::class => function (ContainerInterface $c) {
            $settings = $c->get('settings');

            $loggerSettings = $settings['logger'];
            $logger = new Logger($loggerSettings['name']);

            $processor = new UidProcessor();
            $logger->pushProcessor($processor);

            $handler = new StreamHandler($loggerSettings['path'], $loggerSettings['level']);
            $logger->pushHandler($handler);

            return $logger;
        },

        EntityManager::class => function (ContainerInterface $container): EntityManager {
            $settings = $container->get('settings');
            
            $config = Setup::createAnnotationMetadataConfiguration(
                $settings['doctrine']['metadata_dirs'],
                $settings['doctrine']['dev_mode']
            );
        
            $config->setMetadataDriverImpl(
                new AnnotationDriver(
                    new AnnotationReader,
                    $settings['doctrine']['metadata_dirs']
                )
            );
        
            $config->setMetadataCacheImpl(
                new FilesystemCache(
                    $settings['doctrine']['cache_dir']
                )
            );
        
            return EntityManager::create(
                $settings['doctrine']['connection'],
                $config
            );
        }
    ]);
};

La console Doctrine

Il suffit de crér un ficher cli-config.php à la racine de l’app avec le code suivant :

<?php

// cli-config.php

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

use Doctrine\ORM\Tools\Setup;
use Doctrine\ORM\Tools\Console\Command\SchemaTool;
use Doctrine\ORM\Tools\Console\Command\SchemaTool\CreateCommand;
use DI\ContainerBuilder;

require __DIR__ . '/vendor/autoload.php';

// Instantiate PHP-DI ContainerBuilder
$containerBuilder = new ContainerBuilder();

if (false) { // Should be set to true in production
	$containerBuilder->enableCompilation(__DIR__ . '/var/cache');
}

// Set up settings
$settings = require __DIR__ . '/app/settings.php';
$settings($containerBuilder);

// Set up dependencies
$dependencies = require __DIR__ . '/app/dependencies.php';
$dependencies($containerBuilder);

// Build PHP-DI Container instance
$container = $containerBuilder->build();

return ConsoleRunner::createHelperSet($container->get(EntityManager::class));

Le moment crucial !

A partir de là, Doctrine est oppérationnel. Vous pouvez vérifier que tout va bien en utilisant la commande :

php vendor/bin/doctrine

Si vous avez la liste des commandes Docrine, tout va bien. Si vous avez une erreur, commencez par vérifier les déclaration des namespaces. Il en manque peut-être.

Vos entities

Avec la configuration de cet exemple, les classes Entity que vous aller écrire sont à placer dans /src/Domain. Ou dans un sous dossier.

Voici un exemple de classe pour démarrer rapidement dans oublier de namespace :

<?php

namespace App\Domain\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Option
 * @ORM\Entity @ORM\Table(name="options")
 */
class Option
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="key", type="string")
     */
    protected $key;

    /**
     * @ORM\Column(name="value", type="string")
     */
    protected $value;

    public function getKey() {
        return $this->key;
    }
    public function setKey( $key ) {
        $this->key = $key;
    }

    public function getValue() {
        return $this->value;
    }
    public function setValue( $value ) {
        $this->value = $value;
    }
}

Utiliser le EntityManager dans votre code

Créer une Entity

Les classes des repository sont à placer dans le dossier spécifié dans la configuration. Dans cet exemple, /src/Domain

namespace App\Domain\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Exemple
 * @ORM\Entity @ORM\Table(name="exemple")
 */
class Exemple
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer", nullable=false)
     * @ORM\Id
     * * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @var string
     * @ORM\Column(name="name", type="string")
     */
    protected $name;
    

    public function getName() {
        return $this->name;
    }
    public function SetName( $value ) {
        $this->name = $value;
    }

}

Utiliser le EntityManager

L’objet EntityManager de Doctrine peut être passé directement au constructeur la classe Slim. Pour celà il suffit de définir un paramètre correctement typé.

use Doctrine\ORM\EntityManager;

public function __construct(ContainerInterface $container, LoggerInterface $logger, EntityManager $em = null)
{
        $this->logger = $logger;
        $this->container = $container;
        $this->em = $em;
}

Dans cette exemple le constructeur reçoit le container Slim, le logger et l’EntityManager.

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *