1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

✅Laravel×Slack:「ユーザーごとに連携」して「毎朝自動通知」する仕組みの作り方

Last updated at Posted at 2025-08-18

はじめに

ログインユーザーごとにSlack連携を許可 → そのユーザーのタスクを毎朝自動でSlackへ通知
…という仕組みをLaravelアプリで構築した手順をまとめます💪

🔧 ゴール

・タスク管理アプリ(Laravel)とSlackを連携

・ユーザーごとにSlack連携が可能(OAuth)

・毎朝決まった時間に、そのユーザーの今日のタスクをSlackへ自動DM通知

🗺 全体の流れ(設計〜実装)

graph TD
A[① Slackアプリ作成] --> B[② LaravelにSlack認証組込み]
B --> C[③ ユーザーごとに連携情報を保存]
C --> D[④ 通知バッチコマンド作成]
D --> E[⑤ スケジューラ登録]
E --> F[⑥ ngrok本番化 or 本番URLで運用]

① Slackアプリを作成

1. https://api.slack.com/apps へアクセス

2. 「Create New App」 → “From scratch”

3. 「App Name」 と 「Workspace」 を選択

 → 自由で良い

4. 左側メニュー「OAuth & Permissions」を開き以下を設定

設定項目 設定内容
Redirect URL https://ドメイン/slack/callback(後で使う)
Bot Token Scopes chat:write, channels:read, users:read

Redirect URL

スクリーンショット 2025-08-18 17.09.54.png

「Add New Redirect URL」

https://ドメイン/slack/callback」を入力

Redirect URL
本番環境は、本番用のURLでOK

開発環境は、Slack側がhttpを許可してくれないので、httpsを用意する必要がある。
以下で準備する。

https://qiita.com/honaki/items/42e08f9ff9b8a9f77c20

「Save URLs」でOK

Bot Token Scopes

スクリーンショット 2025-08-18 17.14.38.png

「Add an OAuth Scope」

検索欄でchat:write, channels:read, users:readらを入力し、選択でOK

5. 「Install App to Workspace」ボタンを押し、インストール

初回と以下の画像は違う。

「Install to AppName」という白いボタンが
インストール(=Install App to Workspace)に該当します。

スクリーンショット 2025-08-18 17.17.28.png

6. 表示される以下の値を控える(Laravelで使う)

名前 意味
Client ID OAuthのID
Client Secret OAuthのSecret
Bot User OAuth Token xoxb〜)使わないが参考値

「Client ID」「Client Secret」の場所

「Settings」→「Basic Information」→「App Credentials」
スクリーンショット 2025-08-18 17.21.17.png

「Bot User OAuth Token」の場所

「Features」→「OAuth & Permissions」→「OAuth Tokens」
スクリーンショット 2025-08-18 17.24.57.png

② DB用意 & model

Slack連携に必要な情報は、既存の users(ユーザー)・tasks(タスク) テーブルとは別に
新たに slack_notifications テーブルを追加して管理します。

1. 📘 テーブル定義(slack_notifications)

カラム名 NULL 備考
id bigint NO 主キー AUTO_INCREMENT
user_id foreignId NO users.id 外部キー
slack_user_id string YES SlackのユーザーID(DM送信用)
slack_team_id string YES ワークスペースID
bot_access_token text YES OAuthトークン
is_enabled boolean NO 通知ON/OFF(初期値 false)
last_sent_at timestamp YES 最終通知日時
created_at timestamp YES 作成日時
updated_at timestamp YES 更新日時

2. 🔗 ER図(関連図)

ER図【タスクリマインダー】.drawio.png

3. 🛠 マイグレーションファイル

database/migrations/2025_xx_xx_xxxxxx_create_slack_notifications_table.php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    public function up()
    {
        Schema::create('slack_notifications', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->constrained()->onDelete('cascade');
            $table->string('slack_user_id', 100)->nullable();
            $table->string('slack_team_id', 100)->nullable();
            $table->text('bot_access_token')->nullable();
            $table->boolean('is_enabled')->default(false);
            $table->timestamp('last_sent_at')->nullable();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::dropIfExists('slack_notifications');
    }
};

4. Model

app/Models/SlackNotification.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class SlackNotification extends Model
{
    use HasFactory;

    protected $fillable = [
        'user_id',
        'slack_user_id',
        'slack_team_id',
        'bot_access_token',
        'is_enabled',
        'last_sent_at',
    ];

    // ▼Userリレーション
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}
app/Models/User.php
public function slackNotification()
{
    return $this->hasOne(SlackNotification::class);
}

③ .env

.env
SLACK_CLIENT_ID=xxxxxxxxx
SLACK_CLIENT_SECRET=xxxxxxxx
SLACK_REDIRECT_URI=https://〜/slack/callback

④ routes → Blade →Controller

1. routes/web.php

routes/web.php
Route::get('/slack/authorize', 'SlackController@redirectToSlack')->name('slack.authorize');
Route::get('/slack/callback',   'SlackController@handleCallback')->name('slack.callback');

2. 「Slack連携」ボタン(Blade)

// blade
<a href="{{ route('slack.authorize') }}" class="btn btn-primary">
    Slack連携
</a>

3. SlackController

SlackController.php
class SlackController extends Controller
{
    public function redirectToSlack()
    {
        $url = "https://slack.com/oauth/v2/authorize";
        $params = [
            'client_id'     => env('SLACK_CLIENT_ID'),
            'scope'         => 'chat:write,channels:read,users:read',
            'redirect_uri'  => env('SLACK_REDIRECT_URI'),
        ];
        return redirect($url . '?' . http_build_query($params));
    }

    public function handleCallback(Request $request)
    {
        $code = $request->code;

        $response = Http::asForm()->post('https://slack.com/api/oauth.v2.access',[
            'client_id'     => env('SLACK_CLIENT_ID'),
            'client_secret' => env('SLACK_CLIENT_SECRET'),
            'code'          => $code,
            'redirect_uri'  => env('SLACK_REDIRECT_URI'),
        ])->json();

        // ユーザーごとのアクセストークンを保存
        $token       = $response['access_token'];    // ユーザー用
        $team_id     = $response['team']['id'];
        $slack_user  = $response['authed_user']['id'];

        SlackNotification::updateOrCreate(
            ['user_id' => auth()->id()],    // ログイン中ユーザー
            [
                'slack_user_id'    => $slack_user,
                'slack_team_id'    => $team_id,
                'bot_access_token' => $token,
                'is_enabled'       => true
            ]
        );

        return redirect()->route('tasks.one_day')->with('success','Slack連携が完了しました!');
    }
}

⑤ 通知の作成

1. バッチ用 Artisanコマンドを作る

php artisan make:command SendDailySlackNotification

2. バッチ用 Artisanコマンド内に記入

SendDailySlackNotification.php
<?php

namespace App\Console\Commands;

use App\Models\SlackNotification;
use App\Models\Task;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;

class SendDailySlackNotification extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'send:daily-slack';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Slackに毎日通知する';

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
            $users = SlackNotification::where('is_enabled', true)->get();

    foreach($users as $sn) {
        // 本日のタスクを取り出す例
        $tasks = Task::where('user_id', $sn->user_id)
                     ->whereDate('end_at', now()->toDateString())
                     ->get();

        $text = "今日のタスク一覧:\n";
        foreach($tasks as $t){
            $text .= "- {$t->title} ({$t->end_at})\n";
        }

        Http::withToken($sn->bot_access_token)
            ->post('https://slack.com/api/chat.postMessage',[
                'channel' => $sn->slack_user_id,
                'text'    => $text,
            ]);
    }

}

3. Kernel に登録

/app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    $schedule->command('send:daily-slack')->dailyAt('04:00');
}

⑥ 開発環境でのテスト

1. Kernel.phpで、->everyMinute()に変更

/app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
    // $schedule->command('send:daily-slack')->dailyAt('04:00');
    $schedule->command('send:daily-slack')->everyMinute();
}

2. アプリがある箇所で、ngrokを起動

pwd
# 例 /Users/hondaakihito/Laravel/Docker/task-reminder

ngrok http 8080

以下のような黒い画面が出てくる
スクリーンショット 2025-08-18 19.25.58.png

Docker内で、コマンドを打つ

php artisan schedule:run

⑦ 本番環境

cron に登録

* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1

以下のようにDONEになったら成功
スクリーンショット 2025-08-18 19.27.45.png

Slackへ連絡がくる
(以下はいろんなメッセージを試しているので様々な通知になっています)
スクリーンショット 2025-08-18 19.30.15.png

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?