在阅读以下内容后,我一直在“ Fat模型,瘦控制器”的概念上吸引其他开发人员的注意:
大多数受访者都在使用我认为的胖控制器。
尽管话题已经在Stack Overflow上讨论了,但实际上我还没有找到对该方法的详尽描述。
您将在PHP(香草,Laravel或Symfony)中看到的越来越多,这是有史以来最纤细的控制器。这是您在Rails中已经看到的东西,并且人们也开始将其称为六边形(通过一些其他实践)。控制器中只需要一行代码,实际上他们说这应该是所有方法的目标。这是一个示例,是的,还不止于此,但仍然很瘦:
<?php
class PostController extends Controller {
private $repository;
public function __construct(PostRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function store()
{
try
{
$this->repository->create(Input::all());
}
catch (ValidationException $e)
{
return Redirect::back()->withInput()->withErrors($e->all());
}
return Redirect::route('posts');
}
}
控制器是HTTP请求,业务逻辑和表示层之间的桥梁。因此,它应该接收一个请求,将其发送到注入的对象,该对象将对其进行处理,然后重定向到负责向客户(或用户)提供反馈的路由(或渲染视图)。其他所有内容,包括验证,都应在您的存储库,服务,模型(MVC,是的!)等中进行。
但是我们可以以六边形的方式重构此控制器,以达到每方法一线的目标:
<?php
class PostController extends Controller {
private $repository;
public function __construct(PostRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function store()
{
return $this->repository->create(Input::all(), $this);
}
public function createSucceeded()
{
return Redirect::route('posts');
}
public function createFailed()
{
return Redirect::back()->withInput()->withErrors($e->all());
}
}
基本上,您的存储库类将使用自己的调用程序($this
)触发succeeded
和failed
方法。
模型与您的数据过于相关,有时它们是您的ORM,并且直接与您的数据库服务器对话,因此,如今,您会看到人们将存储库和服务用作它们之间的层。
存储库是一类,通过直接与您的模型进行对话,处理和收集应用程序所需的信息。您的应用程序应该不知道在数据库中选择某些信息,选择,位置,顺序,分组依据是什么,而有时候这些只是您的模型应该知道的,所以这是一个存储库:
class PostRepository implements PostRepositoryInterface {
private $model;
public function __construct(PostInterface $model)
{
$this->model = $model;
}
public function create($input)
{
return $this->model->create($input);
}
public findBySlug($slug)
{
return $this->model->where('slug', $slug)->first();
}
}
一切不直接属于您的业务逻辑的东西,主要是外部服务,离您的应用程序代码最远,构建它们的解耦越好。为这些服务创建外部程序包(Composer程序包)是将它们与其他所有功能分离的好方法,并且,如果您使它们与框架无关,则您将获得10个Sturgeon点。在Laravel中,您可以通过集成三种类来创建服务:
1)服务类:负责执行您的服务必须做的事情,所有服务逻辑都在这里。
2)服务提供者:负责启动您的服务并将其添加到Laravel的IoC容器中,以便可以随时使用,但是请注意,Laravel仅在您的应用程序真正使用它们时才实例化您的服务类。
3)Facade:可让您使用静态(::)语法从应用程序中的任何位置访问服务:
Mailer::send($user->id, 'Thanks for registering', 'emails.registered');
邮件服务:
<?php namespace ACR\Services\Mailer;
use Illuminate\Mail\Mailer as IlluminateMailer;
use Sentry;
class Service {
public function __construct(IlluminateMailer $mailer)
{
$this->mailer = $mailer;
}
public function send($userId, $subject, $view, $data = [])
{
return $this->mailer->queue($view, $data, function($message) use ($userId, $subject)
{
$user = Sentry::findUserById($userId);
$message->to($user->email, $user->name);
$message->subject($subject);
});
}
}
<?php namespace ACR\Services\Mailer;
use Illuminate\Support\ServiceProvider as IlluminateServiceProvider;
use ACR\Services\Mailer\Service as Mailer;
class ServiceProvider extends IlluminateServiceProvider {
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->bind('acr.mailer', function($app) {
return new Mailer($app->make('mailer'));
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return array('acr.mailer');
}
}
<?php namespace ACR\Services\Mailer;
use Illuminate\Support\Facades\Facade as IlluminateFacade;
class Facade extends IlluminateFacade {
protected static function getFacadeAccessor() { return 'acr.mailer'; }
}
这些家伙应该是高度可交换的,今天您可能正在使用Eloquent作为您的ORM,将数据存储在数据库中,但是您可能需要将其更改为其他内容,某些fok会将其100%的数据存储在Redis中,所以您更好通过在ORM和域loginc之间使用接口(合同)层为这种变化做好准备,并真正为接口而不是具体的类进行开发。泰勒·奥特威尔(Taylor Otwell)在他的书中甚至说,您应该完全删除您的models文件夹。
interface PostInterface {
public function all();
public function find($id);
}
class DbPost extends Eloquent implements PostInterface {
}
class RedisPost extends Eloquent implements PostInterface {
}
其背后的想法是轻松交换实现,因此在Laravel中,您可以使用IoC容器来告诉Laravel您正在使用哪个实现:
App::bind('PostInterface', 'DbPost');
因此,如果您的存储库正在使用PostInterface:
class PostRepository implements PostRepositoryInterface {
private $model;
public function __construct(PostInterface $model)
{
$this->model = $model;
}
}
Laravel IoC容器将使用DbPost实例自动实例化此存储库。而且,如果您需要将其更改为Redis,则只需更改一行:
App::bind('PostInterface', 'RedisPost');
最愚蠢的敬畏者。
视图仅负责显示信息。视图不应该知道您的模型,服务,存储库或系统中的任何其他内容。视图应该可由webesigners编辑,因此,您在视图上拥有的代码越多,您的非php-programmer-designer所添加的错误也就越多。您的控制器应从存储库中收集信息,并将其传递给您的视图:
<?php
class PostController extends Controller {
private $repository;
public function __construct(PostRepositoryInterface $repository)
{
$this->repository = $repository;
}
public function index()
{
return View::make('posts.index')->with('posts', $this->repository->getPaginated());
}
}
您视图的唯一责任应该是显示以下数据:
@extends('layout')
@section('contents')
<ul>
@foreach($posts as $post)
<li>
{{ $post->title }} - {{ $post->author }} - {{ $post->published_at }}
</li>
@endforeach
</ul>
{{ $users->links() }}
@stop
您如何格式化数据?您可以在视图中编写原始属性,但是在幕后,应该使用演示者来演示数据。演示者通常使用装饰器设计模式来格式化要在页面中显示的数据。这是使用Shawn McCool的LaravelAutoPresenter的示例:
<?php namespace App\Presenters;
use McCool\LaravelAutoPresenter\BasePresenter;
class Post extends BasePresenter {
public function __construct(UserModel $user)
{
$this->resource = $user;
}
public function author()
{
return $this->resource->author->name;
}
public function published_at()
{
return $this->date($this->resource->created_at);
}
public function dateTime($date)
{
return \Carbon\Carbon::createFromFormat('d-m-Y', $date, 'Sao_Paulo/Brazil')
->toFormattedDateString();
}
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句