
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、ご存知の「LINE」がメッセージアプリだけじゃなく、ビジネスでも重要なのをご存知でしょうか。なぜなら、
9,500万人という圧倒的なユーザーがいるから
ですね。
しかも、1日に1回以上利用するユーザーは86%
!
引用:LINEのユーザーはどんな人? – LINEキャンパス
実際に私も1日に数回使っている印象です。
そして、今回LINE
でつくりたくなったのは・・・・・・
リッチメニュー
です。
リッチメニューとは、LINE
のタイムライン下部に表示されるメニューのこと。
リッチメニューは、以下のように使われたりします。
- 動画を配信する
- 診断機能を開始する
- 販売ページへ移動する
つまり、マーケティングとして活用されているわけですね。
そこで!
Laravel
を使ってリッチメニューを切り替える機能をつくってみます。
今回は以下のような方を想定して記事を書きました。
- リッチメニューがどんなものか知りたい
- リッチメニューとLaravelがどう連携するのか知っておきたい
- リッチメニューをつかってマーケティングに役立てたい
ぜひ最後まで読んでくださいね!
「18きっぷを使って、
片道2時間の町にある
ビール醸造所いってきました」
目次 [非表示]
リッチメニューを操作するLINE Messaging APIが使えるようにする
前提としてLINE Messaging API
が使えないと何もできません。
このブログでは以下のページで手順をまとめてますので、先に準備をしておいてください。
参考ページ:Laravel: LINE の顧客リストを失わないリスクヘッジ(前提として)
※今回はテストなのでngrok
をつかってローカルで運用します。
リッチメニューなど各種データが保持できるようにする
必要になるのは友だち登録してくれたLINE
ユーザーの情報です。
以下のコマンドでサクッとつくっていきましょう!
php artisan make:model LineUser -m
ファイルが2つ作成されます。
それぞれ中身を変更しましょう!
database/migrations/****_**_**_******_create_line_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('line_users', function (Blueprint $table) {
$table->id();
$table->string('line_id')->comment('LINEのID');
$table->string('mode')->comment('チャネルの状態');
$table->string('display_name')->comment('LINEの名前');
$table->string('rich_menu_type')->comment('リッチメニューの種類');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('line_users');
}
};
app/Models/LineUser.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class LineUser extends Model
{
use HasFactory;
protected $fillable = [
'line_id',
'mode',
'display_name',
'rich_menu_type',
];
}
ではこの状態でデータベースを初期化してみましょう。
以下のコマンドを実行してください。
php artisan migrate:fresh --seed
実際のテーブルはこうなりました。
リッチメニューの背景につかう画像を用意する
リッチメニューで表示する画像を用意します。
というのも、リッチメニューで表示されるボタンの実体は単なる画像なんです。
仕様としてはこんなカンジです。
- 画像をタップする
- タップされた画像の場所によってアクションが変わる
今回はそこまできれいな画像は不要なので、LINE
がサンプルで用意してくれてるものをつかって実装しましょう。
画像は「storage/app/rich_menu_images/background.png」として設置しておいてください。
※なお、サンプル画像のダウンロードは、以下のとおりです。
- LINE Official Account Managerにアクセス
- リッチメニューを追加したいチャンネルをクリック
- 画面左側の「リッチメニュー」をクリック
- 「デザインガイド」というボタンをクリック
- 「テンプレートをダウンロード」ボタンをクリック
リッチメニューを登録できるArtisanコマンドをつくる
結論から言うと、ウェブ上(LINE Official Account Manager
)でリッチメニューをつくると切り替えはできません。
ウェブ版は、表示期間が重複していると登録できないからです。
ではどうするかと言うと、プログラムからリッチメニューをつくります。(※postman
でもいいですね)
そのため、Laravel
のArtisan
コマンドをつくります!
以下のコマンドを実行してください。
php artisan make:command MakeLineRichMenuCommand
ファイルが作成されるので中身を以下のように変更します。
app/Console/Commands/MakeLineRichMenuCommand.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Http;
class MakeLineRichMenuCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'make:line-rich-menu {name} {action_text}';
/**
* The console command description.
*
* @var string
*/
protected $description = 'LINEのリッチメニューを作成します';
/**
* Execute the console command.
*/
public function handle()
{
$access_token = env('LINE_ACCESS_TOKEN'); // 本来はconfigから取得すべき
$this->client = Http::withToken($access_token);
$name = $this->argument('name');
$action_text = $this->argument('action_text');
$rich_menu_id = $this->createRichMenu($name, $action_text);
$image_path = storage_path('app/rich_menu_images/background.png');
$result = $this->uploadRichMenuImage($rich_menu_id, $image_path);
if($result === true) {
$this->info('リッチメニューの作成に成功しました');
} else {
$this->error('リッチメニューの作成に失敗しました');
}
}
private function createRichMenu(string $name, string $action_text): string
{
$rich_menu_data = [
'size' => [
'width' => 1200,
'height' => 405,
],
'selected' => false,
'name' => $name,
'chatBarText' => 'メニュー',
'areas' => [
[
'bounds' => [
'x' => 0,
'y' => 0,
'width' => 800,
'height' => 405,
],
'action' => [
'type' => 'message',
'label' => '左側ボタン',
'text' => $action_text,
],
],
[
'bounds' => [
'x' => 800,
'y' => 0,
'width' => 400,
'height' => 405,
],
'action' => [
'type' => 'message',
'label' => '右側ボタン',
'text' => 'リッチメニュー切り替え',
],
],
],
];
try {
$response = $this->client
->withHeader('Content-Type', 'application/json')
->post('https://api.line.me/v2/bot/richmenu', $rich_menu_data);
$rich_menu_id = $response->json('richMenuId');
$this->info('RichMenuId: ' . $rich_menu_id);
return $rich_menu_id;
} catch (\Exception $e) {
throw $e;
}
}
private function uploadRichMenuImage(string $rich_menu_id, string $image_path): bool
{
try {
$response = $this->client
->withHeaders([
'Content-Type' => 'image/png',
])
->withBody(fopen($image_path, 'r'), 'image/png')
->post('https://api-data.line.me/v2/bot/richmenu/'. $rich_menu_id .'/content');
$this->info($response->body());
return $response->ok();
} catch (\Exception $e) {
throw $e;
}
}
}
ここでやっている手順は以下のとおりです。
- リッチメニュー本体を作成
- 結果からリッチメニューIDを取得
- IDを使って画像をアップロード
つまり2段階でAPI
にアクセスしています。
これで以下のようにコマンドを実行すると、リッチメニューが登録され画像もアップロードされることになります。
php artisan make:line-rich-menu "リッチメニュー1" "リッチメニュー1がタップされました"
では、上記のコマンドと以下のコマンドで2つリッチメニューをつくり、IDを取得しておきましょう。
php artisan make:line-rich-menu "リッチメニュー2" "リッチメニュー2がタップされました"
うまくいくと以下のようになります。{}
は成功した証です。
では、取得した2つのIDを.env
にセットしておきましょう。
.env
RICH_MENU_ID_1="richmenu-********************************"
RICH_MENU_ID_2="richmenu-********************************"
Enum(列挙型)でリッチメニューの種類を定義する
今回は2種類のリッチメニューを切り替えますので、データを取得しやすいようにEnum
をつくっておきましょう。
※Enum
の詳しい説明は以下ページをご覧ください。
【Laravel】PHP 8.1 から使える Enumで、選択肢〜バリデーションまで実装してみる
app/Enums/RichMenuType.php
<?php
namespace App\Enums;
enum RichMenuType: string
{
case RichMenu1 = 'rich-menu-1';
case RichMenu2 = 'rich-menu-2';
public function id(): string
{
return match ($this) {
self::RichMenu1 => env('RICH_MENU_ID_1'),
self::RichMenu2 => env('RICH_MENU_ID_2'),
};
}
}
コントローラーでウェブフックをつくり、リッチメニューを操作する
ウェブフック有効にすると、以下のような操作があったときにLaravel
側へ通知をしてくれます。
- 友だち登録したとき
- メッセージを受信したとき
- 画像が送信されたとき
このウェブフックをLaravel
で作っていきます。
以下のコマンドを実行してください。
php artisan make:controller LineRichMenuWebhookController
ファイルが作成されるので、中身を以下のように変更します。
app/Http/Controllers/LineRichMenuWebhookController.php
<?php
namespace App\Http\Controllers;
use App\Enums\RichMenuType;
use App\Models\LineUser;
use GuzzleHttp\Client as GuzzleClient;
use Illuminate\Http\Request;
use LINE\Clients\MessagingApi\Configuration;
use LINE\Clients\MessagingApi\Api\MessagingApiApi;
use LINE\Clients\MessagingApi\Model\TextMessage;
use LINE\Clients\MessagingApi\Model\Message;
use LINE\Clients\MessagingApi\Model\ReplyMessageRequest;
use LINE\Constants\MessageType;
use LINE\Parser\EventRequestParser;
use LINE\Webhook\Model\FollowEvent;
use LINE\Webhook\Model\PostbackEvent;
class LineRichMenuWebhookController extends Controller
{
public function __construct()
{
// 本来は config から取得するべきです
$this->channel_secret = env('LINE_CHANNEL_SECRET');
$this->access_token = env('LINE_ACCESS_TOKEN');
}
public function receive(Request $request): void
{
$request_body = $request->getContent();
$hash = hash_hmac('sha256', $request_body, $this->channel_secret, true);
$signature = base64_encode($hash);
if($signature === $request->header('X-Line-Signature')) {
$client = new GuzzleClient;
$config = new Configuration();
$config->setAccessToken($this->access_token);
$this->api = new MessagingApiApi(
client: $client,
config: $config,
);
try {
$event_parser = EventRequestParser::parseEventRequest($request_body, $this->channel_secret, $signature);
foreach($event_parser->getEvents() as $event) {
if($event instanceof FollowEvent) { // お友だち登録したとき
$this->onFollow($event);
} else if($event instanceof PostbackEvent) { // ポストバック:リッチメニューがタップされたとき
$this->onPostback($event);
}
}
} catch (\Exception $e) {
logger()->error($e->getMessage());
}
} else {
abort(404);
}
}
private function onFollow(FollowEvent $event): void
{
$reply_token = $event->getReplyToken();
$line_id = $event->getSource()->getUserId();
$mode = $event->getMode();
$profile = $this->api->getProfile($line_id);
$display_name = $profile->getDisplayName();
$line_user = LineUser::firstOrNew(['line_id' => $line_id]);
$line_user->display_name = $display_name;
$line_user->mode = $mode;
$line_user->rich_menu_type = RichMenuType::RichMenu1;
$line_user->save();
$message = new TextMessage([
'type' => MessageType::TEXT,
'text' => '友だち登録ありがとうございます!',
]);
$this->replyMessage($reply_token, $message);
$this->setRichMenu($line_id, RichMenuType::RichMenu1);
}
private function onPostback(PostbackEvent $event): void
{
$line_id = $event->getSource()->getUserId();
$data = $event->getPostback()->getData();
parse_str($data, $parsed_data);
$action = data_get($parsed_data, 'action');
if($action === 'switch_rich_menu') {
$line_user = LineUser::where('line_id', $line_id)->first();
if(! is_null($line_user)) {
$rich_menu_type = ($line_user->rich_menu_type === RichMenuType::RichMenu1->value)
? RichMenuType::RichMenu2
: RichMenuType::RichMenu1;
$line_user->rich_menu_type = $rich_menu_type->value;
$line_user->save();
$this->setRichMenu($line_id, $rich_menu_type);
}
}
}
private function replyMessage(string $reply_token, Message $message): bool
{
try {
$request = new ReplyMessageRequest([
'replyToken' => $reply_token,
'messages' => [$message],
]);
$this->api->replyMessage($request);
return true;
} catch (\Exception $e) {
logger()->error($e->getMessage());
}
return false;
}
private function setRichMenu(string $line_id, RichMenuType $rich_menu_type): void
{
$rich_menu_id = $rich_menu_type->id();
try {
$this->api->linkRichMenuIdToUser($line_id, $rich_menu_id);
} catch (\Exception $e) {
logger()->error($e->getMessage());
}
}
}
リッチメニューを操作するためのウェブフックのルートをつくる
最後にルートです。
routes/web.php
// 省略
use App\Http\Controllers\LineRichMenuWebhookController;
// 省略
// LINE rich menu
Route::prefix('line_rich_menu')->group(function(){
Route::prefix('webhook')->controller(LineRichMenuWebhookController::class)->group(function(){
Route::post('receive', 'receive')->name('line_marketing.receive');
});
});
// 省略
これで作業は完了です!
お疲れ様でした
実際にLINEのリッチメニューが切り替わるかテストしてみる
テストの手順は次のとおりです。
- LINEのQRコードを使って友だち登録する
- メニューが表示されているか確認
- 左側エリアをタップして「今がどっちのリッチメニューか」確認
- 右側エリアをタップしてリッチメニュー切り替え
- 再度、左側エリアをタップして「どっちのリッチメニューか」確認
では、友だち登録してメニューを見てみましょう。
登録が完了して・・・・・・
はい!
画面下部にリッチメニューが表示されました。
では、左側エリア(A)をタップしてみましょう。
どうなるでしょうか・・・・・・
はい!
まずは現在が「リッチメニュー1」であることがわかりました。
では、右側部分(B)をタップして、リッチメニューを切り替え、同じくリッチメニュー2の(A)部分をタップしてみましょう。
うまくいくでしょうか・・・・・・
はい!
現在がリッチメニュー2であることがわかります。
すべて成功です
※今回は同じ画像を使ったので見た目に変化はありませんが、もちろん別の画像を使うこともできるので、全く違ったリッチメニューに切り替えることもできますよ!
リッチメニューを活用したい企業様へのご提案
LINE
がメッセージアプリだけでなく、マーケティング・ツールとして利用されることは珍しくなくなりました。
今回のようにリッチメニューを切り替えることで、より見込み客への「価値観教育」をすることができるようになります。
また、LINEのメッセージ開封率やリンククリック率は、メルマガと比べても格段に多いことがわかっています。
もしLINE
を使った施策をご希望でしたら、ぜひお問い合わせからご連絡ください。お待ちしております。
おわりに
ということで、今回はLaravel
でLINE
のリッチメニュー切り替えを実装してみました。
ちなみに、LINE
のcomposer
パッケージが新しくなってから毎回なのですが、情報が少なすぎてメソッドはどれを使えばいいかわからないんですよね…。
なので、いっそのことパッケージなんか使わずにHTTP
クライアントを使ったところスンナリいきました(笑)
今後はLINE Messaging API
使うときはもうパッケージは使わないかもしれません(わりと本気!)
ぜひ皆さんも試してみてくださいね。
ではでは〜!
「いや、夜中3時で
気温28度って
罰ゲームですか…」