今回は投稿フォームを作成して掲示板に投稿する機能を追加します。
Contents
テンプレートを共通化する
作成するページとして投稿された文章を表示する掲示板のトップページ、新規投稿用のフォームを表示するページ、既存の投稿を編集するフォームを表示するページ、個別の投稿文を表示するページがあります。しかしこれらにはヘッダーやフッターなど共通部分があります。そこでその部分を先に作成します。
共通部分をまとめてしまえば、Webデザインの作成やメンテナンスも効率よく進められます。
そのために(プロジェクトのフォルダ)\resources\viewsのなかにlayout.blade.phpを作成します。ファイルの名前は○○.blade.phpでなければなりません。
resources\views\layout.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<!DOCTYPE html> <html> <head> <meta charset='utf-8'> @yield('page-title') <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.0/jquery.min.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"></script> <style> body { padding: 80px; } </style> <meta name='csrf-token' content='{{ csrf_token() }}'> </head> <body> <nav class='navbar navbar-expand-md navbar-dark bg-dark fixed-top'> <a class="navbar-brand" href="{{ route('article.list') }}">掲示板 TOP</a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <ul class="navbar-nav ml-auto"> @guest <li class="nav-item"> <a class="nav-link" href="{{ route('login') }}">ログイン</a> </li> @if (Route::has('register')) <li class="nav-item"> <a class="nav-link" href="{{ route('register') }}">ユーザー登録</a> </li> @endif @else <li class="nav-item"> <a class="nav-link" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">現在 {{ Auth::user()->name }}でログインしています。ログアウトする</a> <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;"> @csrf </form> </li> @endguest </ul> </div> </nav> <div class="container"> @yield('content') </div> </body> </html> |
ルーティングの設定
それからルーティングの設定をおこないます。
(プロジェクトのフォルダ)\routes\web.phpを以下のように編集します。
前回モデルArticleを作成してマイグレーションをしましたが、このときにArticleControllerクラスが自動生成されます。以下のように記述することで、トップページにアクセスされた場合はindex関数が実行されてページが表示されます。投稿の個別ページを見るのであれば/article/{id}にアクセスすればよく、そのときの処理はArticleControllerクラスのshow関数におこなわせるという感じです。
routes\web.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
<?php use Illuminate\Support\Facades\Route; // ここから下を追加 // トップページ Route::get('/', 'App\Http\Controllers\ArticleController@index')->name('article.list'); // 投稿の個別ページ Route::get('/article/{id}', 'App\Http\Controllers\ArticleController@show')->name('article.show'); // 新規投稿をするためのフォームがあるページ Route::get('/new', 'App\Http\Controllers\ArticleController@create')->name('article.new'); // 新規投稿をしたときにpostされるページ Route::post('/article', 'App\Http\Controllers\ArticleController@store')->name('article.store'); // 既存の投稿を修正するためのフォームが表示されるページと修正後にpostされるページ Route::get('/article/{id}/edit', 'App\Http\Controllers\ArticleController@edit')->name('article.edit'); Route::post('/article/{id}/update', 'App\Http\Controllers\ArticleController@update')->name('article.update'); // 既存の投稿を削除するためのページ Route::delete('/article/{id}/delete', 'App\Http\Controllers\ArticleController@destroy')->name('article.delete'); // ここまで追加 // ここから下は「Laravel(ララベル)で掲示板を作成する(1)」の最後に書いてあった // $ composer require laravel/ui // $ php artisan ui vue --auth // $ npm install // $ npm run dev // 上記コマンドを実行していれば自動生成されているはずです。 Auth::routes(); Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home'); |
トップページの作成
まずトップページを作成します。
(プロジェクトのフォルダ)\resources\views\index.blade.phpというファイルを作成し、そこに以下を記述します。
resources\views\index.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
@extends('layout') @section('page-title') <title>鳩でもわかるLaravel</title> @endsection @section('content') <h1>鳩でもわかるLaravel</h1> <p>{{ $message }}</p> @include('search') <table class='table table-striped table-hover'> @foreach ($articles as $article) @php $title = ""; if ($article->title != "") $title = $article->title; else $title = "タイトルなし"; $user_name = ""; if ($article->user_name != "") $user_name = $article->user_name; else $user_name = "名無しさん"; @endphp <tr> <td> <p> {{ $article->id }}: <b><a href="{{route('article.show', [ "id" => $article->id, ])}}">{{ $title }}</a></b> post by <b>{{ $user_name }}</b> 作成 {{ $article->created_at }} 更新 {{ $article->updated_at }} </p> <p>{{ $article->content }}</p> </td> </tr> @endforeach </table> @auth <div> <!-- ログインしていない状態では新規投稿のボタンは表示されない --> <!-- また後述するArticleControllerのコンストラクタの効果で直接ブラウザにurlを打ち込んでも --> <!-- ログインページにリダイレクトされる --> <a href={{ route('article.new') }} class='btn btn-outline-primary'>新規投稿</a> </div> @endauth @endsection |
モデルArticleを作成してマイグレーションしたときに(プロジェクトのフォルダ)\app\Http\ControllersのなかにArticleController.phpというファイルが自動生成されているはずです。ここに以下を記述します。
すでにユーザー管理機能を追加するために必要なライブラリ laravel/uilをインストールしているので簡単にログインしていないユーザーができることを制限することができます。コンストラクタのなかに$this->middleware(‘auth’)->except([‘index’, ‘show’]);と書いてしまえば新規投稿や既存の投稿を編集したり削除することができなくなります。
app\Http\Controllers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?php namespace App\Http\Controllers; use App\Models\Article; use Illuminate\Http\Request; class ArticleController extends Controller { public function __construct() { // ログインしないとトップページと既存の投稿されたページ以外アクセスできなくする $this->middleware('auth')->except(['index', 'show']); } public function index() { $message = '投稿された記事一覧です。'; $articles = Article::all(); return view('index', ['message' => $message, 'articles' => $articles]); } // 他にもcreate関数などが自動生成されているが、いまは放置でよい public function create() { } } |
投稿の新規作成機能の追加
新規作成の処理をするにあたってデータベースにカラムを追加します。
1 2 |
> cd プロジェクトのフォルダに移動 > php artisan make:migration add_column_user_id --table=articles |
するとマイグレーションファイルが自動生成されるので編集します。user_idというカラムを追加したいだけなので1行追加するだけです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php // 自動生成されたコードのうち変更部分以外は省略している return new class extends Migration { public function up() { Schema::table('articles', function (Blueprint $table) { // この行だけ追加 $table->string('user_id')->default(''); }); } } |
マイグレーションファイルを編集したら忘れずにマイグレーションしておきましょう。
1 |
> php artisan migrate |
次に新規投稿のページを表示するために(プロジェクトのフォルダ)\resources\viewsフォルダのなかにnew.blade.phpというファイルを作成し、以下を記述します。
resources\views\new.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@extends('layout') @section('page-title') <title>新規投稿|鳩でもわかるLaravel</title> @endsection @section('content') <h1>新規投稿</h1> <p>{{ $message }}</p> {{ Form::open(['route' => 'article.store']) }} <div class='form-group'> <!-- デフォルトでユーザー登録した名前を入力しておく。ユーザーの判断で変更可能 --> {{ Form::label('user_name', '名前:') }} {{ Form::text('user_name', Auth::user()->name) }} </div> <div class='form-group'> {{ Form::label('title', 'タイトル:') }} {{ Form::text('title', '') }} </div> <div class='form-group'> {{ Form::label('content', '本文:') }} {{ Form::text('content', '') }} </div> <div class="form-group"> {{ Form::submit('作成する', ['class' => 'btn btn-primary']) }} <a href="{{ route('article.list') }}" class ="btn btn-outline-secondary">一覧に戻る</a> </div> {{ Form::close() }} @endsection |
次にArticleControllerクラスのcreate関数を定義します。
app\Http\Controllers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
class ArticleController extends Controller { public function create() { $message = 'New article'; return view('new', ['message' => $message]); } public function store(Request $request) { $article = new Article; $user = \Auth::user(); $article->content = $request->content; $article->user_name = $request->user_name; $article->title = $request->title; // データベースに投稿したユーザーIDも保存し、 // 編集・削除の処理が行なわれるまえにそのユーザーが投稿したユーザーと同一であるか確認できるようにする $article->user_id = $user->id; $article->save(); return redirect()->route('article.show', ['id' => $article->id]); } } |
個別の投稿内容を表示するページを作成する
次に投稿内容を表示するページに関する処理を示します。(プロジェクトのフォルダ)\resources\viewsのなかにshow.blade.phpを作成します。
resources\views\show.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
@extends('layout') @php $title = ""; if ($article->title != "") $title = $article->title; else $title = "タイトルなし"; $user_name = ""; if ($article->user_name != "") $user_name = $article->user_name; else $user_name = "名無しさん"; @endphp @section('page-title') <title>{{ $user_name }}の投稿|鳩でもわかるLaravel</title> @endsection @section('content') <h1>{{ $user_name }}の投稿</h1> <p>{{ $message }}</p> <p>{{ $article->id }}:<br> {{ $title }}</p> <p>{{ $article->content }}</p> <p>post by <b>{{ $user_name }}</b> 作成 {{ $article->created_at }} 更新 {{ $article->updated_at }}</p> @if(auth()->user()) @if ($article->user_id == $login_user_id) <p> <a href={{route('article.list')}} class="btn btn-outline-primary">トップページに戻る</a> <a href={{ route('article.edit', ["id" => $article->id]) }} class='btn btn-outline-primary'>編集</a> </p> <div> {{ Form::open(['method' => 'delete', 'route' => ['article.delete', $article->id]]) }} {{ Form::submit('削除', ['class' => 'btn btn-danger'])}} {{ Form::close() }} </div> @else <p><a href={{route('article.list')}} class="btn btn-outline-primary">トップページに戻る</a></p> @endif @else <p><a href={{route('article.list')}} class="btn btn-outline-primary">トップページに戻る</a></p> @endif @endsection |
次にArticleControllerクラスのshow関数を定義します。
app\Http\Controllers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class ArticleController extends Controller { public function show(Request $request, $id, Article $article) { $user = \Auth::user(); if ($user) { $login_user_id = $user->id; } else { $login_user_id = ""; } $message = 'Show article ' . $id; $article = Article::find($id); return view('show', ['message' => $message, 'article' => $article, 'login_user_id' => $login_user_id]); } } |
投稿の編集機能の追加
投稿したらこれを編集できるようにしなければなりません。(プロジェクトのフォルダ)\resources\viewsのなかにedit.blade.phpを作成します。
resources\views\edit.blade.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@extends('layout') @section('page-title') <title>投稿の編集|鳩でもわかるLaravel</title> @endsection @section('content') <h1>投稿の編集</h1> <p>{{ $message }}</p> {{ Form::model($article, ['route' => ['article.update', $article->id]]) }} <div class='form-group'> {{ Form::label('user_name', '名前:') }} {{ Form::text('user_name', null) }} </div> <div class='form-group'> {{ Form::label('title', 'タイトル:') }} {{ Form::text('title', null) }} </div> <div class='form-group'> {{ Form::label('content', '本文:') }} {{ Form::text('content', null) }} </div> <div class="form-group"> {{ Form::submit('保存する', ['class' => 'btn btn-primary']) }} <a href="{{ route('article.show', ['id' => $article->id]) }}" class ="btn btn-outline-secondary">戻る</a> </div> {{ Form::close() }} @endsection |
次にArticleControllerクラスのedit関数とupdate関数を定義します。
ログインしていない状態で該当ページにアクセスしようとするとログインページにリダイレクトされます。しかしログインしている場合は他人の投稿文を編集したり削除することができてしまいます。そこで投稿文を作成したユーザーとログインしているユーザーのユーザーIDを比較しています。
ログインしているユーザーであっても自分が投稿したものでないなら編集は不可とします。いまの状態ではページにリンクは表示されないようにしていますが、urlを自分で生成すればページにアクセスすることはできてしまいます。そこでログインしているユーザーであり、他人が投稿したものを編集しようとした場合はトップページへリダイレクトすることにします。
app\Http\Controllers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
class ArticleController extends Controller { public function edit(Request $request, $id, Article $article) { $user = \Auth::user(); if ($user) { $login_user_id = $user->id; $article = Article::find($id); // ログインしているユーザーでも自分が投稿したものでないなら編集は不可とする if($article->user_id == $login_user_id) { $message = 'Edit your article ' . $id; return view('edit', ['message' => $message, 'article' => $article]); } } // ログインしているユーザーで他人が投稿したものを編集しようとした場合はトップページへリダイレクトする return redirect()->route('article.list'); } public function update(Request $request, $id, Article $article) { $user = \Auth::user(); if ($user) { $login_user_id = $user->id; $article = Article::find($id); // ログインしているユーザーでも自分が投稿したものでないなら編集は不可とする if($article->user_id == $login_user_id) { $article->content = $request->content; $article->user_name = $request->user_name; $article->title = $request->title; $article->save(); return redirect()->route('article.show', ['id' => $article->id]); } } // ログインしているユーザーで他人が投稿したものを編集しようとした場合はトップページへリダイレクトする return redirect()->route('article.list'); } } |
投稿を削除する機能を追加する
削除するときもログインしていないユーザーであればコンストラクタの記述により削除処理は行なわれませんが、ログインしているユーザーの場合は他のユーザーの投稿であっても削除できてしまいます。そこで投稿文を編集するときと同様、投稿文を作成したユーザーとログインしているユーザーのユーザーIDを比較し、一致しない場合は処理をおこなわずトップページにリダイレクトさせています。
app\Http\Controllers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
class ArticleController extends Controller { public function destroy(Request $request, $id, Article $article) { $user = \Auth::user(); if ($user) { $login_user_id = $user->id; $article = Article::find($id); if($article->user_id == $login_user_id) { $article->delete(); } } return redirect()->route('article.list'); } } |