无框架环境下如何通过PHP-DI从控制器向服务层传递用户数据
我来帮你梳理下在PHP-DI中配置Account和User对象注入的方案,结合你提到的不同场景(Web会话用户、后端队列进程)来处理:
核心思路
因为Account和User的获取逻辑依赖运行环境(Web请求用会话、后端进程用队列上下文),所以我们需要用动态提供者或者工厂模式让PHP-DI根据当前上下文正确实例化/获取这两个对象,避免硬编码依赖。
1. 优先定义接口(可选但推荐)
先给Account和User定义接口,这样能实现业务逻辑与具体实现的解耦,后续替换实现或者扩展会更灵活:
// 定义核心接口 interface AccountInterface { public function getId(): int; public function getPermissions(): array; // 其他你需要的核心方法 } interface UserInterface { public function getAccount(): AccountInterface; public function getEmail(): string; // 其他你需要的核心方法 } // 实现类 class Account implements AccountInterface { public function __construct(private int $id, private array $permissions) {} public function getId(): int { return $this->id; } public function getPermissions(): array { return $this->permissions; } } class User implements UserInterface { public function __construct(private AccountInterface $account, private string $email) {} public function getAccount(): AccountInterface { return $this->account; } public function getEmail(): string { return $this->email; } }
2. 为不同场景配置PHP-DI提供者
针对Web会话和后端队列两种场景,分别编写动态获取逻辑:
2.1 Web请求场景(从会话获取)
在Web环境下,从会话中读取登录用户标识,再从数据库加载对应的User和Account:
use DI\ContainerBuilder; use DI\Container; $containerBuilder = new ContainerBuilder(); $containerBuilder->addDefinitions([ // 注入User对象 UserInterface::class => function(Container $container) { // 处理未登录情况,这里可以返回Guest用户或者抛出异常 if (!isset($_SESSION['user_id'])) { throw new RuntimeException('当前用户未登录'); } $userId = $_SESSION['user_id']; // 假设你有UserRepository负责从数据库加载用户 $userRepo = $container->get(UserRepository::class); return $userRepo->findById($userId); }, // 通过已注入的User关联获取Account AccountInterface::class => function(Container $container) { $user = $container->get(UserInterface::class); return $user->getAccount(); }, ]); $container = $containerBuilder->build();
2.2 后端队列进程场景(从队列任务获取)
后端队列任务通常会携带用户/账户的标识,我们可以先解析任务数据,再临时覆盖容器定义:
// 假设你的队列任务类需要依赖Account和User class ProcessOrderTask { public function __construct( private AccountInterface $account, private UserInterface $user, private OrderProcessor $processor ) {} public function handle(): void { // 使用注入的Account和User做权限校验、数据过滤 $this->processor->processForAccount($this->account); } } // 从队列取出任务数据(示例) $taskData = $queue->fetchTask(); // 比如返回 ['user_id' => 1001, 'account_id' => 5001] // 临时添加队列场景的对象定义 $containerBuilder->addDefinitions([ AccountInterface::class => function(Container $container) use ($taskData) { $accountRepo = $container->get(AccountRepository::class); return $accountRepo->findById($taskData['account_id']); }, UserInterface::class => function(Container $container) use ($taskData) { $userRepo = $container->get(UserRepository::class); return $userRepo->findById($taskData['user_id']); }, ]); // 获取任务实例并执行 $task = $container->get(ProcessOrderTask::class); $task->handle();
3. 自动区分环境(可选优化)
如果你的应用需要自动识别Web/CLI环境,可以在容器定义中加入环境判断:
$isCli = php_sapi_name() === 'cli'; $containerBuilder->addDefinitions([ UserInterface::class => function(Container $container) use ($isCli) { if ($isCli) { // 队列场景:从任务上下文获取用户ID(这里假设你有QueueContext类管理任务数据) $queueContext = $container->get(QueueContext::class); $userRepo = $container->get(UserRepository::class); return $userRepo->findById($queueContext->getUserId()); } else { // Web场景:从会话获取 if (!isset($_SESSION['user_id'])) { throw new RuntimeException('当前用户未登录'); } $userRepo = $container->get(UserRepository::class); return $userRepo->findById($_SESSION['user_id']); } }, // Account的定义逻辑类似... ]);
4. 服务层注入示例
现在你的服务类可以直接注入接口,PHP-DI会自动根据上下文提供正确的实例:
class OrderService { public function __construct( private AccountInterface $account, private UserInterface $user, private OrderRepository $orderRepo ) {} public function getUserOrders(): array { // 用Account做数据权限限制,只返回当前账户的订单 return $this->orderRepo->findByAccountId($this->account->getId()); } } // 直接从容器获取服务,无需手动传递Account/User $orderService = $container->get(OrderService::class); $orders = $orderService->getUserOrders();
内容的提问来源于stack exchange,提问作者user3159921
相关产品推荐
相关产品推荐

