# laravel (Presenter)(presentation)邏輯 https://blog.turn.tw/?p=2511 https://www.kancloud.cn/curder/laravel/408486 ## 底層用這個 https://github.com/robclancy/presenter/blob/master/src/Presenter.php ## 使用原因 跟model有關的資料組裝都算在persenter controller只是做一些拼裝的(除了業務邏輯之外的都要做) 若将显示逻辑都写在 View,会造成 View 代码臃肿而难以维护,基于 SOLID 原则,应该使用 Presenter 模式辅助 View,将相关的显示逻辑封装在不同的 Presenter ,方便中大型项目的维护。 **显示逻辑** 在实际开发中,显示逻辑常见的如下: 将资料显示不同资料: 如 性别字段为 M,就显示 Mr.,若性别字段为 F,就显示 Mrs. 是否显示某些资料:如 根据字段值是否等于 T,要不要显示改字段 依需求显示不同格式:如 依不同的语系,显示不同的日期格式 **使用步驟** 1. 将相依无间注入到 Presenter 1. 在 presenter 内写格式转换 1. 将 Presenter 注入到 View ``` class Article extends Eloquent { public function getDate(){/*...*/} // 呈現給台灣地區的時間格式 public function getTaiwaneseDateTime(){/*...*/} // 呈現給歐美地區的時間格式 public function getWesternDateTime(){/*...*/} public function getTaiwaneseDate(){/*...*/} public function getWesternDate(){/*...*/} } ``` 這樣導致model過胖 許多框架採用了「helper function」的作法, 也就是定義好幾個functions用來處理呈現邏輯。 除了helpers的作法之外,還有一個非常簡單有效的方法。 也就是使用Decorator pattern來封裝出presenter ex ``` class ArticlePresenter { protected $article; public function __construct(Article $article) { $this->article = $article; } // 呈現給台灣地區的時間格式 public function getTaiwaneseDateTime(){ return date('Y-m-d', $this->article->created_at); } // 呈現給歐美地區的時間格式 public function getWesternDateTime(){/*...*/} public function getTaiwaneseDate(){/*...*/} public function getWesternDate(){/*...*/} } ``` 概念上只是把原本的model從constructor傳進去,原本的function搬過去, 把$this改寫成$this->article即可。 **But** 這樣會導致view裡面需要初始化物件: ``` @foreach($articles as $article) <?php $presenter = new ArticlePresenter($article); ?> 發文日期:{{ $presenter->getTaiwaneseDate() }} @endforeach ``` **在view裡面做這種事,怎麼看都是很醜的。** so 多寫一個function即可改善 ``` { public function present() { return new ArticlePresenter($this); } } ``` **but** 新的問題來了:多次呼叫present會浪費記憶體 改善 ``` { protected $presenterInstance; public function present() { if ( ! $this->presenterInstance) { $this->presenterInstance = new ArticlePresenter($this); } return $this->presenterInstance; } } ``` **but** 多個model都使用presenter的話,這段code會重複。善用PHP trait可以解決這個問題: ``` trait PresentableTrait { protected $presenterInstance; public function present() { // ... if ( ! $this->presenterInstance) { $this->presenterInstance = new $this->presenter($this); } return $this->presenterInstance; } } ``` Laravel社群至少有3套成熟的presenter實作: https://github.com/laracasts/Presenter https://github.com/robclancy/presenter https://github.com/laravel-auto-presenter/laravel-auto-presenter 有的功能豐富、有的比較陽春,但是概念上一樣,都是decorator pattern的應用。 ## 實際用 https://www.kancloud.cn/curder/laravel/408486 看完就懂 主要用在 将资料显示不同资料 @if() .. @endif(判斷是不是男女 男女顯示不一樣) 是否显示某些资料 @if() .. @endif(男生才顯示)(這邊記得使用要{!! 跳脫字元出來 !!}) 依需求显示不同格式 `<h2>hello world!</h2>` 主要都使用 @inject() 這樣去引入class 然後用方法 **最好都定義街口**(ex format('d M, Y') format('Y/m/d') formate('M d, Y') ) 不懂看連結 對了 **Presenter 工厂** 由于每个语言的日期格式都是一个 presenter 物件,那势必遇到一个最基本的问题: 我们必须根据不同的语言去实例化不同的 Presenter 物件,我们可能会在 Controller 中去 实例化。如下: ``` /** * @param \Illuminate\Http\Request $request * * @return int */ public function index(Request $request) { $locate = 'hk'; switch ($locate){ case 'uk': $presenter = new DateFormatPresenterUk(); break; case 'tw': $presenter = new DateFormatPresenterTw(); break; default: $presenter = new DateFormatPresenterUs(); } return $presenter; } ``` 这种写法虽然可行,但是有如下问题: * 违反了 SOLID 的开放封闭原则:若将来有新的语言需求,只能不断去修改 index() ,然后不断的新增 elseif() ,计算改用 switch{ .. } 也是一样 * 违反了 SOLID 的依赖反转原则:Controller 直接根据语言去实例化对应的 Class ,高层直接相依于底层,直接将实例化对象写死在代码里 * 无法单元测试:由于 Presenter 直接 New 在 Controller ,因此要测试时,无法对 Presenter 做 mock **解决方式是使用 Factory Pattern** ``` <?php namespace App\Presenters; /** * Class DateFormatPresenterFactory * * @package \App\Presenters */ class DateFormatPresenterFactory { /** * @param $locale * * @return \Illuminate\Foundation\Application|mixed */ public static function bind($locale) { return app()->singleton(DateFormatPresenterInterface::class, 'App\Presenters\DateFormatPresenter' . ucwords($locale)); } } ``` ## 公司用法 用Traits 比較乾淨 在model上面use use Presentable 下面設置變數 ``` /** * The attribute that point the presenter. * * @var string */ protected $presenter = FaqUrlPath::class; ``` trait裡面用 ``` public function present() { return true === is_null($this->present) ? $this->present = new $this->presenter($this) : $this->present; } ``` 這樣以後就只要呼叫這個function就好 ###### tags: `Laravel`
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up