Open13

[Laravel]Udemyで学びながらメモするところ

ShiroshitaShiroshita

仮想サーバー

フロント側の簡易サーバー

npm run dev

アプリ側の簡易サーバー

php artisan serve

ShiroshitaShiroshita

名前付きルート

ziggyライブラリにより名前付きルートが使える

名前付きルートとは

名前付きルートは特定のルートへのURLを生成したり、リダイレクトしたりする場合に便利です。 ルート定義にnameメソッドをチェーンすることで、そのルートに名前がつけられます。 ルート定義に名前を付ければ、命名したルート名を指定することでURLの呼び出しができます。

InertiaTest.vue
<Link :href="route('inertia.index')"></Link>
route/web.php
use App\Http\Controllers\InertiaTestController;
~略~
Route::get('/inertia/index',[Inertia TestController::class,'index'])->name('inerta.index');
コントローラー作成
# php artisan make:controller InertiaTestController

   INFO  Controller [C:\xampp\htdocs\uCRM\app/Http/Controllers/InertiaTestController.php] created successfully.

ルート確認
# php artisan route:list

  ~略~
▼設定したものが表示されればOK
  GET|HEAD  inertia/index inertia.index › InertiaTestController@index
  ~略~

出てきたエラー

エラー2

[plugin:vite:vue] At least one <template> or <script> is required in a single file component.

/mnt/c/xampp/htdocs/uCRM/resources/js/Pages/Inertia/Index.vue

直訳

[plugin:vite:vue] 1 つのファイル コンポーネントには少なくとも 1 つの <template> または <script> が必要です。

解決

講座では文字だけだったが、<template></template>をつける必要がある
(バージョンのせい?)

追記
8:40あたりでvueファイルを修正する工程がありました。
見逃していてすみません

uCRM\resources\js\Pages\Inertia\Index.vue
<script setup>
</script>
<template>
    ああああああ
</template>
ShiroshitaShiroshita

ルートパラメータ

laravelは連想配列だったが、
{}(オブジェクト)でパラメータで引数を渡す

resources/js/Pages/InertiaTest.vue
<Link :herf="route('inertia.show', {id : 1 })">button</Link>
routes/web.php
Route::get('/inertia/show/{id}', [InertiaTestController::class,'show'])->name('inertia.show');
InertiaTestController.php
public function show($id)
  {
    //dd($id);
   return Inertia::render('Inertia/Show',[
     'id' => $id
   ]);
  }

dd($id)をコメントアウトしなかった場合の実行結果

以下はVue3の機能

resources/js/Pages/Inetia/Show.vue
<script setup>
defineProps({
    id : String
})
</script>
<template>
    {{  id  }}
</template>
ShiroshitaShiroshita

DB

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('inertia_tests', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('content');
            $table->timestamps();

        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('inertia_tests');
    }
};
DBに反映させるコマンド
# php artisan migrate

   INFO  Running migrations.

  2023_11_25_211538_create_inertia_tests_table  8ms DONE

ShiroshitaShiroshita

LINKコンポーネントでストア保存

InertiaTest.vueファイル
<script setup>
// C:\xampp\htdocs\uCRM\node_modules\@inertiajs\inertia-vue3\src\link.js
import { Link } from '@inertiajs/inertia-vue3';
// Linkコンポーネントでsutore保存
import { ref } from 'vue'

const newTitle = ref('')
const newContent = ref ('')


// defineProps({
//     canLogin: Boolean,
//     canRegister: Boolean,
//     laravelVersion: String,
//     phpVersion: String,
// })
</script>


<template>
Inertiaテストです。<br>
<a href="/">aタグ経由です</a><br>
<Link href="/">Link経由です</Link><br>
<!-- vue.jsの機能で、:hrefだと動的にリンクをかえることができる -->
<Link :href="route('inertia.index')">名前付きルートの確認です</Link><br>
<!-- ↓Linkルートパラメータ実装 -->
<Link :href="route('inertia.show', { id: 1 })">ルートパラメータのテストです</Link>

<!-- Linkコンポーネントでsutore保存 -->
<div class="mb-8"></div>
<input type="text" name="newTitle" v-model="newTitle"><br>
<input type="text" name="newContent" v-model="newContent"><br>
<Link as="button" :href="route('inertia.store')" :data="{
    title: newTitle,
    content: newContent
}">DB保存テスト</Link>
</template>

:::detailes InertiaTestContoroller.php

InertiaTestContoroller.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
// 追加
use Inertia\Inertia;
// Linkコンポーネントでstore保存
use App\Models\InertiaTest;

class InertiaTestController extends Controller
{
    public function index()
    {
        return Inertia::render('Inertia/Index');
    }
    public function show($id)
    {
        // dd()
        // ララベルのデバッグ
        // 処理を止めて変数の値をみることができる
        // dd($id);
        return Inertia::render('Inertia/Show',
        [
            // 引数として入ってきた値をvue側に渡す
            'id' => $id
        ]);
    }
    // Linkコンポーネントでstore保存
    public function store(Request $request)
    {
        $inertiaTest = new InertiaTest;
        $inertiaTest->title = $request->title;
        $inertiaTest->content = $request->content;
        // DBに保存
        $inertiaTest->save();
        //laravel9の機能
        return to_route('inertia.index');
    }
}

:::

ルート側の設定

// Linkコンポーネントでsutore保存
Route::post('/inertia',[InertiaTestController::class, 'index'])->name('inertia.store');
web.phpの全コード
<?php

use App\Http\Controllers\ProfileController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Controllers\InertiaTestController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// Inertia
Route::get('/inertia-test', function () {
    return Inertia::render('InertiaTest');
}
);
// 追加
Route::get('/inertia/index',[InertiaTestController::class, 'index'])->name('inertia.index');

// Linkコンポーネントでsutore保存
Route::post('/inertia',[InertiaTestController::class, 'store'])->name('inertia.store');


// ↓Linkルートパラメータ実装
Route::get('/inertia/show/{id}',[InertiaTestController::class, 'show'])->name('inertia.show');


Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    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');
});

require __DIR__.'/auth.php';

この時点で出てくるエラー

エラー解決

Postをいれる必要がある

InertiaTest.vue
<Link as="button" method="post" :href="route('inertia.store')" :data="{
    title: newTitle,
    content: newContent
}">DB保存テスト</Link>
ShiroshitaShiroshita

フォーム(create)

ルート

web.php
//フォーム(create)
Route::get('/inertia/create',[InertiaTestController::class, 'create'])->name('inertia.create');

コントローラー

InertiaTestController.php
    //フォーム(create) 一般的にはindexメソッドの下に書くことがおおい
    public function create()
    {
        return Inertia::render('Inertia/Create'); //コンポーネント
    }

コンポーネント作成

resources\js\Pages\Inertia\Create.vue
<script setup>
import { reactive } from 'vue';
import { Inertia } from '@inertiajs/inertia'

const form = reactive({
    title: null,
    content: null
})

// フォームを入力したときの処理
const submitFunction = () => {
    Inertia.post('/inertia', form)
}

</script>
<template>
    <!-- フォーム(create) -->
    <form @submit.prevent="submitFunction">
        <input type="text" name="title" v-model="form.title">
        <input type="text" name="content" v-model="form.content">
        <button>送信</button>
    </form>
</template>
ShiroshitaShiroshita

CSRF(シーサーフ)

Cross-Site Request Forgeries(偽造)

🫠詳しく

有名なWEB関連の攻撃方法

悪意のあるURLにアクセスし、思わぬリクエストを利用されてしまう
->正しいページからアクセスできているかトークンを発行して確認する

フラッシュメッセージ

🫠詳しく

保存・登録・削除した際に一度だけ表示させるメッセージ

InertiaController.php
    // Linkコンポーネントでstore保存処理
    public function store(Request $request)
    {
        $request->validate([
            'title' => ['required','max:20'],
            'content' => ['required'],
        ]);

        $inertiaTest = new InertiaTest;
        $inertiaTest->title = $request->title;
        $inertiaTest->content = $request->content;
        // DBに保存
        $inertiaTest->save();
        //laravel9の機能
        return to_route('inertia.index')->with([ //リダイレクトに続けてwithをつけ、セッションメッセージを渡すことができる
            'message' => '登録しました'
        ]);
    }

app\Http\Middleware\HandleInertiaRequests.php
    public function share(Request $request): array
    {
        return array_merge(parent::share($request), [
            'auth' => [
                'user' => $request->user(),
            ],
            'ziggy' => function () use ($request) {
                return array_merge((new Ziggy)->toArray(), [
                    'location' => $request->url(),
                ]);
            },
            //Inertiaのミドルウェア 部分リロード
            'flash' => [
                'message' => fn() => $request->session()->get('message')
            ]
        ]);
    }

resources\js\Pages\Inertia\Index.vue
<script setup>
</script>
<template>
    <div v-if="$page.props.flash.message" class="bg-blue-300">
    {{ $page.props.flash.message }}
    </div>
    ああああああ
</template>
ShiroshitaShiroshita

Indexにv-forを追加してみる

🫠コントローラー側

Laravelでモデル軽油でデータ取得する場合、配列を拡張したコレクション型になる

InertiaTestController.php
    public function index()
    {
        //第二引数は連想配列でkeyとvalueを渡す
        return Inertia::render('Inertia/Index',[
            'blogs'=>InertiaTest::all()
        ]);
    }

全コード
InertiaTestController.php
<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
// 追加
use Inertia\Inertia;
// Linkコンポーネントでstore保存
use App\Models\InertiaTest;

class InertiaTestController extends Controller
{
    public function index()
    {
        //第二引数は連想配列でkeyとvalueを渡す
        return Inertia::render('Inertia/Index',[
            'blogs'=>InertiaTest::all()
        ]);
    }

    //フォーム(create) 一般的にはindexメソッドの下に書くことがおおい
    public function create()
    {
        return Inertia::render('Inertia/Create'); //コンポーネント
    }

    public function show($id)
    {
        // dd()
        // ララベルのデバッグ
        // 処理を止めて変数の値をみることができる
        // dd($id);
        return Inertia::render('Inertia/Show',
        [
            // 引数として入ってきた値をvue側に渡す
            'id' => $id
        ]);
    }
    // Linkコンポーネントでstore保存処理
    public function store(Request $request)
    {
        $request->validate([
            'title' => ['required','max:20'],
            'content' => ['required'],
        ]);

        $inertiaTest = new InertiaTest;
        $inertiaTest->title = $request->title;
        $inertiaTest->content = $request->content;
        // DBに保存
        $inertiaTest->save();
        //laravel9の機能
        //リダイレクトに続けてwithをつけ、セッションメッセージを渡すことができる
        return to_route('inertia.index')
        ->with([
            'message' => '登録しました'
        ]);
    }
}

🫠vue

  • blogs
  • v-for ひとつずつ取り出すことができる
    • v-forを付けるときにはソートや削除した時に値を保持するために:keyもつける
Inertia/Index.vue
<script setup>
// コントローラーから渡す際はdefinePropsを書く
defineProps({
    blogs: Array
})
</script>
<template>
    <!-- フラッシュメッセージなので、リロードしたら消える -->
    <div v-if="$page.props.flash.message" class="bg-blue-300">
    {{ $page.props.flash.message }}</div>
    <ul>
        <li v-for="blog in blogs" :key="blog.id">
            件名:{{ blog.title }},
            本文:{{ blog.content }}
        </li>
    </ul>
</template>

🫠実行結果

件名を表示するとDBのinertia_tests.idにあたる数字が表示される

ShiroshitaShiroshita

イベントコールバック

🫠詳しく

処理の前後にフックさせて処理する仕組み
削除→本当に削除しますか?など

🫠ルート

routes/web.php
//イベントコールバック
Route::delete('/inertia/{id}',[InertiaTestController::class, 'delete'])->name('inertia.delete');
全コード
routes/web.php
<?php

use App\Http\Controllers\ProfileController;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
use App\Http\Controllers\InertiaTestController;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
// Inertia
Route::get('/inertia-test', function () {
    return Inertia::render('InertiaTest');
}
);
// 追加
Route::get('/inertia/index',[InertiaTestController::class, 'index'])->name('inertia.index');

//フォーム(create)
Route::get('/inertia/create',[InertiaTestController::class, 'create'])->name('inertia.create');


// Linkコンポーネントでsutore保存
Route::post('/inertia',[InertiaTestController::class, 'store'])->name('inertia.store');


// ↓Linkルートパラメータ実装
Route::get('/inertia/show/{id}',[InertiaTestController::class, 'show'])->name('inertia.show');

//イベントコールバック
Route::delete('/inertia/{id}',[InertiaTestController::class, 'delete'])->name('inertia.delete');



Route::get('/', function () {
    return Inertia::render('Welcome', [
        'canLogin' => Route::has('login'),
        'canRegister' => Route::has('register'),
        'laravelVersion' => Application::VERSION,
        'phpVersion' => PHP_VERSION,
    ]);
});

Route::get('/dashboard', function () {
    return Inertia::render('Dashboard');
})->middleware(['auth', 'verified'])->name('dashboard');

Route::middleware('auth')->group(function () {
    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');
});

require __DIR__.'/auth.php';

🫠コントローラー

showメソッドに追記

    public function show($id)
    {
        // dd()
        // ララベルのデバッグ
        // 処理を止めて変数の値をみることができる
        // dd($id);
        return Inertia::render('Inertia/Show',
        [
            // 引数として入ってきた値をvue側に渡す
            'id' => $id,
            'blog' => InertiaTest::findOrFail($id)
        ]);
    }

コントローラーにデリートメソッドを追記

InertiaTestController
    // 削除処理
    public function delete($id)
    {
        $book = InertiaTest::findOrFail($id);
        // 削除完了
        $book->delete();

        return to_route('inertia.index')
        ->with([
            'message' => '削除しました'
        ]);
    }

🫠vue

Inertia/Show.vue
<!-- Linkルートパラメータ実装 -->
<script setup>
import { Inertia } from '@inertiajs/inertia';

defineProps({
    id : String,
    blog: Object
})

// 引数が一つの場合(id)ではなくidだけで良き
const deleteConfirm = id => {
    // //確認用
    // console.log(id)

    //Inertiaコードをかく
    Inertia.delete(`/inertia/${id}`,{
        onBefore: () => confirm('本当に削除しますか')
    })
}
</script>
<template>
    {{  id  }}<br>
    {{ blog.title }}
    <button @click="deleteConfirm(blog.id)">削除</button>
</template>
ShiroshitaShiroshita

他のコンポーネントを使ってみる

🫠ルート

web.php
// 他のコンポーネントを使ってみる
Route::get('/component-test', function () {
    return Inertia::render('ComponentTest');
}
);

🫠Vueファイル🆕

ComponentTest.vue
<script setup>
import GuestLayout from '@/Layouts/GuestLayout.vue'
import Label from '@/Components/InputLabel.vue'
import Input from '@/Components/TextInput.vue'
</script>

<template>
<GuestLayout>
    やっほー
</GuestLayout>
</template>

🫠実行結果