在Slim框架中使用Doctrine
这份指南说明了如何从头开始将广泛使用的 Doctrine ORM 集成到 Slim 4 应用程序中。
添加 Doctrine 到你的应用程序
首先,使用 composer 导入 Doctrine ORM 到你的项目中。
composer require doctrine/orm symfony/cache
请注意,在 2021年4月30日,Doctrine 在官方正式废弃了 doctrine/cache,当发布 v2.0.0 版本时,该版本删除了库中的所有缓存实现。从那以后,他们推荐使用 symfony/cache,一个符合 PSR-6 标准的实现。如果你想在生产环境中缓存 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)]
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;
}
}
提供数据库凭据
接下来,将 Doctrine 设置与你的 Slim 配置一起添加。
<?php
// settings.php
define('APP_ROOT', __DIR__);
return [
'settings' => [
'slim' => [
// 是否在响应中显示错误细节
'displayErrorDetails' => true,
// 是否在内部PHP日志中显示错误
'logErrors' => true,
// 如果为true,则在PHP日志上显示具有消息和堆栈跟踪的完整错误。
// 如果为false,则在PHP日志上只显示“Slim Application Error”。
// 当'logErrors'为false时不起作用。
'logErrorDetails' => true,
],
'doctrine' => [
// 是否启用Doctrine元数据缓存,用于性能或开发方便
'dev_mode' => true,
// 在'dev_mode'为false时,Doctrine将缓存处理后的元数据的路径
'cache_dir' => APP_ROOT . '/var/doctrine',
// Doctrine将搜索元数据的路径列表,元数据可以是YML/XML文件或使用注释或PHP8属性进行注释的PHP类。
'metadata_dirs' => [APP_ROOT . '/src/Domain'],
// 连接到数据库所需的参数
// 这些参数取决于驱动程序(例如'pdo_sqlite'驱动程序需要一个'path'参数,并且不使用此示例中显示的大多数参数)。
// 请参阅Doctrine文档以获取完整的有效参数列表: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 服务
现在我们定义 EntityManager 服务,这是与 ORM 的主要交互点。
Slim 4 要求你自己提供 PSR-11 容器实现。
这个示例使用 uma/dic,一个简单而简洁的 PSR-11 容器。根据你选择的容器进行适配即可。
传统上,注解元数据读取器是最流行的,但是从 doctrine/orm 2.10.0 开始,他们将对 doctrine/annotations 的依赖关系设置为可选项,并暗示该项目希望用户迁移到现代的 PHP8 属性表示法。
这里我们展示如何使用 PHP8 属性配置元数据读取器。
如果您尚未迁移到 PHP8 或者想使用传统的 PHPDoc 注释,您需要在 Composer 中显式地要求 doctrine/annotations,并调用 Setup::createAnnotationMetadataConfiguration(...)(而不是像下面的示例那样调用 Setup::createAttributeMetadataConfiguration(...))。
<?php
// bootstrap.php
use Doctrine\Common\Cache\Psr6\DoctrineProvider;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Setup;
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');
// 根据'dev_mode'设置使用ArrayAdapter或FilesystemAdapter
// 你可以将FilesystemAdapter替换为symfony/cache库中的任何其他缓存
$cache = $settings['doctrine']['dev_mode'] ?
DoctrineProvider::wrap(new ArrayAdapter()) :
DoctrineProvider::wrap(new FilesystemAdapter(directory: $settings['doctrine']['cache_dir']));
$config = Setup::createAttributeMetadataConfiguration(
$settings['doctrine']['metadata_dirs'],
$settings['doctrine']['dev_mode'],
null,
$cache
);
return EntityManager::create($settings['doctrine']['connection'], $config);
});
return $container;
创建 Doctrine 控制台
为了运行数据库迁移、验证类注释等操作,你需要使用 doctrine CLI 应用程序,该应用程序已经存在于 vendor/bin 目录下。但是为了正常工作,这个脚本需要在项目的根目录下有一个 cli-config.php 文件,告诉它如何找到我们刚刚设置的 EntityManager。
我们的 cli-config.php 只需要在容器中检索我们刚刚定义的 EntityManager 服务,并将其传递给 ConsoleRunner::createHelperSet() 方法。
<?php
// cli-config.php
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\ConsoleRunner;
use Slim\Container;
/** @var Container $container */
$container = require_once __DIR__ . '/bootstrap.php';
return ConsoleRunner::createHelperSet($container->get(EntityManager::class));
请花点时间验证一下控制台应用程序是否正常工作。当正确配置时,输出结果将会类似于以下内容:
$ php vendor/bin/doctrine
Doctrine Command Line Interface 2.11.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:reserved-words Checks if the current database contains identifiers that are reserved.
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:convert-d1-schema [orm:convert:d1-schema] Converts Doctrine 1.x schema into a Doctrine 2.x schema
orm:convert-mapping [orm:convert:mapping] Convert mapping information between supported formats
orm:ensure-production-settings Verify that Doctrine is properly configured for a production environment
orm:generate-entities [orm:generate:entities] Generate entity classes and method stubs from your mapping information
orm:generate-proxies [orm:generate:proxies] Generates proxy classes for entity classes
orm:generate-repositories [orm:generate:repositories] Generate repository classes from your mapping information
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
此时,你可以从命令行管理你的数据库,并在代码中需要时使用 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;
}
}
其他资源
- 官方Doctrine ORM文档。
- 完整示例,展示了上述设置在一个小而完整的项目中的用法。