在 Slim 中使用 Doctrine

本食譜章節說明如何從基礎將廣泛使用的 Doctrine ORM 整合到 Slim 4 應用程式中。

將 Doctrine 加入應用程式

首先,使用 composer 將 Doctrine ORM 導入您的專案中。

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

請注意,Doctrine 在 2021 年 4 月 30 日發布 v2.0.0 版本時,正式將 doctrine/cache 標示為已不建議使用,並刪除了該函式庫中的所有快取執行個體。從那時開始,他們建議改用 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 開頭,並可以用 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)));
圖 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 服務。

其他資源