You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无框架环境下如何通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:57:57