LaravelでFacadeを作ってSlackに通知してみた

## お知らせ

改善を加えた新記事は[こちら](https://qiita.com/freeneer/items/aa82a0c49842b7c379ec)になります。

## 概要

既存のシステムをメンテナンスする上でエラーが発生したときにSlackに通知させるようにしたかったので、そのときの実装内容をメモとして残しておきます。

参考にさせていただいたサンプルではモデルを通知可能（Notifiable）にして通知処理を記述してありましたが、どこからでも呼び出せるように
Facadeを作成して通知する形にしました。


## 環境
Laravel：5.8
PHP：7.2

## SlackのWebhook URLを取得

こちらからSlackと連携するためのWebhook URLを取得します。

[https://[通知したいワークスペース名].slack.com/apps/A0F7XDUAZ--incoming-webhook-?next_id=0](https://slack.com/apps/A0F7XDUAZ--incoming-webhook-?next_id=0)

取得したURLは.envファイルに記載しておきます。
通知先のチャネルや投稿されるときの名前等は上記の設定画面で設定しておくこともできますが、通知するときに指定することもできるので合わせて.envファイルに記載しておきます。

``` shell:.env
SLACK_USERNAME=エラー通知
SLACK_ICON=:fire:
SLACK_CHANNEL=notice-error
SLACK_URL=https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxXXxxXxXXxxXXXXXxx
```

## Slack通知チャンネルを追加する

Laravel5.7以前はGuzzle HTTPクライアントがあればよかったのですが、5.8以降はSlack通知チャンネルのインストールが必要になります。

```
> composer require laravel/slack-notification-channel
```

## Notificationクラスを作成する

以下のコマンドでNotificationクラスを作成します。

``` shell
> php artisan make:notification SlackNotification
```

app/Notifications/SlackNotification.phpというファイルが作成されますので、Slack通知用に変更していきます。

``` php:app/Notifications/SlackNotification.php
<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Messages\SlackAttachment;

class SlackNotification extends Notification
{
    use Queueable;

    protected $message;
    protected $attachment;

    /**
     * Create a new notification instance.
     *
     * @return void
     */
    public function __construct($message = null, $attachment = null)
    {
        $this->message = $message;
        $this->attachment = $attachment;
    }

    /**
     * Get the notification's delivery channels.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['slack'];
    }

    /**
     * Slack通知表現を返します
     *
     * @param  mixed  $notifiable
     * @return \Illuminate\Notifications\Messages\SlackMessage
     */
    public function toSlack($notifiable)
    {
        $message = (new SlackMessage)
            ->from(env('SLACK_USERNAME'), env('SLACK_ICON'))
            ->to(env('SLACK_CHANNEL'))
            ->content($this->message);

        if (!is_null($this->attachment) && is_array($this->attachment)) {
            $message->attachment(function (SlackAttachment $attachment) {
                if (isset($this->attachment['title'])) {
                    $attachment->title($this->attachment['title']);
                }
                if (isset($this->attachment['content'])) {
                    $attachment->content($this->attachment['content']);
                }
                if (isset($this->attachment['field']) && is_array($this->attachment['field'])) {
                    foreach($this->attachment['field'] as $k => $v) {
                        $attachment->field($k, $v);
                    }
                }
            });
        }
        return $message;
    }

    /**
     * Get the array representation of the notification.
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}
```

## Slack通知ファサードを作成する

### Slack通知処理本体の作成

- Notifiableトレイトを追加する
- 通知先のURLを返すrouteNotificationForSlackメソッドを追加する
- 通知処理の中で$this->notify()を呼び出す


``` php:app/Services/Slack/SlackService.php
<?php
namespace App\Services\Slack;

use Illuminate\Notifications\Notifiable;
use App\Notifications\SlackNotification;

class SlackService
{
    use Notifiable;

    public function send($message = null, $attachment = null)
    {
        $this->notify(new SlackNotification($message, $attachment));
    }

    protected function routeNotificationForSlack()
    {
        return env('SLACK_URL');
    }
}
```

### ファサードクラスを作成

``` php:app/Services/Slack/SlackFacade.php
<?php
namespace App\Services\Slack;

use Illuminate\Support\Facades\Facade;

class SlackFacade extends Facade
{
    protected static function getFacadeAccessor()
    {
        return 'slack';
    }
}
```

### サービスプロバイダの作成

以下のコマンドでサービスプロバイダを作成します。

``` shell
> php artisan make:provider SlackServiceProvider
```

app/Providers/Slack/SlackServiceProvider.phpというファイルが作成されますので、registerの中にキーとサービスをバインドするための記述を追加します。

``` php:app/Providers/Slack/SlackServiceProvider.php

<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SlackServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(
            'slack',
            'App\Services\Slack\SlackService'
        );
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

```

### サービスプロバイダとエイリアスを追記する

``` php:app/config/app.php

    'providers' => [
        (省略)
        App\Providers\SlackServiceProvider::class,
        (省略)
    ],
    'aliases' => [
        (省略)
        'Slack' => App\Services\Slack\SlackFacade::class,
        (省略)
    ],
```

## 通知処理

以下のように記述すればどこででもSlackへの通知が可能です。

``` php
\Slack::send('Hello World!');
```



## 参考にさせていただいたサイト
https://readouble.com/laravel/5.8/ja/notifications.html
https://www.ritolab.com/entry/110
https://larapet.hinaloe.net/2017/04/09/slack-notification/
https://qiita.com/nekyo/items/95ede7f8d5c5bd3357fb