はじめに
はじめまして! webエンジニア転職を目指して三ヶ月が経ちました。 スクールでは共同開発が始まり、カート機能を実装しております。 そこで、終盤において商品追加機能を担当し、 復習&アウトプットも兼ねてご覧の皆さんに共有したいと思い、記事投稿に至ります! 至らぬ点が多々あるとは思いますが、何かの実装の参考になれば幸いです。😆目次
1, 完成品 2, 環境 3, 使用するソースコード 3-① Route 3-② Controller 3-③ View 3-④ Migration 3-⑤ Model 4, 各実装の説明 ①〜⑤ 参考にしたサイト まとめ1, 完成品
① 完成画面 [![Image from Gyazo](https://i.gyazo.com/8684883ea26c0c50768a4702856f27bf.gif)](https://gyazo.com/8684883ea26c0c50768a4702856f27bf) ② DB確認(Tinker) ![スクリーンショット 2021-04-28 17.13.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1178769/1dea4916-613d-150a-1280-f3f8c2999bf0.png)2, 環境
macOS Big Sur 11.2.2
Apache 2.4.46
MySQL 5.7
PHP 7.2.34
Laravel 5.8
実は今回、共同開発において
VirtualBoxを用いたVagrantによる仮想開発環境で統一したかったのですが、
投稿主のmacが最近出たばかりのM1チップが搭載されております。
そして、悲しいことに当時(2月中旬)はVirtualBoxなどバージョンが追いついておらず、
仕方なくAWS(Could9)での環境設定を余儀なくされました。。
3, 使用するソースコード
3 - ① Route
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
//商品追加
Route::get('newAdd', 'NewAddController@index')->name('newAdd.index');
Route::post('newProduct', 'NewAddController@store')->name('newProduct.post');
3 - ② Controller
<?php
namespace App\Http\Controllers;
use App\User;
use App\Models\Product;
use App\Models\Category;
use App\Models\ProductStatus;
use App\Models\Sale;
use Illuminate\Http\Request;
use App\Http\Requests\ProductRequests;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
class NewAddController extends Controller
{
public function index(Request $request)
{
return view('products.newAdd', [
'user' => Auth::user(),
'categories' => Category::all(),
'products_status' => ProductStatus::all(),
'sales' => Sale::all(),
]);
}
public function store(ProductRequests $request)
{
$newProduct = new Product;
$newProduct->product_name = $request->product_name;
$newProduct->description = $request->description;
$newProduct->price = $request->price;
$newProduct->category_id = $request->category_id;
$newProduct->product_status_id = $request->product_status_id;
$newProduct->sale_status_id = $request->sale_status_id;
$newProduct->user_id = Auth::user()->id;
$newProduct->save();
return view('welcome');
}
}
3 - ③ View
@extends('layouts.app')
@section('content')
<main>
<div class="page-header mt-5 text-center">
<h2>商品追加</h2>
</div>
<div class="row mt-5 mb-5">
<div class="col-sm-5 mx-auto">
<form method="POST" action="{{ route('newProduct.post') }}">
@csrf
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品名</label>
<div class="product-info width-control">
<input type="text" name="product_name" class="content-half-width form-control-sm d-inline">
<!-- @if ($errors->has('product_name'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('product_name') }}</span>
</div>
</div>
@endif -->
</div>
</div>
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品説明</label>
<div class="product-info width-control">
<textarea name="description" class="content-width form-control-sm"></textarea>
<!-- @if ($errors->has('description'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('description') }}</span>
</div>
</div>
@endif -->
</div>
</div>
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">価格</label>
<div class="product-info width-control">
<input type="text" name="price" class="content-half-width form-control-sm d-inline">
<!-- @if ($errors->has('price'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('price') }}</span>
</div>
</div>
@endif -->
</div>
</div>
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品カテゴリー</label>
<div class="product-info width-control">
<select class="content-half-width form-control-sm d-inline" id="changeSelect" name="category_id" onchange="entryChange2();">
<option value="">未選択</option>
@foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->category_name }}</option>
@endforeach
<!-- @if ($errors->has('category_id'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('category_id') }}</span>
</div>
</div>
@endif -->
</select>
</dov>
</div>
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品状態</label>
<div class="product-info width-control">
<select class="content-half-width form-control-sm d-inline" id="changeSelect" name="product_status_id" onchange="entryChange2();">
<option value="">未選択</option>
@foreach ($products_status as $product_status)
<option value="{{ $product_status->id }}">{{ $product_status->product_status_name }}</option>
@endforeach
<!-- @if ($errors->has('product_status_id'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('product_status_id') }}</span>
</div>
</div>
@endif -->
</select>
</dov>
</div>
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">販売状態</label>
<div class="product-info width-control">
<select class="content-half-width form-control-sm d-inline" id="changeSelect" name="sale_status_id" onchange="entryChange2();">
<option value="">未選択</option>
@foreach ($sales as $sale)
<option value="{{ $sale->id }}">{{ $sale->sale_status_name }}</option>
@endforeach
<!-- @if ($errors->has('sale_status_id'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('sale_status_id') }}</span>
</div>
</div>
@endif -->
</select>
</dov>
</div>
<div class="text-center mt-5">
<button type="submit" class="btn btn-primary w-50">追加</button>
<p class="mt-5">
<a href="#" class="text-primary d-inline">商品一覧</a>
</p>
</div>
</form>
</div>
</div>
</main>
@endsection
3 - ④ Migration
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateMProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('m_products', function (Blueprint $table) {
$table->increments('id');
$table->string('product_name', 64);
$table->unsignedInteger('category_id');
$table->integer('price');
$table->string('description', 256);
$table->unsignedInteger('sale_status_id');
$table->unsignedInteger('product_status_id');
$table->timestamp('regist_date');
$table->unsignedInteger('user_id');
$table->char('delete_flag', 1)->default(0);
$table->foreign('user_id')->references('id')->on('m_users')->onDelete('cascade');
$table->foreign('category_id')->references('id')->on('m_categories')->onDelete('cascade');
$table->foreign('sale_status_id')->references('id')->on('m_sale_statuses')->onDelete('cascade');
$table->foreign('product_status_id')->references('id')->on('m_product_statuses')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('m_products');
}
}
3 - ⑤ Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $table = 'm_products';
public $timestamps = false;
//商品追加のホワイトリスト
protected $fillable = [
'product_name',
'description',
'price',
];
// Userモデルを親に持つことを明記
public function user()
{
return $this->belongsTo('App\User');
}
// Categoryモデルを親に持つことを明記
public function category()
{
return $this->belongsTo('App\Models\Category');
}
// Saleモデルを親に持つことを明記
public function sale()
{
return $this->belongsTo('App\Models\Sale');
}
// ProductStatusモデルを親に持つことを明記
public function productstatus()
{
return $this->belongsTo('App\Models\ProductStatus');
}
}
他のモデルについては同様の処理(リレーション)のため、今回は割愛させて頂きます。🙇♂️
当時の私は、下記サイトを参考に実装していました。
参考 : リレーションについて
4, 各実装の説明 ①〜⑤
4 - ① Route
//商品追加
Route::get('newAdd', 'NewAddController@index')->name('newAdd.index');
Route::post('newProduct', 'NewAddController@store')->name('newProduct.post');
こちらでは
Route::getでViewを呼び出しています。(indexメソッド)
Route::postでDBへの登録をしています。(storeメソッド)
それぞれメソッド、命名など異なりますので、読み間違え等に気をつけましょう。
4 - ② Controller
public function index(Request $request)
{
return view('products.newAdd', [
'user' => Auth::user(),
'categories' => Category::all(),
'products_status' => ProductStatus::all(),
'sales' => Sale::all(),
]);
}
完成品を確認して頂くと、[カテゴリー] [商品状態] [販売状態] が選択になっています。
なので、indexメソッドでは各Modelを通して
登録する [ユーザー情報] や選択肢で表示させるための [3つの情報] を取得し、Viewに渡します。
また、今回はそこまで複雑な処理ではないため上のように記述しましたが、
以下のようにcompact関数を使っても問題なく渡すことができます!
public function index(Request $request)
{
$user = Auth::user();
$categories = Category::all();
$products_status = ProductStatus::all();
$sales = Sale::all();
return view('products.newAdd', compact('categories', 'user', 'sales', 'products_status'));
}
次にstoreメソッドですが、
public function store(ProductRequests $request)
{
$newProduct = new Product;
$newProduct->product_name = $request->product_name;
$newProduct->description = $request->description;
$newProduct->price = $request->price;
$newProduct->category_id = $request->category_id;
$newProduct->product_status_id = $request->product_status_id;
$newProduct->sale_status_id = $request->sale_status_id;
$newProduct->user_id = Auth::user()->id;
$newProduct->save();
return view('welcome');
}
public function store(ProductRequests $request)
こちらの ProductRequests はフォームリクエストによるバリデーションをかけています。
今回の実装ではバリデーションについての解説は入れてませんので、
Request に書き換えてもらってOKです!
$newProduct = new Product;
ここの一文は「これからProductモデルに新しい値を追加していくよー」っていう感じです。笑
あとは $newProduct という変数に画面で入力(選択)した値が格納されていくようになっています。
よくエラーが起きてしまう原因...😂↓↓↓
<?php
namespace App\Http\Controllers;
use App\User;
use App\Models\Product;
use App\Models\Category;
use App\Models\ProductStatus;
use App\Models\Sale;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
Controller上部ではモデルやリクエストインスタンスを持ってきているので、
ここが一つでも抜けていると does not exit エラーが起きてしまいます...。
気が抜けないですね。。
5 - ③ View
Viewについては
「入力値」と「選択値」の2種類があるので、分けて説明していきます。
と、その前に...
<form method="POST" action="{{ route('newProduct.post') }}">
フォームタグでPOST送信の設定を忘れずに。
action="{{ route( ' 命名したルート名 ' ) }}" でルーティングしてあげます!
↓↓ 入力値 ↓↓
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品名</label>
<div class="product-info width-control">
<input type="text" name="product_name" class="content-half-width form-control-sm d-inline">
<!-- @if ($errors->has('product_name'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('product_name') }}</span>
</div>
</div>
@endif -->
</div>
</div>
ここはControllerで指定したnameと一致させるため
フォームタグで囲ってあるので、あとは上記のようにHTMLで入力欄を作ればOKです!
if文以降はエラー時のバリデーションを設定してます。
不要でしたら削除しても構いません。
↓↓ 選択値 ↓↓
<div class="form-group-sm clearfix">
<label for="formGroupExampleInput2" class="mt-3 mb-0">商品カテゴリー</label>
<div class="product-info width-control">
<select class="content-half-width form-control-sm d-inline" id="changeSelect" name="category_id" onchange="entryChange2();">
<option value="">未選択</option>
@foreach ($categories as $category)
<option value="{{ $category->id }}">{{ $category->category_name }}</option>
@endforeach
<!-- @if ($errors->has('category_id'))
<div class="row justify-content-center">
<div class="cal-xs-4">
<span style="color:red">{{ $errors->first('category_id') }}</span>
</div>
</div>
@endif -->
</select>
</dov>
</div>
5 - ④ Migration
使用したソースコード参照5 - ⑤ Model
//商品追加のホワイトリスト
protected $fillable = [
'product_name',
'description',
'price',
];
$fillableで書き換えれるように設定しましょう。
ここでは使いませんが、$guardedはその逆です。
// Userモデルを親に持つことを明記
public function user()
{
return $this->belongsTo('App\User');
}
// Categoryモデルを親に持つことを明記
public function category()
{
return $this->belongsTo('App\Models\Category');
}
// Saleモデルを親に持つことを明記
public function sale()
{
return $this->belongsTo('App\Models\Sale');
}
// ProductStatusモデルを親に持つことを明記
public function productstatus()
{
return $this->belongsTo('App\Models\ProductStatus');
}
各リレーションを行っています。
詳しくは下記リンクを参考に。
個人的にすごく分かりやすかったのでオススメします!
参考 : リレーションについて