Hello, World!!

むずかしいことはかけません

複数のステータスでclass名を指定したい

Laravelのconst.phpで以下のように定義されているとします。

'grade' => [
    'passing' => [
        10
    ],
    'scoring' => [
        20,
    ],
    'failed' => [
        30, 40, 50,
    ],
],

$student->grade が30,40,50の場合、class名を変えたい場合

<article class="bg_color {{ in_array($student->grade, config('const.grade.failed'), true) ? "gray" : "" }}">
  <h2>タイトル</h2>
  <p>テキストテキストテキスト</p>
</article>

JavaScriptでデータを絞り込んで表示させたい

例えば各ステータスで絞り込んだデータを画面遷移せずに画面に表示させたい場合
LaravelとJavaScriptで実装したパターンとHTMLとJavaScriptで実装したパターンを載せます。

LaravelとJavaScript

Laravelの書き方だとCodePenで動かないのでそのままこちらに載せます。

<div>
    <label>
        {!! Form::radio('check_status', 0, true, ['onchange' => 'check_status()']) !!}
        <i class="icon"></i>すべて
    </label>
    <label>
        {!! Form::radio('check_status', 10, false, ['onchange' => 'check_status()']) !!}
        <i class="icon"></i>合格
    </label>
    <label>
        {!! Form::radio('check_status', 20, false, ['onchange' => 'check_status()']) !!}
        <i class="icon"></i>不合格
    </label>
</div>

<div id="show">
{{-- ※ここに絞り込んだデータが表示される --}}
</div>
<script>
    function check_status() {
      var checkStatus = document.getElementsByName("check_status");
      
        // データの取得
        var students = @json($students);

        // ラジオボタンでデータ絞込み
        if (checkStatus[0].checked) { // すべて 
            var studentList = students.data;
            ArrayByJson(studentList);
        } else if (checkStatus[1].checked) { // 合格
            var statusId=checkStatus[1].value;
            var studentList = students.data.filter(function(x) {
                return x.status == statusId;
            });
            ArrayByJson(studentList);
        } else if (checkStatus[2].checked) { // 不合格
            var statusId=checkStatus[2].value;
            var studentList = students.data.filter(function(x) {
                return x.status == statusId;
            });
            ArrayByJson(studentList);
        }
    }

    // データ表示
    function ArrayByJson(studentList){
        clear();
        for(var i = 0; i < studentList.length; i++){
            document.getElementById('show').insertAdjacentHTML('beforeend', studentList[i].user.last_name + '<br>');
        }
    }
     
    // 表示されたデータの初期化
    function clear(){
        document.getElementById('show').textContent = '';
    }
     
</script>

HTMLとJavaScript

以下のデータを絞り込みます。

var students = [
        {id:1, name:"yamada", status:10},
        {id:2, name:"tanaka", status:30},
        {id:3, name:"saitou", status:20},
    ];

See the Pen check_box by ひややっこ (@to_fu) on CodePen.

参考サイト

指定した順番でソートしたい

例えば以下のようなステータスがあったとします。
- 合格
- 採点中
- 不合格

studentsテーブルで、A組の生徒を以下の順番で絞り込みたい場合

合格(20)>採点中(10)>不合格の順(30,40,50)

const.phpでは以下のように定義されてするとします。

'scoreStatus' => [
            10 => '採点中',
            20 => '合格',
            30 => '不合格(カンニング)',
            40 => '不合格(未提出)',
            50 => '不合格',
        ],

SQL

SELECT * FROM students where class_id=100
ORDER BY CASE status 
    WHEN 20 THEN '1' 
    WHEN 10 THEN '2'
    ELSE '99' END 
\G;

orderByRaw メソッドで、CASEを使用し優先度を指定(ORDER BY CASE)してあげればOKです。
こうすることでstudentsテーブルのstatusカラムが20は1番目、statusカラムが10は2番目に表示。
それ以外(statusカラムが30,40,50)は最後の方に表示することができます。

Laravel

Laravelでは以下のように書きます。

$students = Student::orderByRaw("CASE status
                WHEN 20 THEN '1'
                WHEN 10 THEN '2'
                ELSE '99' END")
                ->get();

参考サイト

Laravel で アイテムをゲットするときに指定した順番でソートしたい|conocode

データを複数の条件で絞り件数を指定して表示したい場合

例えばあるデータを様々な条件で取得し、件数を指定して表示したい場合

以下の条件でバナーを表示させるとします。

公開ステータスの順番の優先順位
1. 順番指定の番号が若い順
2. 更新日時順
3. 登録日順
公開されているデータが6件を超えた場合、上記順番をもとに6件以上は非表示とする

ソースコード

$banners = Banner::orderByRaw('number IS NULL ASC')
            ->orderByRaw('number ASC, updated_at DESC, created_at DESC')
            ->latest('id')
            ->where('status', config('const.bannerStatus.open'))
            ->take(6)
            ->get();

それぞれの絞り込みの役割

Banner::orderByRaw('number IS NULL ASC')

`number IS NULL ASC` としないと、numberを昇順(asc)でorderbyしたときにnullが先頭に来てしまいます。
これはMySQLの仕様らしいです。

ORDER BY を実行する場合、NULL 値は ORDER BY ... ASC では最初に表示され、ORDER BY ... DESC では最後に表示されます。

MySQL :: MySQL 5.6 リファレンスマニュアル :: 3.3.4.6 NULL 値の操作より

->orderByRaw('number ASC, updated_at DESC, created_at DESC')
  • 表示順番の昇順
  • 更新日の降順
  • 作成日の降順

の潤で表示させます。

->latest('id')

IDの若い順から表示します。

->where('status', config('const.bannerStatus.open'))

バナーのステータスが「公開」のものを表示します。

->take(6)

データを6件取得します。

【Laravel】LINEのMessaging APIを使ってLINE公式アカウントからメッセージを送る

目標

LINEでテキストを送ったら、メッセージが自動で返信される処理を実装する。
f:id:eeko-amaryllis:20210114162111j:plain:w300

主に以下のサイトを参考に実装しました。
LaravelでLINEにチャットボットをつくる(QRコード作成) – console dot log
今回はQRコードは返さずテキストのみ返します。

実装

まず初めにLaravel projectを作成します。

$ laravel new blog

ローカルにあるLaravel projectをサーバーにあげる必要があるので ngrok を使います。

$ cd blog
$ php artisan serve

$ ngrok http localhost:8000

こんな感じになります。
f:id:eeko-amaryllis:20210114170910p:plain:w600

LINE Developersへのアカウントを登録

次はLINE Developersへのアカウントを登録し、以下を設定する必要があります。

  • プロバイダーを登録する
  • Messaging APIを有効にする

これらの手順は上記に記載されているサイトをみながら行ってください。

Messaging APIに必要な情報を取得する

APIのアクセスに必要な「チャネルシークレット」と「チャネルアクセストークン」を取得します。
これら2つはLaravelの.envに記載しておきましょう。

LINE_CHANNEL_SECRET=XXXXXXXXXXXXXXXXXXXXXX
LINE_ACCESS_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
webhookを有効にする

BOTが自動返信できるようにするには、webhookの設定をしないと使えないため、以下のように「Webhook送信」をオンにし、「Webhook URL」を登録します。
ここでのWebhook URLは先ほどのngrokのURLになります。(今回は参考にしたサイトよりパスは /qr-bot としています)
f:id:eeko-amaryllis:20210114173000p:plain:w600

パッケージをインストール

Messaging API対応のLINE公式SDKをインストールします。

[/blog] $ composer require linecorp/line-bot-sdk
ルーティング設定

今回LINEからアクセスされるURLをhttps://[ngrok-url]/qr-botとします。

Route::post('/qr-bot', 'QrBotController@reply');
CSRF対策を解除

app/Http/Middleware/VerifyCsrfToken.phpの$exceptに先ほどのURLを追加します。

  /**
   * The URIs that should be excluded from CSRF verification.
   *
   * @var array
   */
  protected $except = [
    'qr-bot'  // 追加
  ];
App/Providers/RouteServiceProvider.php の$namespace のコメントアウトを外す

Laravel 8.xから、app/Providers/RouteServiceProvider.phpで定義されていた$namespaceがコメントアウトされているため、上記のようにルーティングを書くなら、以下のようにコメントを外す必要があります。

/**
 * The controller namespace for the application.
 *
 * When present, controller route declarations will automatically be prefixed with this namespace.
 *
 * @var string|null
 */
protected $namespace = 'App\\Http\\Controllers'; // ここのコメントアウトを外す

参考サイト
kawax.biz

ここで登録したチャネルのMessaging API設定から設定したWebhook URLが接続できるか確認します。
f:id:eeko-amaryllis:20210114175216p:plain:w400
成功とでたらWebhook設定がうまく行っています。
f:id:eeko-amaryllis:20210114175014p:plain:w400

Controllerの設定

コントローラーを作成します。

php artisan make:controller QrBotController
<?php

namespace App\Http\Controllers;

use Endroid\QrCode\QrCode;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use LINE\LINEBot;
use LINE\LINEBot\HTTPClient\CurlHTTPClient;
use LINE\LINEBot\MessageBuilder\ImageMessageBuilder;
use LINE\LINEBot\Event\MessageEvent;
use LINE\LINEBot\Event\MessageEvent\TextMessage;
use Illuminate\Support\Facades\Log;

class QrBotController extends Controller
{
    public function reply(Request $request) {
        $channel_secret = env('LINE_CHANNEL_SECRET');
        $access_token = env('LINE_ACCESS_TOKEN');
        $request_body = $request->getContent();
        $hash = hash_hmac('sha256', $request_body, $channel_secret, true);
        $signature = base64_encode($hash);

        if($signature === $request->header('X-Line-Signature')) {   // LINEからの送信を検証

            // アクセストークンを使いCurlHTTPClientをインスタンス化
            $client = new CurlHTTPClient($access_token);
            // CurlHTTPClientとシークレットを使いLINEBotをインスタンス化
            $bot = new LINEBot($client, ['channelSecret' => $channel_secret]);


            try {
                $events = $bot->parseEventRequest($request_body, $signature);
                foreach ($events as $event) {
                    if($event instanceof MessageEvent && $event instanceof TextMessage) {   // テキストメッセージの場合

                        $reply_token = $event->getReplyToken(); // 返信用トークン

                        $textMessageBuilder = new \LINE\LINEBot\MessageBuilder\TextMessageBuilder('hello');
                        $response = $bot->replyMessage($reply_token, $textMessageBuilder);
                        echo $response->getHTTPStatus() . ' ' . $response->getRawBody();
                    }
                }
            } catch (\Exception $e) {}
        }
    }
}

Controller内で何をしているかの解説は別の記事で書こうと思います。

以上で、作成したLINE公式アカウント(TEST)で適当なメッセージを送信すると「hello」と自動で送信されると思います。

日付の曜日まで日本語表示させたい

$information->created_at->formatLocalized("%Y/%m/%d (%a) %H:%M")

と書いてましたが、以下でいけるらしい。

<time class="time">
  {{ $information->created_at->isoFormat("YYYY/MM/DD (ddd) hh:mm") }}
</time>