【Laravel】繰り返し処理は@includeを使う。PHPはビューではなくコントローラに記述する（リファクタリング）

リファクタリングの個人メモです。

- ビューの中に繰り返し処理を記述している場合は、処理を別ファイルに切り出し、`@include`で呼び出す

- PHPの処理はビューの中に書かず、コントローラで渡す（他でも使う処理の場合は自作のヘルパー関数を作って渡す）



##リファクタリング事例

###リファクタリング前のコード
以下のように同じコードが繰り返されている処理をリファクタリングする。

```php:phones.balde.php
  <div class="p-service__list">
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
      <section>
          <h2 class="c-page-headline">xperia</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
  </div>
</div>
```

コードを見ると`section`が繰り返されているのがわかる。

```php:繰り返しのコード
<section>
          <h2 class="c-page-headline">iphone</h2>
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
              <section class="p-service-section">
                  <div>
                  <span class="p-service-section__img-border">
                      <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
                  </span>
                  </div>
                  <div>
                      <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                      <p class="p-service-section__lead">{{ $service['lead'] }}</p>
                  </div>
              </section>
              @endif
          @endforeach
      </section>
```

` @foreach(__('service') as $service)`
言語ファイル（ resources > lang > ja ）のservice.phpに記述された配列を、`$service`として一つづつ抜き出している。


`@if( $service['category'] === 'iphone' )`
抜き出した配列のキー名`category`の値が`iphone`なら以下の処理を実行する。

このように、`category`の値によって表示するタイトルとデータを変えている処理。


<br>
##リファクタリング
###1. 繰り返しのコードを別ファイルに移動する。
繰り返されているコードを別ファイルに記述する。ここでは、section.blade.phpを作成。

呼び出しは`@include`を使う。

```php:section.blade.php
<section>
    <h2 class="c-page-headline">{{ $categoryTitle }}</h2> 
    @foreach($services as $service)
        <section class="p-service-section">
            <div>
            <span class="p-service-section__img-border">
                <img class="p-service-section__img" src="{{ asset('/img/service/'. $service['logo_file_name']) }}" alt="{{ $service['name'] }}">
            </span>
            </div>
            <div>
                <h3 class="p-service-section__name">{{ $service['name'] }}</h3>
                <p class="p-service-section__lead">{{ $service['lead'] }}</p>
            </div>
        </section>
    @endforeach
</section>
```

[1] **`<h2 class="c-page-headline">{{ $categoryTitle }}</h2>`**

h2タグのデータは変数`$categoryTitle`とし、`@include`した時に渡すようにする。


<br>
[2] **`@foreach($services as $service)`**

```php:以前の処理
@foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
```

foreachでデータを一つづつ取得し、指定のカテゴリ名に一致する場合のみ実行（フィルタリング）していた処理を変更し、フィルタリング後のデータを`$services`として渡すようにする。


<br>
###2. PHPの処理をコントローラに移動する

```php:コントローラ（リファクタリング前）
public function index(Request $request)
    {
        return view('contents.service.index');
    }
```

元の状態はビューファイルを返しているだけ。

<br>

```php:コントローラ（リファクタリング後）
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }
```

リファクタリング後はビューの中の`foreach`処理をコントローラ内で実行し、ビューに渡している。


**▼foreachの処理**

```php:
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
```

**・`if( isset($service['category']))`**
変数$serviceのキーcategoryに値があるか確認。

**・`$services[$service['category']][] = $service;`**
変数`$services`を新たに用意。

キー名がカテゴリー名となる連想配列にforeachで抜き出した値（`$service`）を追加していく。


<br>
**▼注意点**
配列に要素を追加する場合は`array[] = '追加する値'`とする。

`array[キー名]`のようにキー名を指定して代入すると上書きになってしまう。


<br>
###3. ビューの修正（`@include`で読み込む）
切り出したビューを読み込む。

・`@include('ビュー', ['変数名'=>'渡すデータ'])`
  ┗ ビューはresources > views　配下のディレクトリパスで指定。
　┗ ディレクトリはドットでつなぐ。
　┗ 第２変数でビューにデータを渡す。


```php:
        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>
``` 

あんなに長かったビューが驚くほどシンプルになった。。
繰り返し処理があったら`@include`が便利。



<br>
##（補足）PHP処理のリファクタリングの考え方

###リファクタリングLevel 0
リファクタリングなし

```php:.blade.php
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'iphone' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'android' )
                //処理
              @endif
          @endforeach
          @foreach(__('service') as $service)
              @if( isset($service['category']) && $service['category'] === 'xperia' )
                //処理
              @endif
          @endforeach
```

　↓　処理を外部ファイルに移動し`@import`で読み込む

PHP処理をビューファイルの情報に

###リファクタリングLevel 1

```php:.blade.php
        @php
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }
        @endphp

        <div class="p-service__list">
            @include('contents.service.section', ['categoryTitle'=>"iphone", 'services'=>$iphone])

            @include('contents.service.section', ['categoryTitle'=>"android", 'services'=>$android])

            @include('contents.service.section', ['categoryTitle'=>"xperia", 'services'=>$xperia])
        </div>
```

　↓ PHP処理をコントローラに移動

###リファクタリングLevel 2

```php:コントローラ
public function index(Request $request)
    {
           foreach(__('service') as $service){
              if( isset($service['category']) && $service['category'] === 'iphone' ){
                 $iphone[] = $service;
              } 
              elseif( isset($service['category']) && $service['category'] === 'android' ){
                 $android[] = $service;
              }
              elseif( isset($service['category']) && $service['category'] === 'xperia' )
                 $xperia[] = $service;
              }
           }

        return view('contents.service.index', compact('iphone', 'android', 'xperia'));
    }
```

ビューのPHP処理をそのままコントローラに移動。（★基本的に、PHP処理はビューではなくコントローラで行う）


・`compact('変数名', '変数名', '変数名',,,,,)`
compactメソッドは指定した変数を連想配列にしてくれる。
キー名は変数名、値は変数に入っている値が自動的に適用される。

>※compactの注意点
変数は`$変数名`ではなく、`'変数名'`で渡す。


　↓　ビューに連想配列を渡す（compactメソッドを使わない）


###リファクタリングLevel 3

```php:コントローラ
public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category']) && $service['category'] === 'iphone' ){
                $services["iphone"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'android' ){
                $services["android"][] = $service;
            }
            else if( isset($service['category']) && $service['category'] === 'xperia' ){
                $services["xperia"][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }
```

・`$services["キー名"][] = 値`
変数`$services`を新たに定義し、指定したキーに値を追加していく。

　↓ カテゴリ名の分岐にifを使わない

###リファクタリングLevel 4

```php:コントローラ
    public function index(Request $request)
    {
        foreach(__('service') as $service){
            if( isset($service['category'])){
                $services[$service['category']][] = $service;
            }
        }

        return view('contents.service.index', $services);
    }
```

・`if( isset($service['category']))`
ifを使うのは対象の値の存在確認のみ。

・`$services[$service['category']][]`
キー名はテキストでベタ打ちせず、`$service['category'`で動的に指定する。