为控制器分离职责php

时间:2014-09-06 21:11:44

标签: php laravel structure

所以我在我的网站上使用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');
        }
    }

}

现在我的问题在于将控制器传递给处理器,有点挫败责任分离,但我想不出另一种方法。

总结一下:保持彼此纠缠的课程责任的最佳方法是什么?

如果不知道它是如何工作的,我该如何使用另一个...

2 个答案:

答案 0 :(得分:3)

在我看来,拥有在页面处理器上显示编辑页面的逻辑可能会让你在某些时候混合可信度。从这里看,你的代码是非常好的,瘦的控制器和外部控制器的业务逻辑。但我会把观点带回控制器:

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()

答案 1 :(得分:1)

我认为,OOP的规则并不像你要做的那样严格。是的,责任分离要求创建可推广的类,这是将某些逻辑分解为自己的类的一个原因。

但是,例如,你可以在其中写入PageController逻辑PageProcessor。也许那只是笨重的代码。创建具有特定职责的任意数量的课程也不是坏事,这些课程仅在另一个课程中引用 - 特别是如果您认为PageProcessor可能被其他课程使用将来