🙆‍♀️

Laravelでクエリパラメータを持たせたURLのQRコードを発行する

2025/02/06に公開

LaravelでのQRコード発行が思ったよコード発行が思ったより簡単だったので、忘備録に残します。

環境

  • PHP 8.3.12 (cli) (built: Sep 24 2024 18:08:04) (NTS)
  • Laravel Framework 11.41.3
  • react 18.2.0
  • typescript 5.0.2
  • Breeze

何をやるのか

  • tokenIdを紐付けたURLを発行する。
  • tokenIdはクエリパラメータに掲載。

パラメータとは

知ってる方は飛ばしてOK。パラメータは大まかにいうと2種類ある。

  • ルートパラメータ
  • クエリパラメータ

ルートパラメータ

URLのパスの一部として値を埋め込む形式。
REST APIではリソースを特定するために使用される。
(例)https://domein.com/blog/123

クエリパラメータ

? の後に key=value 形式で追加。
フィルタリング、検索、ページネーション、認証などに利用。
& で複数指定可能(例:?page=2&sort=desc)
(例)https://domein.com/login?UUID=123

パラメータの作成方法は今回は割愛します。

動的に生成したURLをQRコードにする

ユーザーに合わせてQRコードを発行できたら、ユーザーはアクセスしやすいですね。
その手順の一つを残します。

使ったライブラリ

endroid/qr-codeを使用します。下記のコマンドをターミナルで使用します。(アプリのディレクトリ内で)

composer require endroid/qr-code

コントローラーを編集

今回はユーザー情報にあるtokenIdを紐付けるつもりで解説します。

//QRコードライブラリに必要な読み込み
use Endroid\QrCode\QrCode;
use Endroid\QrCode\Writer\PngWriter;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\ErrorCorrectionLevel;
use Endroid\QrCode\RoundBlockSizeMode;
use Endroid\QrCode\Color\Color;

//認証機能からユーザー情報を引き出すための読み込み
use Illuminate\Support\Facades\Auth;

//引き出した情報を暗号化させるための読み込み
use Illuminate\Support\Facades\Crypt;

//フロントエンドでReactを使うための読み込み
use Inertia\Inertia;

public function add()
{
    // 認証済みユーザー情報を取得
    $user = Auth::user();
    
    // token_id を取得
    $tokenId = $user->token_id;

    // .env の APP_URL を取得(ローカル開発・本番でURLを切り替え可能)
    $appUrl = config('app.url');

    // token_id を暗号化
    $encryptedTokenId = Crypt::encryptString($tokenId);

    // QRコードに埋め込むログインURLを作成
    $loginChildUrl = $appUrl . '/login?token_id=' . urlencode($encryptedTokenId);

    // QRコードを生成
    $qrCode = QrCode::create($loginChildUrl)
        // 文字エンコーディングの設定(UTF-8にすることで日本語などの特殊文字も対応可能)
        ->setEncoding(new Encoding('UTF-8'))

        // 誤り訂正レベル(QRコードの一部が欠けても復元できる強度)
        // High(30% まで復元可能)を設定
        ->setErrorCorrectionLevel(ErrorCorrectionLevel::High)

        // QRコードのサイズ(200px 四方に設定)
        ->setSize(200)

        // QRコードの余白(10px に設定)
        // 余白がないと一部のリーダーで読み取れない可能性がある
        ->setMargin(10)

        // QRコードの四角形のサイズを調整(ブロックサイズを余白基準に設定)
        ->setRoundBlockSizeMode(RoundBlockSizeMode::Margin)

        // QRコードの前景色(黒に設定)
        ->setForegroundColor(new Color(0, 0, 0))

        // QRコードの背景色(白に設定)
        ->setBackgroundColor(new Color(255, 255, 255));

    // QRコードをPNGで出力
    $writer = new PngWriter();
    $result = $writer->write($qrCode);

    // Base64エンコード
    $qrCodeBase64 = 'data:' . $result->getMimeType() . ';base64,' . base64_encode($result->getString());

    // フロントに渡す
    return Inertia::render('Teams/MemberAdd', compact(
        'tokenId',
        'loginChildUrl',
        'qrCodeBase64'
    ));
}

React側でQRコード表示

import { Head, Link, useForm, usePage } from '@inertiajs/react';
import InputError from '@/Components/InputError';
import InputLabel from '@/Components/InputLabel';
import PrimaryButton from '@/Components/PrimaryButton';
import Checkbox from '@/Components/Checkbox';
import TextInput from '@/Components/TextInput';
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { FormEventHandler } from 'react';

export default function MemberAdd({
    qrCodeBase64,
    loginChildUrl,
}: {
    qrCodeBase64: string;
    loginChildUrl: string;
}) {

    return (
        <AuthenticatedLayout>
            <Head title="RQコード表示" />
                    <p>ログインURLはこちら</p>
                    <p className='break-all'>URL: {loginChildUrl}</p>
                    {/* QRコードを表示 */}
                    {qrCodeBase64 && (
                     <div>
                        <p>QRコード:</p>
                         <img src={qrCodeBase64 ?? ''} alt="ログインQRコード" />
                     </div>
             )}
             </>
        </AuthenticatedLayout>
    );
}

ブラウザで見るとこんな感じ

URLは暗号化によりめちゃくちゃ長くなるものの、QRコードを発行できました。
スマホで読み込んでみると、今はlocalhostで同一のページは見れないものの、URL自体は末尾まで同じものが排出されました。
※今回tokenIdで説明している部分をカスタムしているため、画像を一部修正しています

おまけ

最初は「Simple QR Code」というものを利用しようと思ったものの、Laravel11では使用できないと見かけたので断念。以前のLaravelでは標準搭載だったとの記述も見かけたので、バージョンによってはもっと手軽に作成することもできるかもしれません。

Discussion