
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、この間ある記事を読んで「面白そう!」という思う内容がありました。
それは・・・・・・
QRコードで駐車エリアを保存し、後でそれがどこだったかが分かる
というシステムでした。
つまり、大きなショッピングセンターにお買い物にいって「あれ、ウチの車どこに停めたっけ!?」がなくなるシステムということですね。
そして、この記事を読んでみて「へぇ、QRコードのこんな使い方があるんだ!」と感心すると同時に、どうしても自分で作ってみたくなりました。
そこで
今回はLaravel
を使ってこの「駐車位置・保存システム」を実装してみたいと思います。
ぜひ学習のお役にたてますと嬉しいです。
(最後に、今回実際に開発したソースコード一式をダウンロードできますよ)
「ペーパー・ドライバーです
」
開発環境: Laravel 8.x、Vue 3
目次 [非表示]
やりたいこと
今回実装するのは、以下の流れです。
- 駐車したエリアにあるQRコードを読み取る(= エリアデータをブラウザに保存)
- (お買い物に行く)
- 出口に設置されたQRコードを読み取ると、保存エリアにマークがついた地図が表示される
なお、今回テストで使う地図は以下になります。
※ 今回はテストなのでたった4エリアですが、実際はもっとたくさんあることを想定しています。
では、楽しくやっていきましょう
パッケージをインストールする
先にPHP
でQRコード
を作成することができるように「php-qrcode」というパッケージをインストールしておきます。
以下のコマンドを実行してください。
composer require chillerlan/php-qrcode
そして、画像を操作できる「intervention」です。
同じくインストールしてください。
composer require intervention/image
ルートをつくる
では、続いてルートをつくります。
routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use \App\Http\Controllers\ParkingController; //
ここも忘れず!
// 省略
Route::prefix('parking')->group(function(){
Route::get('/qr_code/{parking?}', [ParkingController::class, 'qr_code'])->name('parking.qr_code');
Route::get('/show', [ParkingController::class, 'show'])->name('parking.show');
});
1つ目のルートがQRコードを表示するページです。
なお、{parking?}
とハテナマークがついているのは、「パラメータがなくてもOK 」にするためです。
つまり、以下のようなURLがOKになります。
- http://******/parking/qrcode/1 :数字はIDで可変
- http://******/parking/qrcode : こっちもOK
そして、2つ目ですが、これは「駐車位置のパラメータがある or ない」でそれぞれ以下のように動作が変わるようにしています。
- パラメータがある: そのパラメータをブラウザへ保存
- パラメータがない: すでに保存されたデータから地図を表示
モデル&マイグレーションをつくる
続いて、データを管理するモデルとマイグレーション(DBテーブル)をつくります。
以下のコマンドを実行してください。
php artisan make:model Parking -m
すると、モデルとマイグレーションのファイルが作成されるので、マイグレーションを以下のように変更してください。
database/migrations/****_**_**_******_create_parkings_table.php
// 省略
public function up()
{
Schema::create('parkings', function (Blueprint $table) {
$table->id();
$table->string('name')->comment('エリア名');
$table->integer('location_x')->comment('エリア位置:X軸');
$table->integer('location_y')->comment('エリア位置:Y軸');
$table->timestamps();
});
}
// 省略
なお、この中のlocation_x
とlocation_y
は、最初に見てもらった地図画像上で「そのエリアがどこにあるの?」が分かる座標になります。
単位はピクセルです。
駐車エリアの情報を登録する
そして、DBテーブル「parkings」に登録するデータをつくります。
以下のコマンドでSeeder
ファイルを作成してください。
php artisan make:seed ParkingsTableSeeder
すると、ファイルが作成されるので、中身を以下のように変更します。
database/seeders/ParkingsTableSeeder.php
// 省略
public function run()
{
$areas = [
[
'name' => 'ライオンエリア',
'location_x' => 160,
'location_y' => 265,
],
[
'name' => '猫エリア',
'location_x' => 480,
'location_y' => 265,
],
[
'name' => 'うさぎエリア',
'location_x' => 160,
'location_y' => 410,
],
[
'name' => 'パンダエリア',
'location_x' => 480,
'location_y' => 410,
],
];
foreach ($areas as $area) {
$parking = new Parking();
$parking->name = $area['name'];
$parking->location_x = $area['location_x'];
$parking->location_y = $area['location_y'];
$parking->save();
}
}
// 省略
続いて、このファイルが有効になるようにLaravel
に登録します。
database/seeders/DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*
* @return void
*/
public function run()
{
// User::factory(10)->create();
$this->call(ParkingsTableSeeder::class); //
ここを追加しました
}
}
では、以下のコマンドを実行してDBテーブルを構築しましょう。
php artisan migrate:fresh --seed
実行するとテーブルはこうなります。
コントローラーをつくる
次に、コントローラーをつくります。
以下のコマンドを実行してください。
php artisan make:controller ParkingController
すると、ファイルが作成されるので中身を以下のように変更します。
app/Http/Controllers/ParkingController.php
<?php
namespace App\Http\Controllers;
use App\Models\Parking;
use chillerlan\QRCode\QRCode;
use Illuminate\Http\Request;
class ParkingController extends Controller
{
public function qr_code(Parking $parking) {
$url = route('parking.show', [
'x' => $parking->location_x,
'y' => $parking->location_y
]);
$data = (new QRCode())->render($url);
return \Image::make($data)->response();
}
public function show(Request $request) {
return view('parking.show')->with([
'x' => intval($request->x),
'y' => intval($request->y)
]);
}
}
この中でやっているのは以下のとおりです。
qr_code()
このメソッドでQRコードを作成します。
やっている事は、該当するID番号の「駐車エリア・データ」を取得し、その位置情報をx
、y
として「/parking/show」のURLに含め、それをQRコード化しています。
つまり、以下のようなURLになります。
http://******/parking/show?x=160&y=410
show()
ここでは、2つのコンテンツを切り替えることになります。
その詳細は次のとおりです。
- 位置情報のパラメータがある場合: そのデータを
localStorage
へ保存 - パラメータがない場合: 保存されている駐車エリアを表示
つまり、上の項目が駐車した直後に読み取るQRコードで、下が駐車位置を探すときのQRコードになります。
ビューをつくる
では、最後にビューをつくります。
以下のファイルを作成してください。
resources/views/parking/show.blade.php
<html>
<head>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<!-- 駐車したエリアのデータを保存する場合 -->
<div v-if="mode=='save'">
<div class="p-3 text-center" v-if="locationSaved">
駐車位置を保存しました。
</div>
</div>
<!-- 駐車したエリアのデータを表示する場合 -->
<div id="canvas-box" v-else-if="mode=='show'">
<canvas id="canvas"></canvas>
</div>
</div>
<script src="https://unpkg.com/vue@3.0.2/dist/vue.global.prod.js"></script>
<script>
Vue.createApp({
data() {
return {
location: {
x: {{ $x }},
y: {{ $y }},
},
locationSaved: false,
canvas: null,
context: null,
mapImage: null
}
},
methods: {
save() {
try {
const parkingData = JSON.stringify(this.location);
localStorage.setItem('parking_location', parkingData);
this.locationSaved = true;
} catch (e) {
console.log(e);
alert('残念ながらブラウザが対応していません。');
}
},
show() {
this.context.drawImage(this.mapImage, 0, 0, this.canvas.width, this.canvas.height);
const parkingData = JSON.parse(
localStorage.getItem('parking_location')
);
const parkingX = parkingData.x;
const parkingY = parkingData.y;
const radius = this.canvas.width * 0.085;
const x = this.canvas.width * parkingX / this.mapImage.width;
const y = this.canvas.height * parkingY / this.mapImage.height;
this.context.arc(x, y, radius, 0, 2 * Math.PI);
this.context.strokeStyle = '#ffff99';
this.context.lineWidth = 10;
this.context.stroke();
}
},
computed: {
mode() {
return (this.location.x > 0 && this.location.y > 0)
? 'save'
: 'show';
}
},
mounted() {
if(this.mode === 'save') {
this.save();
} else if(this.mode === 'show') {
this.mapImage = new Image;
this.mapImage.onload = () => {
const canvasWidth = document.querySelector('#canvas-box').clientWidth;
const canvasHeight = canvasWidth * this.mapImage.height / this.mapImage.width;
this.canvas = document.querySelector('#canvas');
this.canvas.width = canvasWidth;
this.canvas.height = canvasHeight;
this.context = this.canvas.getContext('2d');
this.show();
};
this.mapImage.src = '/images/parking_map.png';
}
}
}).mount('#app');
</script>
</body>
</html>
このビューは、パラメータの状態によって以下2つのmode
になります。
- save: 駐車した場所を「保存」する
- show: 駐車した場所を「表示」する
ではひとつずつご紹介します。
saveモードの場合
ページが表示されるとすぐにsave()
メソッドが実行されます。
このメソッドの中では、ブラウザのlocalStorage
へデータを保存することになりますが、念のためlocalStorage
が使えないブラウザのことも考え、try ~ catch
で例外処理をつけています。
show モードの場合
このモードの場合、いろいろとやっているのですが、目的は「横幅いっぱいに地図を表示する」です。
つまり、今回の地図画像は640 x 480 ピクセル
ですが、表示する環境はそれぞれ違ってきますので、100%
の横幅にします。
ただ、そうなってくると、location_x
、location_y
として取得した値が「拡大 or 縮小」している地図に対して、正しい位置ではなくなってしまいます。
そのため、位置を「割合」にすることでこれを解決しています。(こういう計算って難しいですよね・・・)
テストしてみる
では、実際にテストしてみましょう
なお、地図はこうなっています。
駐車位置を保存するテスト
まずは「ライオンエリア」に車を停めることを想定してやってみます。
ブラウザで「http://******/parking/qr_code/1」にアクセスしてください。
QRコードが表示されるのでこれを読み取り、ブラウザで表示します。
すると、駐車位置がブラウザに保存されます。
では、本当にlocalStorageに保存されてるか確認してみましょう。
まずは1段階目は成功ですね
駐車位置を表示するテスト
そして、次は「お買い物後」を想定してテストします。
今回はパラメータをつけずにQRコード・ページへアクセスします。URLは、「http://******/parking/qr_code」です。
すると、またQRコードが表示されるので、再度読み取ってブラウザで表示します。
すると・・・・・・
はい
うまく「ライオンエリア」に○マークがつきました。
成功です
では、念のために同じ手順で「うさぎエリア」だった場合でもテストしてみます。
- 「http://******/parking/qr_code/3」へアクセス
- QRコード読み取り(駐車位置を保存)
- 「http://******/parking/qr_code」へアクセス
- QRコード読み取り
- 確認
すると・・・・・・
はい
今度は「うさぎエリア」に○マークがつきました。
こちらも成功です
ダウンロードする
以下から今回実際に開発したソースコード一式をダウンロードすることができます。
【Laravel】QRコードで駐車位置・表示システムをつくる※ ただし、マイグレーションなどはご自身で実行してください。
※ また、地図に含まれているイラストはフリーライセンスで公開していないので、この中には含まれていません。テストする場合は、このページからダウンロードして使ってください。
おわりに
ということで、今回はQRコードを使った「駐車位置システム」を作ってみました。
ちなみに、今回は駐車位置を表示するだけでしたが、当初は「現在地からどう行けばそのエリアに到着できるか?」という2地点を想定していました。
しかし、あまりにも複雑になりそうで断念することになりました(また、2階・3階などの情報も扱ってみたかったんですが・・・それもやっちゃうと学習には不向きになるという結論になり、これもやめました)
とはいえ、基本のコードを使っていろいろ拡張ができると思いますので、ぜひ試して見てくださいね。
ではでは〜
「えっ、カビキラーって、
こんな落ちるんですね」