【Laravel】便利なService（サービス）の使い方を実際に使って理解する。ブレードやコントローラで使う実例。

LaravelのServiceを使うと、Servicesの配下に作成したphpファイルに記述した処理をブレードの中で実行することができる。

`@inject`ディレクティブを使うと超簡単に呼び出せる。


##目次

1. [Serviceの用途](#serviceの用途)
1. [ファイルの作成](#1-ファイルの作成)
1. [ブレードで呼び出す方法](#2-ブレードで呼び出す方法)
1. [コントローラで呼び出す方法](#3-コントローラで呼び出す方法)
1. [ブレードでifと組み合わせる方法](#4-ブレードでifと組み合わせる方法)


<br>
##Serviceの用途
ブレードやコントローラの中で呼び出す関数やプロパティを定義できる。使い方は自由度が高く、例えば以下があげられる。

- コントローラの処理をサービスに移行する
- ルート名によってmeta情報の適用を変更する
- メール送信用の本文を作成する
- チャットにメッセージを投げる
- APIのデータを取得する

仕組みはとても簡単なので、実際に使ってみるとわかりやすい。

<br>
##1. ファイルの作成
サービスファイルは app > Services 配下に作成する。ファイル名はアッパーキャメルで記載する。

ファイルは冒頭に名前空間`namespace App\Services;`を記述してからクラスを記述する。


###作成例
testServiceというサービスコンテナを作成する。

```
$ mkdir app/Services
$ vi app/Services/testService.php
```

```php:testService.php
<?php
namespace App\Services;

class TestService
{
    public static function test()
    {
        return "staticメソッド in TestService";
    }

    public function test1()
    {
        return "インスタンスメソッド in TestService";
    }
}
```

試しに、staticメソッド(test)とインスタンスメソッド(test1)を作成している。

・staticメソッドとは、インスタンスを生成せずともクラスから直接呼び出せるメソッド。

・インスタンスメソッドとは、生成したインスタンスから呼び出せるメソッド。

（参考）[PHP staticの使い方と意味](https://qiita.com/yuta-38/items/3011ffb0aaa1edac4d0d)


<br>
##2. ブレードで呼び出す方法
・`@inject( '変数名', 'サービスの名前空間' )`

```php:.blade.php
@inject ( 'TestService', 'App\Services\TestService' )
```

これで、作成したサービスを`$TestService`としてブレード内で呼び出せる。

###メソッドの呼び出し
PHPのルールに従って、staticメソッドを呼び出す場合は`クラス名::メソッド名`、インスタンスメソッドは`インスタンス名::メソッド名`。

@injectで読み込んだ変数はクラスとしてもインスタンス（オブジェクト）としても使える。

```php:.blade.php
@inject ( 'TestService', 'App\Services\TestService' )

{{-- staticメソッドの呼び出し --}}
<div>{{ $TestService::test() }}</div>

{{-- インスタンスメソッドの呼び出し --}}
<div>{{ $TestService->test1() }}</div>
```

`{{-- --}}`はコメントアウト。

<br>
**▼ブラウザの表示**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/0139178c-2092-27ee-a69b-9f51d990a062.png)|
|:--|

サービスに記述した処理を簡単に呼び出すことができた。


<br>
##3. コントローラで呼び出す方法
コントローラで呼び出す場合は冒頭で`use 名前空間`で読み込めば使えるようになる。

```php:Controller.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;

//サービスの読み込み
use App\Services\TestService;

class UserController extends Controller
{
    public function index(TestService $instance) {
        //(1)サービスのstaticメソッド呼び出し
        dump( TestService::test() );

        //(2)サービスのインスタンスメソッド呼び出し
        $obj = new TestService;
        dump( $obj->test1() );

        //(3)サービスのインスタンスメソッド呼び出し（依存注入）
        dump( $instance->test1() )

        return view('user');
}
```

(2)(3)はインスタンスメソッドを呼び出すため、インスタンス（オブジェクト）を生成する必要がある。

(3)は関数の引数の中でクラスをインスタンス化し変数に注入している。依存注入と呼ぶ。


<br>
**▼ブラウザの表示**

|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/563526/0fbd2c1e-c413-0365-783e-c410c07a0360.png)|
|:--|

コントローラの中でサービスのメソッドを呼び出せている。



<br>
##4. ブレードでifと組み合わせる
サービスとif文を組み合わせれば、ルート名によってif文で処理を切り分けることも可能。

例えば、ページによってメタタグに渡す値を変更する処理を記述した`metaService`というサービスを作成する。

中にエラーの場合の処理や、引数を渡した場合の処理などを記述しておくと、ページに合わせてメタ情報を切り替えることができる。


```php:base.blade.php
@inject('metaService', 'App\Services\metaService')

@php
if(isset($exception)){
    $meta = $metaService::createMetaForError($exception);
}elseif(Route::currentRouteName() === 'blog.show' || Route::currentRouteName() === 'blog.preview'){
    $meta = $metaService::createMeta($article);
}else{
    $meta = $metaService::createMeta();
}
@endphp

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
    <meta name="description" content="{{ $meta['description'] }}">
    <meta name="keywords" content="{{ $meta['keywords'] }}">
    <title>{{ $meta['title'] }}</title>

以下省略,,,
```

サービスの例は以下。

```php:metaService.php
<?php
namespace App\Services;

use LaravelMicresArticle\Services\ItemService;

class PageService
{
    public static function createMetaForError($exception):array
    {
        $code = $exception->getStatusCode();
        if(__('error.' . $code . '.message') === 'error.' . $code . '.message'){
            $description = __('page.errors.common.description');
        }else{
            $description = __('error.' .$code. '.message');
        }
        $meta['title'] = __('page.errors.common.title', ['code' => $code]) . ' | サイト名';
        $meta['keywords'] = ' キーワード１, キーワード2 ' . __('page.errors.common.keywords', ['code' => $code]) ;
        $meta['description'] = $description;
        return $meta;
    }

    //$articleのデフォルト値はnull
    public static function createMeta($article = null):array
    {
        $meta = [];
        $routeName = \Route::currentRouteName();
        $categorySlug = array_key_exists('category_slug', \Route::current()->parameters())? \Route::current()->parameters()['category_slug'] : null;
        if($categorySlug) $routeName .= '.' . $categorySlug;
        
        //title
        if(__('page.' . $routeName . '.title') === 'page.' . $routeName . '.title'){
            $meta['title'] = 'サイト名';
        }else{
            $meta['title'] = __('page.' . $routeName . '.title') . ' | サイト名';
        }
        //keywords
        $meta['keywords'] = ' キーワード1, キーワード2 ' . __('page.' . $routeName . '.keywords');
        //description
        $meta['description'] = __('page.' . $routeName . '.description');
                
        //article
        if(\Route::currentRouteName() === 'blog.show' && $article){
            $meta['title'] = $article->title . ' | サイト名';
            $meta['keywords'] = 'キーワード1, キーワード2, ' . $article->keywords;
            $meta['description'] = $article->desc;
        }

        return $meta;
    }
```

必要に応じて条件分岐や、メソッドを追加することでメンテできる。