このクックブックエントリでは、広く使用されているDoctrine ORMをSlim 4アプリケーションにゼロから統合する方法について説明します。
最初のステップは、composerを使用してDoctrine ORMをプロジェクトにインポートすることです。
composer require doctrine/orm:^3.0 doctrine/dbal:^4.0 symfony/cache
2021年4月30日、Doctrineはバージョンv2.0.0をリリースした際にdoctrine/cache
を正式に非推奨としました。これにより、そのライブラリからすべてのキャッシュ実装が削除されました。それ以来、PSR-6準拠の実装であるsymfony/cache
を使用することを推奨しています。本番環境でDoctrineのメタデータをキャッシュする場合にのみ必要ですが、実行することによるデメリットはありませんので、設定方法を示します。
PHP8に移行していない場合、またはエンティティのアノテーションに従来のPHPDocコメントを引き続き使用したい場合は、doctrine/annotations
パッケージもインポートする必要があります。これは以前はdoctrine/orm
の依存関係でしたが、2.10.0以降はオプションとなっています。
composer require doctrine/annotations
このステップをスキップして、実際のDoctrineエンティティを使用できます。以下は単なる例です。
PHP8属性を使用していることに注意してください。必要に応じて、PHPDocアノテーションに変換してください。
<?php
// src/Domain/User.php
use DateTimeImmutable;
use Doctrine\ORM\Mapping\Column;
use Doctrine\ORM\Mapping\Entity;
use Doctrine\ORM\Mapping\GeneratedValue;
use Doctrine\ORM\Mapping\Id;
use Doctrine\ORM\Mapping\Table;
#[Entity, Table(name: 'users')]
final class User
{
#[Id, Column(type: 'integer'), GeneratedValue(strategy: 'AUTO')]
private int $id;
#[Column(type: 'string', unique: true, nullable: false, options: ['collation' => 'NOCASE'])]
private string $email;
#[Column(name: 'registered_at', type: 'datetimetz_immutable', nullable: false)]
private DateTimeImmutable $registeredAt;
public function __construct(string $email)
{
$this->email = $email;
$this->registeredAt = new DateTimeImmutable('now');
}
public function getId(): int
{
return $this->id;
}
public function getEmail(): string
{
return $this->email;
}
public function getRegisteredAt(): DateTimeImmutable
{
return $this->registeredAt;
}
}
次に、Slimの設定とともにDoctrineの設定を追加します。
<?php
// settings.php
define('APP_ROOT', __DIR__);
return [
'settings' => [
'slim' => [
// Returns a detailed HTML page with error details and
// a stack trace. Should be disabled in production.
'displayErrorDetails' => true,
// Whether to display errors on the internal PHP log or not.
'logErrors' => true,
// If true, display full errors with message and stack trace on the PHP log.
// If false, display only "Slim Application Error" on the PHP log.
// Doesn't do anything when 'logErrors' is false.
'logErrorDetails' => true,
],
'doctrine' => [
// Enables or disables Doctrine metadata caching
// for either performance or convenience during development.
'dev_mode' => true,
// Path where Doctrine will cache the processed metadata
// when 'dev_mode' is false.
'cache_dir' => APP_ROOT . '/var/doctrine',
// List of paths where Doctrine will search for metadata.
// Metadata can be either YML/XML files or PHP classes annotated
// with comments or PHP8 attributes.
'metadata_dirs' => [APP_ROOT . '/src/Domain'],
// The parameters Doctrine needs to connect to your database.
// These parameters depend on the driver (for instance the 'pdo_sqlite' driver
// needs a 'path' parameter and doesn't use most of the ones shown in this example).
// Refer to the Doctrine documentation to see the full list
// of valid parameters: https://www.doctrine-project.org/projects/doctrine-dbal/en/current/reference/configuration.html
'connection' => [
'driver' => 'pdo_mysql',
'host' => 'localhost',
'port' => 3306,
'dbname' => 'mydb',
'user' => 'user',
'password' => 'secret',
'charset' => 'utf-8'
]
]
]
];
ここで、EntityManager
サービスを定義します。これは、コードでORMと対話する主要なポイントです。
Slim 4では、独自のPSR-11コンテナ実装を提供する必要があります。この例では、シンプルで簡潔なPSR-11コンテナであるuma/dic
を使用しています。お好みのコンテナに合わせて適宜変更してください。
従来、アノテーションメタデータリーダーが最も人気がありましたが、doctrine/orm
2.10.0以降、doctrine/annotations
への依存関係はオプションとなり、プロジェクトはユーザーが最新のPHP8属性表記に移行することを推奨しています。
ここでは、PHP8属性を使用してメタデータリーダーを構成する方法を示します。PHP8に移行していない場合、または従来のPHPDocアノテーションを使用する場合は、Composerでdoctrine/annotations
を明示的に必要とし、次の例のようにSetup::createAttributeMetadataConfiguration(...)
ではなくSetup::createAnnotationMetadataConfiguration(...)
を呼び出す必要があります。
<?php
// bootstrap.php
use Doctrine\DBAL\DriverManager;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\ORMSetup;
use Psr\Container\ContainerInterface;
use Symfony\Component\Cache\Adapter\ArrayAdapter;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use UMA\DIC\Container;
require_once __DIR__ . '/vendor/autoload.php';
$container = new Container(require __DIR__ . '/settings.php');
$container->set(EntityManager::class, static function (Container $c): EntityManager {
/** @var array $settings */
$settings = $c->get('settings');
// Use the ArrayAdapter or the FilesystemAdapter depending on the value of the 'dev_mode' setting
// You can substitute the FilesystemAdapter for any other cache you prefer from the symfony/cache library
$cache = $settings['doctrine']['dev_mode'] ?
new ArrayAdapter() :
new FilesystemAdapter(directory: $settings['doctrine']['cache_dir']);
$config = ORMSetup::createAttributeMetadataConfiguration(
$settings['doctrine']['metadata_dirs'],
$settings['doctrine']['dev_mode'],
null,
$cache
);
$connection = DriverManager::getConnection($settings['doctrine']['connection']);
return new EntityManager($connection, $config);
});
return $container;
データベースマイグレーションの実行、クラスアノテーションの検証などを行うには、doctrine
CLIアプリケーションを作成します。このファイルは、コンテナで定義したEntityManagerサービスを取得し、ConsoleRunner::run(new SingleManagerProvider($em))
に渡すだけです。
.php拡張子なしでこのファイルを作成し、実行可能にするのが一般的です。プロジェクトのルートまたはbin/
サブディレクトリに配置できます。このファイルは#!/usr/bin/env php
で始まり、Linuxではchmod +x doctrine
コマンドで実行可能にすることができます。
#!/usr/bin/env php
<?php
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Doctrine\ORM\Tools\Console\EntityManagerProvider\SingleManagerProvider;
use Slim\Container;
/** @var Container $container */
$container = require_once __DIR__ . '/bootstrap.php';
ConsoleRunner::run(new SingleManagerProvider($container->get(EntityManager::class)));
コンソールアプリが機能することを確認してください。正しく設定されている場合、出力はおおよそ次のようになります。
$ ./doctrine
Doctrine Command Line Interface 3.1.1.0
Usage:
command [options] [arguments]
Options:
-h, --help Display help for the given command. When no command is given display help for the list command
-q, --quiet Do not output any message
-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
Available commands:
completion Dump the shell completion script
help Display help for a command
list List commands
dbal
dbal:run-sql Executes arbitrary SQL directly from the command line.
orm
orm:clear-cache:metadata Clear all metadata cache of the various cache drivers
orm:clear-cache:query Clear all query cache of the various cache drivers
orm:clear-cache:region:collection Clear a second-level cache collection region
orm:clear-cache:region:entity Clear a second-level cache entity region
orm:clear-cache:region:query Clear a second-level cache query region
orm:clear-cache:result Clear all result cache of the various cache drivers
orm:generate-proxies [orm:generate:proxies] Generates proxy classes for entity classes
orm:info Show basic information about all mapped entities
orm:mapping:describe Display information about mapped objects
orm:run-dql Executes arbitrary DQL directly from the command line
orm:schema-tool:create Processes the schema and either create it directly on EntityManager Storage Connection or generate the SQL output
orm:schema-tool:drop Drop the complete database schema of EntityManager Storage Connection or generate the corresponding SQL output
orm:schema-tool:update Executes (or dumps) the SQL needed to update the database schema to match the current mapping metadata
orm:validate-schema Validate the mapping files
php vendor/bin/doctrine orm:schema-tool:create
を実行することで、データベースを初期化し、スキーマをロードできます。
おめでとうございます!これで、コマンドラインからデータベースを管理し、コードに必要な場所にEntityManager
を使用できるようになりました。
// bootstrap.php
$container->set(UserService::class, static function (Container $c) {
return new UserService($c->get(EntityManager::class));
});
// src/UserService.php
final class UserService
{
private EntityManager $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function signUp(string $email): User
{
$newUser = new User($email);
$this->em->persist($newUser);
$this->em->flush();
return $newUser;
}
}