第239章 Controller→Service
239.1 提出背景
第236章建立了 Controller 工程,第237章建立了 Model 工程,第238章建立了 View 工程。
在 MVC 工程中,Controller 并不是整个业务系统的处理中心。Controller 的主要作用是接收外部请求、处理进入系统的参数、调用 Service,并将 Service 返回的结果转换为页面或其他响应。
因此:
Controller→Service\boxed{ Controller\rightarrow Service }
是 MVC 中最重要的业务入口关系之一。
完整请求过程为:
Request→Controller→Parameter→Service→Result→Controller→ResponseRequest \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow Result \rightarrow Controller \rightarrow Response
当执行过程中发生异常时,则进入:
Exception→Controller→ErrorResponseException \rightarrow Controller \rightarrow ErrorResponse
因此,本章重点建立五个概念:
Request+Parameter+Service+Return+ExceptionRequest+ Parameter+ Service+ Return+ Exception
239.2 Controller→Service定义
Controller→Service是指 Controller 接收外部请求并完成必要的输入参数处理后,将符合接口要求的参数传递给 Service,由 Service 执行具体业务流程并返回处理结果的工程关系。
定义:
Controller→ServiceController\rightarrow Service
其基本职责为:
Controller=RequestInput+ParameterPreparation+ServiceInvocation+ResponsePreparationController= RequestInput+ ParameterPreparation+ ServiceInvocation+ ResponsePreparation
而:
Service=BusinessProcessService= BusinessProcess
因此:
Controller≠ServiceController\neq Service
Controller 负责:
请求如何进入系统。
Service 负责:
请求进入系统以后应该执行什么业务过程。
239.3 请求
**Request(请求)**是客户端向 ICAI 系统发送的操作要求。
请求可以来自:
- 浏览器;
- HTML 表单;
- AJAX;
- API;
- 内部系统;
- CLI;
- 其他程序。
基本结构:
Request=Method+URL+Parameters+Headers+Body+SessionRequest= Method+ URL+ Parameters+ Headers+ Body+ Session
例如浏览器请求:
GET /admin/individual/detail.php?id=15
其中:
Method=GETMethod=GET URL=/admin/individual/detail.phpURL=/admin/individual/detail.php Parameter={id=15}Parameter=\{id=15\}
Controller 首先接收这个 Request。
239.4 Request与Controller
请求进入系统后:
Request→ControllerRequest\rightarrow Controller
例如:
class IndividualController
{
public function detail()
{
$id = $_GET['id'];
// 后续处理
}
}
这里 Controller 是请求进入 Model 的第一层业务入口。
完整关系:
HTTPRequest→ControllerHTTPRequest \rightarrow Controller
但 Controller 不应该直接完成完整业务计算。
错误:
Request
↓
Controller
↓
查询数据库
↓
计算能力
↓
判断风险
↓
执行决策
↓
修改数据
正确:
Request
↓
Controller
↓
Parameter
↓
Service
↓
Domain / Engine / Repository
239.5 参数
**Parameter(参数)**是 Request 中携带并供业务流程使用的数据。
例如:
id
type
name
status
page
keyword
请求:
GET /admin/individual/detail.php?id=15
得到:
Parameter={id=15}Parameter=\{id=15\}
表单:
POST /admin/individual/store.php
name=Robot
type=machine
得到:
Parameter={name=Robot,type=machine}Parameter= \{ name=Robot, type=machine \}
239.6 参数处理
Controller 对参数进行的是输入层处理,而不是完整业务判断。
基本过程:
Request→ReadParameter→Normalize→Validate→ServiceRequest \rightarrow ReadParameter \rightarrow Normalize \rightarrow Validate \rightarrow Service
例如:
$id = isset($_GET['id'])
? (int) $_GET['id']
: 0;
然后:
if ($id <= 0) {
return $this->error(
'参数错误'
);
}
这里 Controller 解决的是:
请求有没有提供一个基本合法的 id。
而不是解决:
这个 id 对应的机器个体是否允许执行某项业务。
后者属于 Service/Model。
239.7 参数标准化
不同请求可能产生不同格式的数据,因此 Controller 可以进行基础标准化。
例如:
$page = isset($_GET['page'])
? (int) $_GET['page']
: 1;
if ($page < 1) {
$page = 1;
}
形成:
RawParameter→NormalizedParameterRawParameter \rightarrow NormalizedParameter
例如:
"15"
↓
15
或者:
" robot "
↓
"robot"
这种处理属于输入层。
239.8 参数验证
参数验证可以分为两个层次。
第一层:输入参数验证
由 Controller 处理:
InputValidationInputValidation
例如:
ID>0ID>0 Page≥1Page\geq1 Name≠∅Name\neq\varnothing
第二层:业务规则验证
由 Service/Engine 处理:
BusinessValidationBusinessValidation
例如:
CapabilityAvailable=TrueCapabilityAvailable=True GoalValid=TrueGoalValid=True DecisionAllowed=TrueDecisionAllowed=True
因此:
InputValidation≠BusinessValidationInputValidation\neq BusinessValidation
Controller 不应该把业务规则全部塞入参数验证代码。
239.9 参数对象
当参数较多时,可以使用 Parameter Object。
例如:
class IndividualRequest
{
protected $id;
protected $name;
protected $type;
protected $status;
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getType()
{
return $this->type;
}
public function getStatus()
{
return $this->status;
}
}
Controller:
$request = new IndividualRequest();
$request->setId($id);
$request->setName($name);
$request->setType($type);
然后:
Controller→RequestObject→ServiceController \rightarrow RequestObject \rightarrow Service
这样可以避免 Service 接收大量无意义的独立参数。
239.10 Service
Service 是 Controller 调用的业务流程对象。
定义:
Service=BusinessProcessService= BusinessProcess
例如:
class IndividualService
{
public function create($params)
{
// 创建机器个体业务流程
}
public function find($id)
{
// 查询机器个体
}
public function update($id, $params)
{
// 更新机器个体
}
}
Controller:
$result = $this->individualService
->create($params);
形成:
Controller→IndividualServiceController \rightarrow IndividualService
239.11 Controller调用Service
Controller 调用 Service 的基本形式:
public function store()
{
$params = $this->getRequestParams();
$result = $this->individualService
->create($params);
return $this->response(
$result
);
}
完整过程:
Request→ControllerRequest \rightarrow Controller Controller→ParameterController \rightarrow Parameter Parameter→ServiceParameter \rightarrow Service Service→ResultService \rightarrow Result Result→ControllerResult \rightarrow Controller
239.12 Service参数边界
Controller 传给 Service 的参数应该是已经完成基础输入处理的数据。
例如:
$params = array(
'name' => trim($_POST['name']),
'type' => trim($_POST['type'])
);
然后:
$result = $service->create(
$params
);
Service 接收到:
NormalizedInputNormalizedInput
而不是完全未经处理的:
RawHTTPInputRawHTTPInput
因此:
HTTP→Controller→NormalizedParameter→ServiceHTTP \rightarrow Controller \rightarrow NormalizedParameter \rightarrow Service
239.13 Service内部执行
Service 接收到参数以后,可以组织完整业务流程。
例如创建机器个体:
CreateIndividualServiceCreateIndividualService
内部:
Parameter→Validate→DomainObject→Engine→Repository→ResultParameter \rightarrow Validate \rightarrow DomainObject \rightarrow Engine \rightarrow Repository \rightarrow Result
例如:
Controller
↓
IndividualService
↓
MachineIndividual
↓
InitializationEngine
↓
VerificationEngine
↓
IndividualRepository
↓
MySQL
Controller 不需要知道这些内部步骤。
因此:
Controller→ServiceController \rightarrow Service
而不是:
Controller→Engine1→Engine2→RepositoryController \rightarrow Engine_1 \rightarrow Engine_2 \rightarrow Repository
239.14 Controller与Engine的边界
Controller 可以间接使用 Engine,但通常不应该直接组织复杂 Engine 调用链。
不推荐:
Controller
↓
GoalEngine
↓
CapabilityEngine
↓
MethodEngine
↓
DecisionEngine
↓
Repository
推荐:
Controller
↓
DecisionService
↓
GoalEngine
↓
CapabilityEngine
↓
MethodEngine
↓
DecisionEngine
↓
Repository
这样:
Controller=入口Controller=入口 Service=业务协调Service=业务协调 Engine=领域计算Engine=领域计算
239.15 返回结果
Service 执行完成以后,需要向 Controller 返回结果。
定义:
ServiceResult=Status+Data+Message+Error+MetaServiceResult= Status+ Data+ Message+ Error+ Meta
例如:
return array(
'status' => true,
'data' => $individual,
'message' => '创建成功',
'error' => null
);
失败:
return array(
'status' => false,
'data' => null,
'message' => '创建失败',
'error' => 'individual_invalid'
);
因此:
Service→ServiceResult→ControllerService \rightarrow ServiceResult \rightarrow Controller
239.16 返回结果状态
结果至少需要区分:
ResultStatusResultStatus
例如:
ResultStatus∈{Success,Failed,Blocked,Invalid,NotFound,Pending}ResultStatus\in \{ Success, Failed, Blocked, Invalid, NotFound, Pending \}
这些状态的含义不同。
Success
业务正常完成:
Success=TrueSuccess=True
Failed
业务执行失败:
ExecutionFailed=TrueExecutionFailed=True
Blocked
因为条件、规则、安全或能力限制而无法执行:
Blocked=TrueBlocked=True
Invalid
输入或业务对象不符合要求:
Valid=FalseValid=False
NotFound
目标对象不存在:
Object=∅Object=\varnothing
Pending
业务已经建立,但尚未完成:
Status=PendingStatus=Pending
不能简单使用:
true / false
表达所有业务状态。
239.17 返回数据
Service 可以返回 Domain Object:
Service→DomainObjectService\rightarrow DomainObject
也可以返回计算结果:
Service→ResultService\rightarrow Result
例如:
$result = array(
'status' => 'success',
'data' => array(
'id' => $individual->getId(),
'type' => $individual->getType()
)
);
Controller 再将结果转换为:
ViewDataViewData
或者:
JSONResponseJSONResponse
因此:
ServiceResult→Controller→OutputServiceResult \rightarrow Controller \rightarrow Output
239.18 Service异常
业务执行过程中可能出现异常。
例如:
- Domain Object 创建失败;
- Repository 查询失败;
- 数据库连接异常;
- Engine 计算异常;
- 参数导致业务无法继续;
- 文件操作异常;
- 系统配置异常。
因此:
Service→ExceptionService \rightarrow Exception
异常不是普通业务结果。
Exception≠BusinessResultException\neq BusinessResult
例如:
try {
$result = $this->service->create(
$params
);
} catch (Exception $e) {
return $this->error(
'系统处理异常'
);
}
239.19 业务失败与异常
必须区分:
BusinessFailureBusinessFailure
和:
ExceptionException
例如:
用户创建一个机器个体,但名称为空:
Name=∅Name=\varnothing
这是正常业务验证失败:
BusinessFailureBusinessFailure
而数据库连接突然断开:
DatabaseConnectionErrorDatabaseConnectionError
属于:
ExceptionException
因此:
BusinessFailure≠ExceptionBusinessFailure\neq Exception
业务失败可以作为正常 Service Result 返回:
return array(
'status' => false,
'code' => 'INVALID_NAME'
);
而系统异常则通过异常机制处理。
239.20 异常分类
ICAI Controller→Service 可以把异常分为:
Exception=InputException+BusinessException+DomainException+PersistenceException+SystemExceptionException= InputException+ BusinessException+ DomainException+ PersistenceException+ SystemException
InputException
输入参数无法满足接口要求。
BusinessException
业务过程无法继续。
DomainException
领域对象或领域规则出现异常。
PersistenceException
数据库或持久化过程发生异常。
SystemException
系统运行环境发生异常。
这种分类有利于 Controller 做不同响应。
239.21 Service异常处理原则
Service 不应该简单地把所有异常吞掉。
错误:
try {
// ...
} catch (Exception $e) {
return null;
}
这种处理会导致:
Exception→nullException\rightarrow null
Controller 无法知道发生了什么。
更合理:
try {
// business process
} catch (Exception $e) {
// record log
throw $e;
}
然后由更上层统一处理。
因此:
Service→Exception→ExceptionHandlerService \rightarrow Exception \rightarrow ExceptionHandler
239.22 Controller异常处理
Controller 是外部请求边界,因此可以负责将异常转换为适合客户端的响应。
例如:
try {
$result = $this->service->create(
$params
);
return $this->response($result);
} catch (Exception $e) {
return $this->error(
'系统处理异常'
);
}
对于 HTML:
Exception→ErrorPageException \rightarrow ErrorPage
对于 JSON:
Exception→JSONErrorException \rightarrow JSONError
因此:
Exception→ResponseException \rightarrow Response
239.23 不向用户暴露内部异常
数据库异常可能包含:
SQL
Table
FilePath
StackTrace
ServerInformation
这些内容不应该直接输出给普通页面用户。
例如:
错误:
PDOException:
SQLSTATE[42S02]:
Table 'xxx.xxx' doesn't exist
/home/site/app/...
页面应该输出:
系统暂时无法完成操作,请稍后重试。
同时内部日志保留真实异常信息:
Exception→LogException \rightarrow Log
而用户获得:
SafeMessageSafeMessage
239.24 Controller→Service完整流程
完整请求:
Request
↓
Controller
↓
读取参数
↓
参数标准化
↓
输入验证
↓
Service
↓
业务流程
↓
Engine
↓
Domain Object
↓
Repository
↓
Result
↓
Service Return
↓
Controller
↓
ViewData / JSON
↓
Response
异常:
Request
↓
Controller
↓
Service
↓
Exception
↓
Exception Handler
↓
Error Response
239.25 Controller→Service参数方向
正常情况下,数据方向是单向进入:
Request→Controller→ServiceRequest \rightarrow Controller \rightarrow Service
Service 返回:
Service→ControllerService \rightarrow Controller
因此:
Controller↔ServiceController \leftrightarrow Service
但这种双向关系不是任意对象互相调用,而是:
RequestDirection:Controller→ServiceRequestDirection: Controller\rightarrow Service ReturnDirection:Service→ControllerReturnDirection: Service\rightarrow Controller
形成一次完整的请求—返回周期。
239.26 Controller不保存业务状态
Controller 通常是请求级对象。
因此不应该把长期 ICAI 状态直接放进 Controller:
错误:
class IndividualController
{
protected $individualState;
protected $knowledge;
protected $memory;
protected $capability;
}
真正的 ICAI 状态应该属于 Domain Object:
Individual→StateIndividual \rightarrow State
知识:
Individual→KnowledgeIndividual \rightarrow Knowledge
记忆:
Individual→MemoryIndividual \rightarrow Memory
能力:
Individual→CapabilityIndividual \rightarrow Capability
Controller 只负责本次请求。
239.27 Controller与Service依赖
Controller 可以通过依赖注入获得 Service。
例如:
class IndividualController
{
protected $service;
public function __construct(
IndividualService $service
) {
$this->service = $service;
}
}
调用:
$result = $this->service->find($id);
形成:
Controller→DependencyServiceController \xrightarrow{Dependency} Service
这种依赖属于第158章对象关系模型中的:
DependencyDependency
即 Controller 为完成请求处理,需要使用 Service。
239.28 Service接口
为了降低 Controller 与具体 Service 实现之间的耦合,可以定义接口:
interface IndividualServiceInterface
{
public function find($id);
public function create($params);
public function update($id, $params);
}
然后:
class IndividualService
implements IndividualServiceInterface
{
}
Controller 依赖:
IndividualServiceInterfaceIndividualServiceInterface
而不是必须依赖某个具体实现。
239.29 Controller→Service在ICAI中的意义
ICAI 系统存在大量领域流程:
CognitionCognition LearningLearning MatchingMatching DecisionDecision BehaviorBehavior MemoryMemory MaintenanceMaintenance
这些流程都不应该直接写入 Controller。
例如认知:
CognitiveController
↓
CognitiveService
↓
CognitiveEngine
决策:
DecisionController
↓
DecisionService
↓
DecisionEngine
行为:
BehaviorController
↓
BehaviorService
↓
BehaviorEngine
维护:
MaintenanceController
↓
MaintenanceService
↓
MaintenanceEngine
因此:
Controller→Service→EngineController \rightarrow Service \rightarrow Engine
成为 ICAI 各领域统一的工程入口结构。
239.30 Controller→Service与ICAI运行
ICAI 的运行过程:
Cognition→Need→Goal→Capability→Matching→Method→Decision→Behavior→Action→ResultCognition \rightarrow Need \rightarrow Goal \rightarrow Capability \rightarrow Matching \rightarrow Method \rightarrow Decision \rightarrow Behavior \rightarrow Action \rightarrow Result
不能全部放在 Controller。
应该按照领域 Service 进行组织。
例如:
CognitiveService
↓
NeedService
↓
GoalService
↓
CapabilityService
↓
MatchingService
↓
MethodService
↓
DecisionService
↓
BehaviorService
↓
ResultService
每一个 Service 再调用对应 Engine 和 Domain Object。
因此:
ControllerController
只是整个 ICAI Runtime 的入口之一。
239.31 Controller→Service异常闭环
正常:
Request→Controller→Service→Result→ControllerRequest \rightarrow Controller \rightarrow Service \rightarrow Result \rightarrow Controller
异常:
Request→Controller→Service→ExceptionRequest \rightarrow Controller \rightarrow Service \rightarrow Exception
然后:
Exception→LogException \rightarrow Log
同时:
Exception→SafeResponseException \rightarrow SafeResponse
因此:
Exception→Record→Handle→Respond\boxed{ Exception \rightarrow Record \rightarrow Handle \rightarrow Respond }
异常处理本身也应该成为系统工程的一部分。
239.32 Controller→Service工程代码结构
一个完整的 Controller 可以保持简单:
class IndividualController
{
protected $service;
protected $view;
public function store()
{
try {
$params = $this->getParams();
$result =
$this->service->create(
$params
);
return $this->view->display(
'individual/result.tpl',
$result
);
} catch (Exception $e) {
return $this->handleException(
$e
);
}
}
}
真正的业务过程进入:
class IndividualService
{
public function create($params)
{
// 参数业务验证
// 创建Domain Object
// Engine处理
// Repository保存
// 返回结果
}
}
形成:
Controller=ThinController=Thin Service=BusinessProcessService=BusinessProcess
这就是 Controller→Service 的核心工程思想。
239.33 Controller→Service完整模型
最终可以建立:
Request→Controller→Parameter→Service→BusinessProcess→Result→Controller→Response\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow BusinessProcess \rightarrow Result \rightarrow Controller \rightarrow Response }
异常分支:
Service→Exception→Handler→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Handler \rightarrow SafeResponse }
其中:
Request=外部请求Request=外部请求 Parameter=请求参数Parameter=请求参数 Service=业务流程Service=业务流程 Result=业务结果Result=业务结果 Exception=异常状态Exception=异常状态
239.34 本章总结
Controller→Service 是 ICAI MVC 中连接外部请求与内部业务模型的核心工程关系。
其基本流程是:
Request→Controller→Parameter→Service\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service }
Service 执行业务过程:
Service→DomainObject→Engine→Repository\boxed{ Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository }
然后返回:
Result→Controller→View/JSON→Response\boxed{ Result \rightarrow Controller \rightarrow View/JSON \rightarrow Response }
异常则形成:
Service→Exception→Handler→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Handler \rightarrow SafeResponse }
因此五个核心概念可以统一为:
Request→Parameter→Service→Return→Exception\boxed{ Request \rightarrow Parameter \rightarrow Service \rightarrow Return \rightarrow Exception }
其中 Controller 的核心职责是:
接收请求、处理输入、调用Service、接收结果、返回响应\boxed{ 接收请求、处理输入、调用Service、接收结果、返回响应 }
而 Service 的核心职责是:
组织业务流程、调用领域对象、调用Engine、协调Repository并产生业务结果\boxed{ 组织业务流程、调用领域对象、调用Engine、协调Repository并产生业务结果 }
最终形成 ICAI MVC 的稳定入口:
Controller→Service→Model→Result\boxed{ Controller \rightarrow Service \rightarrow Model \rightarrow Result }
并通过 View 完成:
Result→ViewData→Smarty→HTML\boxed{ Result \rightarrow ViewData \rightarrow Smarty \rightarrow HTML }
这样 Controller 保持简单,Service 承担业务流程,Engine 承担领域计算,Domain Object 承担领域对象,Repository 承担持久化,各层职责清晰且可以独立维护。
239.35 可运行完整代码示例
为了使 Controller→Service 的工程关系能够直接运行,本节建立一个最小 ICAI MVC 示例。
示例实现:
Request→Controller→Parameter→Service→DomainObject→Repository→Result→Controller→ResponseRequest \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow DomainObject \rightarrow Repository \rightarrow Result \rightarrow Controller \rightarrow Response
异常:
Service→Exception→Controller→ErrorResponseService \rightarrow Exception \rightarrow Controller \rightarrow ErrorResponse
本示例使用 PHP 原生代码,可以直接在 PHP CLI 环境中运行。
239.36 完整运行代码
建立文件:
controller_service_demo.php
完整代码如下:
<?php
/**
* ICAI Controller -> Service
*
* PHP CLI 可直接运行:
*
* php controller_service_demo.php
*
* 本示例不依赖数据库。
* Repository 使用内存数组模拟数据存储。
*/
/**
* =========================================================
* 1. Domain Object
* =========================================================
*
* MachineIndividual
* 机器个体领域对象
*/
class MachineIndividual
{
protected $id;
protected $name;
protected $type;
protected $status;
public function __construct(
$id,
$name,
$type,
$status = 'created'
) {
$this->id = $id;
$this->name = $name;
$this->type = $type;
$this->status = $status;
}
public function getId()
{
return $this->id;
}
public function getName()
{
return $this->name;
}
public function getType()
{
return $this->type;
}
public function getStatus()
{
return $this->status;
}
public function setName($name)
{
$this->name = $name;
}
public function setType($type)
{
$this->type = $type;
}
public function setStatus($status)
{
$this->status = $status;
}
public function toArray()
{
return array(
'id' => $this->id,
'name' => $this->name,
'type' => $this->type,
'status' => $this->status
);
}
}
/**
* =========================================================
* 2. Repository
* =========================================================
*
* 这里使用数组模拟 MySQL。
*
* 真实项目中可以替换为:
*
* IndividualRepository
* ↓
* PDO
* ↓
* MySQL
*/
class IndividualRepository
{
protected $data = array();
protected $nextId = 1;
public function create(MachineIndividual $individual)
{
$id = $this->nextId++;
$individual = new MachineIndividual(
$id,
$individual->getName(),
$individual->getType(),
$individual->getStatus()
);
$this->data[$id] = $individual;
return $individual;
}
public function find($id)
{
$id = (int) $id;
if (isset($this->data[$id])) {
return $this->data[$id];
}
return null;
}
public function update(MachineIndividual $individual)
{
$id = $individual->getId();
if (!isset($this->data[$id])) {
return false;
}
$this->data[$id] = $individual;
return true;
}
public function all()
{
return array_values($this->data);
}
}
/**
* =========================================================
* 3. Service Exception
* =========================================================
*
* Service 层业务异常。
*/
class BusinessException extends Exception
{
}
/**
* =========================================================
* 4. Service
* =========================================================
*
* Service 负责业务流程。
*
* Controller 不直接操作 Repository。
*/
class IndividualService
{
protected $repository;
public function __construct(
IndividualRepository $repository
) {
$this->repository = $repository;
}
/**
* 创建机器个体
*/
public function create($params)
{
/**
* -----------------------------------------
* 4.1 业务参数验证
* -----------------------------------------
*/
$name = isset($params['name'])
? trim($params['name'])
: '';
$type = isset($params['type'])
? trim($params['type'])
: '';
if ($name === '') {
throw new BusinessException(
'机器个体名称不能为空'
);
}
if ($type === '') {
throw new BusinessException(
'机器个体类型不能为空'
);
}
/**
* -----------------------------------------
* 4.2 创建 Domain Object
* -----------------------------------------
*/
$individual = new MachineIndividual(
0,
$name,
$type,
'created'
);
/**
* -----------------------------------------
* 4.3 Repository 持久化
* -----------------------------------------
*/
$individual =
$this->repository->create(
$individual
);
/**
* -----------------------------------------
* 4.4 返回业务结果
* -----------------------------------------
*/
return array(
'status' => 'success',
'message' => '机器个体创建成功',
'data' => $individual->toArray()
);
}
/**
* 查询机器个体
*/
public function find($id)
{
$id = (int) $id;
if ($id <= 0) {
throw new BusinessException(
'机器个体ID无效'
);
}
$individual =
$this->repository->find($id);
if ($individual === null) {
return array(
'status' => 'not_found',
'message' => '机器个体不存在',
'data' => null
);
}
return array(
'status' => 'success',
'message' => '查询成功',
'data' => $individual->toArray()
);
}
/**
* 修改机器个体
*/
public function update($id, $params)
{
$id = (int) $id;
if ($id <= 0) {
throw new BusinessException(
'机器个体ID无效'
);
}
$individual =
$this->repository->find($id);
if ($individual === null) {
return array(
'status' => 'not_found',
'message' => '机器个体不存在',
'data' => null
);
}
if (isset($params['name'])) {
$name = trim($params['name']);
if ($name === '') {
throw new BusinessException(
'名称不能为空'
);
}
$individual->setName($name);
}
if (isset($params['type'])) {
$type = trim($params['type']);
if ($type === '') {
throw new BusinessException(
'类型不能为空'
);
}
$individual->setType($type);
}
$this->repository->update(
$individual
);
return array(
'status' => 'success',
'message' => '机器个体更新成功',
'data' => $individual->toArray()
);
}
}
/**
* =========================================================
* 5. Controller
* =========================================================
*
* Controller 负责:
*
* Request
* Parameter
* Service
* Return
* Exception
*
* Controller 不直接访问 Repository。
*/
class IndividualController
{
protected $service;
public function __construct(
IndividualService $service
) {
$this->service = $service;
}
/**
* 处理创建请求
*/
public function store($request)
{
try {
/**
* -------------------------------------
* 5.1 Request
* -------------------------------------
*/
$params =
$this->getStoreParams(
$request
);
/**
* -------------------------------------
* 5.2 Service
* -------------------------------------
*/
$result =
$this->service->create(
$params
);
/**
* -------------------------------------
* 5.3 Return
* -------------------------------------
*/
return $this->success(
$result
);
} catch (BusinessException $e) {
/**
* -------------------------------------
* 5.4 Business Exception
* -------------------------------------
*/
return $this->businessError(
$e
);
} catch (Exception $e) {
/**
* -------------------------------------
* 5.5 System Exception
* -------------------------------------
*/
return $this->systemError(
$e
);
}
}
/**
* 查询请求
*/
public function detail($request)
{
try {
/**
* -------------------------------------
* Request
* -------------------------------------
*/
$id = isset($request['id'])
? (int) $request['id']
: 0;
/**
* -------------------------------------
* Parameter Validation
* -------------------------------------
*/
if ($id <= 0) {
return $this->error(
'参数错误:id必须大于0'
);
}
/**
* -------------------------------------
* Service
* -------------------------------------
*/
$result =
$this->service->find($id);
/**
* -------------------------------------
* Return
* -------------------------------------
*/
return $this->response(
$result
);
} catch (BusinessException $e) {
return $this->businessError(
$e
);
} catch (Exception $e) {
return $this->systemError(
$e
);
}
}
/**
* 修改请求
*/
public function update($request)
{
try {
/**
* -------------------------------------
* Request
* -------------------------------------
*/
$id = isset($request['id'])
? (int) $request['id']
: 0;
/**
* -------------------------------------
* Parameter
* -------------------------------------
*/
$params = array();
if (isset($request['name'])) {
$params['name'] =
trim($request['name']);
}
if (isset($request['type'])) {
$params['type'] =
trim($request['type']);
}
/**
* -------------------------------------
* Service
* -------------------------------------
*/
$result =
$this->service->update(
$id,
$params
);
/**
* -------------------------------------
* Return
* -------------------------------------
*/
return $this->response(
$result
);
} catch (BusinessException $e) {
return $this->businessError(
$e
);
} catch (Exception $e) {
return $this->systemError(
$e
);
}
}
/**
* Controller层参数处理
*/
protected function getStoreParams(
$request
) {
$name = isset($request['name'])
? trim($request['name'])
: '';
$type = isset($request['type'])
? trim($request['type'])
: '';
/**
* 输入层验证
*/
if ($name === '') {
throw new BusinessException(
'参数错误:name不能为空'
);
}
if ($type === '') {
throw new BusinessException(
'参数错误:type不能为空'
);
}
return array(
'name' => $name,
'type' => $type
);
}
/**
* 成功响应
*/
protected function success($result)
{
return $this->response(
$result
);
}
/**
* 普通错误
*/
protected function error($message)
{
return array(
'status' => 'error',
'message' => $message,
'data' => null
);
}
/**
* 业务异常响应
*/
protected function businessError(
BusinessException $e
) {
return array(
'status' => 'business_error',
'message' => $e->getMessage(),
'data' => null
);
}
/**
* 系统异常响应
*/
protected function systemError(
Exception $e
) {
/**
* 真实项目中这里应该写入日志。
*/
error_log(
$e->getMessage()
);
/**
* 不直接把内部异常信息返回给用户。
*/
return array(
'status' => 'system_error',
'message' => '系统处理异常',
'data' => null
);
}
/**
* 统一响应
*/
protected function response($result)
{
return $result;
}
}
/**
* =========================================================
* 6. Application Bootstrap
* =========================================================
*
* 创建对象依赖关系:
*
* Repository
* ↓
* Service
* ↓
* Controller
*/
$repository =
new IndividualRepository();
$service =
new IndividualService(
$repository
);
$controller =
new IndividualController(
$service
);
/**
* =========================================================
* 7. 模拟 Request
* =========================================================
*/
echo "=============================\n";
echo "1. 创建机器个体\n";
echo "=============================\n";
$request = array(
'name' => 'Robot-001',
'type' => 'robot'
);
$result =
$controller->store(
$request
);
print_r($result);
/**
* =========================================================
* 8. 查询
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "2. 查询机器个体\n";
echo "=============================\n";
$request = array(
'id' => 1
);
$result =
$controller->detail(
$request
);
print_r($result);
/**
* =========================================================
* 9. 修改
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "3. 修改机器个体\n";
echo "=============================\n";
$request = array(
'id' => 1,
'name' => 'Robot-001-Updated',
'type' => 'industrial_robot'
);
$result =
$controller->update(
$request
);
print_r($result);
/**
* =========================================================
* 10. 再次查询
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "4. 查询更新后的机器个体\n";
echo "=============================\n";
$request = array(
'id' => 1
);
$result =
$controller->detail(
$request
);
print_r($result);
/**
* =========================================================
* 11. 测试业务异常
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "5. 测试业务异常\n";
echo "=============================\n";
$request = array(
'name' => '',
'type' => 'robot'
);
$result =
$controller->store(
$request
);
print_r($result);
/**
* =========================================================
* 12. 测试对象不存在
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "6. 测试对象不存在\n";
echo "=============================\n";
$request = array(
'id' => 999
);
$result =
$controller->detail(
$request
);
print_r($result);
/**
* =========================================================
* 13. 测试参数异常
* =========================================================
*/
echo "\n";
echo "=============================\n";
echo "7. 测试参数异常\n";
echo "=============================\n";
$request = array(
'id' => 0
);
$result =
$controller->detail(
$request
);
print_r($result);
239.37 运行方式
将代码保存为:
controller_service_demo.php
然后在 PHP 环境中执行:
php controller_service_demo.php
不需要 MySQL,也不需要 Composer。
运行结果类似:
=============================
1. 创建机器个体
=============================
Array
(
[status] => success
[message] => 机器个体创建成功
[data] => Array
(
[id] => 1
[name] => Robot-001
[type] => robot
[status] => created
)
)
=============================
2. 查询机器个体
=============================
Array
(
[status] => success
[message] => 查询成功
[data] => Array
(
[id] => 1
[name] => Robot-001
[type] => robot
[status] => created
)
)
更新以后:
=============================
4. 查询更新后的机器个体
=============================
Array
(
[status] => success
[message] => 查询成功
[data] => Array
(
[id] => 1
[name] => Robot-001-Updated
[type] => industrial_robot
[status] => created
)
)
参数错误:
=============================
7. 测试参数异常
=============================
Array
(
[status] => error
[message] => 参数错误:id必须大于0
[data] =>
)
239.38 代码结构对应理论
这段代码不是简单的 PHP 示例,而是直接对应 ICAI 工程模型。
第一层:
Request
对应:
RequestRequest
第二层:
IndividualController
对应:
ControllerController
第三层:
IndividualService
对应:
ServiceService
第四层:
MachineIndividual
对应:
DomainObjectDomainObject
第五层:
IndividualRepository
对应:
RepositoryRepository
最终形成:
Request→Controller→Service→DomainObject→Repository\boxed{ Request \rightarrow Controller \rightarrow Service \rightarrow DomainObject \rightarrow Repository }
239.39 Controller代码边界
Controller 中最重要的代码是:
$params =
$this->getStoreParams(
$request
);
$result =
$this->service->create(
$params
);
return $this->response(
$result
);
它表达:
Request→Parameter→Service→ReturnRequest \rightarrow Parameter \rightarrow Service \rightarrow Return
Controller 没有:
↓
直接访问 MySQL
↓
直接创建 PDO
↓
直接执行 SQL
↓
直接计算 ICAI 能力
↓
直接执行决策
因此 Controller 保持较薄。
239.40 Service代码边界
Service 中:
public function create($params)
{
// 参数业务验证
// 创建 Domain Object
// Repository 持久化
// 返回业务结果
}
这里体现:
Service=BusinessProcessService= BusinessProcess
以后随着 ICAI 工程继续扩展,可以变成:
IndividualService
↓
IndividualInitializationEngine
↓
CapabilityEngine
↓
VerificationEngine
↓
IndividualRepository
或者:
DecisionService
↓
DecisionEngine
↓
RiskEngine
↓
MethodSelectionEngine
↓
DecisionRepository
Controller 不需要知道这些内部过程。
239.41 Repository代码边界
本示例中的:
class IndividualRepository
使用:
protected $data = array();
模拟数据库。
真实 MySQL 工程中可以替换为:
class IndividualRepository
{
protected $pdo;
public function __construct(PDO $pdo)
{
$this->pdo = $pdo;
}
public function find($id)
{
$sql = "
SELECT
id,
name,
type,
status
FROM individuals
WHERE id = :id
LIMIT 1
";
$statement =
$this->pdo->prepare($sql);
$statement->execute(
array(
':id' => $id
)
);
$row =
$statement->fetch(
PDO::FETCH_ASSOC
);
if (!$row) {
return null;
}
return new MachineIndividual(
$row['id'],
$row['name'],
$row['type'],
$row['status']
);
}
}
这样:
Repository→PDO→MySQLRepository \rightarrow PDO \rightarrow MySQL
而 Service 和 Controller 的接口基本不需要改变。
239.42 从内存Repository到MySQL
本章示例:
Service→MemoryRepositoryService \rightarrow MemoryRepository
生产工程:
Service→MySQLRepository→PDO→MySQLService \rightarrow MySQLRepository \rightarrow PDO \rightarrow MySQL
这正是 Repository 的意义:
业务层不直接依赖数据库实现\boxed{ 业务层不直接依赖数据库实现 }
因此可以:
MemoryRepository↔MySQLRepositoryMemoryRepository \leftrightarrow MySQLRepository
而:
IndividualServiceIndividualService
保持基本不变。
239.43 Controller→Service完整对象关系
对象依赖关系为:
Controller→DependsOnServiceController \xrightarrow{DependsOn} Service
Service:
Service→DependsOnRepositoryService \xrightarrow{DependsOn} Repository
Service 创建:
Service→CreatesDomainObjectService \xrightarrow{Creates} DomainObject
Repository 保存:
Repository→PersistsDomainObjectRepository \xrightarrow{Persists} DomainObject
最终:
IndividualController
│
│ depends on
↓
IndividualService
│
├──── creates ────→ MachineIndividual
│
└──── depends on ─→ IndividualRepository
│
↓
Storage
这正好对应第158章中的对象关系模型:
DependencyDependency
以及:
CompositionComposition
239.44 Controller→Service异常关系
异常关系可以表示为:
Request
↓
Controller
↓
Service
↓
Exception
↓
Controller
↓
ErrorResponse
业务异常:
BusinessExceptionBusinessException
系统异常:
SystemExceptionSystemException
最终:
Exception→ErrorResponseException \rightarrow ErrorResponse
但真实内部异常信息应该进入:
Exception→LogException \rightarrow Log
而不是直接进入用户页面。
239.45 与ICAI核心理论的关系
本示例虽然只实现“创建、查询、更新机器个体”,但它可以继续向 ICAI 核心理论扩展。
例如:
IndividualController
↓
IndividualService
↓
MachineIndividual
↓
CognitiveEngine
↓
GoalEngine
↓
CapabilityEngine
↓
MatchingEngine
↓
MethodEngine
↓
DecisionEngine
↓
BehaviorEngine
↓
FeedbackEngine
↓
MemoryEngine
↓
MaintenanceEngine
↓
Repository
↓
MySQL
最终:
Controller→Service→ICAI Runtime→Repository\boxed{ Controller \rightarrow Service \rightarrow ICAI\ Runtime \rightarrow Repository }
因此 Controller→Service 并不是独立于 ICAI 理论之外的普通 Web 工程,而是 ICAI 理论进入实际系统运行环境的入口。
239.46 本节最终模型
本章 Controller→Service 可以最终形式化为:
Request→Controller→Parameter→Service→BusinessProcess→Result→Controller→Response\boxed{ Request \rightarrow Controller \rightarrow Parameter \rightarrow Service \rightarrow BusinessProcess \rightarrow Result \rightarrow Controller \rightarrow Response }
异常:
Service→Exception→Log→Controller→SafeResponse\boxed{ Service \rightarrow Exception \rightarrow Log \rightarrow Controller \rightarrow SafeResponse }
工程对象:
Controller→DependencyService→DependencyRepository\boxed{ Controller \xrightarrow{Dependency} Service \xrightarrow{Dependency} Repository }
业务对象:
Service→DomainObject→Engine→Repository\boxed{ Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository }
因此,本章最终建立:
Controller=请求入口\boxed{ Controller=请求入口 } Service=业务流程\boxed{ Service=业务流程 } DomainObject=业务对象\boxed{ DomainObject=业务对象 } Engine=领域计算\boxed{ Engine=领域计算 } Repository=数据持久化\boxed{ Repository=数据持久化 }
形成完整的 ICAI Web 工程链:
HTTP→Controller→Service→DomainObject→Engine→Repository→MySQL\boxed{ HTTP \rightarrow Controller \rightarrow Service \rightarrow DomainObject \rightarrow Engine \rightarrow Repository \rightarrow MySQL }
再返回:
MySQL→Repository→DomainObject→Service→Controller→View→HTML\boxed{ MySQL \rightarrow Repository \rightarrow DomainObject \rightarrow Service \rightarrow Controller \rightarrow View \rightarrow HTML }
这就是 ICAI 从理论模型进入可执行 PHP MVC 系统的一个最小、完整、可运行的 Controller→Service 实现。