mrhdolek/slim4-boirlerplate

使用 AMQP、DDD 和 Doctrine 的 Slim 4 框架骨架


README

Slim

CI Codecov.io License PHPStan Enabled PHP

使用 AMQP 和 DDD 的 Slim 4 框架骨架

我受到 robiningelbrecht 的启发,创建了此骨架。

项目设置

开发

如果权限有问题,请在 make example 前添加 sudo

  • sudo make install
  • sudo make start

运行 env for Mac/Linux

  • make install
  • make start
  • make db-create

运行 env for Windows

请安装 Windows 的 packages makefile http://gnuwin32.sourceforge.net/packages/make.htm

  • make install
  • make start
  • make db-create

环境可用的地址

  • https://

Rest Api 文档

  • https:///docs/v1

RabbitMq 控制台

  • https://:15672

所有命令

  • make help

一些示例

注册新路由

namespace App\Application\Actions\User;

class GetAllUsersAction extends UserAction
{
    public function __construct(
        private readonly UserService $userService,
        protected LoggerInterface $logger,
    ) {
        parent::__construct($logger);
    }

    protected function action(): Response
    {
        $user = $this->userService->getAllUsers();

        return $this->respondWithJson(new UsersResponseDto($user));
    }
}

转到 config/routes.php 并为您的 RequestHandler 添加路由

return function (App $app) {
        $group->get("/users", GetAllUsersAction::class)
            ->setName("getAllUsers");
};

控制台命令

控制台应用程序使用 Symfony 控制台组件来利用 CLI 功能。

#[AsCommand(name: 'app:user:create')]
class CreateUserConsoleCommand extends Command
{
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        // ...
        return Command::SUCCESS;
    }
}

调度命令

要调度一个命令,我们使用 GO\Scheduler 类。此类允许我们定义命令执行的时间和频率。以下是一个每天运行命令的示例

$scheduler = new GO\Scheduler();
$scheduler->php('/path/to/command app:user:create')->daily();
$scheduler->run();

在此示例中,app:user:create 命令被安排每天运行。

运行调度器

调度器应该由系统 cron 作业触发,以确保定期运行。通常,您会设置一个 cron 作业来执行初始化和运行调度器的 PHP 脚本。

例如,每分钟运行一次的 cron 作业可能看起来像这样

* * * * * ./bin/console.php schedule

此设置确保您的计划命令可靠且按时执行。

领域事件和事件处理器

框架实现了 amqp 协议,并带有处理程序,允许轻松将事件推送到队列。每个事件都必须实现一个处理程序,以消费事件。

创建新事件

class UserWasCreated extends DomainEvent
{
 
}

创建对应的事件处理程序

namespace App\Domain\Entity\User\DomainEvents;

#[AsEventHandler]
class UserWasCreatedEventHandler implements EventHandler
{
    public function __construct(
    ) {
    }

    public function handle(DomainEvent $event): void
    {
        assert($event instanceof UserWasCreated);

        // Do stuff.
    }
}

事件

创建新事件

class UserWasCreated extends DomainEvent
{
    public function __construct(
        private UserId $userId,
    ) {
    }

    public function getUserId(): UserId
    {
        return $this->userId;
    }
}

使用 RabbitMQ 异步处理命令

此项目的 AMQP 实现是 RabbitMQ,但它可以轻松切换到例如 Amazon 的 AMQP 解决方案。

注册新队列

#[AsAmqpQueue(name: "user-command-queue", numberOfWorkers: 1)]
class UserEventQueue extends EventQueue
{
}

排队事件

final readonly class UserEventsService
{
    public function __construct(
        private UserEventQueue $userEventQueue,
    ) {}

    public function userWasCreated(User $user): void
    {
        $this->userEventQueue->queue(new UserWasCreated($user));
    }
}

消费您的队列

> docker-compose run --rm php bin/console.php app:amqp:consume user-command-queue

创建新实体

如果您已创建新实体并想将其映射到数据库,必须在 src/Infrastructure/Persistence/Doctrine/Mapping 中创建一个 xml。它必须命名,以便确切地表示要映射的实体位置。

接口/全局对象注册表的绑定

要注册依赖关系或创建单个配置的全局实例,您需要转到 config/container.php

将数据库数据映射到自定义对象

要将数据库中的数据映射到自定义对象,您需要扩展 Doctrine/DBAL/Types。

use App\Domain\ValueObject\UserType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\StringType;

class UserTypeType extends StringType
{
    const TYPE_NAME = 'UserType';

    public function convertToPHPValue($value, AbstractPlatform $platform): ?UserType
    {
        return null !== $value ? UserType::fromString($value) : null;
    }

    public function getName()
    {
        return self::TYPE_NAME;
    }
}

扩展后,您需要在 xml 中添加注释,说明您正在将字段映射到对象。

    <field name="type" type="UserType" column="type" nullable="true"/>

最后,您必须将新类型添加到配置 doctrine 的 config/container.php 中

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DriverManager;
use Doctrine\DBAL\Types\Type;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\Tools\Setup;

return [
...
    EntityManager::class => function (Settings $settings): EntityManager {
        $config = Setup::createXMLMetadataConfiguration(
            $settings->get("doctrine.metadata_dirs"),
            $settings->get("doctrine.dev_mode"),
        );
        if (!Type::hasType('UserType')) {
          Type::addType('UserType', UserTypeType::class);
        }
        return EntityManager::create($settings->get("doctrine.connection"), $config);
    },
 ...
];

数据库迁移

为了管理数据库迁移,使用 doctrine/migrations 包。

<doctrine-mapping
xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping
https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd">
<entity name="App\Domain\Entity\User\User" table="users">
<id name="id" type="integer" column="id">
<generator strategy="SEQUENCE"/>
<sequence-generator sequence-name="user_id_seq" allocation-size="1" initial-value="1"/>
</id>
<field name="username" type="string" column="name" length="64" nullable="true"/>
<field name="firstName" type="string" column="surname" length="64" nullable="true"/>
<field name="lastName" type="string" column="email" length="64" nullable="true"/>
<options>
<option name="collate">utf8mb4_polish_ci</option>
</options>
</entity>
</doctrine-mapping>

映射是通过一个 yaml 实现的,它将你的实体从域映射到数据库中的结构。如果你在 yaml 中更改了某些内容,可以使用以下命令根据差异生成迁移。

> docker-compose run --rm php vendor/bin/doctrine-migrations diff
> docker-compose run --rm php vendor/bin/doctrine-migrations migrate

Swoole

要使用 swoole,只需在 .env 中设置 DOCKER_TARGET_APP=swoole 并重新构建应用程序容器。

文档

了解更多信息,请查看以下链接