前回LaravelのEloquentでレコード削除を行いました。 実際データベースレコードの物理削除は、レコード自体が削除されるためあまり実用的とは言えません。実際のサービス運用では論理削除が利用されることが多いです。論理削除ではレコードは実際に削除されず、削除されたという追加情報を付与することで実現されます。 そのため、データベースの処理上はレコード更新になります。 今回はレコード削除より利用されるレコード更新を学んでいきます。 論理削除、物理削除についてはMySQLのレコード削除をご覧ください。

LaravelのEloquentでレコード更新

3年以内に更新されました。情報が古い可能性があります。
更新日 : 2020年02月05日

前回LaravelのEloquentでレコード削除を行いました。

実際データベースレコードの物理削除は、レコード自体が削除されるためあまり実用的とは言えません。実際のサービス運用では論理削除が利用されることが多いです。論理削除ではレコードは実際に削除されず、削除されたという追加情報を付与することで実現されます。
そのため、データベースの処理上はレコード更新になります。

今回はレコード削除より利用されるレコード更新を学んでいきます。

論理削除、物理削除についてはMySQLのレコード削除をご覧ください。

事前準備

事前準備としてまずは編集フォームを作成しましょう。
LaravelのEloquentでレコード登録を実装した際に利用した、グループ化されたルーティングに記事編集用フォームを作成していきます。

Route::prefix('post')->group(function () {
    // フォーム表示用
    Route::get('add', 'PostController@addForm');
    // POSTデータを受け取る用
    Route::post('add', 'PostController@add');
    // 記事リストを表示するフォーム
    Route::get('list', 'PostController@list');
    // 選択された記事を削除する処理
    Route::post('delete', 'PostController@delete');
    // idを受け取り、そのidと一致する記事を編集するフォーム
    Route::get('edit{id}', 'PostController@editForm');
});

編集フォームのURLとしてRoute::get('edit{id}', 'PostController@editForm');を定義しています。

今まではHTTPのGETメソッドでのアクセス時にパラメータを受け取る必要はありませんでしたが、今回は記事の編集機能を実装したいため記事に紐づくidをURLの一部として受け取り、元の記事の情報をフォームに表示したいです。

記事のidを受け取るために、ルーティングの第一引数ではedit{id}としています。{id}とすることで、コントローラーなどで引数$idとして受け取ることができます。

これをもとにPostControllereditFormメソッドを実装していきましょう。

public function editForm($id) {
    $post = Post::find($id);
    if(is_null($post)) {
        return redirect('post/add');
    }
    return view('post.editForm', ['post' => $post]);
}

先程の説明どおり、メソッドの引数として$idを定義しています。

受け取った記事idを元に編集対象となる記事情報をPost::find($id)で取得しています。代入された$postの存在チェックのみを行い、記事情報をビューへ渡しています。

最後にeditForm.blade.phpを作成しましょう。

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravelでレコード登録</title>

    <!-- Fonts -->
    <link href="https://fonts.googleapis.com/css?family=Nunito:200,600" rel="stylesheet">

    <!-- Styles -->
    <style>
        html, body {
            background-color: #fff;
            color: #636b6f;
            font-family: 'Nunito', sans-serif;
            font-weight: 200;
            height: 100vh;
            margin: 0;
        }

        .flex-center {
            align-items: center;
            margin: 100px 300px 100px 300px;
            justify-content: center;
        }

        .position-ref {
            position: relative;
        }
    </style>
</head>
<body>
    <div class="flex-center position-ref">
        <h1>ブログ記事編集フォーム</h1>
        <form method="POST" action="edit">
            @csrf
            <input type="hidden" name="id" value="{{ $post->id }}">
            <dl>
                <dt>タイトル</dt>
                <dd><input type="text" name="title" required value="{{ $post->title }}"></dd>
            </dl>
            <dl>
                <dt>本文</dt>
                <dd><textarea name="content" required>{{ $post->content }}</textarea></dd>
            </dl>
            <input type="submit" value="更新">
        </form>
    </div>
</body>
</html>

これで編集フォームの完成です。

<input type="hidden" name="id" value="{{ $post->id }}">とすることで、非表示データを付与できます。今回は編集する記事idを、データを送った先でも参照できるように非表示データとして送信するようにしています。

また、元の記事の情報(タイトル・本文)をフォーム内部に表示できるようにvalue属性やtextareaタグの内部で格納済みで表示しています。

このフォームデータの送信先はaction="edit"となっているため、実際に更新処理を行うeditをルーティングで定義していきましょう。

POSTデータを受け取り、受け取ったデータで記事情報を更新

まずは先程のルーティングを定義していきましょう。

Route::prefix('post')->group(function () {
    // フォーム表示用
    Route::get('add', 'PostController@addForm');
    // POSTデータを受け取る用
    Route::post('add', 'PostController@add');
    // 記事リストを表示するフォーム
    Route::get('list', 'PostController@list');
    // 選択された記事を削除する処理
    Route::post('delete', 'PostController@delete');
    // idを受け取り、そのidと一致する記事を編集するフォーム
    Route::get('edit{id}', 'PostController@editForm');
    // 記事編集フォームからデータを受け取り、実際に更新する処理
    Route::post('edit', 'PostController@edit');
});

グループ化されたルーティング設定の最後にRoute::post('edit', 'PostController@edit');を追加しました。

これで編集フォームのデータを受け取るURLができたので、実際の処理をPostControllereditメソッドに実装していきましょう。

public function edit(Request $request) {
    $request->validate([
        'id' => 'required',
        'title' => 'required|string',
        'content' => 'required|string'
    ]);

    $post = Post::find($request->id);
    $post->title = $request->title;
    $post->content = $request->content;
    $post->save();
    return redirect('post/list');
}

HTTPのPOSTリクエストを受け取るため、引数はRequest $requestです。

最初に受け取ったデータのバリデーションを行っています。Laravelのバリデーションに関してはこちらから。

実際の更新処理を以下で解説します。

  1. まずは編集するべき記事の情報を$post = Post::find($request->id)で取得
    先程のフォームで非表示データをidというname属性で設定していたため、$request->idで記事idを取得できます。これで$postにはPostモデルのインスタンスが格納されます。
  2. 編集するべき項目を$post->title = $request->titleのようにして代入
    記事のタイトルや本文をリクエストのタイトルや本文情報で上書きするため、まずはモデルのそれぞれのプロパティに代入します。これで編集前のデータだったインスタンスのプロパティを代入し直して更新します。
  3. 変更をデータベースに反映するために$post->save()で保存
    レコード登録時に実行したsaveメソッドを実行することでレコードの更新(UPDATE)を行うことができます。

これで更新処理が完成です。最後にreturn redirect('post/list')としていますが、これはリダイレクトです。保存処理完了後はpost/listへページ遷移(リダイレクト)をしています。もちろん適当なビューを作成して表示しても構いません。

これで編集するべき記事idを編集フォームURLにpost/edit{記事id}とすれば、編集フォームが表示され、記事を更新することができます。

実行結果(編集フォームの表示)は以下のようになります。筆者が編集する記事idを18とした場合のURLとなります。

編集ボタン押下すると記事リストページにリダイレクトされます。

まとめ

レコード更新処理はsaveメソッドを呼ぶことで更新できました。更新する前にレコードをモデルから取得して、更新するべき情報を代入後にsaveメソッドを呼ぶことでレコードを更新できます。

つまり、レコードを新規登録する場合にはモデルのインスタンスを新規作成(new モデル名)してからsaveメソッドを実行、レコードを更新する場合にはモデルからレコード情報をインスタンスとして取得してからsaveメソッドを実行するという違いだけです。