snootbeest/tantrum

基于 Slim 3 的 RESTful API 框架

v0.2.0alpha 2018-02-05 19:19 UTC

This package is not auto-updated.

Last update: 2024-09-19 01:32:30 UTC


README

Tantrum 是一个基于 Slim 3 框架的 RESTful API 框架。
它在多个方面扩展了 Slim 3

可配置服务

Tantrum 内置了一个基于配置和依赖注入的服务层。
要创建服务,只需创建一个实现了 SnootBeest\Tantrum\Service\ServiceProvider 接口和几行配置的类。

dependencies:
  Psr\Log\LoggerInterface:
    providerClass: MyProject\Namespace\LoggerProvider
    dependencyType: default
    dependencies:
      - Doctrine\ORM\EntityManagerInterface
  ...
  Doctrine\ORM\EntityManagerInterface
    providerClass: MyProject\Namespace\EntityManagerProvider
  ...
configuration:
  Doctrine\ORM\EntityManagerInterface
     host: mysql
     port: 3306
     user: root
     password:
  ...

这里我们看到配置的一部分,详细说明了依赖项(服务)。
该特定服务提供了一个 LoggerInterface 实例。 Psr\Log\LoggerInterface 是依赖注入容器中将使用的键。
使用完全限定的接口命名空间作为容器键 - 虽然不是必需的 - 提供了几个好处

  • 消除歧义 - 您可以确切知道将返回什么
  • 避免命名冲突 - 命名空间必须是唯一的
  • 帮助实现构造函数注入 - 将容器键与类型提示的构造函数参数匹配非常酷。

providerClass

providerClass 是将被依赖注入容器调用的 ServiceProvider,它最终将返回 LoggerInterface 实例。它包含所有实例化的逻辑。

dependencyType

dependencyType 键是可选的,并且可以有三个可能的值

  • factory: 映射到 factory 方法 Pimple。每次调用都会返回一个新的实例。
  • protect: 可预测地映射到 protect 方法 Pimple。如果您需要返回匿名函数,则非常有用。
  • singleton: 这是 Pimple 的默认行为,如果您打算这样使用,则不需要包含依赖类型参数。

dependencies

依赖注入容器对于 ServiceProvider 不可用,以避免全局服务定位器反模式。因此,我们在此定义了一个子依赖项列表(完全限定命名空间的接口,希望如此!)!它们按定义的顺序注入到 ServiceProvider 构造函数中(这里没有反射)。在那里,它们可以提供给服务本身。
这意味着它们 必须 在应用程序的依赖项的其他地方定义,才能在容器中可用。

configuration

在上面的示例中,ORM 显然需要一个数据库连接,但这里没有提供这些详细信息。
在配置的不同部分,我们定义了依赖项的配置值,键再次是接口。

为什么不在依赖项旁边定义配置值?

依赖项通常比应用程序的配置更少动态。例如,您的应用程序始终需要其 LoggerInterface 实现,但数据库对每个环境都不同。此外,配置值可能在许多不同的服务之间共享和修改,最好将这些定义在其他地方。

路由检查

关于Slim 3,最令人满意的一点是能够快速将路由添加到应用程序中。这对于快速开发来说非常出色,但我在大型应用程序中发现,使用闭包添加路由很快就会变得难以控制。此外,单元测试闭包也相当困难。

Tantrum在构建过程中提供了路由检查(我通常在composer install之后创建一个构建脚本)这种技术会在构建时反映路由并缓存结果,以便在运行时使用。以这种方式预先处理并存储路由允许进行构造函数注入,使得控制器易于测试,同时也给我们提供了将命名查询参数直接传递给路由方法的机会。

控制器配置在controllers键下,它只是一个命名空间列表

...
controllers:
  - Acme\Shop\Controllers\WidgetController
  - Acme\Shop\Controllers\UserController
  ...

它要求你的控制器扩展一个小的类:SnootBeest\Tantrum\Controller,并且你的控制器类应包含一些简单的注释以帮助路由器。让我们看看一个例子

<?php
namespace Acme\Shop;

use Snootbeest\Tantrum\Controller;
use Psr\Log\LoggerInterface;

class Widget extends Controller
{
    /** @var LoggerInterface $logger */
    private $logger;

    /**
     * Widget controller constructor
    */
    public function __constructor(LoggerInterface $logger, int $requiredParameter, string $optionalParameter = 'some string')
    {
        $this->logger = $logger;
    }

    /**
     * Gets a widget from the given widget id
     *
     * @httpMethod GET
     * @httpMethod HEAD
     * @pattern /widget/{:widgetId}
    */
    public function getOneWidget(int $widgetId)
    {
        // Get and return the widget
        $this->logger->debug(sprintf('Returning widget #%d', $widgetId));
    }

    ...
}

这是我们的商店小部件控制器的示例。

构造函数注入

注意我们的__construct方法有几个依赖项。你可以看到它需要一个Psr\Log\LoggerInterface实例作为第一个参数。这是通过上面的过程配置的,并且会自动从依赖注入容器中提供。

第二个参数$requiredParameter也是必需的,但没有类型提示。分发器会尝试从容器中获取它。如果没有找到,它将从配置中获取。如果仍然找不到,将抛出异常,导致返回500响应。

第三个参数$optionalParameter有一个默认值。如果其名称在容器或配置中找不到,将返回默认值。

我们还可以在路由声明中看到一些自定义注释。

@httpMethod

如果需要,可以提供多个@httpMethod注释。

@route

@route注释是Slim路由器需要解析请求到该方法的正则表达式。任何命名占位符都作为方法参数传递。请参阅https://slim.php.ac.cn/docs/objects/router.html#route-placeholders

当然,如果你需要利用Slim 3的更高级功能,你仍然可以在Slim 3文档中描述的方法中添加路由。我会随着时间的推移尝试添加更多的这些到路由检测中。