Skip to content

在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;
    }
}

其他资源