💡

サブディレクトリのLaravelでAjax通信するには

2024/10/13に公開

まえがき

Webアプリケーションは通常、プロジェクトルートにデプロイするので、ローカル環境とパスがずれることはありません。

しかし、本番環境と違って、リモート共有開発環境とか、テスト環境、ステージング環境などは複数の案件で1つのドメインしかなく、サブディレクトリに公開するケースも稀にあります。

この場合、デプロイするとパスがずれて画像取得やAjaxでレスポンスが取得できず苦労します。

みなさん工夫してどうにかしていると思いますが、割とスッキリかけたかな?って思うので私のやり方を公開します。

typescriptを使っていますが、javascriptでも同様にできるので適宜読み替えてください。

やり方

.envの設定

あらかじめLaravelの.envにプロジェクトルートを記述しておきます。
サブディレクトリ名を含める点が通常と違います。

APP_URL=http://sample.com/sub_dir

設定できたら設定を反映させましょう。

php artisan optimize

bladeのmetaタグにプロジェクトルートを埋め込む

以下のようにしてheadタグ内に、metaタグを追加します。
config('app.url')に上記で設定した.envの値が展開されます。
csrfトークンも同様に記述します。

個別のページのbladeよりlayout.blade系の共通枠に書くのがオススメ。

ちなみにbodyタグ内にhiddenタグなどを用意するやり方もありますが、HTMLレイアウトに関係ないものはあまり本文に入れたくないので、jsでしか使わないものはmetaタグの方がスッキリするかなと思います。

layout.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

    {{-- jsで実行環境のプロジェクトルートを取得するために使用 --}}
    <meta name="project-root-path" content="{{ config('app.url') }}">

    {{-- jsでAjax通信するために使用 --}}
    <meta name="csrf-token" content="{{ csrf_token() }}">

typescript側からbladeの値を取得する。

querySelectorで前の項で埋め込んだmetaタグの値をts側に持ってきます。
モジュールに切り出して共通関数にしておきます。

HtmlQueryHelper.ts
/**
 * HTMLタグからの値の取得を簡略化する。
 */
export const HtmlQueryHelper = {
    /**
     * Metaタグから値を取得する。
     *
     * @param metaName
     * @return metaタグのcontent属性の値
     */
    getMetaTagContent(metaName: string): string {
        const elemMeta = document.querySelector(`meta[name="${metaName}"]`);

        let result = "";
        if (elemMeta) {
            const value = elemMeta.getAttribute("content");
            result = value ?? "";
        }

        return result;
    },
};

Ajax通信の共通モジュールを作成する。

Ajaxリクエストの共通モジュールを作成します。
サブディレクトリから先のパスを受け取って、ここまでに取得したプロジェクトルートの文字列と結合してURLを取得します。
これで環境差分を解消することが出来ました。

RequestHelper.ts
import { HtmlQueryHelper } from "./HtmlQueryHelper";

/**
 * サーバサイドにAPIリクエストを要求する処理を簡略化する。
 */
export const RequestHelper = {
    postRequest(path: string, params: object): void {
        const projectRoot =
            HtmlQueryHelper.getMetaTagContent("project-root-path");

        const url = projectRoot + path;

        const csrfToken = HtmlQueryHelper.getMetaTagContent("csrf-token");
        console.log("csrfToken", csrfToken);

        console.log("[Ajax][POST][" + url + "]", { url: url });
        (async () => {
            try {
                const response = await fetch(url, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/json",
                        "X-CSRF-TOKEN": csrfToken,
                    },
                    body: JSON.stringify(params),
                });

                if (response) {
                    const data = await response.json();
                    console.log("[Ajax][通信成功]", data);
                }
            } catch (error) {
                console.error("[Ajax][通信失敗]" + error);
            }
        })();
    },
};

必要なパラメータ等を渡して実行

あとはこんな感じで呼び出すだけです。
ここまで共通化したので以降はこれだけの記述で動かせますね。

main.ts

import { RequestHelper } from "./RequestHelper";

    /**
     * リクエストする。
     */
    postTicketPoistion(): void {

        // AjaxでPOSTリクエストを投げる。
        const params = {
            message: "hello world",
        };

        RequestHelper.postRequest("/greet/store", params);
    },

おわりに

いかがだったでしょうか?
今回の手法はサブディレクトリに限らず、bladeからjs/tsへデータを引き継ぐ場合に色々使い道があると思います。

もっといい方法あるよって方はコメントに書いていただければいいかなと思います。

株式会社ONE WEDGE

【Serverlessで世の中をもっと楽しく】 ONE WEDGEはServerlessシステム開発を中核技術としてWeb系システム開発、AWS/GCPを利用した業務システム・サービス開発、PWAを用いたモバイル開発、Alexaスキル開発など、元気と技術力を武器にお客様に真摯に向き合う価値創造企業です。
https://onewedge.co.jp

Discussion