将 Doctrine 与 Slim 结合使用

本食谱条目介绍如何将广泛使用的 Doctrine ORM 从头开始集成到 Slim 4 应用程序中。

向应用程序中添加 Doctrine

第一步是使用 composer 将 Doctrine ORM 导入项目中。

composer require doctrine/orm:^3.0 doctrine/dbal:^4.0 symfony/cache

请注意,Doctrine 于 2021 年 4 月 30 日官方弃用了 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, 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;
    }
}
图 1:示例 Doctrine 实体。

提供数据库凭据

接下来,在 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'
            ]
        ]
    ]
];
图 2:Slim 设置数组。

定义 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\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;
图 3:定义 EntityManager 服务。

创建 Doctrine 控制台

要运行数据库迁移,验证类注释等,您将创建 doctrine CLI 应用程序。此文件只需检索我们刚刚在容器中定义的 EntityManager 服务,并将其传递给 ConsoleRunner::run(new SingleManagerProvider($em))

通常创建此文件而无需 .php 扩展名,并使其可执行。它可以放置在项目的根目录或 bin/ 子目录中。该文件以 #!/usr/bin/env php 开头,可以使用 chmod +x doctrine 命令在 Linux 上使其可执行。

#!/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)));
图 4:启用 Doctrine 的控制台应用程序。

花点时间验证控制台应用程序是否正常工作。配置正确后,其输出将或多或少如下所示

$ ./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
图 5:动态检查 Doctrine 的 CLI。

此时,您可以通过运行 php vendor/bin/doctrine orm:schema-tool:create 来初始化数据库并加载架构。

在自己的代码中使用 EntityManager

恭喜!您现在可以从命令行管理数据库,并在需要在代码中使用 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;
    }
}
图 6:在自己的代码中使用 EntityManager 服务。

其他资源