search
LoginSignup
2

More than 1 year has passed since last update.

posted at

updated at

【Laravel】検索機能の実装② ~複数条件&プルダウンからの選択形式~

作成物

この記事では、「キーワード検索」+「プルダウンより選択形式の検索」による複数条件での検索機能を持つ教材管理アプリを作成します。

スクリーンショット 2021-09-12 18.00.03.png

テーブル構成

■教材テーブル
・教材ID
・教材名
・媒体ID
・カテゴリーID

■媒体テーブル
・媒体ID
・媒体

■カテゴリーテーブル
・カテゴリーID
・カテゴリー

手順

  1. マイグレーションファイルの作成
  2. サンプルデータの挿入(シーダー)
  3. モデルの作成
  4. ビューファイルの作成
  5. コントローラーの作成
  6. ルーティングの設定

1. マイグレーションファイルの作成

テーブル作成

教材テーブル・媒体テーブル・コンテンツテーブルの3つを作成します。
※MySQLでデータベースを作成し、MySQLとの接続は済んでいるものとします。

$ php artisan make:migration create_TeachingMaterials_table
$ php artisan make:migration create_Media_table
$ php artisan make:migration create_Categories_table

※媒体:単数形「medium」 / 複数形「media」に注意。

マイグレーションファイルの編集

各マイグレーションファイルのスキーマを構築します。

外部キー参照先のマイグレーションファイルが実行されていない状態で、外部キーを設定したマイグレーションファイルが実行されると、参照するテーブルがないとエラーを返されます。
マイグレーションはファイルを作成した順(ファイル名になっている作成日時順)に実行されます。

よってここでは、MediaテーブルとCategoriesテーブルのマイグレーションファイルの作成日時が、Teaching Materialsテーブルのマイグレーションファイルよりも先でなければいけません。

2021_09_10_222418_create__media_table.php
class CreateMediaTable extends Migration
{
    public function up()
    {
        Schema::create('media', function (Blueprint $table) {
            $table->increments('id')->comment('媒体ID');
            $table->string('medium')->comment('媒体名(書籍/動画/etc...)');
        });    }
2021_09_10_222418_create__categories_table.php
class CreateCategoriesTable extends Migration
{
    public function up()
    {
        Schema::create('categories', function (Blueprint $table) {
            $table->increments('id')->comment('コンテンツID');
            $table->string('category')->comment('コンテンツ名(Laravel/Git/etc...)');
        });
    }
2021_09_10_222419_create__teaching_materials_table.php
class CreateTeachingMaterialsTable extends Migration
{
    public function up()
    {
        Schema::create('teaching_materials', function (Blueprint $table) {
            $table->increments('id')->comment('教材ID');
            $table->string('name')->comment('教材名');
            $table->unsignedInteger('medium_id');
            $table->unsignedInteger('category_id');

            //外部キー制約の設定
            $table->foreign('medium_id')->references('id')->on('media')->comment('媒体ID');
            $table->foreign('category_id')->references('id')->on('categories')->comment('コンテンツID');
        });
    }

マイグレーションの実行

$ php artisan migrate

スクリーンショット 2021-09-11 15.18.53.png

テーブルを作成することができました。

2. サンプルデータの挿入(シーダー)

シーダーファイルの作成

$ php artisan make:seed TeachingMaterialsTableSeeder
$ php artisan make:seed MediaTableSeeder
$ php artisan make:seed CategoryTableSeeder

/アプリ名/database/seedersにシーダーファイルが作成されます。

シーダーファイルの編集

3つのシーダーファイルに、サンプルデータを記述していきます。

TeachingMaterialsTableSeeder.php
class TeachingMaterialsTableSeeder extends Seeder
{
    public function run()
    {
        \DB::table('teaching_materials')->insert([
        'id' => '1',
        'name' => 'Git: もう怖くないGit!チーム開発で必要なGitを完全マスター',
        'medium_id' => '1',
        'category_id' => '3'
        ]);

        \DB::table('teaching_materials')->insert([
        'id' => '2',
        'name' => 'PHP+MySQL(MariaDB) Webサーバーサイドプログラミング入門',
        'media_id' => '1',
        'category' => '4'
        ]);

        \DB::table('teaching_materials')->insert([
        'id' => '3',
        'name' => '最短・最速で学ぶFirebase Hosting + Vue Todoアプリ実装',
        'media_id' => '1',
        'category_id' => '7'
        ]);

        // 略

}
MediaTableSeeder.php
class MediaTableSeeder extends Seeder
{
    public function run()
    {
        \DB::table('media')->insert([
        'id' => '1',
        'medium' => 'Udemy'
        ]);

        \DB::table('media')->insert([
        'id' => '2',
        'medium' => '書籍'
        ]);

        \DB::table('media')->insert([
        'id' => '3',
        'medium' => 'Techpit'
        ]);
    }
}
CategoriesTableSeeder.php
class CategoriesTableSeeder extends Seeder
{
    public function run()
    {
        \DB::table('categories')->insert([
        'id' => '1',
        'category' => 'Laravel'
        ]);

        \DB::table('categories')->insert([
        'id' => '2',
        'category' => 'Web技術'
        ]);

        \DB::table('categories')->insert([
        'id' => '3',
        'category' => 'Git'
        ]);

        // 略
    }
}

DatabaseSeeder.phpの編集

上記で作成したシーダーファイルを大元のシーダーファイル(DatabaseSeeder.php)から呼び出します。

TeachingMaterialsTableにおいてMediaTablesとCategoriesTableを参照しているので、Seederを呼び出す順番は、参照元を先にしなければいけません。

DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
    public function run()
    {
        $this->call(MediaTableSeeder::class);
        $this->call(CategoriesTableSeeder::class);
        $this->call(TeachingMaterialsTableSeeder::class);
    }
}

シーダーの実行

$ php artisan db:seed

スクリーンショット 2021-09-11 13.47.27.png

サンプルデータを作成することができました。

3. モデルの作成

媒体:教材 = 1:多
コンテンツ:教材 = 1:多
の関係になります。

各モデルにリレーションを書いていきます。

TeachingMaterial.php
class TeachingMaterial extends Model
{
    use HasFactory;

    public function medium()
    {
        return $this->belongsTo(Medium::class);
    }

    public function category()
    {
        return $this->belongsTo(Category::class);
    }
}
Medium.php
class Medium extends Model
{
    use HasFactory;

    public function teaching_material()
    {
        return $this->hasMany(TeachingMaterial::class);
    }
}
Category.php
class Category extends Model
{
    use HasFactory;

    public function teaching_material()
    {
        return $this->hasMany(TeachingMaterial::class);
    }
}

4. ビューファイルの作成

今回はbladeの機能は使わず、簡易的に1つのビューファイルに記述します。

検索機能は3つです。
・キーワードを入力し、教材名から部分一致検索
・媒体をプルダウンより選択して検索
・カテゴリーをプルダウンより選択して検索

プルダウンの選択肢には、データベースに保存されている値を呼び出しています。

index.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Teaaching Materials</title>
    <link rel="stylesheet" href="{{ url('css/style.css') }}">
</head>
<body>

    //* 検索機能ここから *//
    <div class="search">
        <form action="{{ route('index') }}" method="GET">
            @csrf

            <div class="form-group">
                <div>
                    <label for="">キーワード
                    <div>
                        <input type="text" name="keyword" value="{{ $keyword }}">
                    </div>
                    </label>
                </div>

                <div>
                    <label for="">媒体
                    <div>
                        <select name="medium" data-toggle="select">
                            <option value="">全て</option>
                            @foreach ($media_list as $media_item)
                                <option value="{{ $media_item->getMedium() }}" @if($medium=='{{ $media_item->getMedium() }}') selected @endif>{{ $media_item->getMedium() }}</option>
                            @endforeach
                        </select>
                    </div>
                    </label>
                </div>

                <div>
                    <label for="">カテゴリー
                    <div>
                        <select name="category" data-toggle="select">
                            <option value="">全て</option>
                            @foreach ($categories_list as $categories_item)
                                <option value="{{ $categories_item->getCategory() }}" @if($category=='{{ $categories_item->getCategory() }}') selected @endif>{{ $categories_item->getCategory() }}</option>
                            @endforeach
                        </select>
                    </div>
                    </label>
                </div>

                <div>
                    <input type="submit" class="btn" value="検索">
                </div>
            </div>
        </form>
    </div>
    //* 検索機能ここまで *//

    <table>
        <tr>
            <th>教材名</th>
            <th>媒体</th>
            <th>コンテンツ</th>
        </tr>

        @foreach ($items as $item)
        <tr>
            <td>{{ $item->name }}</td>
            <td>{{ $item->medium }}</td>
            <td>{{ $item->category }}</td>
            //mediaテーブルとcategoriesテーブルを結合しているので、この記述でアクセスできる
        </tr>
        @endforeach
    </table>

</body>
</html>


5. コントローラーの作成

リレーション先のテーブルのカラムも検索対象になっているので、joinでテーブル結合します。

TeachingMaterialsController.php
class TeachingMaterialController extends Controller
{
    public function index(Request $request)
    {
        //検索フォームに入力された値を取得
        $medium = $request->input('medium');
        $category = $request->input('category');
        $keyword = $request->input('keyword');

        $query = TeachingMaterial::query();
        //テーブル結合
        $query->join('media', function ($query) use ($request) {
            $query->on('teaching_materials.medium_id', '=', 'media.id');
            })->join('categories', function ($query) use ($request) {
            $query->on('teaching_materials.category_id', '=', 'categories.id');
            });

        if(!empty($medium)) {
            $query->where('medium', 'LIKE', $medium);
        }

        if(!empty($category)) {
            $query->where('category', 'LIKE', $category);
        }

        if(!empty($keyword)) {
            $query->where('name', 'LIKE', "%{$keyword}%");
        }

        $items = $query->get();

        $media_list = Medium::all();
        $categories_list = Category::all();

        return view('index', compact('items', 'keyword', 'medium', 'category', 'media_list', 'categories_list'));
    }
}

6. ルーティングの設定

web.php
Route::get('/', [TeachingMaterialController::class, 'index'])->name('index');



以上です^^

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
What you can do with signing up
2