Laravelで「再配達」システムをつくる

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、最近特にネットショッピングで商品を買うことが増えてはいないでしょうか。

私の場合もコンピュータ機器だけでなく、服なんかもインターネットで購入するようになりました。

しかし、その便利さの裏でちょっとした「社会現象」になっているのが、

再配達問題

ですよね。

そして、数日前に私宛で再配達の通知が来ていたのですが、その不在連絡票には以下のようなQRコードが貼ってありました。

これ、見ていただいたとおりQRコードを読み取るだけで簡単に再配達を依頼することができるようになっているんですね。(さすが郵便局です😊)

そこで❗
今回はこの再配達の依頼システムをLaravelでつくってみることにしました。

ぜひ学習のお役にたてましたら嬉しいです😊✨
(最後に今回実際に開発したソースコード一式をダウンロードできますよ👍)

「ネットショッピング、
帽子だけサイズ感が難しいです😂」

開発環境: Laravel 8.x、Vue 3

DBテーブルとモデルをつくる

まず最初に再配達データを管理するDBテーブルとそのテーブルを操作するモデルをつくります。

以下のコマンドを実行してください。

php artisan make:model Redelivery -m

すると、マイグレーション・ファイルとモデルが作成されるので中身を以下のように変更します。

database/migrations/****_**_**_******_create_redeliveries_table.php

// 省略

public function up()
{
    Schema::create('redeliveries', function (Blueprint $table) {
        $table->id();
        $table->uuid('uuid')->comment('UUID');
        $table->date('redelivery_date')->nullable()->comment('再配達希望日');
        $table->integer('redelivery_time_id')->nullable()->comment('再配達希望時間帯 ID');
        $table->timestamps();
    });
}

// 省略

そして、モデルです。

app/Models/Redelivery.php

<?php

namespace App\Models;

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

class Redelivery extends Model
{
    use HasFactory;

    // Accessor
    public function getUrlAttribute() { // 再配達依頼ページのURL

        return route('redelivery.edit', $this->uuid);

    }

    public function getRedeliveryRequestedAttribute() { // すでに再配達依頼しているかどうか

        return (
            !is_null($this->redelivery_date) &&
            !is_null($this->redelivery_time_id)
        );

    }
}

この中には以下2つのAccessorを登録しています。

  • $redelivery->url: 再配達依頼ページのURLを取得する
  • $redelivery->redelivery_requested: すでに再配達依頼が完了しているかどうかを true / false で取得

テストデータをつくる

続いて、Seeder機能を使ってテストデータをいくつか用意していきましょう。
以下のコマンドを実行してください。

php artisan make:seed RedeliveriesTableSeeder

するとファイルが作成されるので、中身を以下のように変更してください。

database/seeders/RedeliveriesTableSeeder.php

// 省略

public function run()
{
    for($i = 0 ; $i < 5 ; $i++) {

        $redelivery = new Redelivery();
        $redelivery->uuid = Str::uuid();
        $redelivery->save();

    }
}

// 省略

変更したら、RedeliveriesTableSeederLaravelへ登録します。

database/seeders/DatabaseSeeder.php

// 省略

public function run()
{
    $this->call(RedeliveriesTableSeeder::class); // 👈 ここを追加しました
}

// 省略

※ なお、お好みでFactoryを使いたい場合は、以下のURLをご覧ください。

📝参考ページ: Laravel 8.x の新機能・変更点のまとめ > Factory の構造が変更になる

登録が完了したら、以下のコマンドでDBテーブル&テストデータを再構築します。

php artisan migrate:fresh --seed

すると実際のテーブルはこうなりました。

※ なお、redelivery_dateredelivery_time_idはお客さんの操作で登録されることになります。

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

ではLaravel側の開発に行く前に、必要なパッケージをインストールしておきましょう。

今回QRコードJavaScriptで描画します。
以下のコマンドを実行してください。

npm i qrcode --save

続いて、ビルドするための設定をします。

resources/js/app.js

// 省略

window.QRCode = require('qrcode'); // 👈 ここを追加

なお、window.QRCodeは、<script> 〜 </script>のなかでQRCodeとして呼び出すことができるようになります。

では、以下のコマンドでビルドしましょう。

npm run dev

これで、public/js/app.jsの中にパッケージがひとまとめになりました。

時間帯のコンフィグをつくる

実際にはデータベースの中に「マスターデータ」として保存しておいたほうがいいかもしれませんが、今回は「こういうやりかたもあるよ👍」というのをご紹介したいので、再配達の時間帯はconfigフォルダの中にセットすることにします。

config/redelivery.php

<?php

return [
    'times' => collect([
        ['id' => 1, 'text' => '8~12時'],
        ['id' => 2, 'text' => '12~14時'],
        ['id' => 3, 'text' => '14~16時'],
        ['id' => 4, 'text' => '16~18時'],
        ['id' => 5, 'text' => '18~20時'],
    ])
];

ちなみに、わざわざcollect()を使ってコレクション化しているのは、以下のようにすると自動的にJSON化してくれるからです。(通常の配列の場合はエラーになります)

<!-- いちいち json_encode() を実行しなくて済みます👍 -->
{!! config('redelivery.times') !!}

不在連絡票部分をつくる

では、まずは「配達をする人」が使うことになる「不在連絡票」の部分をつくります。

※ 実際にはこのコンテンツを印刷して使うことになると思います。

コントローラーをつくる

以下のコマンドを実行してください。

php artisan make:controller DeliveryNoticeController

すると、ファイルが作成されるので中身を以下のように変更してください。

app/Http/Controllers/DeliveryNoticeController.php

<?php

namespace App\Http\Controllers;

use App\Models\Redelivery;
use Illuminate\Http\Request;

class DeliveryNoticeController extends Controller
{
    public function show(Redelivery $redelivery) {

        return view('delivery_notice.show')->with([
            'redelivery' => $redelivery
        ]);

    }
}

【追記:2021.03.29】訪問ユーザーさんからご指摘をいただきまして、コードが古いままになっていたのを修正しました。みなさん、いつもご協力ありがとうございます😊✨

ビューをつくる

次に、先ほどのコントローラーで指定したビューをつくります。

resources/views/delivery_notice/show.blade.php

中身はこうなります。

<html>
<body>
<div>
    このQRコードを読み取ると再配達依頼ができます。<br>
    <canvas id="qrcode_canvas"></canvas><br>
    {{ $redelivery->url }}
</div>
<script src="/js/app.js"></script>
<script>

    window.onload = () => {

        const canvas = document.querySelector('#qrcode_canvas');
        const url = '{{ $redelivery->url }}';

        QRCode.toCanvas(canvas, url, error => {

            if(error) {

                console.error(error); // エラーになった場合

            }

        });

    }

</script>
</body>
</html>

中身としては、DBから取得した再配達データから「再配達依頼ページ」へのURLを取得し、それをQRコード化しているだけです。

ルートをつくる

では、最後にルートです。
以下のコードを追加してください。

routes/web.php

use App\Http\Controllers\DeliveryNoticeController;

// 省略

// 不在連絡票
Route::get('delivery_notice/{redelivery}', [DeliveryNoticeController::class, 'show']);

そして、「http://******/delivery_notice/1」にアクセスすると以下のようになります。(ID番号を変更すると自動でQRコードも変わります)

再配達を依頼する部分をつくる

続いて、「荷物を受け取る人」が使うことになる再配達・依頼ページです。

コントローラーをつくる

以下のコマンドでコントローラーをつくってください。

php artisan make:controller RedeliveryController

するとファイルが作成されるので、中身を以下のように変更します。

app/Http/Controllers/RedeliveryController.php

<?php

namespace App\Http\Controllers;

use App\Models\Redelivery;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class RedeliveryController extends Controller
{
    public function edit(Redelivery $redelivery) {

        $this->checkValid($redelivery);

        return view('redelivery.edit')->with([
            'redelivery' => $redelivery
        ]);

    }

    public function update(Request $request, Redelivery $redelivery) {

        $time_ids = config('redelivery.times')->pluck('id');
        $request->validate([
            'date' => [
                'required',
                'date',
                'after_or_equal:today'
            ],
            'time_id' => [
                'required',
                Rule::In($time_ids)
            ]
        ]);
        $this->checkValid($redelivery);

        $redelivery->redelivery_date = $request->date;
        $redelivery->redelivery_time_id = $request->time_id;
        $result = $redelivery->save();

        // 保存に成功したらここでメールやLINE通知などをするといいでしょう

        return [
            'result' => $result
        ];

    }

    private function checkValid($redelivery) {

        if(is_null($redelivery)) {

            abort(404);

        } else if($redelivery->redelivery_requested) {

            abort(400, 'すでに再配達依頼は登録されています');

        }

    }
}

この中で少しイレギュラーなのは、checkValid()の部分です。

この中では、ルートからバインディングされてきた$redeliveryが有効 or 無効をチェックし、もし無効ならその時点で処理を終了させるようにしています。

なお、update()の中でデータ保存が完了したら、メールやLINE通知で配達員さんに通知するのもアリかもしれません。

ビューをつくる

では、次にビューです。

resources/views/redelivery/edit.blade.php

<html>
<head>
    <link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<div id="app">
    <div class="leading-loose">
        <form class="max-w-xl m-4 p-10 bg-white rounded shadow-xl">
            <p class="text-gray-500 font-medium mb-3 font-bold">再配達の依頼</p>
            <div class="">
                <label class="block text-sm text-gray-00" for="cus_name">希望日</label>
                <input class="w-full px-5 py-1 text-gray-700 bg-gray-200 rounded" type="date" min="{{ date('Y-m-d') }}" v-model="params.date">
            </div>
            <div class="mt-3">
                <label class="block text-sm text-gray-600" for="cus_email">時間帯</label>
                <select class="w-full px-4  py-3 text-gray-700 bg-gray-200 rounded" v-model="params.time_id">
                    <option></option>
                    <option v-for="time in redeliveryTimes" :value="time.id" v-text="time.text"></option>
                </select>
            </div>
            <div class="mt-5">
                <button class="px-4 py-1 text-white font-light tracking-wider bg-blue-500 rounded" type="button" @click="onSubmit">送信する</button>
            </div>
        </form>
    </div>
</div>
<script src="/js/app.js"></script>
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.19.2/axios.min.js"></script>
<script>

    Vue.createApp({
        data() {
            return {
                params: {
                    date: '',
                    time_id: ''
                },
                redeliveryTimes: {!! config('redelivery.times') !!}
            }
        },
        methods: {
            onSubmit() {

                const url = '{{ route('redelivery.update', $redelivery->uuid) }}';
                axios.put(url, this.params)
                    .then(response => {

                        alert('再配達依頼が完了しました!');

                    })
                    .catch(error => {

                        alert('入力が正しくありません。');

                    });

            }
        }
    }).mount('#app');

</script>
</body>
</html>

この中では日付と時間帯を選択肢し、送信しているだけです。

なお、今回はCDNを使っていますが、本来Vueaxiosnpmでインストールしてビルドし、public/js/app.js内へひとまとめにしておくべきです。

ルートをつくる

では、最後にルートを追加します。

routes/web.php

use \App\Http\Controllers\RedeliveryController;

// 省略

// 再配達依頼
Route::get('redelivery/{redelivery:uuid}', [RedeliveryController::class, 'edit'])->name('redelivery.edit');
Route::put('redelivery/{redelivery:uuid}', [RedeliveryController::class, 'update'])->name('redelivery.update');

テストしてみる

では実際にテストしてみましょう❗

まず「http://*****/delivery_notice/1」にアクセスしてみましょう。

すると、QRコードが表示されるので、スマホで読み取って再配達・依頼URLへアクセスします。

そして、日付&時間帯を選択して「送信する」ボタンを押します。

すると・・・・・・

はい❗
完了メッセージが表示されました。

では、データベースの方も確認してみましょう。

はい❗
うまくデータ保存されています。

なお、URLが間違っている場合もチェックしてみましょう。

404エラーが返ってきました。
そして、すでに再配達依頼が完了している場合のチェックです。

こちらもうまくいきました❗

成功です😊✨

ダウンロードする

今回実際に開発したソースコード一式を以下からダウンロードすることができます。

Laravelで「再配達」システムをつくる

※ ただしパッケージのインストールやビルドなどはご自身で実行してください。

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回は再配達システムをLaravel + Vueで作ってみました。

今回はデータベースはとてもシンプルな内容でしたが、実際の配送システムのように「保管期限」や「配達員名」など別の情報を保存しておくとより使いやすいものになるんじゃないでしょうか。

なお、どうやら今回受け取った不在連絡票を見ると電話での再配達依頼もできるようになっているようです。(スゴイですね❗)

ということで、いつかTwilioと絡めて電話での再配達依頼のシステムも作ってみたいと思います。

ぜひご期待下さい。

ではでは〜❗

「サムネイルを
けいちょんチャンネル仕様に変えました👍」

このエントリーをはてなブックマークに追加       follow us in feedly