LaravelでFacadeを作ってSlackに通知してみた（Laravel6.xバージョン）

## 概要

[前回](https://qiita.com/freeneer/items/a78fb7e270b141c793b1)の記事から約1年経って、Laravelのバージョンも上がったし、私の知識も少しだけ増えたので書き直してみました。


## 環境
Laravel：6.18.31
PHP：7.4.6

## SlackのWebhook URLを取得

SlackにメッセージをPOSTするためのWebhook URLを取得します。

### Incoming Webhook を Slack に追加

アプリから取得する場合は、ワークスペースの「App」メニューから検索欄に“Incoming Webhook”と入力して検索してください。
ブラウザで取得する場合は、以下のURLにアクセスしてください。

[https://slack.com/apps/A0F7XDUAZ--incoming-webhook-](https://slack.com/apps/A0F7XDUAZ--incoming-webhook-)

- `Slack に追加` ボタンをクリック
- 通知するチャンネルを選択し、`Incoming Webhook インテグレーションの追加` ボタンをクリック
- Webhook URLをコピーする

通知先のチャネルや投稿されるときの名前等は設定画面で登録しておくことができますが、通知するときに指定することもできます。

![Incoming Webhook.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157083/86609453-5a0a-f1f4-9835-e3dc1c0d3448.png)



取得したURLは.envファイルに記載しておきます。

``` shell:.env
SLACK_URL=https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/xxxxxXXxxXxXXxxXXXXXxx
```

また、Slack 通知用の設定ファイルも用意しておきましょう。

``` php:app/config/slack.php
<?php

return [
    // Webhook URL
    'url' => env('SLACK_URL'),
];
```

## Slack 通知の準備

Slack 通知については公式ドキュメントに記載がありますのでそちらも参考にしてください

[Slack 通知 - Lavavel 6.x](https://readouble.com/laravel/6.x/ja/notifications.html#slack-notifications)

### Slack 通知チャンネルのインストール

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

## 通知クラスの作成

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

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

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

- `via` メソッドが返す配信チャンネルを slack に変更する
- SlackMessage クラスのインスタンスを返す `toSlack` メソッドを追加する

SlackMessage クラスの `content` メソッドでテキストメッセージを送信することができますが、 `attachment` メソッドで活用することでより詳細な情報をSlackに送ることができます。



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

namespace App\Notifications;

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

class SlackNotification extends Notification
{
    use Queueable;

    /**
     * 通知チャンネル情報
     *
     * @var array
     */
    protected $channel;

    /**
     * 通知メッセージ
     *
     * @var string
     */
    protected $message;

    /**
     * 添付情報
     *
     * @var array
     */
    protected $attachment;

    /**
     * 通知インスタンスの作成
     *
     * @return void
     */
    public function __construct($channel = null, $message = null, $attachment = null)
    {
        $this->channel = $channel;
        $this->message = $message;
        $this->attachment = $attachment;
    }

    /**
     * 通知の配信チャンネルの取得
     *
     * @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($this->channel['username'], $this->channel['icon'])
            ->to($this->channel['channel'])
            ->content($this->message);

        if (!is_null($this->attachment) && is_array($this->attachment)) {
            $message->attachment(function ($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;
    }
}
```


## Slack通知ファサードの作成

### 設定ファイルにチャンネル情報を追加

``` php:app/config/slack.php
<?php

return [
    // Webhook URL
    'url' => env('SLACK_URL'),

    // チャンネル設定
    'default' => 'work',

    'channels' => [
        'work' => [
            'username' => '作業通知',
            'icon' => ':face_with_rolling_eyes:',
            'channel' => 'notice-work',
        ],
        'error' => [
            'username' => 'エラー通知',
            'icon' => ':scream:',
            'channel' => 'notice-error',
        ],
    ],
];
```

### Slack通知サービスの作成

以下の処理を実行するためのサービスを作成します。

- Notifiableトレイトを利用する
- 通知先のURLを返す `routeNotificationForSlack` メソッドを追加する
- 通知するチャンネルを指定する `channel` メソッドを追加する
- `send` メソッドで Slack 通知クラスのインスタンスを引数として `$this->notify()` を呼び出す


``` php:app/Services/SlackService.php
<?php

namespace App\Services;

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

class SlackService
{
    use Notifiable;

    /**
     * 通知チャンネル情報
     *
     * @var array
     */
    protected $channel = null;

    /**
     * 通知チャンネルを指定
     * 
     * @param array $channnel
     * @return this
     */
    public function channel($channel)
    {
        $this->channel = config('slack.channels.' . $channel);

        return $this;
    }

    /**
     * 通知処理
     *
     * @param string $message
     * @return void
     */
    public function send($message = null)
    {
        if (!isset($this->channel)) {
            $this->channel(config('slack.default'));
        }

        $this->notify(new SlackNotification($this->channel, $message));
    }

    /**
     * Slack通知用URLを指定する
     *
     * @return string
     */
    protected function routeNotificationForSlack()
    {
        return config('slack.url');
    }
}
```

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

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

use Illuminate\Support\Facades\Facade;

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

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

AppServiceProvider に記載することも可能ですが、常に必要な処理ではないので個別にサービスプロバイダを作成することにします。
サービスプロバイダは以下のコマンドで作成します。

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

`app/Providers/SlackServiceProvider.php` というファイルが作成されますので、`register` メソッドにキーとサービスをバインドする初期を記述します。

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

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use App\Services\SlackService;

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

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

```

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

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

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

## 通知処理

以下のように記述すればシステム内のどこからでも Slack への通知が可能です。

``` php
// メッセージを送信
\Slack::send('Hello World!');

// チャンネルを指定して送信する場合
\Slack::channel('error')->send('Error!!');
```

## おまけ

Tinkerを使えば、画面やコンソールコマンドを用意しなくても動作が確認できます。

``` shell
php artisan tinker
Psy Shell v0.10.4 (PHP 7.4.6 ― cli) by Justin Hileman
>>> \Slack::send('Hello World!');
=> null
>>> exit
Exit:  Goodbye
```

詳しくはこちらをご確認ください。

[Tinker - Lavavel 6.x](https://readouble.com/laravel/6.x/ja/artisan.html#tinker)

## サンプルソース
[https://github.com/freeneer/slack-notice-test](https://github.com/freeneer/slack-notice-test)