Sab*_*ett 115 php design-patterns laravel laravel-4
假设每当我进行CRUD操作或以特定方式修改关系时,我也想做其他事情.例如,每当有人发布帖子时,我也想将某些内容保存到表中进行分析.也许不是最好的例子,但总的来说有很多这种"分组"的功能.
通常我会将这种逻辑放入控制器中.在你想要在很多地方重现这个功能之前,这一切都很好.当你开始进入局部,创建一个API并生成虚拟内容时,就会出现干涸问题.
我看到管理它的方法是事件,存储库,库和添加到模型.以下是我对每个人的理解:
服务:这是大多数人可能会放置此代码的地方.我对服务的主要问题是,有时很难在其中找到特定功能,我觉得他们会忘记人们何时专注于使用Eloquent.我怎么知道我需要publishPost()
在库中调用方法$post->is_published = 1
?
我看到这个工作得很好的唯一条件是,如果你只使用服务(理想情况下,Eloquent不能以某种方式从控制器一起无法访问).
最终,如果您的请求通常遵循您的模型结构,这似乎只会创建一堆额外的不必要的文件.
存储库:从我的理解,这基本上就像一个服务,但有一个接口,所以你可以在ORM之间切换,我不需要.
事件:从某种意义上说,我认为这是最优雅的系统,因为你知道你的模型事件总是会在Eloquent方法中被调用,所以你可以像往常一样编写你的控制器.我可以看到这些变得混乱,如果有人有大型项目的例子使用事件进行关键耦合,我希望看到它.
模型:传统上我会有类执行CRUD并处理关键耦合的类.这实际上让事情变得简单,因为你知道CRUD的所有功能+无论如何都要做到这一点.
很简单,但在MVC架构中,这通常不是我所看到的.从某种意义上说,虽然我更喜欢这种服务,因为它更容易找到,而且需要跟踪的文件较少.但它可能会有点混乱.我想听听这种方法的失败,以及为什么大多数人似乎都没有这样做.
每种方法有哪些优点/缺点?我错过了什么吗?
Luí*_*ruz 147
我认为只要遵循SOLID原则,您提供的所有模式/体系结构都非常有用.
对于添加逻辑的位置,我认为参考单一责任原则很重要.另外,我的回答是你正在研究中/大型项目.如果它是一个扔在页面上的东西,请忘记这个答案并将其全部添加到控制器或模型中.
简短的回答是:对你有意义(有服务).
答案很长:
控制器:控制器的责任是什么?当然,您可以将所有逻辑放在控制器中,但控制器是否有责任?我不这么认为.
对我来说,控制器必须接收请求并返回数据,这不是放置验证,调用db方法等的地方.
模型:这是一个添加逻辑的好地方,例如当用户注册或更新帖子的投票数时发送欢迎电子邮件?如果您需要从代码中的其他位置发送相同的电子邮件,该怎么办?你创建一个静态方法吗?如果该电子邮件需要来自其他模型的信息怎么办?
我认为该模型应该代表一个实体.随着Laravel,我只用模型类添加之类的东西fillable
,guarded
,table
和关系(这是因为我使用Repository模式,否则模型本来也有save
,update
,find
,等方法).
存储库(存储库模式):一开始我对此非常困惑.并且,和你一样,我认为"好吧,我使用MySQL就是那样."
但是,我已经平衡了使用存储库模式的优点和缺点,现在我使用它.我想现在,就在这个时刻,我只需要使用MySQL.但是,如果从现在起三年后我需要改变像MongoDB这样的东西,大部分工作都已完成.所有这一切都以一个额外的接口和一个$app->bind(«interface», «repository»)
.
事件(观察者模式):事件对任何给定时间可以在任何类中抛出的事物都很有用.例如,考虑向用户发送通知.在您需要时,您可以触发事件以在您的应用程序的任何类别发送通知.然后,您可以拥有一个类UserNotificationEvents
来处理用户通知的所有已触发事件.
服务:到目前为止,您可以选择向控制器或模型添加逻辑.对我来说,在服务中添加逻辑是有意义的.让我们面对现实吧,服务是课程的一个奇特名称.你可以在你的应用程序中拥有尽可能多的课程.
举个例子:不久之前,我开发了类似Google Forms的东西.我开始与一个CustomFormService
并结束了CustomFormService
,CustomFormRender
,CustomFieldService
,CustomFieldRender
,CustomAnswerService
和CustomAnswerRender
.为什么?因为它对我有意义.如果您与团队合作,您应该将逻辑放在对团队有意义的地方.
使用服务与控制器/模型的优点是您不受单个控制器或单个模型的约束.您可以根据应用程序的设计和需求根据需要创建任意数量的服务.除此之外,还可以在应用程序的任何类中调用服务.
这很长,但我想告诉你我如何构建我的应用程序:
app/
controllers/
MyCompany/
Composers/
Exceptions/
Models/
Observers/
Sanitizers/
ServiceProviders/
Services/
Validators/
views
(...)
Run Code Online (Sandbox Code Playgroud)
我将每个文件夹用于特定功能.例如,该Validators
目录包含一个BaseValidator
类负责处理所述验证的基础上,$rules
和$messages
特定验证器(通常为每个模型).我可以很容易地将这些代码放在一个服务中,但即使只在服务中使用(现在),我也有一个特定的文件夹.
我建议你阅读以下文章,因为他们可能会向你解释一些事情:
Dayle Rees(CodeBright的作者)打破模具:这就是我把它放在一起的地方,即使我改变了一些东西以满足我的需要.
使用 Chris Goosey使用存储库和服务在Laravel中解耦代码:这篇文章很好地解释了什么是服务和存储库模式以及它们如何组合在一起.
Laracasts还有简单存储库和单一责任存储库,它们是实用示例的良好资源(即使您必须付费).
Sab*_*ett 20
我想对自己的问题发表回复.我可以谈论这几天,但我会尽快发布,以确保我得到它.
我最终利用了Laravel提供的现有结构,这意味着我将文件保存为模型,视图和控制器.我还有一个Libraries文件夹,用于可重用的组件,而不是真正的模型.
我没有在服务/图书馆中包装我的模型.所提供的所有理由并非100%让我相信使用服务的好处.虽然我可能错了,但据我所知,它们只会产生大量额外的空文件,我需要创建并在使用模型时切换,并且还真正降低了使用雄辩的好处(特别是当涉及到RETRIEVING模型时)例如,使用分页,范围等).
我把业务逻辑放在模型中并直接从我的控制器访问口才.我使用了许多方法来确保不会绕过业务逻辑:
解决使用模型时人们的顾虑:
附加说明:我觉得在服务中包装你的模型就像拥有瑞士军刀,有很多工具,并在它周围建造另一把刀基本上做同样的事情?是的,有时候你可能想要剥离刀片或确保两个刀片一起使用......但通常还有其他方法可以做到......
什么时候使用服务:这篇文章非常清楚地阐述了什么时候使用服务(提示:它不常见).他说,基本上当你的对象在其生命周期的奇怪部分使用多个模型或模型时,这是有道理的.http://www.justinweiss.com/articles/where-do-you-put-your-code/
Rub*_*zzo 17
我用来在控制器和模型之间创建逻辑的方法是创建一个服务层.基本上,这是我的应用程序中的任何操作的流程:
我是这样做的:
这是控制器创建一些东西的方法:
public function processCreateCongregation()
{
// Get input data.
$congregation = new Congregation;
$congregation->name = Input::get('name');
$congregation->address = Input::get('address');
$congregation->pm_day_of_week = Input::get('pm_day_of_week');
$pmHours = Input::get('pm_datetime_hours');
$pmMinutes = Input::get('pm_datetime_minutes');
$congregation->pm_datetime = Carbon::createFromTime($pmHours, $pmMinutes, 0);
// Delegates actual operation to service.
try
{
CongregationService::createCongregation($congregation);
$this->success(trans('messages.congregationCreated'));
return Redirect::route('congregations.list');
}
catch (ValidationException $e)
{
// Catch validation errors thrown by service operation.
return Redirect::route('congregations.create')
->withInput(Input::all())
->withErrors($e->getValidator());
}
catch (Exception $e)
{
// Catch any unexpected exception.
return $this->unexpected($e);
}
}
Run Code Online (Sandbox Code Playgroud)
这是执行与操作相关的逻辑的服务类:
public static function createCongregation(Congregation $congregation)
{
// Log the operation.
Log::info('Create congregation.', compact('congregation'));
// Validate data.
$validator = $congregation->getValidator();
if ($validator->fails())
{
throw new ValidationException($validator);
}
// Save to the database.
$congregation->created_by = Auth::user()->id;
$congregation->updated_by = Auth::user()->id;
$congregation->save();
}
Run Code Online (Sandbox Code Playgroud)
这是我的模特:
class Congregation extends Eloquent
{
protected $table = 'congregations';
public function getValidator()
{
$data = array(
'name' => $this->name,
'address' => $this->address,
'pm_day_of_week' => $this->pm_day_of_week,
'pm_datetime' => $this->pm_datetime,
);
$rules = array(
'name' => ['required', 'unique:congregations'],
'address' => ['required'],
'pm_day_of_week' => ['required', 'integer', 'between:0,6'],
'pm_datetime' => ['required', 'regex:/([01]?[0-9]|2[0-3]):[0-5]?[0-9]:[0-5][0-9]/'],
);
return Validator::make($data, $rules);
}
public function getDates()
{
return array_merge_recursive(parent::getDates(), array(
'pm_datetime',
'cbs_datetime',
));
}
}
Run Code Online (Sandbox Code Playgroud)
有关这种方式的更多信息,我用来组织Laravel应用程序的代码:https://github.com/rmariuzzo/Pitimi
在我看来,Laravel已经有很多选项供您存储业务逻辑.
简短回答:
Request
对象自动验证输入,然后将数据保留在请求中(创建模型).由于所有用户输入都在请求中直接可用,我相信在此处执行此操作是有意义的.Job
对象来执行需要单个组件的任务,然后只需调度它们.我想Job
包括服务类.他们执行任务,例如业务逻辑.长(呃)回答:
必要时使用
存储库:存储库必须过度浮动,并且大多数情况下,仅用作accessor
模型的存储库.我觉得他们肯定有一些用处,但除非你正在开发一个庞大的应用程序,需要你有足够的灵活性才能完全抛弃laravel,远离存储库.你以后会感谢自己,你的代码会更直接.
问问自己是否有可能改变PHP框架或 laravel不支持的数据库类型.
如果您的答案是"可能不是",那么请不要实现存储库模式.
除此之外,请不要像Eloquent那样在一个极好的ORM上打一个模式.你只是增加了不需要的复杂性,它根本不会让你受益.
谨慎使用服务: 对我来说,服务类只是存储业务逻辑以执行具有给定依赖关系的特定任务的地方.Laravel开箱即用,称为"Jobs",它们比自定义Service类具有更大的灵活性.
我觉得Laravel对MVC
逻辑问题有一个全面的解决方案.这只是一个问题或组织.
例:
要求:
namespace App\Http\Requests;
use App\Post;
use App\Jobs\PostNotifier;
use App\Events\PostWasCreated;
use App\Http\Requests\Request;
class PostRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required',
'description' => 'required'
];
}
/**
* Save the post.
*
* @param Post $post
*
* @return bool
*/
public function persist(Post $post)
{
if (!$post->exists) {
// If the post doesn't exist, we'll assign the
// post as created by the current user.
$post->user_id = auth()->id();
}
$post->title = $this->title;
$post->description = $this->description;
// Perform other tasks, maybe fire an event, dispatch a job.
if ($post->save()) {
// Maybe we'll fire an event here that we can catch somewhere else that
// needs to know when a post was created.
event(new PostWasCreated($post));
// Maybe we'll notify some users of the new post as well.
dispatch(new PostNotifier($post));
return true;
}
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
控制器:
namespace App\Http\Controllers;
use App\Post;
use App\Http\Requests\PostRequest;
class PostController extends Controller
{
/**
* Creates a new post.
*
* @return string
*/
public function store(PostRequest $request)
{
if ($request->persist(new Post())) {
flash()->success('Successfully created new post!');
} else {
flash()->error('There was an issue creating a post. Please try again.');
}
return redirect()->back();
}
/**
* Updates a post.
*
* @return string
*/
public function update(PostRequest $request, $id)
{
$post = Post::findOrFail($id);
if ($request->persist($post)) {
flash()->success('Successfully updated post!');
} else {
flash()->error('There was an issue updating this post. Please try again.');
}
return redirect()->back();
}
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,请求输入会自动验证,我们需要做的就是调用persist方法并传入新的Post.我认为可读性和可维护性应该总是胜过复杂和不必要的设计模式.
然后,您可以使用完全相同的持久方法来更新帖子,因为我们可以检查帖子是否已经存在并在需要时执行交替逻辑.