【メモ】Laravelで作っているサービスをリファクタリングした時の学び(箇条書き/現在進行中のため随時追加)

クラス設計の勉強がてら、Laravelで作っているサービスをリファクタリングしてみようと思い学んだことをメモして行きます。

##■Viewに渡す変数を減らす
###1.一つのインスタンスに関連する複数のインスタンスをViewに渡したい
　変更前：`$user`,`$orders`,`$favorites`等複数変数をViewに渡していた
　　↓
　変更後：`$orders`,`$favorites`は`$user`とhasManyの関係にあるため、`$user`だけ渡せばview側で`$user->favorites`とすることでデータを表示出来る。また、`$favorites`等のデータを用意する必要が無いためコントローラーもスリムになる。
(最初から気づきたかった。。)

###2.userインスタンスの一部だけ整形して表示したい
変更前：`$birthday`のように新しい変数を定義していたためViewに渡す変数が増えていた
　↓
変更後：`$user->birthday = 整形用メソッド($user->birth)`のようにすることで`$user`だけViewに渡せばOKにした


##■コントローラーを小さく

###１.1controllerに対して扱えるendpointを一つに絞る
　変更前：UserControllerの中でUserのプロフィール情報を更新するメソッド等を用意しており、その中で`$user`だけでなく`$favorites`等Userと関連するデータも複数扱っていたためメソッドがめちゃ肥大化していた
　　↓
　変更後：UserControllerはUserのみを扱うようにして、その他のFavorite等のプロフィール情報を扱うのはFavoriteController等に分割した。

##■Viewを綺麗に
###１.Viewにロジックを載せないためにクラス側で整形作業をやってしまう
　変更前：プロフィール画面(bladeテンプレ)で例えば誕生日を以下のように記載していた

```html
{{$birthArr[0]}}.'年'.{{$birthArr[1]}}.'月'.{{$birthArr[2]}}.'日 /'
```
　↓
　変更後：表示するデータの整形や条件分岐による表示内容の場合分けを、例えば`DisplayData`という名前のトレイトにまとめてしまい、UserControllerでトレイト内の関数を使って整形することで、View側で整形作業を行わなくて良いようにする
※参考：http://niconare.nicovideo.jp/watch/kn2591

##■共通のメソッドを別クラスに切り出す時の注意点
１.returnを書かないとデータがかえって来ないので、返り値を前提とした処理を書いている場合は必ずreturnを書く

##■複数のテンプレートを出し分ける(自分のプロフィールとフレンドのプロフィール等)
例えばSNS等でプロフィール情報を表示する場合に、自分のプロフィールを表示する場合とフレンドのプロフィールを表示する場合で使うテンプレートを分けたい場合があります。

その場合、１つのテンプレート内で`@if`等で場合分けしているとテンプレートが大きくなりすぎる可能性があるのでコントローラーでうまく場合分け出来ないかやってみました。

###1.自分のプロフィールを表示
Laravelのresourceコントローラーについているshowメソッドを使いますが、showメソッドから直接`return view()`するのではなく、別に`chooseTemplate()`というテンプレートを選択するメソッドを呼び出します。


```php
//UserController.php
public function show($id)
{
   //viewに渡すデータの準備 (割愛)

   return $this->chooseTemplate($datas);
}

public function chooseTemplate($datas)
{
    //テンプレートにデータを渡す
    return view('profile',$datas);
}
```

これでテンプレート側(`profile.blade.php`)で`$datas`のデータを使うことが出来ます。

###2.フレンドのテンプレートを表示
フレンド情報を扱う`FriendController`クラスを作成し、`UserController`クラスを継承します。
これで`UserController`クラスの`show`メソッドを使うことが出来るようになります。

また、`chooseTemplate`メソッドをオーバーライドして、friend.blade.phpの方のテンプレートを表示するようにしています。

```php
//FriendController.php
class FriendController extends UserController
{
    public function showProfile($id,$friend_id)
    {
        //フレンド表示に必要な処理とか

        if(!$user->friends->where('friend_id',$friend_id)->isEmpty()){
            //UserControllerクラスを継承しているのでshowメソッドが使える
            return $this->show($friend_id);
        }
        return 'ご指定の友達は存在しません';
    }
    //chooseTemplateメソッドをオーバーライドしてfriend.blade.phpを表示させる
    public function chooseTemplate($datas)
    {
        return view('friend',$datas);
    }

}
```

##■複数ページから遷移する可能性があるページの「戻る」ボタンのリンク
複数ページから遷移する可能性があるページの場合、「戻る」ボタンのリンクを固定してしまうと本来のページとは別のページに戻ってしまいます。

`$_SERVER['HTTP_REFERER']`をテンプレートに渡すことで戻るボタンのリンク先を動的に変更することが出来ます。

例えば先ほどの`chooseTemplate`メソッドで`backUrl`という変数を定義し、値に`$_SERVER['HTTP_REFERER']`を設定し、テンプレートに渡します。

```php
//FriendController.php
public function chooseTemplate($datas)
{
    $datas['backUrl'] = $_SERVER['HTTP_REFERER'];
    return view('friend',$datas);
}
```

テンプレート側ではこのように書けます。

```html
<!--friend.blade.php-->
<p ><a href="{{ $backUrl }}">戻る</a></p>
```

これでテンプレート側でif文等使わずに、直前のページに戻るリンクを動的に設定することが出来ます。

###※直アクセスへの対応
上記の場合、直アクセスだと`$_SERVER['HTTP_REFERER']`にURLが入らないのでエラーになります。

これには`$_SERVER['HTTP_REFERER']`の有無でリンク自体を表示・非表示にする処理で対応しました。

```php
public function chooseTemplate($datas)
{
    $datas['hasMatched'] = $this->matchingService->hasMatched($datas['user']);
    //$_SERVER['HTTP_REFERER']が無くてもエラーにならないように
    $datas['backUrl'] = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
    //戻るリンクに設定するクラスを指定
    $datas['backUrlClass'] = $this->isBackUrl();
    return view('friend',$datas);
}

//直前のURLが存在するか(直アクセスでないかどうか)チェック
public function isBackUrl()
{
    if(isset($_SERVER['HTTP_REFERER'])){
        return 'disblo';//display:block;
    }
    //無い場合はdisplay:none;を設定
    return 'disnone';
}
```
`FriendController`で設定したクラスをリンクに設定し、表示・非表示を出しわけます。
場合わけのロジックをクラス側に置くことでViewにロジックを書かずに済みました。

```html
<!-- friend.blade.php -->
<p class="link_back"><a class="{{ $backUrlClass }}" href="{{ $backUrl }}">戻る</a></p>
```




引き続き学びつつ、実践しつつ、随時追加して行きます^^