<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 如何使用Presenter模式 若將顯示邏輯都寫在 View,會造成 View 代碼臃腫而難以維護,基于 SOLID 原則,應該使用 Presenter 模式輔助 View,將相關的顯示邏輯封裝在不同的 Presenter ,方便中大型項目的維護。 ### 版本 Lararvel 5.4.17 ## 顯示邏輯 在實際開發中,顯示邏輯常見的如下: * 將資料顯示不同資料: 如 `性別字段為 M,就顯示 Mr.,若性別字段為 F,就顯示 Mrs.` * 是否顯示某些資料:如 `根據字段值是否等于 T,要不要顯示改字段` * 依需求顯示不同格式:如 `依不同的語系,顯示不同的日期格式` ## Presenter ### 將資料顯示不同資料 如 `性別字段為 M,就顯示 Mr.,若性別字段為 F,就顯示 Mrs.`,我們可能會直接用 blade 寫在 view 里,如下: ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @foreach($users as $user) <div> <h2>@if($user->gender == 'm'){{ "Mr." }} @else {{ "Mrs." }} @endif {{$user->name}}</h2> <h2>{{ $user->email }}</h2> </div> @endforeach </div> </body> </html> ``` 在中大型項目中,會有幾個問題: * 由于 Blade 與 Html 夾雜,不太適合寫太復雜的代碼,只適合做一些簡單的 binding ,否則很容易寫成傳統的 PHP 的意大利面代碼 * 無法對顯示邏輯做重構與物件導向 比較好的方式是使用 Presenter,具體步驟如下: * 將相依無間注入到 Presenter * 在 presenter 內寫格式轉換 * 將 Presenter 注入到 View #### 定義UserPresenter `app\Presenters\UserPersenter.php` 代碼如下: ``` <?php namespace App\Presenters; /** * Class UserPresenter * * @package App\Presenters */ class UserPresenter { /** * @param string $gender * @param string $name * * @return string */ public function getFullName($gender, $name) { return $gender == 'M' ? 'Mr. ' . $name : 'Mrs. ' . $name; } } ``` 將原本在 blade 中用 `@if(){ .. }@else .. @endif` 寫的邏輯改寫在 Presenter 中。 #### 視圖中使用UserPresenter 使用 `@inject()` 注入 `UserPresenter`,讓 View 可以如 Controller 一樣使用注入的物件。 將來如亂顯示邏輯怎么修改,都不用改到 Blade ,直接在相關 Presenter 中修改即可。 ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @inject('userPresenter','App\Presenters\UserPresenter') @foreach($users as $user) <div> {{--<h2>@if($user->gender == 'm'){{ "Mr." }} @else {{ "Mrs." }} @endif {{$user->name}}</h2>--}} <h2>{{ $userPresenter->getFullName($user->gender,$user->name) }}</h2> <h2>{{ $user->email }}</h2> </div> @endforeach </div> </body> </html> ``` 改用這種重寫,有幾個優點: * 將資料顯示不同個格式的顯示邏輯改寫在 presenter,解決了 blade 不容易維護的問題 * 可以顯示邏輯做重構于物件導向 ## 是否顯示某些資料 如 `根據字段值是否為 T ,要不要顯示該字段`,我們常常會直接用 blade 寫在 View 中。 ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @foreach($users as $user) <h2>{{ $user->name }}</h2> @if($user->is_hidden == 'F') <h2>{{ $user->email }}</h2> @endif @endforeach </div> </body> </html> ``` 在中大型項目中,會有幾個問題: * 由于 blade 與 HTML 夾雜,不太適合寫太復雜的業務代碼,只適合做一些簡單的 binding,否則很容易寫成傳統的 PHP 的意大利面代碼 * 無法對顯示邏輯做重構與物件導向 比較好的方式是使用 Presenter,具體步驟如下: * 將相依無間注入到 Presenter * 在 presenter 內寫格式轉換 * 將 Presenter 注入到 View `app\Presenters\UserPresenter.php` 代碼: ``` <?php namespace App\Presenters; use App\User; /** * Class UserPresenter * * @package App\Presenters */ class UserPresenter { /** * @param \App\User $user * * @return string */ public function showEmail(User $user) { if ($user->is_hidden == 'F') { return '<h2>' . $user->email '</h2>'; } return ''; } } ``` 將 `@if() .. @endif` 的 boolean 判斷封裝在 Presenter 內,改由 Presenter 負責輸出 HTML。 ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @inject('userPresenter','App\Presenters\UserPresenter') @foreach($users as $user) <h2>{{ $user->name }}</h2> {!! $userPresenter->showEmail($user) !!} @endforeach </div> </body> </html> ``` 使用 `@inject()` 注入 `UserPresenter`,讓 View 也可以如 Controller 一樣使用注入的物件。 `{!! !!}` 會保留原來的 HTML 格式。 將來無論顯示邏輯怎么修改,都不用改到 Blade ,直接在 Presenter 內修改。 改用這種寫法,有幾個優點: * `是否顯示某些資料` 的顯示邏輯改為在 Presenter,解決寫在 Blade 不容易維護的問題 * 可對顯示邏輯做重構與物件導向 ### 依需求顯示不同格式 如 `按照不同的語系,顯示不同的日期格式`,我們常常會直接用 Blade 寫在 View 里。 如下: ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @foreach($users as $user) <div> @if(App::getLocale() == 'uk') <h2>{{ $user->created_at->format('d M, Y') }}</h2> @elseif(App::getLocale() == 'tw') <h2>{{ $user->creaetd_at->format('Y/m/d') }}</h2> @else <h2>{{ $user->created_at->formate('M d, Y') }}</h2> @endif </div> @endforeach </div> </body> </html> ``` 在中大型的醒目中,會有幾個問題: * 由于 Blade 與 HTML 夾雜,不太適合寫太復雜的代碼,只適合做一些簡單的 binding,否則很容易寫成傳統的 PHP 的意大利面代碼 * 無法對顯示邏輯做重構與物件導向 比較好的方式是使用 Presenter,具體步驟如下: * 將相依無間注入到 Presenter * 在 presenter 內寫不同的日期格式轉換邏輯 * 將 Presenter 注入到 View #### 定義接口 定義接口代碼 `app\Presenters\DataFormatPresenterInterface.php` ,具體代碼如下: ``` <?php namespace App\Presenters; use Carbon\Carbon; /** * Interface DateFormatPresenterInterface * * @package App\Presenters */ interface DateFormatPresenterInterface { /** * 顯示日期格式 * * @param \Carbon\Carbon $data * * @return string */ public function showDateFormat(Carbon $data); } ``` 定義了 `showDateFormat()`,各語言必須在 `showDateFormat()` 使用 Carbon 的 `format()` 去轉換日期格式。 #### 一些Presenter `app\Presenters\DateFormatPresenterTW.php`,具體代碼內容如下: ``` <?php namespace App\Presenters; use Carbon\Carbon; /** * Class DateFormatPresenterTw * * @package \App\Presenters */ class DateFormatPresenterTw implements DateFormatPresenterInterface { /** * @param \Carbon\Carbon $date * * @return string */ public function showDateFormat(Carbon $date) { return $date->format('Y/m/d'); } } ``` `app\Presenters\DateFormatPresenterUk.php`,具體代碼內容如下: ``` <?php namespace App\Presenters; use Carbon\Carbon; /** * Class DateFormatPresenterUk * * @package \App\Presenters */ class DateFormatPresenterUk implements DateFormatPresenterInterface { /** * @param \Carbon\Carbon $data * * @return string */ public function showDateFormat(Carbon $data) { return $data->format('d M, Y'); } } ``` `app\Presenters\DateFormatPresenterUs.php`,具體代碼內容如下: ``` <?php namespace App\Presenters; use Carbon\Carbon; /** * Class DateFormatPresenterUs * * @package \App\Presenters */ class DateFormatPresenterUs implements DateFormatPresenterInterface { /** * @param \Carbon\Carbon $date * * @return string */ public function showDateFormat(Carbon $date) { return $date->format('M d,Y'); } } ``` 以上類都實現了 `DateFormatPresenterInterface` 接口,并將轉換成相對應國家日期格式的 Carbon 的 `format()` 寫在 `showDateFormat()` 內。 #### 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** `app/Presenters/DateFormatPresenterFactory.php` 內容如下: ``` <?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)); } } ``` 使用 **Presenter Factory** 的 `create()` 去取代 new 建立物件。 這里當然可以在 `create()` 里去寫 `if () { ... } else { ... }` 去建立 Presenter 物件,不過這樣會違反 SOLID 的開放封閉原則,比較好的方式是改用 `App::bind()`,直接根據 `$locale` 去 binding 相對應的 Class,這樣無論再怎么新增語言與日期格式, Controller 與 Presenter Factory 都不用做任何修改,完全符合開放封閉原則。 ##### 控制器調用 `app\Http\Controllers\UserController.php` 中的內容,如下: ``` public function index(Request $request, DateFormatPresenterFactory $dataFormatPresenterFactory) { $locate = 'uk'; $presenter = $dataFormatPresenterFactory::bind($locate); dd($presenter->showDateFormat(Carbon::now())); return $presenter; } ``` 使用 `$dataFormatPresenterFactory::bind()` 切換 `app()` 的 Presenter 物件,如此 Controller 將開放封閉,將來有新的語言新增或者修改需求,也不用修改 Controller ##### Blade 調用 ``` <!DOCTYPE html> <html lang="{{ config('app.locale') }}"> <head> <title>Users</title> </head> <body> <div class="flex-center position-ref full-height"> @inject('dateFormatPresenter','App\Presenters\DateFormatPresenterInterface') @foreach($users as $user) <div> <h2><?php print_r($dateFormatPresenter->showDateFormat($user->created_at)); ?></h2> </div> @endforeach </div> </body> </html> ``` 使用 `@inject()` 注入 Presenter ,讓 View 也可以如 Controller 一樣使用注入的物件 使用 Presenter 的 `showDateFormate()` 將日期轉換成預計的格式 使用這種寫法有幾個優點 * 將 `依需求顯示不同的格式` 的顯示邏輯寫在 Presenter ,解決寫在 Blade 不容易維護的問題 * 可對顯示邏輯做重構與物件導向 * 符合 SOLID 的開放閉合原則:將來若有新的語言,對于拓展是開放的,只要新增 Class 實現 `DateFormatPresenterInterface` 接口即可;對于修改是封閉的, Controller、FactoryInterface、Factory 與 View 都不用做任何修改 * 不單只有 PHP 可以使用 Service Container,連 Blade 也可以使用 Service Container,甚至搭配 Service Provider * 可單獨對 Presenter 的顯示邏輯做單元測試 * 若使用了 Presenter 輔助 Blade ,在搭配 `@inject()` 注入到 View,View就會非常干凈,可專心處理 `將資料binding到HTML`的職責 * 將來只有 Layout 改變才會動到 Balde ,若是顯示邏輯改變都是修改 Presenter ### 最后 Presenter 使得顯示邏輯從Blade 中解放,不僅更容易維護、更容易擴展、更容易重復使用且更容易測試
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看