Slim 4 中如何为不同路由注入不同的服务参数?
结论
$internal 的值由具体路由决定,是创建 UserService 时传入的运行配置。自动装配无法判断它应该是 true 还是 false。控制器可以注入 UserServiceFactory,再由对应的方法传入这个值。如果两种模式对应不同的业务用例,也可以拆成不同的方法或服务,避免用布尔参数表达业务含义。
推荐做法:注入工厂
工厂负责创建服务,并复用容器提供的 UserModel。控制器方法根据路由明确传入配置:
class UserServiceFactory
{
public function __construct(private UserModel $userModel)
{
}
public function create(bool $internal): UserService
{
return new UserService($this->userModel, $internal);
}
}
控制器注入工厂。这样,同一个预先配置的 UserService 实例就不必同时处理两种模式:
class UserController
{
public function __construct(
private UserServiceFactory $userServiceFactory
) {
}
public function getEmployee(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$userService = $this->userServiceFactory->create(true);
// 使用 $userService 处理 employee 请求。
return $response;
}
public function getApplyant(
ServerRequestInterface $request,
ResponseInterface $response
): ResponseInterface {
$userService = $this->userServiceFactory->create(false);
// 使用 $userService 处理 applyant 请求。
return $response;
}
}
容器可以自动装配工厂,也可以提供 UserModel 及其所需的 \PDO。\PDO 的具体注册方式和控制器路由的解析方式,取决于项目使用的 PHP-DI 配置和 Slim 集成方式。
考虑改用能表达业务含义的接口
如果 internal 决定了不同的业务行为,提供两个含义明确的方法通常更易读,也能减少调用时传错参数的情况:
class UserService
{
public function __construct(private UserModel $userModel)
{
}
public function getEmployee(int $id): mixed
{
// 执行 employee 场景的处理。
}
public function getApplyant(int $id): mixed
{
// 执行 applyant 场景的处理。
}
}
这里的 mixed 只是示意类型,应根据实际返回值替换。控制器可以注入同一个 UserService,再调用对应的方法,无需为每个请求创建带不同布尔值的实例。如果两种场景差异较大,也可以拆成两个服务。
PHP-DI 配置与测试
容器定义适合提供应用范围内固定的依赖,例如数据库连接和模型。路由每次调用时才确定的值,则由调用方或工厂传入。如果确实需要两个长期固定的服务实例,也可以分别在容器中注册并注入,但要明确设置各自的 $internal 值,并让控制器能够区分它们。
测试可以分别覆盖控制器选择场景的逻辑,以及工厂创建服务时是否传入了正确配置。UserModel 依赖 \PDO 时,可以在模型测试中替换数据库依赖。如果直接依赖具体的 \PDO 不便于测试替身,可以考虑在数据访问边界引入接口,再测试依赖该接口的逻辑。
检查类定义
问题中的代码片段有几处类型或属性声明需要修正,否则可能出现 PHP 语法或属性访问问题:
class UserService
{
public UserModel $user_model;
public bool $internal;
public function __construct(UserModel $user_model, bool $internal)
{
$this->user_model = $user_model;
$this->internal = $internal;
}
}
原片段中的 public UserModel = $user_model; 不是有效的属性声明。$internal 的构造参数类型是 bool,对应的属性也宜声明为 bool。UserModel 示例把依赖赋给了 $this->pdo,但声明的是 $db,两者需要统一,例如:
class UserModel
{
private \PDO $db;
public function __construct(\PDO $db)
{
$this->db = $db;
}
}
以上示例展示的是依赖传递方式。具体路由可调用的控制器格式,还需与项目配置的 Slim callable resolver 相匹配。
备注:内容仅供参考。
