所以我的网站使用Laravel,但我在判断代码的结构时遇到了问题,所以我想输入一些关于结构有效性的信息。
我当然使用通过控制器的路由,一个请求进来,它被路由到Processor
,这个处理器还接收一个监听器(控制器),处理器使用它来再次路由。示例
class PageController{
public function __construct(PageProcessor $pageProcessor)
{
$this->processor = $pageProcessor;
}
public function edit($id = null)
{
$this->processor->edit($this, $id);
}
public function editFailed($message)
{
return View::make('edit.failed', $message);
}
public function editSucceed($data)
{
return View::make('edit.succeed', $data);
}
}
class PageProcessor{
public function __construct(PageRepository $pageRepository, PageValidator $pageValidator)
{
$this->repository = $pageRepository;
$this->validator = $pageValidator;
}
public function edit($id = null)
{
// logic for showing the edit page
//
// more logic
if($logicSucceeded)
{
return $listener->editSucceed(compact('page', 'awesomeData'));
}
else
{
return $listener->editFailed('something failed');
}
}
}
现在我的问题是将控制器传递给处理器,这有点违背了职责分离,但我想不出其他方法来做到这一点。
总结一下:保持相互纠缠的类的责任的最佳方式是什么?
我怎么能让一个使用另一个而不知道它是如何工作的。。。
在我看来,在页面处理器上显示编辑页面的逻辑可能会使您在某个时候混合责任。从这里看,您的代码非常好,瘦控制器和控制器之外的业务逻辑。但我会把观点带回控制器:
class PageController {
public function __construct(PageProcessor $pageProcessor)
{
$this->processor = $pageProcessor;
}
public function edit($id)
{
$this->processor->validate($id, Input::all());
$data = $this->processor->getEditData($id);
return View::make('edit', compact('data'));
}
public function update($id)
{
$this->processor->update($data);
// If the update fails, an exception will be thrown and it will never reach this return
return Redirect::back();
}
public function editFailed($message)
{
return View::make('edit.failed', $message);
}
public function editSucceed($data)
{
return View::make('edit.succeed', $data);
}
}
处理器将只负责数据处理和抛出错误消息:
class PageProcessor {
public function __construct(PageRepository $pageRepository, PageValidator $pageValidator)
{
$this->repository = $pageRepository;
$this->validator = $pageValidator;
}
public function getEditData($id)
{
return $this->repository->getData($id);
}
public function update($data)
{
if($updatedModel = $this->repository->update($data['id'], $data))
{
$listener->editSucceed(compact('page', 'awesomeData'));
throw new Exception("Error Updating Model");
}
$listener->editFailed('something failed');
return $updatedModel;
}
public function validate($id, $input)
{
if ($this->repository->validation->fails($id, $input))
{
throw new Exception("Validation failed", $this->repository->validation->errors());
}
return true;
}
}
有一个共识是,验证应该在控制器中触发,甚至在它之前触发,就像Laravel 4.3中一样,所以我也将其添加到控制器方法中。
编辑:
共识在于处理用户输入是控制器的责任。当然,有时不仅仅是知道电子邮件是否格式正确,有时你的业务逻辑取决于其他因素,但现在我们谈论的是两种不同类型的验证:输入数据和业务数据。meta中对此有一个很好的解释:https://softwareengineering.stackexchange.com/questions/97880/in-mvc-should-a-model-handle-validation.
在Laravel 4.3中,输入数据将由您的控制器处理,实际上是在控制器实例化期间:
所以你会看到控制器是这样做的:
public function store(AddOfficeRequest $request)
{
$this->execute(AddOfficeCommand::class, $input);
Flash::message('Office created');
return Redirect::back();
}
AddOfficeRequest
将是
use use Illuminate'Foundation'Http'FormRequest;
class AddOfficeRequest extends FormRequest {
public function rules()
{
return [
'name' => 'required',
'contact' => 'required',
'email' => 'required|email',
];
}
}
这样做的结果是,只有当输入验证通过时,您的控制器store
方法才会被击中,否则Laravel将自动地Redirect::back()->withInput()->withErrors()
。
我认为OOP的规则并不像你所说的那样严格。是的,职责分离需要创建可概括的类,这也是将一些逻辑分解为自己的类的原因之一。
但是,例如,可以编写带有PageProcessor
逻辑的PageController
。也许这只是一个笨拙的代码。创建任意数量的具有特定职责的类也不是一件坏事,这些类只在另一个类中引用,尤其是当您认为PageProcessor
将来可能会被另一个类别使用时。