(※ この記事は、Laravel8が出るより結構前に書いて、他のブログに載せていました。それを転載したものなので、Laravel7で解説しています。ご了承くださいませ。。(m´・ω・`)m ゴメン…)
環境:XAMPP
前回は、Laravelで作った初めてのアプリとして、簡単なタスク管理アプリのチュートリアル記事を投稿しました。
今回は、もうちょっと色々機能をつけて、レストランのメニュー作成アプリを作ってみようと思います!完成品は以下画像のような感じです。
管理画面で情報を入力して画像をアップロードすると、メニューページに反映されるというものです。今回のアプリで基本的なCRUD機能の作成はマスターできるのかな~と思います。
お客さん用画面はこちら。『おすすめメニュー』とか、『ランチメニュー』とかのカテゴリー別にメニューが表示されます。(カテゴリーも自由に設定できます。)
サイト管理者は管理画面のログインページからログインします。
ログインするとダッシュボードに飛びます。メニューを追加する場合は、右上ボタンから追加できます。
メニュー追加フォームはこちら。カテゴリーは、別ボタンを用意して別ページから追加できる仕様です。
ちなみに私、Webデザインは全然できませんので、フロント側はBootstrapで作ったデザイン性のないシンプル画面です。。。悪しからず。(;'∀') 汗
全体のソースコードはGitHubにアップしております。
必要な方は、以下のリンクからご確認ください。
https://github.com/Tomochan-taco/restaurant_menu
それでは早速スタート!
#1 : プロジェクトの作成
手順1: XAMPPのインストール後、C:\xampp\htdocs に移動して、composerでプロジェクトを作成します。プロジェクト名は restaurant_menu にします。(※ 7系で解説しているので、バージョン指定してください。)
composer create-project --prefer-dist laravel/laravel restaurant_menu "7.*"
手順2: プロジェクトを作成したら、cd コマンドでプロジェクト内『C:\xampp\htdocs\restaurant_menu』に移動します。因みに今回のLaravelのバージョンを確認したら下記のようになりました。
php artisan --version //Laravelバージョン確認用コマンド
Laravel Framework 7.28.3
#2 : BootstrapとFont Awesomeのインストール
今回も前回同様、BootstrapとFont Awesomeでフロント側を作成しています。ですので、BootstrapとFont Awesomeのインストールを先にやっちゃいます。
手順1: Laravelで用意されているBootstrapのscaffoldingは、laravel/uiパッケージに含まれています。その為、まずはcomposerでlaravel/uiパッケージをインストールします。
composer require laravel/ui:^2.4
手順2: 今回は認証機能も使うので、--authオプションをつけてBootstrapのscaffoldingをインストールします。
php artisan ui bootstrap --auth
C:\xampp\htdocs\restaurant_menu\package.json(以後、package.json と略して表記します。) の "devDependencies"箇所に、以下のようにBootstrapが追加されます。
"devDependencies": {
"axios": "^0.19",
"bootstrap": "^4.0.0", // Bootstrapが追加されています。
"cross-env": "^7.0",
"jquery": "^3.2",
"laravel-mix": "^5.0.1",
"lodash": "^4.17.19",
"popper.js": "^1.12",
"resolve-url-loader": "^3.1.0",
"sass": "^1.15.2",
"sass-loader": "^8.0.0"
}
手順3: npmでインストールします。
npm install && npm run dev
これで、C:\xampp\htdocs\restaurant_menu\public\css\app.css にコンパイルされてBootstrapが使えるようになります!
手順4: 次はFont Awesomeをインストールします。まずはnpmでfree versionをインストールします。
npm install --save @fortawesome/fontawesome-free
package.json の "dependencies" の箇所に、以下のようにFont Awesomeが追加されます。
"dependencies": {
"@fortawesome/fontawesome-free": "^5.14.0"
}
手順5: C:\xampp\htdocs\restaurant_menu\resources\sass\app.scss に下記を追記してインポートします。
// Font Awesome
@import '~@fortawesome/fontawesome-free/scss/fontawesome';
@import '~@fortawesome/fontawesome-free/scss/regular';
@import '~@fortawesome/fontawesome-free/scss/solid';
@import '~@fortawesome/fontawesome-free/scss/brands';
手順6: npmで実行します。
npm run dev
これでBootstrapとFont Awesomeが使えるようになります!
手順7: ブラウザ確認します。artisanコマンドでサーバを起動後、http://127.0.0.1:8000/ にアクセスしてみてください。
すると、以下画像のように表示されるはずです。LOGINとREGISTERが右上に表示されます!
php artisan serve
#3 : ロケールの設定
C:\xampp\htdocs\restaurant_menu\config\app.php で、
'timezone' と 'locale' を以下のように変更します。
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
#4 : データベースの作成と、.envファイルの設定
手順1: XAMPPでデータベースを作成します。データベース名は、restaurant_menu にします。(XAMPPでのデータベースの作り方がわからない方は、ご自身で調べてみてください。)
手順2: データベースを作ったら、C:\xampp\htdocs\restaurant_menu\.env
で、 APP_NAME を Restaurant Menu に、DB_DATABASE を restaurant_menu に変更します。(* 空白を含む値を環境変数に定義する場合は、ダブル引用符で囲む必要があります。)
DB_USERNAME と DB_PASSWORD はそのままにします。XAMPP側でデータベースユーザーを設定した人は、こちらも変更してください。
APP_NAME="Restaurant Menu"
|
|途中省略
DB_DATABASE=restaurant_menu
DB_USERNAME=root
DB_PASSWORD=
#5 : モデルとマイグレーションファイル (テーブル) の作成
データベースを作成したら、お次はテーブルを作成していきます。
データベースに入力する必要があるデータは、ダッシュボードの以下黄色枠の部分です。
但し、カテゴリーは、例えば『グランドメニュー』の場合に、デミグラスハンバーグやさんま定食など、一つのカテゴリーに対して複数のメニューがあるので、別テーブルにしていきます。
手順1: artisanコマンドで、『Category』と『Product』の二つのモデルを作成します。-mオプションをつけて同時にマイグレーションファイルも作成します。
php artisan make:model Category -m
php artisan make:model Product -m
手順2:『Category』モデルのマイグレーションファイル(C:\xampp\htdocs\restaurant_menu\database\migrations\2020_09_19_235630_create_categories_table.php)(2020_09_19 の部分は、作成した日付ですので、作成日によって変わります。)へ移動します。カテゴリーテーブルに追加するカラムは、カテゴリー名だけですので、18行目辺りに1行だけ追記します。
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name'); // 追記
$table->timestamps();
});
}
手順3:『Category』モデルの複数代入設定をします。複数代入設定は、意図しないデータを書き換えさせない為に、入力できるデータベーステーブルのカラムを指定するものです。今回は、ホワイトリスト方式である『$fillable方式』を使って、入力できるカラムを指定します。
Categoryモデルファイル(C:\xampp\htdocs\restaurant_menu\app\Category.php *←以後、Category.php と略して表記します。)へ移動します。今回はnameカラム一個だけです。
class Category extends Model
{
protected $fillable=['name'];
}
手順4: Productモデルのマイグレーションファイル(C:\xampp\htdocs\restaurant_menu\database\migrations\2020_09_19_235644_create_products_table.php)へ移動します。
18行目辺りから、以下のように追記します。category_id が、Categoryテーブルとの外部キーです。
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name'); // ここから追記
$table->text('description');
$table->integer('price');
$table->string('image');
$table->integer('category_id'); // ここまで追記
$table->timestamps();
});
}
手順5: Productモデルの複数代入設定をします。モデルファイル
(C:\xampp\htdocs\restaurant_menu\app\Product.php *←以後、Product.phpと略して表記します。) へ移動して、下記の通りカラムを指定します。
class Product extends Model
{
protected $fillable=['name', 'description', 'price', 'image', 'category_id'];
}
手順6:artisanコマンドでマイグレーションします。
php artisan migrate
データベースに戻り、categories テーブルと、products テーブルが作成されていることをご確認ください。
#6 : コントローラーの作成
普通だったらコントローラーより先にルーティングを設定すると思うのですが、ちょっとルーティングの部分でご説明申し上げたい箇所がありまして、それを説明する為に、コントローラーを先に作成します。
artisanコマンドで CategoryController と ProductController を作成します。
-r オプションをつけると、基本的なCRUDルーティングのアクションが自動で入った状態でコントローラーが作成されます!
php artisan make:controller CategoryController -r
php artisan make:controller ProductController -r
作成されたコントローラーファイル(C:\xampp\htdocs\restaurant_menu\app\Http\Controllers\CategoryController.php *←以後、CategoryController.php と略して表記します。また、ProductController の方も同様に略して表記します。)の中身を見てみると、-r オプションをつけたので、以下のようにindex() やらcreate() やら、7つのコントローラーアクションが自動で入力されています!
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CategoryController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
//
}
/**
* Show the form for creating a new resource.
*
* @return \Illuminate\Http\Response
*/
public function create()
{
//
}
以下省略
|
|
#7 : ルーティングの設定
今回のルーティングは、リソースルーティングを使います。
これは、先ほど-r オプションをつけて、7つのアクションが自動で入った状態のコントローラーを作りましたが、そのそれぞれのアクションに対して、典型的なCRUDルーティングをたった一行のコードで割り当てることができるものです。
ルーティングファイル(C:\xampp\htdocs\restaurant_menu\routes\web.php *←以後、web.phpと略して表記します。)にて、以下のように、下二行を追記します。
Route::get('/', function () {
return view('welcome');
});
Auth::routes();
Route::get('/home', 'HomeController@index')->name('home');
Route::resource('category','CategoryController'); //追記
Route::resource('product','ProductController'); //追記
追記後、artisanコマンドでルーティングを確認してみてください。
php artisan route:list
以下のように表示されます。
C:\xampp\htdocs\restaurant_menu>php artisan route:list
+--------+-----------+--------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method | URI | Name | Action | Middleware |
+--------+-----------+--------------------------+------------------+------------------------------------------------------------------------+------------+
| | GET|HEAD | / | | Closure | web |
| | GET|HEAD | api/user | | Closure | api |
| | | | | | auth:api |
| | POST | category | category.store | App\Http\Controllers\CategoryController@store | web |
| | GET|HEAD | category | category.index | App\Http\Controllers\CategoryController@index | web |
| | GET|HEAD | category/create | category.create | App\Http\Controllers\CategoryController@create | web |
| | DELETE | category/{category} | category.destroy | App\Http\Controllers\CategoryController@destroy | web |
| | PUT|PATCH | category/{category} | category.update | App\Http\Controllers\CategoryController@update | web |
| | GET|HEAD | category/{category} | category.show | App\Http\Controllers\CategoryController@show | web |
| | GET|HEAD | category/{category}/edit | category.edit | App\Http\Controllers\CategoryController@edit | web |
| | GET|HEAD | home | home | App\Http\Controllers\HomeController@index | web |
以下省略
|
|
createメソッドやら、updateメソッドやらに自動でルーティングが設定されています!(これを説明する為に、コントローラーを先に作成したのです!)これで、
『 Route::get('/category/create', 'CategoryController@create'); 』
とか、
『 Route::put('/category/{category}', 'CategoryController@update'); 』
とかをいちいち書かずに済みます!便利!
#8:作成手順の説明
さて、土台となるものを一通り作成し終えたところで、これからの開発手順をざっくり説明します。そうじゃないと色々と混乱してくると思うので。。(;^_^A
その1: カテゴリーの追加フォーム及び一覧ページの作成
メニュー追加フォームで、予め作られたカテゴリーをプルダウンから選ぶ項目を作ります。ですので、何が何でもカテゴリーを一番先に作らなくてはいけません。
その2: メニュー追加フォーム及びダッシュボードの作成
カテゴリー関連のページを作り終えたら、メニュー追加フォームと、その追加されたメニューを一覧表示するダッシュボードを作ります。
その3: お客さん用画面の作成
ダッシュボードまで作り終えたら、最後にお客さん用画面に情報を反映させて完成です。
#9:Viewページの作成(カテゴリー)
それでは、カテゴリーの追加フォーム及び一覧ページを作っていきます。一覧では、各カテゴリーの編集と削除もできるようにします。
手順1: まずは、追加フォームのViewを作ります。C:\xampp\htdocs\restaurant_menu\resources\views にcategoryというフォルダを作って、create.blade.php という名前でファイルを作ります。コードは以下の通りです。
@extends('layouts.app')
@section('content')
<div class="container mt-3" style="max-width: 720px;">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
<form>
<div class="form-group">
<label for="categoryAdd" class="font-weight-bold">新規カテゴリー追加</label>
<input type="text" class="form-control" id="categoryAdd" name="name" />
</div>
<button type="submit" class="btn btn-primary">追加</button>
</form>
<div class="my-4">
<a href="{{ url('/category/') }}">> 一覧・編集ページへ</a>
</div>
</div>
@endsection
『@extends('layouts.app') 』の箇所で使われているlayouts.appのページは、特に編集せずにデフォルトのものをそのまま使っています。
@extends() や @section() の使い方等、laravelのbladeに関する基本的な説明は省きますので、ご存知でない方はご自身で調べてみてください。
手順2: ブラウザで表示確認する為に、コントローラーを設定します。
CategoryController.phpで、以下のようにcreate()メソッドを設定します。
public function create()
{
return view('category.create');
}
ブラウザで、『 http://127.0.0.1:8000/category/create 』へアクセスします。すると以下のように表示されるはずです。
手順3: お次は、一覧ページのViewを作ります。
categoryフォルダに index.blade.php という名前でファイルを作ります。
コードは以下の通りです。(*データベースからデータを引っ張ってきて表示させる箇所は、現段階ではサンプル値の手入力です。)
@extends('layouts.app')
@section('content')
<div class="container" style="max-width: 720px">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
<table class="table table-bordered mt-2">
<thead class="table-dark">
<tr>
<th scope="col">
#
</th>
<th scope="col">
作成日
</th>
<th scope="col">
カテゴリー
</th>
<th scope="col">
編集
</th>
<th scope="col">
削除
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">
1
</th>
<td>
2020/2/11
</td>
<td>
グランドメニュー
</td>
<td>
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</td>
<td>
<button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button>
</td>
</tr>
</tbody>
</table>
<div class="my-4">
<a href="{{ url('/category/create') }}">> カテゴリー新規追加ページへ</a>
</div>
</div>
@endsection
手順4: ブラウザで表示確認する為に、コントローラーを設定します。
CategoryController.phpで、以下のようにindex()メソッドを設定します。
public function index()
{
return view('category.index');
}
ブラウザで、『 http://127.0.0.1:8000/category/ 』へアクセスします。すると以下のように表示されるはずです。
#10: データベースへの挿入(カテゴリー)
カテゴリー情報をデータベースに挿入していきます。
手順1: 前回作ったcategoryのviewファイル(create.blade.php)を編集します。ソースコードは以下の通りです。
<form action="{{ route('category.store') }}" method="POST"> //追記
@csrf //追記
<div class="form-group">
<label for="categoryAdd" class="font-weight-bold">新規カテゴリー追加</label>
<input type="text" class="form-control" id="categoryAdd" name="name" />
</div>
<button type="submit" class="btn btn-primary">追加</button>
</form>
9行目辺りの、<form>タグにaction属性とmethod属性を追記します。POSTデータを送信してデータベースに挿入するので、CategoryControllerのstoreメソッドをヒットするようにします。laravelに限らずなのかもしれませんが、フォーム入力されたデータを保存するにはstore()メソッドを使います。
リソースルーティングを設定しているので、category.storeという名前のルーティングが既についています。ですので、action属性には {{ route('category.store') }} と書くことができます。bladeのroute関数についてご存知でない方は、ご自身で調べてみてください。
また、Laravelでお約束ですが、formタグの下には『@csrf 』をつけることを忘れないようにします。CSRF対策の為です。これを書かないと、419|Page Expired エラー等が起きます。
手順2: 正常にデータが送信できるかを確認します。ここではひとまずdd()関数を利用してチェックしてみます。
CategoryController.phpで、store()メソッドを以下のように設定します。
※ フォームから飛んでくるデータはRequest型で渡ってきます。その為、コールバック関数の引数には、Implicit Bindingを利用して、(Request $request) とします。この辺りがよくわからない方は、『Laravel フォーム Request Implicit Binding』等で検索してみてください。
public function store(Request $request)
{
return dd($request->all());
}
手順3:『 http://127.0.0.1:8000/category/create 』にアクセスします。何でもいいのですが、とりあえず『グランドメニュー』と入力して、追加ボタンを押します。
すると、以下のように表示されて、CSRFのトークンと、カテゴリー情報(inputタグの値)が送られてきているのがわかります!
手順4: データベースに挿入していきます。dd()メソッドはコメントアウトして、Eloquentモデルのcreate()メソッドを使って挿入します。
その際、Categoryモデルを使うので、コントローラーに『 use App\Category; 』を追記しておくことを忘れないようにします。
コードは以下の通りです。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Category; //追記
以下省略
|
|
public function store(Request $request)
{
// return dd($request->all());
Category::create([
'name'=>request('name')
]);
}
手順5: データがきちんと挿入されるか確認します。先程と同じようにブラウザで『 グランドメニュー』と入力して追加ボタンを押します。
categoriesテーブルを確認すると、以下の通りグランドメニューが追加されています!データベースへの挿入ができました!
#11:フラッシュメッセージの表示とリダイレクト(カテゴリー)
カテゴリーを追加した際に、直前のページ(要は、カテゴリーを追加した同じページのこと。)にリダイレクトして、フラッシュメッセージを出すようにします。
手順1: まずは、View側でフラッシュメッセージを出すコードを追記します。categoryのviewファイル(create.blade.php)へ移動して、9行目辺りから追記していきます。コードは以下の通りです。
@extends('layouts.app')
@section('content')
<div class="container mt-3" style="max-width: 720px;">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
@if (session('message')) //ここから追記
<div class="alert alert-success" role="alert">{{ session('message') }}</div>
@endif //ここまで追記
<form action="{{ route('category.store') }}" method="POST">
@csrf
以下省略
|
|
グローバルなsession()ヘルパ関数を使います。引数を一つにすることで、キーに対する値を取得することができます。if文にすることで、'message'というキー名でsessionデータがあれば表示しないさい、と書いています。
手順2: 次に、コントローラーに設定していきます。
CategoryControllerのstore()メソッドに、『 return redirect() ~~ 』部分を追記します。
public function store(Request $request)
{
// return dd($request->all());
Category::create([
'name'=>request('name')
]);
return redirect()->back()->with('message', 'カテゴリーが追加されました。'); //追記
}
直前のページにリダイレクトするには、back()メソッドを使います。フラッシュメッセージを出すには、セッションデータを返す必要があるので、with()メソッドを使います。with()メソッドは、リダイレクトする場合や、viewを返す時などにデータを渡せるメソッドです。view側に変数を渡す場合等によく使われます。今回は、『 with('キー', 'バリュー'); 』のように2つ引数を渡すことで、配列としてセッションデータをview側に渡します。
手順3: ブラウザで確認してみます。
再度『 http://127.0.0.1:8000/category/create 』にアクセスして、カテゴリーを入力後に追加ボタンを押すと、リダイレクトされて、以下のようにフラッシュメッセージが表示されます。
#12:一覧ページへ反映させる(カテゴリー)
カテゴリーを無事データベースに追加できたので、それを一覧ページに表示させます。
手順1: まずは、コントローラーに一覧情報を表示させるためのデータ受け渡しの設定を行います。
CategoryController.phpで、元々書いてあった『return view('category.index');』はコメントアウトして、index() メソッドに下二行を追記します。
public function index()
{
// return view('category.index');
$categories = Category::latest()->get(); //追記
return view('category.index', ['categories' => $categories]); //追記
}
latest()は、新しいもの順でデータを取ってくる際に使えるように、Laravelが用意してくれている便利なメソッドです。クエリビルダで言う所の、orderBy('created_at', 'desc')と同じ意味になります。その為、『Category::latest()->get();』は、『Category::orderBy('created_at', 'desc')->get();』と同じ結果になります。
手順2: 先程作った一覧ページのView(index.blade.php)へ移動して、30行目辺りの
以下に、//追記 もしくは //変更 と書かれている部分を追記・変更してください。
@extends('layouts.app')
@section('content')
<div class="container" style="max-width: 720px">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
<table class="table table-bordered mt-2">
<thead class="table-dark">
<tr>
<th scope="col">
#
</th>
<th scope="col">
作成日
</th>
<th scope="col">
カテゴリー
</th>
<th scope="col">
編集
</th>
<th scope="col">
削除
</th>
</tr>
</thead>
<tbody>
@if (count($categories) > 0) //追記
@foreach ($categories as $key=>$category) //追記
<tr>
<th scope="row">
{{ $key+1 }} //変更
</th>
<td>
{{ $category->created_at->format('Y/m/d') }} //変更
</td>
<td>
{{ $category->name }} //変更
</td>
<td>
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</td>
<td>
<button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button>
</td>
</tr>
@endforeach //追記
@else //追記
<tr> //追記
<td colspan="5">追加されたカテゴリーはありません。</td> //追記
</tr> //追記
@endif //追記
</tbody>
</table>
<div class="my-4">
<a href="{{ url('/category/create') }}">> カテゴリー新規追加ページへ</a>
</div>
</div>
@endsection
31行目辺りの @if (count($categories) > 0) の部分で、カテゴリーが1つ以上あればテーブルを表示して、そうでなければ、51行目辺りの、@else 部分で『追加されたカテゴリーはありません。』という文言を表示するようにします。
手順3: @else 部分が正常に表示されるかを確認する為に、データを全部消去します。artisanコマンドで以下の通りmigrate:freshを行います。
php artisan migrate:fresh
手順4: ブラウザで確認します。『 http://127.0.0.1:8000/category 』にアクセスします。すると、以下の通り表示されるはずです。
手順5:『> カテゴリー新規追加ページへ』 のリンクをクリックして、追加ページへ移動します。そこで、『ランチメニュー』『グランドメニュー』『おすすめメニュー』を追加してみます。
手順6:『> 一覧・編集ページへ のリンク』をクリックすると、以下のようにデータが表示されます!
*おまけで解説です。↓↓
index.blade.phpの、『 @foreach ($categories as $key=>$category) 』の部分ですが、これは、通し番号の部分( {{ $key+1 }} のとこ。)を表示させる為に、このように書いています。Laravelではモデルを使った場合に、データベースから返ってくる値は常にCollectionインスタンスになるので、$key変数に、連想配列の”キー”の部分を入れ込むことができます。
* LaravelのCollectionが分からない方は、結構豊富に記事が出ているので調べてみてください!また、PHPの連想配列を取り出す方法に関しては、この記事がわかりやすかったです。
【やさしいPHP】foreach文の基礎を知ってかんたんな応用を試してみる
実際に確認してみます。CategoryController.phpのindex()メソッドに、以下のようにdd() ヘルパ関数を追記してみてください。
public function index()
{
// return view('category.index');
$categories = Category::latest()->get();
dd($categories); //追記
return view('category.index', ['categories' => $categories]);
}
『 http://127.0.0.1:8000/category 』にアクセスすると、以下のように表示されます。Collectionインスタンスが返されて、”キー”の部分には、配列で言う所の配列番号が入っています。これを表示させることで、一覧表の通し番号を表示させています。
#13:編集機能をつける(カテゴリー)
次は、カテゴリー一覧ページの編集ボタンから、カテゴリーを編集できるようにします。
手順1: 一覧ページ(index.blade.php)へ移動して、編集ボタンの箇所を<a>タグで囲みます。
<a href="{{ route('category.edit', [ 'category' => \$category->id ]) }}"> //追記
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</a> //追記
『 route('category.edit', [ 'category' => $category->id ]) 』の箇所で、該当するカテゴリーのidをつけたURLへ遷移します。ブラウザで実際に編集ボタンを押してみると、URLが『 http://127.0.0.1:8000/category/1/edit 』や『 http://127.0.0.1:8000/category/2/edit 』へ飛びます。
php artisan route:list を今一度確認してみてください。
『 category.edit 』 という名前のついたルーティングは、『 category/{category}/edit 』へ遷移するように設定されています。route関数の第二引数につけたパラメーターは、自動的にURLの正しい場所へ埋め込まれます。
手順2: お次は、編集ボタンを押した先にeditページを表示させるようにコントローラーに設定していきます。(editページ自体はこの後作ります。)
CategoryController.phpで、edit() メソッドを以下のように編集します。indexページの編集ボタンを押したときに渡ってきたパラメーターが($id)の部分に入ります。
public function edit($id)
{
$category = Category::find($id);
return view('category.edit', ['category' => $category]);
}
手順3: editページを作成します。C:\xampp\htdocs\restaurant_menu\resources\views\category に『 edit.blade.php 』という名前でファイルを作ります。同じフォルダのcreate.blade.phpファイルとほぼ中身は一緒なので、まずはそれを全てコピペします。そのうえで、以下『//追記 』もしくは 『//変更』と書かれている部分を追記・変更します。
@extends('layouts.app')
@section('content')
<div class="container mt-3" style="max-width: 720px;">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
<form action="{{ route('category.update', [ 'category' => $category->id ]) }}" method="POST"> // 変更
@csrf
@method('PUT') //追記
<div class="form-group">
<label for="categoryAdd" class="font-weight-bold">カテゴリー編集</label> // 変更
<input type="text" class="form-control" id="categoryAdd" name="name" value="{{ $category->name }}" /> // 変更
</div>
<button type="submit" class="btn btn-primary">編集</button> // 変更
</form>
<div class="my-4">
<a href="{{ url('/category/') }}">> 一覧ページへ</a> // 変更
</div>
</div>
@endsection
何点か説明します。
『 <form action="{{ route('category.update', [ 'category' => $category->id ]) }}" method="POST"> 』の箇所は、アップデートしたデータをデータベースに反映させる為に、update() メソッドをヒットするように設定します。また、update() メソッドはPUTリクエストで送信するため、『 @method('PUT') 』という記述を書く必要があります。これは、現段階ではブラウザがGETかPOSTしか理解することができないため、これはPOSTじゃなくて、PUTだよ、ということをブラウザ側に伝える為です。これは、DELETEリクエストの時も同様です。
また、編集ページのため、編集する前のデータがフォームに残った状態で表示させます。その為、inputタグにvalue属性を追記します。
これで、一覧ページの編集ボタンをクリックしてみてください。以下のように該当するカテゴリーが入力された状態で編集フォームが表示されます。
手順4: 編集されたデータをデータベースに保存していきます。CategoryController.php へ移動して、update()メソッドを、以下のように設定します。
public function update(Request $request, $id)
{
$category = Category::find($id);
$category->name = request('name');
$category->save();
return redirect('/category')->with('message', 'カテゴリーが編集されました。');
}
コードの中身を説明します。
① 『$category = Category::find($id);』で、フォームから渡ってきたデータをidで探します。
② 『$category->name = request('name');』で、該当するidのデータのnameカラムを、フォームから渡ってきたRequest型の値に変更します。
③ ②のデータを保存します。
④ 一覧ページにリダイレクトして、そこで編集完了のフラッシュメッセージを出します。
手順5: 一覧ページ(index.blade.php)にフラッシュメッセージを表示させるコードを書いていなかったので、追記しておきます。
@extends('layouts.app')
@section('content')
<div class="container" style="max-width: 720px">
<div class="text-right">
<a href="{{ url('/product/create') }}">< 戻る</a>
</div>
@if (session('message')) //ここから追記
<div class="alert alert-success" role="alert">{{ session('message') }}</div>
@endif //ここまで追記
<table class="table table-bordered mt-2">
<thead class="table-dark">
手順6: 編集ページで、『おすすめメニュー』を『夏のおすすめメニュー』と変更して、編集ボタンを押してみます。そうすると、一覧ページにリダイレクトして、編集された旨のフラッシュメッセージが表示されます。
#14:削除機能をつける(カテゴリー)
一覧ページでカテゴリーを削除できるようにします。削除する機能は、delete()メソッド一発で実現できるので簡単です!
手順1: CategoryController.php のdestroy()メソッドを以下のように設定します。
public function destroy($id)
{
$category = Category::find($id);
$category->delete();
return redirect('/category')->with('message', 'カテゴリーが削除されました。');
}
コードの説明をします。
① 『 $category = Category::find($id); 』で、パラメーターから渡ってきたidを元にデータを探します。
② 『 $category->delete(); 』では、①のデータをdelete()メソッド一発でデータベースから削除しています。
③ 一覧ページにリダイレクトして、フラッシュメッセージ用のデータをwith()関数でbladeに送ります。
手順2: 次は、View側の設定をしていきます。削除する際に、モーダルを使って確認画面を出してみようと思います。一覧ページ(index.blade.php)で、元々削除ボタンがあった
タグ箇所に、Bootstrapのモーダルのコードをとりえあえずコピペします。コピペするのは、以下サイトの『Live demo』のコードです。手順3: コピペしたら、以下『//変更』もしくは 『//追記 』と書かれている部分を変更・追記します。コードは以下の通りです。
<td>
<!-- <button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button> --> //コメントアウト
<!-- Button trigger modal -->
<button type="button" class="btn btn-outline-primary" data-toggle="modal" data-target="#exampleModal{{$category->id}}"><i class="far fa-trash-alt"></i> 削除</button> //変更
<!-- Modal -->
<div class="modal fade" id="exampleModal{{$category->id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true"> //変更
<div class="modal-dialog" role="document">
<form action="{{ route('category.destroy', [ 'category' => $category->id ]) }}" method="POST"> //追加
@csrf //追加
@method('DELETE') //追加
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">カテゴリー削除</h5> //変更
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
本当に削除しますか? //変更
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button> //変更
<button type="submit" class="btn btn-primary">削除</button> //変更
</div>
</div>
</form>
</div>
</div>
</td>
53行目辺りに、元々あった削除ボタンのコード『 <button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button> 』はコメントアウトします。
次に、<div class="modal-dialog" role="document"> の直下、60行目辺りにformタグを追加します。formタグでは、destroy()メソッドをヒットさせるようにします。@csrf と @method('DELETE') をつけるのを忘れないようにしてください。また、55行目あたりの『data-target="#exampleModal{{$category->id}}">』と58行目辺りの『id="exampleModal{{$category->id}}"』の箇所には『{{$category->id}}』を追加しています。
続いて、『カテゴリー削除』『本当に削除しますか?』『キャンセル』『削除』の文言を変更します。『削除』文言の<button>タグは、type="submit" に変更してください。
手順4: ブラウザで確認してみます。削除ボタンを押すとモーダルで確認画面が出てきて、更に削除ボタンを押すとフラッシュメッセージと共に一覧から削除されます。また、データベースからも削除されます。
#15:バリデーションをつける(カテゴリー)
フォームにバリデーションを付けていなかったので、追加ページと一覧ページにつけていきます。
手順1: まずは追加ページの方からです。コントローラの設定を先に行います。CategoryController.phpで、store()メソッドに、request()->validate 箇所を追記します。
public function store(Request $request)
{
// return dd($request->all());
request()->validate(
['name' => 'required|unique:categories'],
['name.required' => 'カテゴリーを入力してください。',
'name.unique' => 'そのカテゴリーは既に追加されています。']
);
Category::create([
'name'=>request('name')
]);
return redirect()->back()->with('message', 'カテゴリーが追加されました。');
}
手順2: 追加ページ(create.blade.php)に移動して、以下『//変更』もしくは 『//追記 』と書かれている部分を変更・追記します。
<form action="{{ route('category.store') }}" method="POST">
@csrf
<div class="form-group">
<label for="categoryAdd" class="font-weight-bold">新規カテゴリー追加</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="categoryAdd" name="name" /> //変更
@error('name') //追記
<p class="text-danger">{{ $message }}</p> //追記
@enderror //追記
</div>
<button type="submit" class="btn btn-primary">追加</button>
</form>
17行目辺りのinputタグのclass名には、『 @error('name') is-invalid @enderror 』を追記しています。これは、@errorディレクティブを利用をして、エラーメッセージがあったらis-invalidクラスを付けてね、ということを表しています。また、その後の18行目から20行目にかけても@errorディレクティブを使って、$message変数を表示させるようにしています。
手順3: カテゴリー追加ページに行って、何も入力せずに追加ボタンを押すと、以下のようにエラーメッセージが表示されます。
重複して追加しようとすると、既に登録されている旨のエラーが出ます。
手順4: 編集ページも同様にバリデーションをかけていきます。
CategoryController.phpのupdate()メソッドにrequest()->validate()部分を追記します。
public function update(Request $request, $id)
{
request()->validate(
['name' => 'required|unique:categories'],
['name.required' => 'カテゴリーを入力してください。',
'name.unique' => 'そのカテゴリーは既に追加されています。']
);
$category = Category::find($id);
$category->name = request('name');
$category->save();
return redirect('/category')->with('message', 'カテゴリーが編集されました。');
}
手順5: View側でも設定していきます。追加ページと同じように追記していきます。編集ページ(edit.blade.php)にて、inputタグのclass名に、『@error('name') is-invalid @enderror』を追記します。その後の18行目から20行目にかけても@errorディレクティブを使って、$message変数を表示させます。
<form action="{{ route('category.update', [ 'category' => $category->id ]) }}" method="POST">
@csrf
@method('PUT')
<div class="form-group">
<label for="categoryAdd" class="font-weight-bold">カテゴリー編集</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="categoryAdd" name="name" value="{{ $category->name }}" /> //変更
@error('name') //追加
<p class="text-danger">{{ $message }}</p> //追加
@enderror //追加
</div>
<button type="submit" class="btn btn-primary">編集</button>
</form>
これで、フォームを空にして編集ボタンを押すと、『カテゴリーを入力してください。』というエラーが出て、何も変更せずに編集ボタンを押すと『そのカテゴリーは既に追加されています。』というエラーが出ます。
#16:Viewページの作成(メニュー)
カテゴリー関連の機能は完成したので、お次はメニュー追加フォームとダッシュボードの作成を行います!
手順1:まずはメニュー追加フォームのViewを作ります。
C:\xampp\htdocs\restaurant_menu\resources\views にproductというフォルダを作って、create.blade.php という名前でファイルを作ります。コードは以下の通りです。
フラッシュメッセージと@errorディレクティブを使ったバリデーションの部分は、既にコード内に記載済みです。カテゴリーの時と同様なので、これに関する説明は省きます。
@extends('layouts.app')
@section('content')
<div class="container mt-3" style="max-width: 720px;">
<div class="text-right">
<a href="{{ url('/product/') }}">< 戻る</a>
</div>
@if ( session('message') )
<div class="alert alert-success" role="alert">{{ session('message') }}</div>
@endif
<form action="{{ route('product.store') }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="form-group" style="margin-top: 30px; margin-bottom: 30px">
<label for="name" class="font-weight-bold">商品名</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" />
@error('name')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="textarea" class="font-weight-bold">詳細</label>
<textarea class="form-control @error('description') is-invalid @enderror" id="textarea" rows="5" name="description"></textarea>
@error('description')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="price" class="font-weight-bold">値段</label>
<input type="text" class="form-control @error('price') is-invalid @enderror" id="price" name="price" />
<small class="form-text text-muted">半角数字で入力してください。</small>
@error('price')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="category" class="font-weight-bold">カテゴリー</label>
<select class="form-control @error('category') is-invalid @enderror" id="category" name="category">
<option value="" disabled selected style="display: none;">カテゴリーを選択してください。</option>
@foreach(App\Category::all() as $category)
<option value="{{ $category->id }}">{{ $category->name }}</option>
@endforeach
</select>
@error('category')
<p class="text-danger">{{ $message }}</p>
@enderror
<div class="text-right mt-2">
<a type="button" href="{{ url('/category/create/') }}" class="btn btn-outline-secondary py-1" role="button">新規追加</a>
<a type="button" href="{{ url('/category/') }}" class="btn btn-outline-secondary py-1" role="button">編集</a>
</div>
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="image" class="font-weight-bold">画像アップロード</label>
<input type="file" class="form-control-file @error('image') is-invalid @enderror" id="image" name="image" />
@error('image')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<button type="submit" class="btn btn-primary my-3">送信</button>
</form>
</div>
@endsection
40行目辺りの<option>タグ『 <option value="" disabled selected style="display: none;">』の箇所について少し説明します。
これは、『カテゴリーを選択してください。』の文章を、下記画像のようにデフォルトで表示させる為に書いています。
属性の説明は下記4点です。
① value値を空にします。( value="")
② 選択できないようにします。(disabled)
③ デフォルトで選択表示されているようにします。(selected)
④ プルダウンの中ではなく、フォームの中に表示させるようにします。(style="display: none;)
また、『@foreach(App\Category::all() as $category)』の部分は、コントローラーから変数が渡って来ている訳ではないので、ここでCategoryモデルにアクセスしています。
手順2: ブラウザで表示確認する為に、コントローラーの設定をします。(*おさらいですが、ルーティングは、リソースルーティングで既に設定しています。)
ProductController.phpで、以下のようにcreate()メソッドを設定します。
public function create()
{
return view('product.create');
}
手順3: ブラウザで、『 http://127.0.0.1:8000/product/create 』へアクセスします。すると以下のように表示されるはずです。
手順4: 次に、ダッシュボードのViewを作ります。
先程作ったproductフォルダに、index.blade.phpと言う名前でファイルを作ります。コードは以下の通りです。(*データベースからデータを引っ張ってきて表示させる箇所は、現段階ではサンプル値の手入力です。)
@extends('layouts.app')
@section('content')
<div class="container-fluid my-2">
<div class="row m-2">
<div class="col">
<h3 class="font-weight-bold">ダッシュボード</h3>
</div>
<div class="col text-right">
<a type="button" href="{{ url('/product/create/') }}" class="btn btn-primary text-right" role="button"><i class="fas fa-plus"></i> 新規追加</a>
</div>
</div>
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th scope="col">
id
</th>
<th scope="col">
画像
</th>
<th scope="col">
商品名
</th>
<th scope="col">
詳細
</th>
<th scope="col">
値段
</th>
<th scope="col">
カテゴリー
</th>
<th scope="col">
編集
</th>
<th scope="col">
削除
</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">
1
</th>
<td style="max-width: 200px;">
<img src="#" class="img-fluid" />
</td>
<td>
デミグラスハンバーグ
</td>
<td style="max-width: 300px;">
お肉の旨味をギュッと閉じ込めたジューシーなハンバーグに、濃厚なデミグラスソースをたっぷりとかけてお楽しみください。
</td>
<td>
880
</td>
<td>
グランドメニュー
</td>
<td>
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</td>
<td>
<button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button>
</td>
</tr>
</tbody>
</table>
</div>
@endsection
手順5: ブラウザで表示確認する為に、コントローラーの設定をします。
ProductController.phpで、以下のようにindex()メソッドを設定します。
public function index()
{
return view('product.index');
}
手順6: ブラウザで、『 http://127.0.0.1:8000/product/ 』へアクセスします。すると以下画像のように表示されるはずです。画像はまだアップしていないので、リンク切れになっています。
#17:データベースへの挿入(メニュー)
手順1: フォームからの送信データをデータベースに挿入していきます。ProductController.phpで、以下のようにstore()メソッドを設定します。コードは以下の通りです。バリデーションの部分は、既に含んでいます。
public function store(Request $request)
{
request()->validate(
['name' => 'required',
'description' => 'required',
'price' => 'required|integer',
'category' => 'required',
'image' => 'required|mimes:jpeg,png,jpg,gif,svg'],
['name.required' => '商品名を入力してください。',
'description.required' => '詳細を入力してください。',
'price.required' => '値段を入力してください。',
'category.required' => 'カテゴリーを入力してください。',
'image.required' => '画像を選択してください。']
);
$image = $request->file('image');
$name = time().'.'.$image->getClientOriginalExtension();
$destinationPath = public_path('/images');
$image->move($destinationPath,$name);
Product::create([
'name'=>request('name'),
'description'=>request('description'),
'price'=>request('price'),
'category_id'=>request('category'),
'image'=>$name
]);
return redirect()->back()->with('message', '商品情報が追加されました。');
}
上記のコードの部分で、画像アップロードに関する部分を説明します。
① まずはフォームからアップされた画像を変数に代入して、($image = $request->file('image');)
② アップロードした日時と画像の拡張子をつけてユニークな画像名にします。($name = time().'.'.$image->getClientOriginalExtension();)
③ C:\xampp\htdocs\restaurant_menu\public にimagesという名前でフォルダを作っておきます。そして、そこに画像を保存するようにします。
($destinationPath = public_path('/images'); $image->move($destinationPath,$name);)
手順2: データの挿入にはProductモデルのcreate()メソッドを使っているので、ProductController.php上部に『use App\Product;』を追記することを忘れないようにします。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product; //追記
以下省略
|
|
手順3: ブラウザに戻って、メニュー追加ページで適当に情報を入力して、送信ボタンを押してみます。正常に送信された場合は、以下画像のようにフラッシュメッセージが表示されます。
何も入力せずに送信ボタンを押すと、以下画像のようにエラーが表示されるはずです。
#18:ダッシュボードへ反映させる
ダッシュボードに追加したメニュー情報を反映させます。
手順1: まずは、コントローラーに情報を表示させるためのデータ受け渡しの設定を行います。
ProductController.phpで、元々書いてあった『return view('product.index');』はコメントアウトして、index() メソッドに下二行を追記します。
public function index()
{
// return view('product.index');
$products = Product::latest()->paginate(5);
return view('product.index', ['products' => $products]);
}
特筆すべきは、paginate(5)のページネーションの箇所です。5項目追加したら次のページに行くように設定しています。
手順2: ダッシュボードのView(index.blade.php)へ移動して、43行目辺りの<tbody>以下に、//追記 もしくは //変更 と書かれている部分を追記・変更してください。@if() 文や、@foreach 、@else 文の箇所は、Categoryの時と同様なので説明は省きます。
また、115行目辺りの$products->links()の箇所は、コントローラーで設定したpaginate(5)の部分を表示させています。
@extends('layouts.app')
@section('content')
<div class="container-fluid my-2">
<div class="row m-2">
<div class="col">
<h3 class="font-weight-bold">ダッシュボード</h3>
</div>
<div class="col text-right">
<a type="button" href="{{ url('/product/create/') }}" class="btn btn-primary text-right" role="button"><i class="fas fa-plus"></i> 新規追加</a>
</div>
</div>
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th scope="col">
#
</th>
<th scope="col">
画像
</th>
<th scope="col">
商品名
</th>
<th scope="col">
詳細
</th>
<th scope="col">
値段
</th>
<th scope="col">
カテゴリー
</th>
<th scope="col">
編集
</th>
<th scope="col">
削除
</th>
</tr>
</thead>
<tbody>
@if(count($products) > 0 ) //追記
@foreach($products as $key=>$product) //追記
<tr>
<th scope="row">
{{ $key+1 }} //変更
</th>
<td style="max-width: 200px;">
<img src="{{asset('images')}}/{{$product->image}}" class="img-fluid" /> //変更
</td>
<td>
{{$product->name}} //変更
</td>
<td style="max-width: 300px;">
{{$product->description}} //変更
</td>
<td>
{{$product->price}} 円 //変更
</td>
<td>
{{$product->category->name}} //変更
</td>
<td>
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</td>
<td>
<button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button>
</td>
</tr>
@endforeach //追記
@else //追記
<tr> //追記
<td colspan="8">追加された商品情報はありません。</td> //追記
</tr> //追記
@endif //追記
</tbody>
</table>
<div class="d-flex"> //追記
<div class="mx-auto"> //追記
{{$products->links("pagination::bootstrap-4")}} //追記
</div> //追記
</div> //追記
</div>
@endsection
手順3: 63 行目辺りの『{{$product->category->name}}』の箇所ですが、これはProductモデルからCategoryモデルにアクセスしようとしている為、(厳密に言うと、Categoryモデルのインスタンスのnameプロパティの値にアクセス)モデルのリレーション設定が必要です。
ProductモデルのモデルファイルProduct.phpで、以下のようにcategory()メソッドを追記します。一つのメニューにつき、一つのカテゴリーがあるので、関係性はhasOneにします。
Laravelのモデルのリレーションについてご存知でない方は、ご自身で調べてみてください。
class Product extends Model
{
protected $fillable=['name', 'description', 'price', 'image', 'category_id'];
public function category(){
return $this->hasOne('App\Category','id','category_id');
}
}
手順4: メニュー追加ページで、適当に情報を入力した後に、『< 戻る』ボタンを押してダッシュボードを表示させてみます。すると、以下画像のように表示されるはずです。
手順5: 画面上部の『Restaurant Menu』の部分ですが、ここをクリックするとダッシュボードに飛ぶように設定しようと思います。C:\xampp\htdocs\restaurant_menu\resources\views\layouts\app.blade.phpで、26行目辺りの以下コード部分(href属性のとこ。)を、『url('/product')』に変更します。
<a class="navbar-brand" href="{{ url('/') }}">
↓変更
<a class="navbar-brand" href="{{ url('/product') }}">
#19:編集/削除機能をつける(メニュー)
ダッシュボードの編集/削除機能を完成させていきます。
手順1: まずはダッシュボードのview画面を作成します。コードは以下の通りです。編集ボタンと削除ボタンの箇所に、カテゴリーでやった時と同様にコードを追記しています。その為、ここでのコードに関する説明は省きます。
@extends('layouts.app')
@section('content')
<div class="container-fluid my-2">
<div class="row m-2">
<div class="col">
<h3 class="font-weight-bold">ダッシュボード</h3>
</div>
<div class="col text-right">
<a type="button" href="{{ url('/product/create/') }}" class="btn btn-primary text-right" role="button"><i class="fas fa-plus"></i> 新規追加</a>
</div>
</div>
@if (session('message')) //追記
<div class="alert alert-success" role="alert">{{ session('message') }}</div> //追記
@endif //追記
<table class="table table-bordered">
<thead class="table-dark">
<tr>
<th scope="col">
#
</th>
<th scope="col">
画像
</th>
<th scope="col">
商品名
</th>
<th scope="col">
詳細
</th>
<th scope="col">
値段
</th>
<th scope="col">
カテゴリー
</th>
<th scope="col">
編集
</th>
<th scope="col">
削除
</th>
</tr>
</thead>
<tbody>
@if(count($products) > 0 )
@foreach($products as $key=>$product)
<tr>
<th scope="row">
{{ $key+1 }}
</th>
<td style="max-width: 200px;">
<img src="{{asset('images')}}/{{$product->image}}" class="img-fluid" />
</td>
<td>
{{$product->name}}
</td>
<td style="max-width: 300px;">
{{$product->description}}
</td>
<td>
{{$product->price}} 円
</td>
<td>
{{$product->category->name}}
</td>
<td>
<a href="{{route('product.edit',[$product->id])}}"> //追記
<button type="button" class="btn btn-outline-danger"><i class="far fa-edit"></i> 編集</button>
</a> //追記
</td>
<td>
<!-- <button type="button" class="btn btn-outline-primary"><i class="far fa-trash-alt"></i> 削除</button> --> //コメントアウト
//ここから追記
<button type="button" class="btn btn-outline-primary" data-toggle="modal" data-target="#exampleModal{{$product->id}}"><i class="far fa-trash-alt"></i> 削除</button>
<!-- Modal -->
<div class="modal fade" id="exampleModal{{$product->id}}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<form action="{{ route('product.destroy', [ 'product' => $product->id ]) }}" method="POST">
@csrf
@method('DELETE')
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">カテゴリー削除</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
本当に削除しますか?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">キャンセル</button>
<button type="submit" class="btn btn-primary">削除</button>
</div>
</div>
</form>
</div>
</div>
//ここまで追記
</td>
</tr>
@endforeach
@else
<tr>
<td colspan="8">追加された商品情報はありません。</td>
</tr>
@endif
</tbody>
</table>
<div class="d-flex">
<div class="mx-auto">
{{$products->links("pagination::bootstrap-4")}}
</div>
</div>
</div>
@endsection
手順2:次に、編集ボタンを押したときの編集画面のViewを作成していきます。productフォルダにedit.blade.phpという名前でファイルを作ります。こちらもカテゴリーの時と同様、追加フォーム画面とほぼ一緒です。コードは以下の通りです。
@extends('layouts.app')
@section('content')
<div class="container mt-3" style="max-width: 720px;">
<div class="text-right">
<a href="{{ url('/product/') }}">< 戻る</a>
</div>
<form action="{{ route('product.update', [$product->id]) }}" method="POST" enctype="multipart/form-data">
@csrf
@method('PUT')
<div class="form-group" style="margin-top: 30px; margin-bottom: 30px">
<label for="name" class="font-weight-bold">商品名</label>
<input type="text" class="form-control @error('name') is-invalid @enderror" id="name" name="name" value="{{ $product->name }}"/>
@error('name')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="textarea" class="font-weight-bold">詳細</label>
<textarea class="form-control @error('description') is-invalid @enderror" id="textarea" rows="5" name="description">{{ $product->description }}</textarea>
@error('description')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="price" class="font-weight-bold">値段</label>
<input type="text" class="form-control @error('price') is-invalid @enderror" id="price" name="price" value="{{ $product->price }}"/>
<small class="form-text text-muted">半角数字で入力してください。</small>
@error('price')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="category" class="font-weight-bold">カテゴリー</label>
<select class="form-control @error('category') is-invalid @enderror" id="category" name="category">
<option value="" disabled selected style="display: none;">カテゴリーを選択してください。</option>
@foreach(App\Category::all() as $category)
<option value="{{ $category->id }}" @if($category->id == $product->category_id) selected @endif>{{ $category->name }}</option>
@endforeach
</select>
@error('category')
<p class="text-danger">{{ $message }}</p>
@enderror
<div class="text-right mt-2">
<a type="button" href="{{ url('/category/create/') }}" class="btn btn-outline-secondary py-1" role="button">新規追加</a>
<a type="button" href="{{ url('/category/') }}" class="btn btn-outline-secondary py-1" role="button">編集</a>
</div>
</div>
<div class="form-group" style="margin-bottom: 30px">
<label for="image" class="font-weight-bold">画像アップロード</label>
<input type="file" class="form-control-file @error('image') is-invalid @enderror" id="image" name="image" />
@error('image')
<p class="text-danger">{{ $message }}</p>
@enderror
</div>
<button type="submit" class="btn btn-primary my-3">送信</button>
</form>
</div>
@endsection
上記のコードで特筆すべきことは、商品名等の各項目のinputタグ等にvalue値を書いていますが、textareaタグにはそもそもvalue属性はないので、タグの中にそのまま書いています。
手順3: コントローラーの設定を行います。ProductController.phpの edit()メソッド、update()メソッド、そしてdestroy()メソッドを以下のように設定します。カテゴリーでやった時とほぼ同様なので、コードの説明は省きます。
public function edit($id)
{
$product = Product::find($id);
return view('product.edit', ['product' => $product]);
}
public function update(Request $request, $id)
{
request()->validate(
['name' => 'required',
'description' => 'required',
'price' => 'required|integer',
'category' => 'required',
'image' => 'mimes:jpeg,png,jpg,gif,svg'],
['name.required' => '商品名を入力してください。',
'description.required' => '詳細を入力してください。',
'price.required' => '値段を入力してください。',
'category.required' => 'カテゴリーを入力してください。']
);
$product = Product::find($id);
$name = $product->image;
if( $request->hasFile('image')) {
$image = $request->file('image');
$name = time().'.'.$image->getClientOriginalExtension();
$destinationPath = public_path('/images');
$image->move($destinationPath,$name);
}
$product->update([
'name'=>request('name'),
'description'=>request('description'),
'price'=>request('price'),
'category_id'=>request('category'),
'image'=>$name
]);
return redirect()->route('product.index')->with('message','商品情報が更新されました。');
}
update() メソッドに関してですが、編集ページなので、画像はアップロードしないで、元の画像を使うときもあるかと思います。なので、バリデーションのrequiredは消します。
public function destroy($id)
{
$product = Product::find($id);
$product->delete();
return redirect('/product')->with('message', '商品情報が削除されました。');
}
手順4: ブラウザで確認してみます。まずは、『 http://127.0.0.1:8000/product/ 』にアクセスして、登録したメニューのどれでもいいのですが、編集ボタンを押します。すると、以下画像のように編集前のデータが入った状態でページが表示されます。
編集して送信ボタンを押すと、ダッシュボードにリダイレクトされてフラッシュメッセージが表示されます。
削除ボタンを押すと、以下画像のようにモーダル表示されて、データを削除することができます。
#20:お客さん用画面を作成する
最後にお客さん用の画面を作成します。
手順1: まずはルートの設定をします。これは、リソースルーティングでは設定されていないページになるので、手動で設定する必要があります。C:\xampp\htdocs\restaurant_menu\routes\web.phpで、以下のように追記します。
Route::get('/', 'ProductController@productTop');
手順2: 次に、コントローラーに設定します。ProductController.phpに、productTop()というメソッドを以下のように追記します。
public function productTop() {
$categories = Category::latest()->get();
return view('product.top', ['categories' => $categories]);
}
手順3: Categoryモデルを使うので、ProductController.phpの上部に『use App\Category;』の追記を忘れないようにします。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
use App\Category; //追記
以下省略
|
|
手順4: 次にViewの設定をします。C:\xampp\htdocs\restaurant_menu\resources\views\productに、top.blade.phpという名前でファイルを作ります。コードは以下の通りです。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="{{ asset('js/app.js') }}" defer></script>
<link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
@foreach($categories as $category)
<div class="container mt-5">
<h3 class="text-danger font-weight-bold">{{$category->name}}</h3>
<hr class="bg-danger" />
<div class="row row-cols-3 mb-5">
@foreach(App\Product::where('category_id',$category->id)->get() as $product)
<div class="card">
<img style="width: 100%; height: 15vw; object-fit: cover;" src="{{ asset('images') }}/{{ $product->image }}" class="card-img-top" />
<div class="card-body">
<h5 class="card-title font-weight-bold" style="display:inline;">{{ $product->name }}</h5><span class="card-title pr-1" style="float: right">{{ $product->price }} 円 + 税</span>
<hr />
<p class="card-text">{{ $product->description }}</p>
</div>
</div>
@endforeach
</div>
</div>
@endforeach
</body>
</html>
ここで特筆すべきは、『 @foreach(App\Product::where('category_id',$category->id)->get() as $product) 』の部分です。
まず、コントローラーからproductモデルの値が渡ってきている訳ではないので、ここでProductモデルにアクセスしています。また、カテゴリー別に分けてメニューを表示する為に、クエリビルダのwhere節を使っています。カテゴリーをチェックして、同じカテゴリーのもののみ、その後の項目を表示させる為です。
Laravelのクエリビルダについて、わかりやすくまとめてらっしゃるサイトがあったので、ご紹介します。↓↓こちらのサイトです。
手順5: メニュー追加ページで情報入力した後に、『 http://127.0.0.1:8000/ 』にアクセスすると、以下のような感じで表示されるはずです。
#21:認証機能をかける
現段階では、ダッシュボード等の管理画面に、ログインしていなくてもアクセスできてしまうので、認証機能をかけます。
Bootstrapをインストールした時に、一緒にLaravelのauthentication機能もインストールしているので、今回やるべきことは簡単です!
ProductController.phpで、categoryとproductのリソースルーティング部分に、以下のように『->middleware('auth')』を追記するだけです!
Route::resource('category','CategoryController')->middleware('auth');
Route::resource('product','ProductController')->middleware('auth');
これで『 http://127.0.0.1:8000/product/create 』や、『 http://127.0.0.1:8000/category 』にアクセスしようとすると、ログインページにリダイレクトされます!
以上で完成です。お疲れさまでした!