0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

web.phpに関数とbladeを使用出来るようにする

Last updated at Posted at 2025-09-06

Laravelのweb.phpで関数を使用するには

色々試した結果以下のやり方で動くことを確認しました。

web.phpにapp/Http/Controllersに定義したapp/Http/Controllers/SpecImageController.phpの内容を使用する場合

web.php でルートを定義して SpecImageController を呼び出す。

web.php
<?php

use App\Http\Controllers\ProfileController;
use App\Http\Controllers\BulletTestCaseController;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\SpecificationController;
use App\Http\Controllers\SpecImageController;   // ← これを追加!
use App\Http\Controllers\SpecMdController;
use App\Models\Project; // ← プロジェクトモデルを使う
use Illuminate\Support\Facades\Route;

Route::middleware(['auth'])->group(function () {
    // / にアクセスしたらプロジェクト選択画面へ
    Route::get('/', function () {
        $projects = Project::orderBy('key')->get();
        return view('projects.select', compact('projects'));
    })->name('projects.select');

    Route::post('/spec-images/upload', [SpecImageController::class, 'store'])->name('spec-images.upload');
    Route::get('/spec-images', [SpecImageController::class, 'index'])->name('spec-images.index');

    Route::get('/spec-md', [SpecMdController::class, 'index'])->name('spec-md.index');
    Route::post('/spec-md/upload', [SpecMdController::class, 'store'])->name('spec-md.upload');

    // routes/web.php
    Route::get('/dashboard', [DashboardController::class, 'index'])
        ->middleware(['verified'])
        ->name('dashboard');

    Route::resource('specifications', SpecificationController::class)->middleware(['auth']);
    Route::post('spec-change-requests/{cr}/approve', [SpecificationController::class,'approve'])
    ->name('spec-change-requests.approve')->middleware(['auth']);

    Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
    Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
    Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');

    // プロジェクト単位のテストケース関連
    Route::prefix('projects/{project}')->group(function () {
        Route::get('bullet-cases', [BulletTestCaseController::class,'index'])->name('bullet-cases.index');
        Route::get('bullet-cases/create', [BulletTestCaseController::class,'create'])->name('bullet-cases.create');
        Route::post('bullet-cases', [BulletTestCaseController::class,'store'])->name('bullet-cases.store');
    });

    Route::patch('bullet-case-rows/{row}', [BulletTestCaseController::class,'update'])
        ->name('bullet-cases.rows.update');
    // 行の完了フラグ切替
    Route::post('bullet-case-rows/{row}/toggle', [BulletTestCaseController::class,'toggle'])->name('bullet-cases.rows.toggle');
});

require __DIR__.'/auth.php';

書いているのはこの部分

Route::post('/spec-images/upload', [SpecImageController::class, 'store'])->name('spec-images.upload');
Route::get('/spec-images', [SpecImageController::class, 'index'])->name('spec-images.index');

name('spec-images.upload')route() ヘルパーで使える定義した name() の値となります

Route::post('/spec-images/upload',  ここの記述は 実行するときのエンドポイントを自分で指定しています。

[SpecImageController::class, 'store']SpecImageControllerクラスのstore関数を使用するという書き方

まとめ

この今回の書き方の場合

  • エンドポイント(実行時のURL) = /spec-images/upload
  • HTTPメソッド = POST
  • 処理内容 = SpecImageController@store が実行される
  • SpecImageControllerクラスstoreメソッドが実行できる

bladeからの呼び出し方

dashboard.blade.php からは route() を使ってフォーム送信、
一覧部分は @include('spec-images.index') で取り込める。

画像保存先は storage/app/public/spec-images → シンボリックリンクで public/storage/spec-images 経由でアクセス。

resources/views/dashboard.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl">ダッシュボード</h2>
    </x-slot>

    <div class="max-w-7xl mx-auto py-6 px-4 sm:px-6 lg:px-8 space-y-8">
        {{-- フラッシュメッセージ任意 --}}
        @if (session('status'))
            <div class="p-3 rounded bg-green-50 border border-green-200 text-green-800">
                {{ session('status') }}
            </div>
        @endif

        {{-- サマリーカード --}}
        <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
            <div class="bg-white shadow rounded-lg p-4">
                <div class="text-gray-500 text-sm">プロジェクト</div>
                <div class="text-2xl font-semibold">{{ $projectsCount ?? 0 }}</div>
            </div>
            <div class="bg-white shadow rounded-lg p-4">
                <div class="text-gray-500 text-sm">テストグループ</div>
                <div class="text-2xl font-semibold">{{ $groupsCount ?? 0 }}</div>
            </div>
            <div class="bg-white shadow rounded-lg p-4">
                <div class="text-gray-500 text-sm">テスト行総数</div>
                <div class="text-2xl font-semibold">{{ $rowsCount ?? 0 }}</div>
            </div>
            <div class="bg-white shadow rounded-lg p-4">
                <div class="text-gray-500 text-sm">完了行</div>
                <div class="text-2xl font-semibold">{{ $rowsDoneCount ?? 0 }}</div>
            </div>
        </div>

        {{-- クイック操作 --}}
        <div class="bg-white shadow rounded-lg p-4">
            <div class="flex items-center justify-between mb-3">
                <h3 class="font-semibold">クイック操作</h3>
                <a href="{{ route('projects.select') }}" class="text-indigo-600 hover:underline">
                    プロジェクト選択へ
                </a>
            </div>
            <div class="space-y-2"> {{-- プロジェクトごとに行を作る --}}
                @foreach(($projects ?? []) as $p)
                    <div class="flex gap-2 items-center"> {{-- 1プロジェクト1 --}}
                        <a href="{{ route('bullet-cases.index', ['project' => $p]) }}"
                        class="px-3 py-1.5 border rounded text-sm hover:bg-gray-50">
                            {{ $p->key }}{{ $p->name }}
                        </a>
                        <a href="{{ route('specifications.index', ['project' => $p]) }}"
                        class="px-3 py-1.5 border rounded text-sm hover:bg-gray-50 text-indigo-600">
                            仕様一覧
                        </a>
                        <a href="{{ route('specifications.create', ['project' => $p->id]) }}"
                        class="px-3 py-1.5 border rounded bg-indigo-600 text-black text-sm hover:bg-indigo-700">
                            仕様追加
                        </a>
                    </div>
                @endforeach
                @if(empty($projects) || count($projects ?? []) === 0)
                    <span class="text-gray-500 text-sm">プロジェクトがありません</span>
                @endif
            </div>
        </div>

        {{-- 画像アップロード追加 --}}
        <div class="bg-white shadow rounded-lg p-4">
            <h3 class="font-semibold mb-3">画像アップロード</h3>

            <form action="{{ route('spec-images.upload') }}" method="POST" enctype="multipart/form-data" class="space-y-3">
                @csrf
                <input type="file" name="image" accept="image/*"
                       class="block w-full text-sm file:mr-3 file:py-1.5 file:px-3 file:rounded file:border file:bg-gray-50 file:hover:bg-gray-100" required>
                @error('image')
                    <div class="text-red-600 text-sm">{{ $message }}</div>
                @enderror

                <div class="flex items-center gap-2">
                    <button type="submit"
                            class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-700">
                        アップロード
                    </button>
                    <a href="{{ route('spec-images.index') }}" class="text-indigo-600 hover:underline">
                        保存済み画像を見る
                    </a>
                </div>
            </form>
        </div>

        {{-- 最近作成したグループ --}}
        <div class="bg-white shadow rounded-lg">
            <div class="px-4 py-3 border-b">
                <h3 class="font-semibold">最近のテストグループ</h3>
            </div>
            <div class="p-0 overflow-x-auto">
                <table class="min-w-full text-sm">
                    <thead class="bg-gray-50">
                        <tr>
                            <th class="px-4 py-2 text-left">プロジェクト</th>
                            <th class="px-4 py-2 text-left">グループタイトル</th>
                            <th class="px-4 py-2 text-left">(完了/総数)</th>
                            <th class="px-4 py-2 text-left">作成日時</th>
                            <th class="px-4 py-2 text-left">操作</th>
                        </tr>
                    </thead>
                    <tbody class="divide-y">
                        @forelse(($recentGroups ?? []) as $g)
                            <tr>
                                <td class="px-4 py-2">
                                    @if(isset($g->project))
                                        <a class="text-indigo-600 hover:underline"
                                           href="{{ route('bullet-cases.index', ['project' => $g->project]) }}">
                                            {{ $g->project->key }}
                                        </a>
                                    @else
                                        <span class="text-gray-500">-</span>
                                    @endif
                                </td>
                                <td class="px-4 py-2">{{ $g->title ?? '-' }}</td>
                                <td class="px-4 py-2">
                                    {{ $g->rows_done_count ?? 0 }}/{{ $g->rows_count ?? 0 }}
                                </td>
                                <td class="px-4 py-2">{{ optional($g->created_at)->format('Y-m-d H:i') }}</td>
                                <td class="px-4 py-2">
                                    @if(isset($g->project))
                                        <a class="px-3 py-1 border rounded text-sm hover:bg-gray-50"
                                           href="{{ route('bullet-cases.index', ['project' => $g->project]) }}">
                                            行を見る
                                        </a>
                                    @else
                                        <span class="text-gray-400 text-sm">-</span>
                                    @endif
                                </td>
                            </tr>
                        @empty
                            <tr>
                                <td class="px-4 py-6 text-center text-gray-500" colspan="5">まだデータがありません</td>
                            </tr>
                        @endforelse
                    </tbody>
                </table>
            </div>
        </div>

    </div>
</x-app-layout>

使用しているのが下記の部分↓
"{{ route('spec-md.index') }}" で使用する関数のパスをname() の値 を使用して
<form action="{{ route('spec-images.upload') }}" method="POST" enctype="multipart/form-data">のようにフォームの送信先に指定したり

<a href="{{ route('spec-images.index') }}"> と書いて blade へのページ遷移を記述することが出来ます。

        {{-- 画像アップロード追加 --}}
        <div class="bg-white shadow rounded-lg p-4">
            <h3 class="font-semibold mb-3">画像アップロード</h3>

            <form action="{{ route('spec-images.upload') }}" method="POST" enctype="multipart/form-data" class="space-y-3">
                @csrf
                <input type="file" name="image" accept="image/*"
                       class="block w-full text-sm file:mr-3 file:py-1.5 file:px-3 file:rounded file:border file:bg-gray-50 file:hover:bg-gray-100" required>
                @error('image')
                    <div class="text-red-600 text-sm">{{ $message }}</div>
                @enderror

                <div class="flex items-center gap-2">
                    <button type="submit"
                            class="px-4 py-2 rounded bg-indigo-600 text-white hover:bg-indigo-700">
                        アップロード
                    </button>
                    <a href="{{ route('spec-images.index') }}" class="text-indigo-600 hover:underline">
                        保存済み画像を見る
                    </a>
                </div>
            </form>
        </div>

web.phpにbladeを引っ張ってくる手順

基本的に web.phpcontroller という関係性で
controllerクラスは

  • 関数を実行
  • 関数内にbladeの呼び出しを書いてweb.phpで使用出来るようにする

controllerクラスの関数内に画像保存処理を書いたり、bladeを呼び出すために関数内に return view() を使用することでweb.phpでbladeをコンポーネントととして使用出来るようになる

なので、web.phpから直接bladeを呼び出しているのではなく
controllerクラスの関数経由して呼び出しているようだ。

view関数の使い方

呼び出す書き方は、基本 resources/views/ 以降のパスを「.」で繋いでいくという考え方です。
例えば、resources/views/spec-images/index.blade.php を呼ぶ場合

$images = [...]; // データを準備
return view('spec-images.index', compact('images'));

'spec-images.index'と書くことで、spec-images/index.blade.php を呼び出せます

compact()関数について

compactは簡単に言うとbladeを呼び出す際の引数を連想配列で渡す為の関数です。

Laravelの view('spec-md.index', [...]) の第二引数は「ビューに渡す変数一覧」なので、結果的に Blade 側で $docs が使えるようになります。

クラスのコンストラクタを使用するときの引数という考え方でいいと思います。

下記の例の場合

$docs = ['a.md', 'b.md', 'c.md'];
return view('spec-md.index', compact('docs'));

このときの動作は:

  • compact('docs') が ['docs' => ['a.md', 'b.md', 'c.md']] を作る
  • view('spec-md.index', ['docs' => [...]] ) と同じ意味になる
  • resources/views/spec-md/index.blade.php がレンダリングされる
  • その Blade 内で $docs が利用可能になる

resources/views/spec-images/index.blade.phpを呼び出す時に
$docs配列を渡している

compact()関数 のイメージ

「Bladeに渡す引数をまとめている」 → この理解は正しいです。
PHPの compact('docs') は ['docs' => $docs] という連想配列を返します。

compact関数を使用せずに書く場合

compact関数自体は、配列を連想配列にすることが役割なので
compact関数を使用せずに書くことも可能です。以下が例になります。

$docs = ['a.md', 'b.md', 'c.md'];
return view('spec-md.index', ['docs' => $docs]);

複数の変数を渡す場合

$docs = ['a.md', 'b.md', 'c.md'];
$images = ['1.png', '2.png'];

return view('dashboard', [
    'docs' => $docs,
    'images' => $images,
]);

Blade 側では $docs と $images の両方が使えるようになります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?