这个食谱条目描述了如何从头开始将广泛使用的Doctrine ORM集成到 Slim 4 应用程序中。
第一步是使用composer将 Doctrine ORM 导入到你的项目中。
composer require doctrine/orm symfony/cache
doctrine/cache
请注意,2021 年 4 月 30 日,Doctrine在发布 v2.0.0 版本时正式弃用,该版本从该库中删除了所有缓存实现。从那时起,他们建议改用symfony/cache
符合 PSR-6 的实现。如果你想在生产中缓存 Doctrine 元数据,你只需要它,但这样做没有任何缺点,所以我们将展示如何设置它。
如果您还没有迁移到 PHP8 或者只是想继续使用传统的 PHPDoc 注释来注释您的实体,您还需要导入包,doctrine/annotations
它曾经是 PHP8 的依赖项,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;
}
}
接下来,在您的 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 容器实现。本示例使用uma/dic
, 一个简洁明了的 PSR-11 容器。使其适应您自己选择的容器。
传统上注解元数据读取器是最流行的,但从doctrine/orm
2.10.0 开始他们将依赖设为doctrine/annotations
可选,暗示该项目更愿意用户迁移到现代 PHP8 属性表示法。
这里我们展示了如何使用 PHP8 属性配置元数据读取器。如果您还没有迁移到 PHP8 或想使用传统的 PHPDoc 注释,您将需要使用doctrine/annotations
Composer 显式要求并调用,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');
// 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'] ?
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
已经存在于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
此时,您可以通过运行来初始化数据库并加载架构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;
}
}