Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

More than 3 years have passed since last update.

商品検索機能の説明

0
Posted at

laravelを使用して商品検索機能を作成しました。
自分の理解のために記事にさせて頂きます。

要件定義

クエリビルダを用いて、以下の4通りのパターンの検索機能の実装方法を解説。

1.何も入力せずに全件表示
2.商品名を入力して検索
3.カテゴリを選択して表示
4.商品名を入力かつカテゴリを選択して絞り込む(and検索)

一つ一つ説明させて頂きます。

1.何も入力せずに全件表示
スクリーンショット 2021-12-14 21.44.23.png

2.商品名を入力して検索
スクリーンショット 2021-12-14 21.48.33.png

3.カテゴリを選択して表示
スクリーンショット 2021-12-14 21.51.04.png

4.商品名を入力かつカテゴリを選択して絞り込む(and検索)

スクリーンショット 2021-12-14 21.55.37.png

#マイグレーションファイルとシーダーを作成

今回必要なデータベースは
・m_products_tableテーブルのid、product_name、category_id、priceと、
・m_categoriesテーブルのid、category_nameです。

xxxx_xx_xx_xxxxxx_create_m_products_table.php
<?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->bigIncrements('id');
            $table->string('product_name', 64)->comment('商品名');
            $table->bigInteger('category_id')->unsigned()->comment('カテゴリーID');
            $table->integer('price')->comment('販売単価');

        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('m_products');
    }
}
ProductsTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class ProductsTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('m_products')->insert([
            [
                'id'                => 1,
                'product_name'      => 'マンゴー',
                'category_id'       => 1,
                'price'             => 800,
                'description'       => '沖縄の美味しいマンゴーです。',
                'sale_status_id'    => 1,
                'product_status_id' => 1,
                'regist_date'       => new DateTime,
                'user_id'           => 1
            ],
            [
                'id'                => 2,
                'product_name'      => 'ワンピース',
                'category_id'       => 2,
                'price'             => 1000,
                'description'       => '限定のワンピースです。',
                'sale_status_id'    => 2,
                'product_status_id' => 2,
                'regist_date'       => new DateTime,
                'user_id'           => 2
            ],
            [
                'id'                => 3,
                'product_name'      => 'Tシャツ',
                'category_id'       => 2,
                'price'             => 800,
                'description'       => '涼しげなTシャツです',
                'sale_status_id'    => 2,
                'product_status_id' => 2,
                'regist_date'       => new DateTime,
                'user_id'           => 3
            ],
            [
                'id'                => 4,
                'product_name'      => '南国バナナ',
                'category_id'       => 1,
                'price'             => 500,
                'description'       => '南国の美味しいバナナです。',
                'sale_status_id'    => 2,
                'product_status_id' => 2,
                'regist_date'       => new DateTime,
                'user_id'           => 2
            ],
            [
                'id'                => 5,
                'product_name'      => 'パジャマ',
                'category_id'       => 2,
                'price'             => 1200,
                'description'       => '寝心地の良いパジャマです',
                'sale_status_id'    => 2,
                'product_status_id' => 2,
                'regist_date'       => new DateTime,
                'user_id'           => 1
            ],
            [
                'id'                => 6,
                'product_name'      => '青森のりんご',
                'category_id'       => 1,
                'price'             => 1000,
                'description'       => '甘い青森のりんごです',
                'sale_status_id'    => 2,
                'product_status_id' => 3,
                'regist_date'       => new DateTime,
                'user_id'           => 2
            ],
            [
                'id'                => 7,
                'product_name'      => 'ジーンズ',
                'category_id'       => 2,
                'price'             => 1500,
                'description'       => '何にでも合わせやすいジーンズです',
                'sale_status_id'    => 1,
                'product_status_id' => 1,
                'regist_date'       => new DateTime,
                'user_id'           => 3
            ],
            [
                'id'                => 8,
                'product_name'      => 'クッション',
                'category_id'       => 3,
                'price'             => 700,
                'description'       => 'フカフカのクッションです',
                'sale_status_id'    => 1,
                'product_status_id' => 1,
                'regist_date'       => new DateTime,
                'user_id'           => 1
            ],
            [
                'id'                => 9,
                'product_name'      => 'カーペット',
                'category_id'       => 3,
                'price'             => 3000,
                'description'       => '季節問わず使用可能のカーペットです',
                'sale_status_id'    => 1,
                'product_status_id' => 1,
                'regist_date'       => new DateTime,
                'user_id'           => 2
            ]
    }
}

カテゴリーのテーブルとシーダーを作成

xxxx_xx_xx_xxxxxx_create_m_categories_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateMCategoriesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('m_categories', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('category_name')->comment('カテゴリー名');
            $table->timestamp('created_at')->nullable();
            $table->timestamp('updated_at')->nullable();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('m_categories');
    }
}

CategoriesTableSeeder.php

<?php

use Illuminate\Database\Seeder;

class CategoriesTableSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        DB::table('m_categories')->insert([
            [
                'id' => 1,
                'category_name' => '食品'
            ],
            [
                'id' => 2,
                'category_name' => '衣類'
            ],
            [
                'id' => 3,
                'category_name' => '雑貨'
            ]
        ]);
    }

}


#モデルの作成

次にモデルを作成します。「カテゴリは複数の商品を持つ(hasMany)」、「商品はカテゴリに属する(belongsTo)」というリレーション関係を作りたいので、以下のように記述します。

Product.php
<?php

namespace App;

use App\Category;
use App\OrderDetail;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    protected $table = 'm_products';

    public function category()
    {
        return $this->belongsTo(Category::class);
    }

    public function orderDetail()
    {
        return $this->hasMany(OrderDetail::class);
    }
}

Category.php
<?php

namespace App;

use App\Product;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $table = 'm_categories';

    public static function getLists()
    {
        $categories = Category::pluck('category_name', 'id');
        return $categories;
    }

    public function products()
    {
        return $this->hasMany(Product::class);
    }
}

protected $table = 'm_products'; は、プロジェクトの関係上
マイグレーションファイルの単数形で名前をつけていないため、連携されていないので、m_productsを連携するようにしています。
protected $table = 'm_categories';も同様で、m_categoriesを連携させる
ために記入をしています。

通常はモデルのファイル名を命名規則通りに付けることで、テーブルと勝手に接続してくれます。

Category.phpの

$categories = Category::pluck('category_name', 'id');
return $categories;

の部分では、m_categoriesのテーブルの中からcategory_name,とidを
取り出して、$categoriesに入れてreturnで返しています。

#商品検索画面へのルーティングを作成

web.php

Route::group(['middleware' => 'auth'], function () {
    Route::prefix('products')->group(function (){
        Route::get('/', 'ProductsController@search')->name('search.products');
        Route::get('/{id}', 'ProductsController@showDetail')->name('search.detail');
    });

Route::group(['middleware' => 'auth']
は、ユーザー登録をしているユーザーのみ開けるよう設定しまとめています。

Route::prefix('products')->group

prefixとは、web.phpでRouteを設定するとき、URLの接頭(プレフィックス)をグループ化して設定できる便利なメソッドです。

prefixを使用しない場合

web.php
Route::get('products/', 'ProductsController@search')->name('search.products');
Route::get('products/{id}', 'ProductsController@showDetail')->name('search.detail');

と、URLの先頭部をわざわざ二つ書かなくてはならない形になります。
今回は2つのみの記載のため、prefixを使用せずとも問題はありませんが、
もし同じURL先頭部でのRouteが多くなってしまう場合はprefixを使用した方が楽かと思います。

#コントローラーの作成

Products.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use App\Product;
use App\Category;

class ProductsController extends Controller
{
    public function search(Request $request)
    {
        $query      = Product::query(); //Productのデーターベースを参照 (queryは参照)
        $categories = Category::getLists();
        $searchWord = $request->input('product_name');
        $categoryId = $request->input('category_id');

        if (isset($searchWord)) {
            $query->where('product_name', 'like', '%' . $searchWord . '%'); // %を二つ書く事で曖昧検索
        }
        if (isset($categoryId)) {
            $query->where('category_id', $categoryId);
        }
        $products = $query->orderBy('category_id', 'asc')->paginate(15); //15件まで出す

        return view('products.search', [
            'products'   => $products,
            'categories' => $categories,
            'searchWord' => $searchWord,
            'categoryId' => $categoryId,
        ]);
    }
    public function showDetail($id)
    {
        $product = Product::findOrFail($id);
        return view('products.detail_a_product',compact('product'));
    }
}



まず、


public function search(Request $request)
    {
        $query      = Product::query(); 
        $categories = Category::getLists();
        $searchWord = $request->input('product_name');
        $categoryId = $request->input('category_id');
   }

実際に検索する際の処理を書いていきます。
search(検索)という関数を作り、中に処理を書きます。

$query      = Product::query(); 
$categories = Category::getLists();

は共にProductとCategoryのデーターベースを参照しています。

Productのテーブルからデーターを読み込んで下さいね。
Categoryのテーブルからデーターを読み込んで下さいね。

これをそれぞれ変数に入れておきます。

$searchWord = $request->input('product_name');
$categoryId = $request->input('category_id');

リクエストされた(ユーザーが入力した)product_nameのデーターを取得します。
同じく、
リクエストされた(ユーザーが入力した)category_idのデーターを取得します。

これもそれぞれの変数に入れます。

if (isset($searchWord)) {
    $query->where('product_name', 'like', '%' . $searchWord . '%'); 
}
if (isset($categoryId)) {
    $query->where('category_id', $categoryId);
}

もし、変数の$searchWordがセットされていたら
Productのテーブルからデーターを読み込んで下さいね。と指定しておいた$queryの
中からwhereで特定の条件を満たすデータを取り出します。

ここでは$searchWord(リクエストされた(ユーザーが入力した)product_nameのデーター)を
product_nameで探してくださいという処理をしています。
また、%を2ついれる事により曖昧検索を可能にしています。
(例えばバナナを「バナ」「バ」「ナ」と一部を入力していれば検索に引っかかるようにしている)

下の$categoryIdも同じ要領です。カテゴリーIDには曖昧検索は必要ありませんね。

$products = $query->orderBy('category_id', 'asc')->paginate(15); 

orderByを使用してカテゴリーのIDを数字の順番に並べ、15件まで表示される
ようになっている。15件以上は次のページになる

return view('products.search', [
            'products'   => $products,
            'categories' => $categories,
            'searchWord' => $searchWord,
            'categoryId' => $categoryId,
        ]);

returnでviewファイルのproductsのsearchに

'products' => $products,
'categories' => $categories,
'searchWord' => $searchWord,
'categoryId' => $categoryId,

を渡します。returnしておく事で、viewで変数を使用できるようになり、
表示できるようになります。


public function showDetail($id)
    {
        $product = Product::findOrFail($id);
        return view('products.detail_a_product',compact('product'));
    }

Product::findOrFail($id) でProductのテーブルからidを

#Viewファイル説明

search.blade.php

@extends('layouts.app')
@section('content')
<div>
    <div class="row mt-4">
        <div class="col-lg-12 text-center">
            <h1>商品検索画面</h1>
        </div>
    </div>
    <form method="get" action="{{ route('search.products') }}">
        @csrf
        <div class="row">
            <div class="input-group mt-4 col-md-7 offset-2">
                <h2 class="mr-4">商品名</h2>
                <input type="text" class="form-control" name="product_name">
                <span class="input-group-btn">
                    <button type="submit" class="btn btn-primary ml-4">検索</button>
                </span>
            </div>
        </div>
        <div class="row">
            <div class="input-group mt-4 col-md-7 offset-2">
                <h2>商品カテゴリ</h2>
                <select name="category_id" class="ml-2" style="width:50%; text-align-last:center;">
                    <option value="">未選択</option>
                    @foreach($categories as $categoryid => $categoryName)
                    <option value="{{ $categoryid }}">
                        {{ $categoryName }}
                    </option>
                    @endforeach
                </select>
            </div>
        </div>
    </form>
    @if (!empty($products))
        <p>全{{ $products->count() }}件</p>
        <table border="1" class="table" style="border-collapse: collapse">
        <thead class="bg-warning">
        <tr>
            <th>商品名</th>
            <th>商品カテゴリ</th>
            <th>価格</th>
            <th></th>
        </tr>
        </thead>
        <tbody>
            @foreach($products as $product)
            <tr>
                <td>{{ $product->product_name }}</td>
                <td>{{ $product->category->category_name }}</td>
                <td>{{ $product->price }}円</td>
                <td name="name"><a href="{{ route('search.detail',$product->id) }}" class="btn btn-primary">商品詳細</a></td>
            </tr>
            @endforeach
        </tbody>
        </table>
    @endif
</div>
@endsection


###GETメソッドとルーティング

viewファイルを中心に解説していきます。

検索フォームのformタグには

とありますが、これは「検索ボタンがクリックされたとき、GETメソッドを実行し、コントローラーのsearch.productsを呼び出す」ことを意味しています。

#商品一覧テーブル

検索が実行されたときにテーブルを表示する
テーブルを@if (!empty($products))で囲います。

表示件数を表示
検索結果を表示したい場合、count()関数を用います。今回は該当する商品($products)の数を表示したいので、以下のように記述します。

全{{ $products->count() }}件

テーブルをforeachで作成
表示したい情報は、「商品名」「商品カテゴリ」「価格」「商品詳細(遷移画面は作りません)」の4つです。
そのためforeachで以下のように記述しています。

@foreach($products as $product)
            <tr>
                <td>{{ $product->product_name }}</td>
                <td>{{ $product->category->category_name }}</td>
                <td>{{ $product->price }}円</td>
                <td name="name"><a href="{{ route('search.detail',$product->id) }}" class="btn btn-primary">商品詳細</a></td>
            </tr>
@endforeach

0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?