solventt/slim-route-strategy 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

solventt/slim-route-strategy

Composer 安装命令:

composer require solventt/slim-route-strategy

包简介

The route invocation strategy for the Slim microframework

README 文档

README

Table of contents

  1. Requirements
  2. Installing
  3. Flexible controller signature
  4. Features
  5. Resolving DTO
  6. Use cases
  7. Writing custom rules

Package is an implementation of a route invocation strategy for the Slim microframework. It allows to flexibly set up resolving of your controller parameters. About the invocation strategy you can read in the Slim docs.

Requirements

  • PHP 7.4+ or 8.0+
  • Slim microframework version 3+ or 4+
  • any DI container. But if it has no autowiring, TypeHintContainerRule and MakeDtoRule will not affect the resolving of controller parameters.

Installing

// php 7.4+
composer require solventt/slim-route-strategy ^0.1

// php 8.0+
composer require solventt/slim-route-strategy ^1.0

Flexible controller signature

By default, Slim controllers have a strict signature: $request, $response, $args

And so you can't omit any of these parameters even if one is not needed. It is called the RequestResponse strategy.

But with this package:

  • you may specify any parameters you need and even an empty controller signature
  • the order of the parameters doesn't matter
  • services will be injected by type-hint
  • in addition to the route placeholders you also can receive request attributes and Data Transfer Objects (instead of the POST/PUT/PATCH arrays) in your controller parameters
  • incoming $id parameter will have integer type instead of default string type (optional)
  • you can add your own parameters resolving functionality, for example, instead of the $id parameter you may receive some entity (User)

Features

The route CustomRulesAggregator strategy consist of the following rules:

1) IdIntegerTypeRule (optional) - casts string type of the 'id' route parameter (if exists) to integer type. It's especially conveniently while using declare(strict_types=1)

$app->get('/profile/{id:\d+}', [ProfileController::class, 'show']);

...

public function show(int $id): Response
{
   // incoming $id has integer type instead of default's string
}

NOTE: the name of the controller parameter and the route placeholder MUST be id.


2) FlexibleSignatureRule - tries to map an associative array of route parameters to the controller parameters names.

Assume there is the controller method:

public function show($request, $response, $id) {}

And there are the route parameters:

[
   'request' => 'value_1', 
   'response' => 'value_2', 
   'id' = '1'
]

Then controller method will receive next parameters values:

public function show($request, $response, $id)
{
  echo $request;   // 'value_1'
  echo $response;  // 'value_2'
  echo $id;        // '1' - string, because the IdIntegerTypeRule is off
}

NOTE: the names of the controller request/response parameters MUST be request and response accordingly.


3) TypeHintContainerRule - injects type-hinted controller parameters using the DI container. But the union types will be ignored.

public function show(Twig $twig, self $surrentClass) 
{
   // The Twig and declaring class instances will be automatically resolved
}

4) NullTypeRule - if a controller parameter does not have a default value, it checks presence of the 'null' parameter type and (if successful) take it for resolving:

public function show(?string $name, ?int $count = 5) 
{
   var_dump($name);   // null
   echo $count;       // 5
}

5) MakeDtoRule - read the next section.

By default, only FlexibleSignatureRule, TypeHintContainerRule and NullTypeRule are active.

Also, you can add your own rules.

Resolving DTO

MakeDtoRule converts a data array of POST|PUT|PATCH requests into a Data Transfer Object (DTO)

public function update(Dto $dto, int $id)
{
   // do something with $dto
}

By default, it will be created the built-in Dto class filled with the request data. But you can define your own DTO class and your own logic for processing the data and filling the object with it, using factories.

Example

Definition for the DI Container:

return [
    'dtoFactories' => [
         
         // key - is a parameter name of a controller method
         // value - a corresponding DTO factory class
         
         'dto' => UserUpdateDtoFactory::class 
    ]
];

Factory logic:

class UserUpdateDtoFactory
{
   public function __invoke(array $requestData): UserUpdateDto
   {
      $dto = new UserUpdateDto();

      foreach ($requestData as $field => $value) {

         $value = match ($field) {
                'phoneType' => (int) $value,
                'date' => new \DateTime($value),
                'isActive' => (bool) $value,
                 default => $value
            };

         $dto->$field = $value;
      }
      
      return $dto;
   }
}

And the controller method:

public function update(UserUpdateDto $dto)
{
   // do something with $dto
}

REMEMBER:

  1. Name of the parameter must contain a 'dto' substring. For example: '$userUpdateDto', '$dto', 'myDto', 'loginDto' and so forth.
  2. You need to specify a parameter name in the DI Container definition as an array key. The value of the array - a corresponding DTO factory class.
  3. The DI container definition must be named as 'dtoFactories' (see the example above).

Use cases

For Slim version ^4.0, index.php:

<?php

use DI\Container;
use Slim\Factory\AppFactory;
use SlimRouteStrategy\CustomRulesAggregator;

require __DIR__ . '/vendor/autoload.php';

$container = new Container();

$app = AppFactory::createFromContainer($container);

$strategy = new CustomRulesAggregator($container);

$app->getRouteCollector()->setDefaultInvocationStrategy($strategy);

$app->get('/hello/{name}', function ($response, $name) {
    $response->getBody()->write($name);

    return $response;
});

$app->run();

For Slim version ^3.0, index.php:

<?php

use Slim\App;
use Slim\Container;
use SlimRouteStrategy\CustomRulesAggregator;

require __DIR__ . '/vendor/autoload.php';

$container = new Container();

$container['foundHandler'] = fn () => new CustomRulesAggregator($container);

$app = new App($container);

$app->get('/hello/{name}', function ($response, $name) {
    $response->getBody()->write($name);

    return $response;
});

$app->run();

About the strategy rules

If you don't provide any rules to the rout strategy constructor, only FlexibleSignatureRule, TypeHintContainerRule and NullTypeRule will be enabled by default.

For example, you want to add IdIntegerTypeRule and MakeDtoRule, then you should define all necessary rules explicitly:

...

$strategyRules = [

    IdIntegerTypeRule::class,
    FlexibleSignatureRule::class,
    MakeDtoRule::class,
    TypeHintContainerRule::class,
    NullTypeRule::class
    
];

$strategy = new CustomRulesAggregator($container, $strategyRules);

...

Or if you want to add only a rule:

...

$strategy = new CustomRulesAggregator($container, [FlexibleSignatureRule::class]);

...

REMEMBER:

  • the rules must be specified as existent class strings
  • the rules order matters. E.g. if you define IdIntegerTypeRule after FlexibleSignatureRule. Then IdIntegerTypeRule will have no effect - the type of the id will be string instead of integer.

The example above shows the correct order of the rules.

Writing custom rules

Your custom rule must implement AggregatorRuleInterface.

Let's look at the simple example. Suppose you want the controller method to receive the User entity as an argument. So the route is:

$app->get('/profile/{user:\d+}', [ProfileController::class, 'show']);

The controller method is:

public function show(User $user){}

And you wrote your custom FindUserEntityRule:

class FindUserEntityRule implements AggregatorRuleInterface
{
    public function __construct(private UserRepository $users){}

    /**
     * @param ReflectionParameter[]  $unresolvedParams parameters that have not yet been resolved
     * @param array $routeParams     request/response objects, route placeholders values, request attributes
     * @param array $resolvedParams  parameters resolved by previous rule (indexed by parameter position)
     * @return array                 parameters resolved by this + by previous rule
     */
    public function resolveParameters(array $unresolvedParams,
                                      array $routeParams,
                                      array $resolvedParams): array
    {
        foreach ($unresolvedParams as $position => $parameter) {

            if ($parameter->name === 'user' && $this->hasAppropriateType($parameter)) {
                if (array_key_exists($parameter->name, $routeParams)) {

                    $userId = (int) $routeParams[$parameter->name];
                    $user = $this->users->findOne($userId);

                    $resolvedParams[$position] = $user;
                }
            }
        }

        return $resolvedParams;
    }

    private function hasAppropriateType(ReflectionParameter $parameter): bool
    {
        $type = $parameter->getType();

        return !$type instanceof ReflectionUnionType && $type->getName() === User::class;
    }
}

solventt/slim-route-strategy 适用场景与选型建议

solventt/slim-route-strategy 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 14 次下载、GitHub Stars 达 2, 最近一次更新时间为 2021 年 09 月 12 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「framework」 「micro」 「router」 「slim」 「strategy」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 solventt/slim-route-strategy 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 solventt/slim-route-strategy 我们能提供哪些服务?
定制开发 / 二次开发

基于 solventt/slim-route-strategy 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 14
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 7
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 2
  • Watchers: 1
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: BSD-3-Clause
  • 更新时间: 2021-09-12