【PHP】Laravel bladeビューのリファクタリング。implode, nullチェック, 変数をタグとして出力

個人用メモです。
rowspanおよびcolspan属性があるth/tdタグの生成に関するコードをリファクタリング。各処理の意味と実行内容を理解する。

##目次

1. [リファクタリング前のコード](#リファクタリング前のコード)
2. [問題点](#問題点)
3. [リファクタリング完了後のコード](#リファクタリング完了後のコード)
4. [リファクタリングの処理内容](#リファクタリングの処理内容)
    5. [class属性に不要なスペースの除去](#1-class属性に不要なスペースの除去)
    6. [rowspanとcolspanの除去とダブルクオテーションの追加](#2-rowspanとcolspanの除去とダブルクオテーションの追加)
    7. [属性がないときの無駄なスペース除去](#3-属性がないときの無駄なスペース除去)
        8. [implodeメソッド](#implodeメソッド)
        9. [nullチェック（if文とissetメソッド）](#nullチェックif文とissetメソッド)
        10. [変数をタグとして表示する`{!! !!}`](#変数をタグとして表示する-)
    11. [`@switch`による冗長処理の簡略化](#4-switchによる冗長処理の簡略化)





<br>

##リファクタリング前のコード

```php:リファクタリング前のコード
@php
if($tdth->getRowspan() > 1){
    $x = $tdth->getRowspan();
    $rowspan = "rowspan = $x ";
  } else {
    $rowspan = "";
  }
  if($tdth->getColspan() > 1){
    $y = $tdth->getColspan();
    $colspan = "colspan = $y";
  } else {
    $colspan = "";
  }

  $rowColspan = $rowspan.$colspan;
@endphp

@switch($type)
  @case('th')
    <th class="{{ $classes }}" {{ $rowColspan }}>{{ $slot }}</th>	
    @break
  @case('td')	
    <td class="{{ $classes }}" {{ $rowColspan }}>{{ $slot }}</td>	
    @break
@endswitch 
```

###処理結果例（ブラウザの表示）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/931c866f-7e96-7aed-4688-30fcb21923cb.png)

結合状態のある表が作成できている。
ただしソースコードがBESTの状態になっていない。

<br>
###問題点

####1) class属性に不要なスペースの除去
`class="article-item td_th "`

```html:
<th class="article-item td_th " rowspan="2" colspan="2">
```

<br>
####2) ソースコードで確認するとrowspanとcolspanにスペースがある + ダブルクオテーションがない

`rowspan = 2`

```html:view-source-code
<td class="article-item td_th " rowspan = 2 colspan = 2>
``` 
chromeが補完して読み取っているが他のブラウザでは読み込めないリスクあり。

> 「ページのソースを表示」で確認するの重要。


<br>
####3) 属性がないときに無駄なスペースが入る
`<th class="{{ $classes }}" {{ $rowColspan }}>`の属性がない場合`<td >`となる。

属性がないときはシンプルに`<td>`になるようにする。
エラーはないが汚い。


<br>
####4) `@switch`によるタグの切り分けが不要。`$type`がそのまま使える。

現在の`@switch`を使った条件分岐のコードが冗長。以下コードを1行で書き直すことができる。

```php:冗長
@switch($type)
  @case('th')
    <th class="{{ $classes }}" {{ $rowColspan }}>{{ $slot }}</th>	
    @break
  @case('td')	
    <td class="{{ $classes }}" {{ $rowColspan }}>{{ $slot }}</td>	
    @break
@endswitch 
```



<br>
##リファクタリング完了後のコード

```php:
@php
  $attrs = [$type];

  if (isset($classes)) {
    $class_str = trim($classes);
    $attrs[] = "class=\"$class_str\"";
  }

  if (isset($tdth)) {
    if($tdth->getRowspan() > 1){
      $x = $tdth->getRowspan();
      $attrs[] = "rowspan=\"$x\"";
    }

    if($tdth->getColspan() > 1){
      $y = $tdth->getColspan();
      $attrs[] = "colspan=\"$y\"";
    }
  }

  $opening_tag = implode(" ", $attrs);
@endphp

<{!! $opening_tag !!}>{{ $slot }}</{{$type}}>
```




<br>
##リファクタリングの処理内容
###1) class属性に不要なスペースが入っている
`trim`メソッドを使うことで余計なスペースを削除することができる。

```php:
$class="article-item td_th ";
$class = trim($class);

#確認
var_export($class);
'article-item td_th'
```


<br>
###▼trimメソッドについて
trimは文字列の**先頭と末尾の空白**などを削除する。
**配列に対しては使えない**。要素を一つづつ抜き出しtrimを適用する。

`trim(文字列)`

削除対象は、空白、タブ、リターン、改行、null、垂直タブ。`\n\r\t\v\0`

つまり、デフォルトの指定は以下と同じ。

・`trim(文字列, "\n\r\t\v\0")`


####第2引数ありの場合
第2引数を削除対象の文字列を任意で指定することも可能。
２文字以上指定した場合はクセがあるので注意が必要。

例えば`ab`を指定すると、最初と最後の文字の`a`か`b`を削除し、削除後の文字列の最初と最後の`a`か`b`を削除する。

このように削除後に該当する１文字があればどんどん削られていく。

```php:abを指定
> var_export( trim('abcba', 'ab'));
'c'
```

```php:baを指定
 > var_export( trim('abcba', 'ba'));
'c'
```

文字は１文字づつ個別であり、複数文字指定しても**連続した文字列とはみなされない**。



先頭と末尾に該当する値がない場合は削られない。

```php:
> var_export( trim('cabcbc', 'ab'));
'cabcbc'

> var_export( trim('abcbc', 'b'));
'abcbc'
```


<br>
###2) rowspanとcolspanの除去とダブルクオテーションの追加

属性値の文字列の生成コードが不適切。

````php:NG
$rowspan = "rowspan = $x ";
$colspan = "colspan = $y";
```

スペースを削除し、ダブルクオテーションをつける。
ダブルクオテーションを文字列として表示するにはエスケープが必要。(`\`を冒頭につける。)

````php:OK
$rowspan = "rowspan=\"$x\"";
$colspan = "colspan=\"$y\"";
```

>補足：`\`はmacなら、option + ¥



<br>
###3) 属性がないときの無駄なスペース除去
`th class="{{ $classes }}" {{ $rowColspan }}`を丸ごとセットで作成する。

開始タグとそれぞれの属性、`th`, `class=`, `rowspan=`, `colspan=`をつなぎ合わせる。存在しない場合は`""`でつなげる。

implodeメソッドを使用する。implodeメソッドは配列の要素を文字列としてつなげる。


####▼implodeメソッド
・`implode(結合する文字, 配列)`

- 結合する文字と配列が逆でも問題なく動く。
- 各要素を空白でつなげたいため`implode(" ", 配列)`とする。

処理内容のイメージは以下。（簡略化のためダブルクオテーションは無視）

```php:やりたい処理
$arr = ["tag", "class=test", "rowspan=2", "colspan=3"];

#implode処理後の出力結果
var_export(implode(" ", $arr));
'tag class=test rowspan=2 colspan=3'
```

```php:tagのみの場合
$arr = ["tag"];

var_export(implode(" ", $arr));

#出力（スペースが入らない）
'tag'
```


**▼引数が逆の場合**

```php:引数が逆パターン
$arr = ["tag", "class=test", "rowspan=2", "colspan=3"];

#引数が逆
var_export(implode($arr, " "));
'tag class=test rowspan=2 colspan=3'
```

引数は配列が前でも後ろでもどちらでも処理結果は同じになる。



####実際の処理

```php:implodeを使ったリファクタリング
@php
  $attrs = [$type];

  if (isset($classes)) {
    $class_str = trim($classes);
    $attrs[] = "class=\"$class_str\"";
  }

  if (isset($tdth)) {
    if($tdth->getRowspan() > 1){
      $x = $tdth->getRowspan();
      $attrs[] = "rowspan=\"$x\"";
    }

    if($tdth->getColspan() > 1){
      $y = $tdth->getColspan();
      $attrs[] = "colspan=\"$y\"";
    }
  }

  $opening_tag = implode(" ", $attrs);
@endphp

@switch($type)
  @case('th')
    <{!! $opening_tag !!}>{{ $slot }}</th>	
    @break
  @case('td')	
    <{!! $opening_tag !!}>{{ $slot }}</td>	
    @break
@endswitch 
```


####nullチェック（if文とissetメソッド）
`$class`と`$tdth`は`null`になる可能性があるため処理前に`isset`と`if`を使ってnulチェックを行う。

**・isset(変数)**
変数が存在すればtrue、存在しない（またはnull）の時はfalseを返す。

```php:null-check
if (isset(変数)){
  処理
}
```



####変数をタグとして表示する`{!! !!}`
変数をブラウザに表示する方法として`{{ }}`があるが、これは文字列となり、タグとして認識されない。

```php:NG
<{{ $opening_tag }}>
```

↓ 

```HTML:HTML
<th class=&quot;article-item td_th&quot; rowspan=&quot;2&quot; colspan=&quot;2&quot;><p id="w8w6yhdn" class="mr-body1 article-item typography ">
```

タグではなく文字列に変換されるため、ダブルクオートが`&quot`に変換されてしまう。このため属性が適用されない。


####▼対処法
変数を属性やタグとして認識させるためには、`{!! !!}`を使う。

blade特有の記述で、波カッコ２つのうち一つをエクスクラメーションマーク（ビックリマーク）２つに変換する。


```php:OK
<{!! $opening_tag !!}>
```

↓ 

```HTML:HTML
 <th class="article-item td_th" rowspan="2" colspan="2">
```



<br>
####4) `@switch`による冗長処理の簡略化

```php:
@switch($type)
  @case('th')
    <{!! $opening_tag !!}>{{ $slot }}</th>	
    @break
  @case('td')	
    <{!! $opening_tag !!}>{{ $slot }}</td>	
    @break
@endswitch 
```

`$type`は状況に応じて`td`, `th`が入るため、`switch`で分岐せず、そのまま`$type`を使えばいい。

```php:リファクタリング後
<{!! $opening_tag !!}>{{ $slot }}</$type>
```


<br>
うーん、かなり簡略化かつキレイなコードになった。（[リファクタリング完了後のコード](#リファクタリング完了後のコード)）