Laravel CRUD処理 

CRUD処理とは

Create / Read / Upload / Delete の処理のことである。
Web開発には必要な要素たちである。

今回はこれらをLaravelというフレームワークを利用してphpで記述していく。

Create投稿ボタンを設置

投稿ボタンを入れたいファイルに<a>タグを挿入する。
そしてclassを設定してCSSで整える。

 <a class='btn-style2 btn-style2--blue box-center' href="{{ route('posts.create') }}">スペースを登録</a>

このようにRouteも指定し

Route::get('/posts/create', [PostController::class,'create'])
    ->name('posts.create');

ROUTEを作り

public function create()
{
    return view('posts.create');
}

メソッドも作り、create.blade.phpファイルも作り
実際に、投稿ボタンを押すと、、


エラーが起きてしまいます。。

何が原因かを考えていきます。

これは、Routeミスです。

Route::get('/', [PostController::class,'index'])
    ->name('posts.index');

Route::get('/posts/{post}', [PostController::class,'show'])
    ->name('posts.show');

Route::get('/posts/create', [PostController::class,'create'])
    ->name('posts.create');

ルーティングは上から順に、チェックしている構造なのです。
つまり、
①リンクが何も指定されてない
②posts/{post}→postsの下階層の文字が引数であれば
③posts/createが指定されたら

このように順を追っていきます。
なので、{post}に文字列createが入ってしまいます。
すると、Controller.phpで(Post $post)の$postにcreateが入ってしまい
エラーに陥ってしまうわけです。。

解決方法は2つ

①ルーティングの順番を変更する
②->whereでルーティングに条件をつけて、createを弾く

①は汎用性がないので、②でいきます。

Route::get('/posts/{post}', [PostController::class,'show'])
    ->name('posts.show')
    ->where('post','[0-9]+');

これで、数字以外を受け付けない設定にできました。

Viewに投稿フォームを追加したのち、CSRF対策を施していきます。

<form action="" method="post">
//Laravelでは@CSRFと入力するだけで、トークンの生成・確認ができる
        @csrf

//labelにすると、タグ内のどこをタッチしても入力モードにできる
        <label>
            スペース名
            <input type="text" name="space" id="">
        </label>

        <label>
            おすすめポイント
            <textarea name="body" id="" cols="30" rows="10"></textarea>
        </label>

        <button>登録</button>
    </form>

CSSはご自由に。

投稿を保存するためのルーティングとメソッドを実装

<form action="{{ route('posts.store') }}" method="post">

投稿を保存するルーティングを設定。
ここからファイル生成やルーティング設定、メソッド設定を行なっていく。

Route::get('/posts/create', [PostController::class,'create'])
    ->name('posts.create');
Route::post('/posts/create/store', [PostController::class,'store'])
    ->name('posts.store');

post型でRouteを指定する。
Createの下にstoreを作る。(ルーティングの構造上)

//route:postで渡された値を、Requestで受け取ることができる。
public function store(Request $request)
{
//データを入れるためのインスタンスを作る。
    $post = new Post();
    $post->title = $request->title;
    $post->body = $request->body;
    $post->save();

    return redirect()
        ->route('posts.index');
}

入力された値を検証し、必要に応じてエラーメッセージを表示

登録ボタンで登録する際に、「空白」入力すると
Nullなので、エラーが起きてしまう。
詳しくは、データベースの設定を見ていただきたい。

なので、入力された値に対して、メッセージを表示する。

public function store(Request $request)
{
    $request->validate([
        'title' => 'required | min:3',
        'body' => 'required'
    ]);

こんな感じで、指定する。

これらに引っかかった時に、HTMLで表示したい内容を
今から書きに行く。

//こんな感じで、@errorで指定する。
<label>
スペース名
<input type="text" name="title">
</label>
@error('title')
<div class="error">{{ $message }}</div>
@enderror

エラーメッセージは$messageで表示されるので、いい感じにエラーを検出して、メッセを出してくれる。

エラー発生時に表示するメッセージをカスタマイズする方法

今のままでは、入力した文字がエラー後、消えてしまうので
エラー前のメッセージを残したままにしておく設定をしていきます。

 <input type="text" name="title" value="{{ old('title') }}">
            </label>
            @error('title')
                <div class="error">{{ $message }}</div>
            @enderror
            <label>
                おすすめポイント
                <textarea name="body">{{ old('body') }}</textarea>

old('name')で、値を設定すると
更新前の値を残したままにしてくれます。

続いて、
エラーメッセージを指定します。

[    'title' => 'required | min:3',
        'body' => 'required'
    ],[
        'title.required' => '入力必須です。',
        'title.min' => ':min 文字以上入力してください。',
        'body.required' => '入力必須です。'
    ]

このように、title.requiredと付けることで、入力必須状態にする。
:minは記入済みの設定文字数を指定できる。

記事詳細ページで改行が反映されるようにnl2br()を使っていきます

このように、改行した場合どうなるでしょうか。

まあ、こんな感じに改行されてないってことですね。

なので、改行していきたいと思います。

//これで改行がつくはず。。
 <p>{{ nl2br($post->body) }}</p>

悪意のある文が挿入されないように、HTMLタグは文字実体参照として認識されてしまう。
なので、これをbladeで値を埋め込みつつ、文字実体参照での変換を無効にする方法を実行してみる。

<p>{!! nl2br($post->body) !!}</p>

このように{{!! !!}}ビックリマークを付ければ、
文字実体参照を回避することができる。

悪意あるコードが実行されないように、e()を使っていきます

今のままでは、悪意のあるコードが有効化されてしまっています。

投稿フォームに<script>alert();</script>と入力すると

このようにJavasciptが適用されてしまいます。

なので、nl2brを有効にしつつ、悪意のあるコードを無効化していきます。

<p>{!! nl2br(e($post->body)) !!}</p>

Laravelではe()で囲むと、htmlspecial Charactersの効果が発揮されます。

ちなみに、二重括弧{{}}は、<?php echo?>の意味があります。
また、悪意のあるコードを弾く効果もあります。(XSS:クロスサイトスクリプティングという攻撃)

今回は、そちらを無効化したためe()を適用しました。

投稿を編集するためのルーティングとメソッドを実装していきます。

投稿画面のタイトル横に「編集」ボタンを設置したいので

<h1>&ndash;  {{  $post->title  }}  &ndash;</h1>

これを

 <h1>
<span>{{  $post->title  }} </span>
<a href="">[Edit]</a>
</h1>

こうすると、こうなる。

ここからは、ルーティングとメソッドを作っていく。

<a href="{{ route('posts.edit', $post) }}">[Edit]</a>
Route::get('/posts/{post}/edit', [PostController::class,'edit'])
    ->name('posts.edit')
    ->where('post','[0-9]+');
public function edit(Post $post){
    return view('posts.edit')
        ->with(['post' => $post]);
}

んで、viewsフォルダの中に、edit.blade.phpを作って
create.phpをコピペしておく。

編集フォームを作ったのち、フォームに値がセットされるようにしていきます。

edit.phpを編集していきます。
タイトルなどは各々変えて見てください。

//バックリンクは投稿画面に戻りたいので
&laquo; <a href="{{ route('posts.show',$post)}}">Back</a>

こんな感じで、以前入力されていた値がなくなっているので、
表示していきます。

//oldには第二引数を渡せる。もし、第一引数がなければ実行される。
{{ old('title',$post->title) }}

うん。いいっすね。

編集した内容でデータを更新するためのルーティングを設定し、PATCH形式でフォームを送信します。

//ルーティングを定義します。
    <form action="{{ route('posts.upload',$post) }}" method="post">
Route::get('/posts/{post}/upload', [PostController::class,'upload'])
    ->name('posts.upload')
    ->where('post','[0-9]+');

これではいけません。
なぜなら、データをgetするのではないから

Route::post('/posts/{post}/upload', [PostController::class,'upload'])

これもあかんとです。
なぜなら、postは新しいデータを作成するとき。
一部の情報を変更する際はpatchを使うルールがあるんですねえ。

Route::patch('/posts/{post}/upload', [PostController::class,'upload'])

続いて、

    <form action="{{ route('posts.upload',$post) }}" method="post">

methodをpostからpatchに変えた方がいいと思いますよね。
しかし、まだLaravelでは対応していないぽく

<form action="{{ route('posts.upload',$post) }}" method="post">
   @method('PATCH')

こう書きます。

データを更新するためのメソッドを実装し、編集機能を完成させます。

コントローラーに移動します。
やり口がstore関数と似ているので、コピペします。

//implicit Bindingを利用するためPostを指定
public function update(Request $request,Post $post)
{
    $request->validate([
        'title' => 'required | min:3',
        'body' => 'required'
    ],[
        'title.required' => '入力必須です。',
        'title.min' => ':min 文字以上入力してください。',
        'body.required' => '入力必須です。'
    ]);

//implicitで受け取ったpostを利用すればいいので、インスタンス生成は消します。
    // $post = new Post();
    $post->title = $request->title;
    $post->body = $request->body;
    $post->save();

//showを示します。
    return redirect()
         ->route('posts.show',$post);

こんな感じでうまくいきます。
ここで、注意したいのは
update.blade.phpの作成は必要ないということです。
なぜなら、遷移するのは既存のページだけだからです。
これもPatch形式をとっているからなのでしょう。

独自のRequestクラスを作成し、バリデーション処理をまとめていきます。

$request->validate([
        'title' => 'required | min:3',
        'body' => 'required'
    ],[
        'title.required' => '入力必須です。',
        'title.min' => ':min 文字以上入力してください。',
        'body.required' => '入力必須です。'
    ]);

validateメソッドが二つあるのが、後々増えた時に
分かりづらくなってしまうので、独自のrequestクラスを作って
そこに、validate関数を置いておきます。

./vendor/bin/sail artisan make:request PostRequest

作成したファイルを開くと、、

public function authorize()
    {
        //ユーザー管理をして、制限などを加える機能
        // return false;
        return true;
    }
public function rules()
    {
        return [
            'title' => 'required | min:3',
            'body' => 'required'
        ];
    }
public function messages()
    {
        return[
    'title.required' => '入力必須です。',
    'title.min' => ':min 文字以上入力してください。',
    'body.required' => '入力必須です。',
        ];
    }

エラーメッセージは、少し変わっていて
messages()を自ら設定しなくてはいけません。

では、実際にPotsContorollerとPostRequestを繋いでいきます。

//PostRequest
namespace App\Http\Requests;

名前空間がついているので、コピペしておきます。

//PostController
use App\Http\Requests\PostRequest;
と指定
public function store(PostRequest $request)
//こうすることで、store関数にわたる前に、PostRequestvalidate関数を実行してくれます。

実際に、挙動するか試してみてください。

OKぽいです。

削除機能のためのフォームを作ります。

<span>{{  $post->title  }} </span>
                <a href="{{ route('posts.edit', $post) }}">[Edit]</a>
                <form action="" method="post">
                    <button class="btn">[x]</button>
                </form>

spanタグとかではなく
削除はformタグなんですね。
おそらく、データを更新するから?

<form action="{{ route('posts.destroy',$post) }}" method="post">
@method('DELETE')
@csrf

削除処理にはdestroyがよく使われます。
また、メソッドはDELETEで指定しましょう。

Route::delete('/posts/{post}/destroy', [PostController::class,'destroy'])
    ->name('posts.destroy')
    ->where('post','[0-9]+');

ルーティングはこんな感じ。

public function destroy(Post $post)
    {
        $post->delete();

        return redirect()
            ->route('posts.index');
    }

Impicit Bindingでpostを受け取り、
リダイレクトはindexを指定します。

これで削除機能は完成ですが、
削除する前に、確認が欲しいところです。

JavaScriptで操作していきます。

この記事が気に入ったらサポートをしてみませんか?