PHP依赖注入下按请求参数动态传递ChannelModel的架构方案咨询
针对你的需求,我们可以通过工厂模式结合依赖注入优化来实现「不在Service/Repository层传递$channelId和$channelType」的目标,同时让模型创建逻辑解耦,符合单一职责原则。下面是分步实现方案:
核心思路梳理
我们的目标是:
- 把请求参数(
channelId/channelType)的处理逻辑从Service/Repo中剥离 - 让Model本身封装查询条件,Repo直接基于Model执行查询
- 用工厂类统一处理「根据channelType创建对应Model」的逻辑
1. 实现Channel Model工厂(核心:动态创建对应Model)
创建独立的ChannelModelFactory类,专门负责根据channelType实例化对应的Model子类,并封装channelId和channelType参数:
class ChannelModelFactory { /** * 根据渠道类型创建对应Model并注入查询参数 * @throws InvalidArgumentException */ public static function create(string $channelType, int $channelId): AbstractChannel { // 拼接Model类名(比如user → UserChannel) $modelClassName = ucfirst(strtolower($channelType)) . 'Channel'; if (!class_exists($modelClassName)) { throw new InvalidArgumentException("不支持的渠道类型: {$channelType}"); } // 假设子类继承AbstractChannel的构造函数,直接注入id和type return new $modelClassName($channelId, $channelType); } }
对应的AbstractChannel子类示例(比如UserChannel):
class UserChannel extends AbstractChannel { public function __construct(int $id, string $type) { $this->id = $id; $this->type = $type; } }
2. 调整Repository与Service:移除参数依赖
修改Repository接口和实现类,让Repo直接基于注入的Model对象执行查询,不再接收零散参数:
调整ChannelRepositoryInterface
interface ChannelRepositoryInterface { // 不再需要传递id和type,直接从注入的Model中获取 public function findRules(): AbstractChannel; }
调整SqlChannelRepository
class SqlChannelRepository implements ChannelRepositoryInterface { private $model; public function __construct(AbstractChannel $model) { $this->model = $model; } public function findRules(): AbstractChannel { // 从Model中读取查询条件,执行查询 return $this->model->where('id', $this->model->id) ->where('type', $this->model->type) ->getModel(); } }
调整ChannelService
class ChannelService { private $channelRepository; public function __construct(ChannelRepositoryInterface $channelRepository) { $this->channelRepository = $channelRepository; } // 方法不再需要参数,直接调用Repo的查询方法 public function getChannelInfo(): AbstractChannel { return $this->channelRepository->findRules(); } }
3. 调整Controller:串联工厂与依赖
在Controller层完成「参数获取→Model创建→Repo/Service实例化」的流程,Service和Repo完全不需要感知请求参数:
class ChannelControllerApi extends Controller { public function getChannelInfo() { // 1. 从请求中获取参数 $channelId = (int)$this->request->Post('channelId'); $channelType = $this->request->Post('channelType'); try { // 2. 用工厂创建对应类型的Model(已封装id和type) $channelModel = ChannelModelFactory::create($channelType, $channelId); // 3. 创建Repo并注入Model(如果用DI容器,可替换为容器解析) $channelRepository = new SqlChannelRepository($channelModel); // 4. 实例化Service并执行业务逻辑 $channelService = new ChannelService($channelRepository); $result = $channelService->getChannelInfo(); // 返回API响应 return $this->response->json($result->toArray()); } catch (InvalidArgumentException $e) { return $this->response->json(['error' => $e->getMessage()], 400); } } }
关键问题解答
Q:应在哪一层将正确的模型对象传递给SqlChannelRepository?
模型对象应该在**Controller层(或应用层的工厂类)**创建后,直接传递给Repository。因为请求参数是Controller层最先获取到的,在这里完成Model的创建和参数封装,能让Service和Repo完全脱离与请求的耦合。
Q:根据请求参数创建对应ChannelModel的逻辑应放在哪里?
必须放在**独立的工厂类(如ChannelModelFactory)**中,而不是散落在Controller或Service里。这符合「单一职责原则」:工厂只负责创建Model,Controller只负责处理请求响应,Service只负责业务逻辑,后续新增Channel类型时,只需要添加新的Model子类,不需要修改工厂以外的代码。
Q:使用哪种设计模式/架构方案?
- 工厂模式:核心用于动态创建对应Model/Repo实例,避免大量if-else判断,提高扩展性。
- 依赖注入:Repo依赖Model、Service依赖Repo接口,通过构造函数注入,降低代码耦合度。
- 实体封装:将查询参数封装到Model对象中,避免在多层传递零散参数,提升代码可读性和维护性。
内容的提问来源于stack exchange,提问作者user3122345

