打造 Laravel 優美架構
談可維護性與彈性設計
Yish
mombuyish
@yishlai
yish
Laravel MVC 目前結構
View
渲染畫面、畫面最終輸出的結果
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row">
@foreach($posts as $post)
<div>
<img src="{{ $post->name }}">
<div class="caption">
<h3>{{ $post->title }}</h3>
<p>{{$post->description }}</p>
</div>
</div>
@endforeach
</div>
<div>
{!! $posts->links() !!}
</div>
</div>
@stop
Example
Response
回傳指定格式資料
[
{
id: 1,
user_id: 2,
title: "Hayley Satterfield",
description: "Voluptatem ab est enim id.",
status: 1,
created_at: "2017-05-13 10:16:00",
updated_at: "2017-05-13 10:16:00"
},
{
id: 2,
user_id: 3,
title: "Rusty Batz",
description: "Dolore et non eos molestiae voluptates sit.",
status: 1,
created_at: "2017-05-13 10:16:00",
updated_at: "2017-05-13 10:16:00"
},
....
]
Example
Controller
放置接口呼叫方法,並 response 結果
<?php
//PostController
public function store(Request $request)
{
$post = $this->postService->create($request->all());
return redirect()->to(route('posts.show', $post->id));
}
Example
Model
僅用於放置 Relations, Scope 和 Eloquent 類的方法
<?php
//Post
namespace App\Entities;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'subject', 'description', 'status'
];
protected $table = 'posts';
public function comments()
{
return $this->hasMany(Comment::class);
}
}
Example
Laravel 的架構很好
but
多人協作時每個人定義不同
像是有人會...
在 route 寫 query and response
<?php
//web.php
Route::get('/', function (Post $post) {
return view('posts.index', compact('post'));
});
//api.php
Route::get('/', function(Post $post) {
return response()->json($post->all());
});
在 controller 寫驗證、商業邏輯、查詢與資料庫操作
ABC
他們總有一個共同的理由
反正都可以動
BUT
超難維護
你要先了解做的那個人思考過程
還有他的想法
就是過去的你
加上功能要做什麼
這跟觀落陰沒有兩樣
所以
如果一個專案很多人維護
* 也許是未來的你(通常就是)
就必須要有規範
逐步改善,加入規範
改善方法
情境
有個原本的代碼在 controller,長這個樣子
把查詢邏輯抽離到獨立的 class: Repository
UserRepository
然後再將她 DI 到 Controller
只是將查詢邏輯放到 Repository 就讓修改需求更容易
假設要修改查詢邏輯
改 Repository 即可
Controller
Model
View
Response
HTTP REQUEST
middleware
Foundation
Repository
Roadmap
Repository
與 Entities 和 DB 做查詢邏輯與資源庫
現實總沒那麼單純
文章建立
* 加入標籤
把商業邏輯放在 Service
把 Repository DI 到 Service
再將她 DI 到 Service
發信通知訂閱者
不會影響查詢邏輯(REPOSITORY)和回傳結果
Controller
Model
View
Response
HTTP REQUEST
middleware
Foundation
Repository
Roadmap
Service
Service
放置商業邏輯與流程
回傳格式轉換
得到資料
性別要在前台顯示中文
但我不能影響到查詢和商業邏輯,僅能影響外觀顯示
你也許會想到
Accessors/Mutators
https://laravel.com/docs/5.4/eloquent-mutators#accessors-and-mutators
很方便,但渲染外觀這件事給 Model?
是要讓 Model 腫起來嗎
他負責的事情已經很多了
可以利用 Service injection
https://laravel.com/docs/5.4/blade#service-injection
Controller
Model
View
Response
HTTP REQUEST
middleware
Foundation
Repository
Roadmap
Service
Presenter
Presenter
轉換渲染顯示
前後端分離!
假如我們有個街口是查詢 user
等等!我只要名字跟性別就好了
這時候有人會想,我改 repository 的 get 欄位啊
可是瑞凡,我後台要看到資料啊
在 Reponse 之前必須作轉換處理
//UserTransformer
//UserController
需求突然要加入user狀態
No problem
//UserTransformer
Controller
Model
View
Response
HTTP REQUEST
middleware
Foundation
Repository
Roadmap
Service
Presenter
Transformer
Transformer
轉換資料
為時麼專案 API 回傳格式都不統一啊?
A
B
我也想,但每次想法都不一樣(?
加入 Formatter,讓每次資料 response 前經過這層處理制定格式
A
B
統一格式處理
Controller
Model
View
Response
HTTP REQUEST
middleware
Foundation
Repository
Roadmap
Service
Presenter
Formatter
Transformer
Formatter
制定回傳格式
文章要有 Emoji 的功能
獨立功能
Post.php
Comment 也要加
Controller
Model
View
Response
HTTP REQUEST
middleware
validation
Foundation
Repository
Roadmap
Service
Presenter
Formatter
Transformer
Foundation
Foundation
獨立運行方法/額外掛載功能
Q&A
Q1
我在每個專案都要這樣用嗎?
A1
依照情境和專案複雜度。
舉例來說如果今天 CURD 相當單純沒有邏輯判斷(或簡單邏輯判斷)直接用上 Repository or Eloquent 也沒有問題。
Q2
這些做法是怎麼產生的?
A2
需求,在寫測試時因為職責分配不清導致難以寫 test case,表示不單一職責,一個類負責太多事。
Q3
A3
總結
單一職責
每個累就只會有一種改變的理由
共識
和夥伴們一起取得共識
One more thing
https://github.com/Mombuyish/Laravel-Oh-Generators
$ php artisan make:service UserService
<?php
class PostsController extends Controller
{
public function show(Post $post, PostFormatter $formatter, PostTransformer $transformer)
{
return response()->json($formatter->format($transform->transform($post)));
}
}
// or using help methods.
public function show(Post $post)
{
return response()->json(
format(request(), PostFormatter::class,
transform(PostTransformer::class, $post)));
}
Result
Thank you for listening.
打造 Laravel 優美架構 - 談可維護性與彈性設計
By Yi-hsuan Lai
打造 Laravel 優美架構 - 談可維護性與彈性設計
- 1,036