Larabel(5.7)でフォームを作成する手順
2019年02月05日
Laravelで簡単なフォームを作るのは基礎中の基礎ですが、慣れないうちは何箇所かつまづいたので記録に残しておきます。
フォームの仕様
記事を投稿してデータベースに保存する
件名(タイトル)と本文を入力し、データベースへ保存する。
サイトへアクセスすることで保存された記事一覧と、
ただこれだけのシンプルな仕様。
まずは環境構築
環境構築にはhomesteadを利用しました。
手順はこちらの記事に書いているので割愛します。
最初にやったのは古いバージョン(Laravel4系)だったので、再度やりなおしてLaravel5系でやってみました。
環境構築に手間取りましたが、ようやく動作したのでフォームの制作
1.マイグレーションでデータベースを作成
まずは、Homesteadの環境にログイン後、Artisanコマンドでテーブルを作成します
今回は、記事を投稿するので、postsというテーブルを作成
php artisan make:migration create_posts_table –create=posts
public function up() { Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('body'); $table->boolean('delete_flg')->default(0); $table->timestamps(); }); }
ファイルを作成した後、
php artisan migrate
でテーブルを作成します。
モデル作成
テーブルを作成後、テーブルに対応したモデルを作成します。
php artisan make:model Post
これで、
/App/Post.php
が作成されました。
これで、テーブルに対応したオブジェクトを呼び出すことができるようになります。
このモデルはあとで使います。
管理画面を作る
まずは、記事を投稿する管理画面を作ります。
まずは管理画面の処理に対応するコントローラーを作成します。
php artisan make:controller AdminEntryController
これで、/Http/Controllers/AdminEntryController.phpが作成されました。
このファイルには管理画面の処理に対応する記述を追加していきます。
この時点では雛形が作られただけで処理は何も入っていません。
ルーティングを記述する
/admin/entry にアクセスしたとき、管理画面の投稿画面を表示するようにします。
ルーティングを記述ファイルは、/routes/web.phpにあります。
これに以下を追記します。
Route::get(‘/admin/entry’, ‘AdminEntryController@top’);
これでは、url「/admin」にアクセスしたときに、コントローラAdminEntryControllerの「top」メソッドにアクセスするということです。
最初は、とりあえずテンプレートを単純に表示するだけにしてみます。
public function top() { return view('admin.entry'); }
これは、このメソッドが実行されたときに
/resources/views/admin/index.blade.phpを表示するという処理です。
まだテンプレートを作成してないので、URLにアクセスしてもエラーになります。
テンプレートを作成します。
bladeテンプレートの作成
bladeテンプレートは、laravelで使われているテンプレートで固有の記法があります。
CMSやフレームワークを使ったことがある人であればテンプレートという説明を聞くと大体イメージがつかめると思いますが、基本的には同じです。
ただ、bladeテンプレートの特徴としては、親と子の継承というものが特徴です。
大体のサイトは、基本となる共通部分があって、後はコンテンツ部分やタイトルなど、一部異なる部分があります。
Laravelのbladeテンプレートでは、共通している部分をまず、親のテンプレートとして作成します。
あとは、それぞれのページは、親のテンプレートを継承することで、異なる部分だけ作成するというようなイメージです。
この特徴により、子のテンプレートは最小限の記述で、また必要なテンプレートも最小限ですみます。
これで、bladeテンプレートの作成が完了したら、/admin/へアクセスすると、エラーがなければサイトが表示されます。
フォームを作成する
とりあえず、作成したフォームの雛形のテンプレートは以下の通りです。
注意が必要な点としては、フォームは、デフォルトの設定では、クロスサイトリクエストフォージェリ対策に、@csrfをテンプレートに埋め込んで置く必要があります。
これを書いていないと投稿時にエラーになります。
@extends('admin.parent') @section('title', '投稿管理(入力画面)') @section('content') <form action="/admin/entry/" method="post" id="formid" enctype="multipart/form-data"> @csrf <table class="table table-striped"> <tr> <td>タイトル</td> <td><input type="text" name="title" value="" placeholder="タイトルが入ります" /></td> </tr> <tr> <td>本文</td> <td><textarea name="body"></textarea></td> </tr> <tr> <td colspan="2" class="text-center"> <input type="submit" name="submit22" value="登録" class="btn btn-primary"> <input type="hidden" name="action" value="ok"> </td> </tr> </table> </form> @endsection
これを、実行したときに登録する処理を追加する必要があります。
まずは、ルーティングに以下を追加します。
Route::post(‘/admin/entry’, ‘AdminEntryController@complete’);
最初のルーティングは、getで書いてましたが、投稿の処理はpostで渡すことで処理を分岐できます。
postでURLにアクセスしたときには、同じコントローラーでも異なるメソッドを実行するようにしています。
上記の処理ではcompleteメソッドを実行します。
completeメソッドの定義
completeメソッドを以下のように定義しました。
public function complete(Request $request) { $post = new Post(); $post->title = $request->input('title'); $post->body = $request->input('body'); //セーブメソッドは、継承している親クラスのモデルオブジェクトに定義されている $post->save(); return view('admin.complete'); }
$post = new Post();
で、最初に作成したPostモデルのオブジェクトを呼び出しています。
この記述で行うには、ファイルの先頭に
use App\Post;
を追記しておく必要があります。
この追記がない場合
$post = new App\Post();
になります。
その変数にポストで投げられた値をリクエストから取得して、saveメソッドでデータベースへ保存します。
その後は、完了画面のテンプレートを表示します。
この処理はかなりシンプルですが、Laravel独特の記法なので、覚えておく必要があります。
エラーがなければ、これでフォームの保存が実行できます。
投稿のサイトへの表示
今度はサイトに表示する処理を書いてみます。
一覧画面と詳細画面に分けて作成します。
まずはルーティングに必要な処理を追加します。
//トップページで一覧表示
Route::get(‘/’, ‘PostsController@index’);
//詳細ページの表示
Route::get(‘/post/{id}’, ‘PostsController@detail’);
この例では、ページの表示処理を、PostsControllerというクラスを作成して行います。
トップページに一覧を表示する(indexメソッドの実行)処理、詳細ページを表示する処理(detailメソッドの実行)に分けています。
PostControllerは、最初と同じく
php artisan make:controller PostController
で作成しておきます。
以下が、PostsControllerクラスに定義した、トップページと詳細ページの表示のメソッドです。
use App\Post; class PostsController extends Controller { //トップページを表示する public function index(Request $request) { $posts = Post::where('delete_flg' , '<>', '1') ->get(); return view('index', compact('posts')); } //ブログ詳細記事を表示する public function detail(Request $request, $id) { $post = Post::find($id); return view('post', compact('post')); } }
indexメソッドでは、削除フラグが1でなく記事を取得するような処理を行っています。
detailメソッドでは、URLから取得したユーザIDに該当する記事を取得して、テンプレートに表示しています。
bladeテンプレートはそれぞれ以下のように定義しています。
@extends('layout') @section('title', "新着記事") @section('content') <ul> @foreach ($posts as $post) <li><a href="/post/{{ $post->id }}">{{ $post->title }}({{ $post->created_at->format('Y年m月d日') }})</a></li> @endforeach </ul> @endsection
@extends('layout') @section('title', $post->title) @section('content') <p class="text-right">{{ $post->created_at->format('Y年m月d日') }}</p> <div class="contents"> {!! nl2br(e($post->body)) !!} </div> @endsection
記事の編集、削除
最後に、管理画面から記事の編集と削除を行えるような処理を追加します。
今回のケースでは、ルートファイルにまず以下のような処理を追記しました。
Route::get('/admin/edit/{id}', 'AdminEntryController@edit'); Route::post('/admin/edit/{id}', 'AdminEntryController@store'); Route::get('/admin/delete/{id}', 'AdminEntryController@delete');
編集画面は、同じURLでアクセスしますが、最初はgetでアクセスし、編集完了処理はpostでアクセスすることで実行するメソッドを分けています。
//記事を編集する public function edit(Request $request, $id) { $post = Post::find($id); return view('admin.edit', compact('post')); } //記事の修正を完了する public function store(Request $request , $id) { $post = Post::find($id); $post->title = $request->input('title'); $post->body = $request->input('body'); //セーブメソッドは、継承している親クラスのモデルオブジェクトに定義されている $post->save(); return view('admin.complete'); } //記事の削除を完了する public function delete(Request $request , $id) { $post = Post::find($id); $post->delete_flg = 1; //セーブメソッドは、継承している親クラスのモデルオブジェクトに定義されている $post->save(); return view('admin.delete'); }
削除処理は、delete処理を使うのではなく、一応データベースには残しておきたいので、delete_flgを1にするアップデート処理にしています。
管理画面のトップページのbledeテンプレートも、記事一覧を表示するように変更しておきました。
@extends('admin.parent') @section('title', '投稿管理') @section('content') <div class="text-right"> <input type="submit" name="submit" value="新規登録" class="btn btn-primary" onclick="location.href='/admin/entry/'" /> </div> <div class="content"> <table> @foreach ($posts as $post) <tr><td><a href="/post/{{ $post->id }}">{{ $post->title }}</a></td><td><input type="submit" value="編集" onclick="location.href='/admin/edit/{{ $post->id }}'" ></td><td><input type="submit" value="削除" onclick="location.href='/admin/delete/{{ $post->id }}'" ></td></tr> @endforeach </table> </div> @endsection
ヘルパーを使ってテンプレートの記述を簡素化する
上記の例では、テンプレートの記述がややごちゃごちゃしていますが、
ヘルパーを導入することで、テンプレートの記述をすっきりすることができます。
詳しくは、以下のサイトに解説があります。
初めてのLaravel 5.6 : (16) Formの作成 – ララ帳
composer require “laravelcollective/html”:”^5.7.0″
これでヘルパーが導入できるので、これで以下のように記述することができます。
@extends('layout') @section('content')Write a New Article
{!! Form::open() !!}{!! Form::label('title', 'Title:') !!} {!! Form::text('title', null, ['class' => 'form-control']) !!}{!! Form::label('body', 'Body:') !!} {!! Form::textarea('body', null, ['class' => 'form-control']) !!}{!! Form::label('published_at', 'Publish On:') !!} {!! Form::input('date', 'published_at', date('Y-m-d'), ['class' => 'form-control']) !!}{!! Form::submit('Add Article', ['class' => 'btn btn-primary form-control']) !!}{!! Form::close() !!} @endsection