序章

MySQLバージョン5.7.8では、JSONドキュメントのデータにアクセスできるJSONデータ型が導入されています。

SQLデータベースは、設計が厳格になる傾向があります。 その性質上、構造化照会言語はデータ型とサイズの制約を適用します。

それに比べて、NoSQLデータベースは設計の柔軟性を促進します。 これらのスキーマのないデータベースでは、構造上の制限はなく、データのみが保存されます。

MySQLのJSONデータ型は、これら両方のシステムの長所を提供します。 これにより、データベースの一部を構造化し、他の部分を柔軟に保つことができます。

この記事の前半では、JSONフィールドを使用してデータベースを設計します。 MySQLで使用可能な組み込み関数を使用して、行を作成、読み取り、更新、および削除する手順を説明します。

この記事の後半では、EloquentORMとLaravelを利用してデータベースと通信します。 製品の表示、新しい製品の追加、既存の製品の変更、および製品の削除をサポートする管理パネルを作成します。

前提条件

この記事をフォローしたい場合は、次のものが必要になります。

注: Laravelは、MySQL、PHP、およびComposerで環境を構成するDockerと連携するSailと呼ばれるツールを提供するようになりました。

ローカル環境のセットアップに問題がある場合、これは代替オプションになる可能性があります。

このチュートリアルは、MySQL v8.0.23、PHP v7.3.24、Composer v2.0.9、およびLaravelv8.26.1で検証されました。

ステップ1—スキーマの定義

このチュートリアルでは、さまざまな電子機器を販売するオンラインストアの在庫を定義するスキーマから構築します。

従来、エンティティ-属性-値モデル(EAV)パターンは、顧客が製品の機能を比較できるようにするために使用されていました。

ただし、JSONデータ型を使用すると、このユースケースへのアプローチが異なる場合があります。

データベースには名前が付けられます e_store と名前の3つのテーブルがあります brands, categories、 と products それぞれ。

を作成します e_store データベース:

CREATE DATABASE IF NOT EXISTS `e_store`
DEFAULT CHARACTER SET utf8
DEFAULT COLLATE utf8_general_ci;

SET default_storage_engine = INNODB;

The brandscategories テーブルにはそれぞれがあります idname 分野。

を作成します brands テーブル:

CREATE TABLE `e_store`.`brands`(
	`id` INT UNSIGNED NOT NULL auto_increment ,
	`name` VARCHAR(250) NOT NULL ,
	PRIMARY KEY(`id`)
);

を作成します categories テーブル:

CREATE TABLE `e_store`.`categories`(
	`id` INT UNSIGNED NOT NULL auto_increment ,
	`name` VARCHAR(250) NOT NULL ,
	PRIMARY KEY(`id`)
);

次に、サンプルを追加します brands:

INSERT INTO `e_store`.`brands`(`name`)
VALUES
	('Samsung');

INSERT INTO `e_store`.`brands`(`name`)
VALUES
	('Nokia');

INSERT INTO `e_store`.`brands`(`name`)
VALUES
	('Canon');

次に、いくつか追加します categories:

INSERT INTO `e_store`.`categories`(`name`)
VALUES
	('Television');

INSERT INTO `e_store`.`categories`(`name`)
VALUES
	('Mobile Phone');

INSERT INTO `e_store`.`categories`(`name`)
VALUES
	('Camera');

次に、 products テーブルと id, name, brand_id, category_id、 と attributes 田畑:

CREATE TABLE `e_store`.`products`(
	`id` INT UNSIGNED NOT NULL AUTO_INCREMENT ,
	`name` VARCHAR(250) NOT NULL ,
	`brand_id` INT UNSIGNED NOT NULL ,
	`category_id` INT UNSIGNED NOT NULL ,
	`attributes` JSON NOT NULL ,
	PRIMARY KEY(`id`) ,
	INDEX `CATEGORY_ID`(`category_id` ASC) ,
	INDEX `BRAND_ID`(`brand_id` ASC) ,
	CONSTRAINT `brand_id` FOREIGN KEY(`brand_id`) REFERENCES `e_store`.`brands`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE ,
	CONSTRAINT `category_id` FOREIGN KEY(`category_id`) REFERENCES `e_store`.`categories`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE
);

このテーブル定義は、外部キー制約を指定します brand_idcategory_id フィールド、それらが参照することを指定する brandscategories それぞれテーブル。 このテーブル定義は、参照される行の削除を許可しないように指定し、更新された場合、変更は参照にも反映される必要があります。

The attributes フィールドの列タイプは、MySQLで現在使用可能なネイティブデータ型であるJSONであると宣言されています。 これにより、MySQLでさまざまなJSON関連の構成を使用できます。 attributes 分野。

作成したデータベースの実体関連図は次のとおりです。

このデータベース設計は、効率と精度の点で最高ではありません。 考慮されていない一般的な実際のユースケースがいくつかあります。 たとえば、価格の列はありません products テーブルであり、複数のカテゴリに属する製品のサポートはありません。 ただし、このチュートリアルの目的は、データベースの設計を教えることではなく、MySQLのJSON機能を使用して単一のテーブルでさまざまな性質のオブジェクトをモデル化する方法を教えることです。

ステップ2—JSONフィールドにデータを作成する

次に、を使用してデータベースに追加する製品を作成します。 INSERT INTOVALUES.

次に、文字列化されたJSONオブジェクトを使用した、画面サイズ、解像度、ポート、スピーカーに関するデータを備えたテレビの例をいくつか示します。

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Prime' ,
	'1' ,
	'1' ,
	'{"screen": "50 inch", "resolution": "2048 x 1152 pixels", "ports": {"hdmi": 1, "usb": 3}, "speakers": {"left": "10 watt", "right": "10 watt"}}'
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Octoview' ,
	'1' ,
	'1' ,
	'{"screen": "40 inch", "resolution": "1920 x 1080 pixels", "ports": {"hdmi": 1, "usb": 2}, "speakers": {"left": "10 watt", "right": "10 watt"}}'
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Dreamer' ,
	'1' ,
	'1' ,
	'{"screen": "30 inch", "resolution": "1600 x 900 pixles", "ports": {"hdmi": 1, "usb": 1}, "speakers": {"left": "10 watt", "right": "10 watt"}}'
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Bravia' ,
	'1' ,
	'1' ,
	'{"screen": "25 inch", "resolution": "1366 x 768 pixels", "ports": {"hdmi": 1, "usb": 0}, "speakers": {"left": "5 watt", "right": "5 watt"}}'
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Proton' ,
	'1' ,
	'1' ,
	'{"screen": "20 inch", "resolution": "1280 x 720 pixels", "ports": {"hdmi": 0, "usb": 0}, "speakers": {"left": "5 watt", "right": "5 watt"}}'
);

この例では、5つの異なるテレビ製品を宣言しています。

または、組み込みのを使用することもできます JSON_OBJECT JSONオブジェクトを作成する関数。

The JSON_OBJECT 関数は、次の形式のキーと値のペアのリストを受け入れます JSON_OBJECT(key1, value1, key2, value2, ... key(n), value(n)) JSONオブジェクトを返します。

これは、を使用した携帯電話の例です。 JSON_OBJECT 関数:

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Desire' ,
	'2' ,
	'2' ,
	JSON_OBJECT(
		"network" ,
		JSON_ARRAY("GSM" , "CDMA" , "HSPA" , "EVDO") ,
		"body" ,
		"5.11 x 2.59 x 0.46 inches" ,
		"weight" ,
		"143 grams" ,
		"sim" ,
		"Micro-SIM" ,
		"display" ,
		"4.5 inches" ,
		"resolution" ,
		"720 x 1280 pixels" ,
		"os" ,
		"Android Jellybean v4.3"
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Passion' ,
	'2' ,
	'2' ,
	JSON_OBJECT(
		"network" ,
		JSON_ARRAY("GSM" , "CDMA" , "HSPA") ,
		"body" ,
		"6.11 x 3.59 x 0.46 inches" ,
		"weight" ,
		"145 grams" ,
		"sim" ,
		"Micro-SIM" ,
		"display" ,
		"4.5 inches" ,
		"resolution" ,
		"720 x 1280 pixels" ,
		"os" ,
		"Android Jellybean v4.3"
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Emotion' ,
	'2' ,
	'2' ,
	JSON_OBJECT(
		"network" ,
		JSON_ARRAY("GSM" , "CDMA" , "EVDO") ,
		"body" ,
		"5.50 x 2.50 x 0.50 inches" ,
		"weight" ,
		"125 grams" ,
		"sim" ,
		"Micro-SIM" ,
		"display" ,
		"5.00 inches" ,
		"resolution" ,
		"720 x 1280 pixels" ,
		"os" ,
		"Android KitKat v4.3"
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Sensation' ,
	'2' ,
	'2' ,
	JSON_OBJECT(
		"network" ,
		JSON_ARRAY("GSM" , "HSPA" , "EVDO") ,
		"body" ,
		"4.00 x 2.00 x 0.75 inches" ,
		"weight" ,
		"150 grams" ,
		"sim" ,
		"Micro-SIM" ,
		"display" ,
		"3.5 inches" ,
		"resolution" ,
		"720 x 1280 pixels" ,
		"os" ,
		"Android Lollipop v4.3"
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Joy' ,
	'2' ,
	'2' ,
	JSON_OBJECT(
		"network" ,
		JSON_ARRAY("CDMA" , "HSPA" , "EVDO") ,
		"body" ,
		"7.00 x 3.50 x 0.25 inches" ,
		"weight" ,
		"250 grams" ,
		"sim" ,
		"Micro-SIM" ,
		"display" ,
		"6.5 inches" ,
		"resolution" ,
		"1920 x 1080 pixels" ,
		"os" ,
		"Android Marshmallow v4.3"
	)
);

この例では、5つの異なる携帯電話製品を宣言しています。

それはまた利用しました JSON_ARRAY 値のセットが渡されたときにJSON配列を返す関数。

1つのキーを複数回指定すると、最初のキーと値のペアのみが保持されます。 これは、MySQLの用語でJSONの正規化と呼ばれます。 また、正規化の一環として、オブジェクトキーが並べ替えられ、キーと値のペア間の余分な空白が削除されます。

さらに、組み込みを使用することができます JSON_MERGE_PRESERVE また JSON_MERGE_PATCH JSONオブジェクトを作成する関数。

注:以前のバージョンのMySQLでは、 JSON_MERGE、ただし、この関数は非推奨になりました。

'JSON_MERGE' is deprecated and will be removed in a future release. Please use JSON_MERGE_PRESERVE/JSON_MERGE_PATCH instead

このチュートリアルでは、 JSON_MERGE_PRESERVE 関数。 この関数は複数のJSONオブジェクトを受け取り、単一の集約オブジェクトを生成します。

これは、を使用したカメラの例です。 JSON_MERGE_PRESERVE 関数:

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Explorer' ,
	'3' ,
	'3' ,
	JSON_MERGE_PRESERVE(
		'{"sensor_type": "CMOS"}' ,
		'{"processor": "Digic DV III"}' ,
		'{"scanning_system": "progressive"}' ,
		'{"mount_type": "PL"}' ,
		'{"monitor_type": "LCD"}'
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Runner' ,
	'3' ,
	'3' ,
	JSON_MERGE_PRESERVE(
		JSON_OBJECT("sensor_type" , "CMOS") ,
		JSON_OBJECT("processor" , "Digic DV II") ,
		JSON_OBJECT("scanning_system" , "progressive") ,
		JSON_OBJECT("mount_type" , "PL") ,
		JSON_OBJECT("monitor_type" , "LED")
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Traveler' ,
	'3' ,
	'3' ,
	JSON_MERGE_PRESERVE(
		JSON_OBJECT("sensor_type" , "CMOS") ,
		'{"processor": "Digic DV II"}' ,
		'{"scanning_system": "progressive"}' ,
		'{"mount_type": "PL"}' ,
		'{"monitor_type": "LCD"}'
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Walker' ,
	'3' ,
	'3' ,
	JSON_MERGE_PRESERVE(
		'{"sensor_type": "CMOS"}' ,
		'{"processor": "Digic DV I"}' ,
		'{"scanning_system": "progressive"}' ,
		'{"mount_type": "PL"}' ,
		'{"monitor_type": "LED"}'
	)
);

INSERT INTO `e_store`.`products`(
	`name` ,
	`brand_id` ,
	`category_id` ,
	`attributes`
)
VALUES(
	'Jumper' ,
	'3' ,
	'3' ,
	JSON_MERGE_PRESERVE(
		'{"sensor_type": "CMOS"}' ,
		'{"processor": "Digic DV I"}' ,
		'{"scanning_system": "progressive"}' ,
		'{"mount_type": "PL"}' ,
		'{"monitor_type": "LCD"}'
	)
);

この例では、5つの異なるカメラ製品を宣言しています。

オブジェクトのみがに渡されることに注意してください JSON_MERGE_PRESERVE 関数。 それらのいくつかは、を使用して構築されています JSON_OBJECT 関数。 その他は有効なJSON文字列として渡されています。

の場合 JSON_MERGE_PRESERVE 関数では、キーが複数回繰り返された場合、その値は出力に配列として保持されます。

たとえば、これは同じJSONオブジェクトのコレクションです network 鍵:

SELECT JSON_MERGE_PRESERVE(
	'{"network": "GSM"}' ,
	'{"network": "CDMA"}' ,
	'{"network": "HSPA"}' ,
	'{"network": "EVDO"}'
);

これにより、値の配列が生成されます。

Output
{"network": ["GSM", "CDMA", "HSPA", "EVDO"]}

これで、この時点で、を使用してクエリを確認できます。 JSON_TYPE フィールド値タイプを表示する関数:

SELECT JSON_TYPE(attributes) FROM `e_store`.`products`;

このクエリは15を生成します OBJECT すべての製品(テレビ5台、携帯電話5台、カメラ5台)を表す結果。

これで、JSONフィールドにデータを作成できます。

ステップ3—JSONフィールドからのデータの読み取り

データベースに使用する製品がいくつかあるので、データの読み取りを試すことができます。

JSON型ではない一般的なMySQL値の場合、通常は WHERE 句。 ヒューリスティックに、JSON列を操作する場合、これは機能しません。

JSONフィールドを使用して行を選択する場合は、パス式の概念に精通している必要があります。 パス式はドル記号を使用します($)およびターゲットオブジェクトキー。

と組み合わせて使用する場合 JSON_EXTRACT 関数を使用すると、指定した列の値を取得できます。

少なくとも1つのUSBポートと1つのHDMIポートを備えたすべてのテレビに関心があるシナリオを考えてみましょう。

SELECT
	*
FROM
	`e_store`.`products`
WHERE
	`category_id` = 1
AND JSON_EXTRACT(`attributes` , '$.ports.usb') > 0
AND JSON_EXTRACT(`attributes` , '$.ports.hdmi') > 0;

への最初の引数 JSON_EXTRACT 関数は、パス式を適用するJSONです。 attributes 桁。 The $ シンボルは、操作するオブジェクトをトークン化します。 The $.ports.usb$.ports.hdmi パス式は、それぞれ「ポートの下でUSBキーを取得する」および「ポートの下でhdmiキーを取得する」という意味になります。

関心のあるキーを抽出したら、大なり記号(>) それらの上に。

このクエリは3つの結果を生成します。

これらの3つのテレビには、少なくとも1つのUSBポートと1つのHDMIポートがあります。 「ブラビア」モデルと「プロトン」モデルは、これらの条件を満たしていません。

または、 JSON_EXTRACT 関数にはエイリアスがあります -> クエリを読みやすくするために使用できます。

前のクエリを修正して、 -> エイリアス:

SELECT
	*
FROM
	`e_store`.`products`
WHERE
	`category_id` = 1
AND `attributes` -> '$.ports.usb' > 0
AND `attributes` -> '$.ports.hdmi' > 0;

これで、JSONフィールドからデータを読み取ることができます。

ステップ4—JSONフィールドのデータを更新する

JSONフィールドのデータを更新できます JSON_INSERT, JSON_REPLACE、 と JSON_SET 機能。 これらの関数には、JSONオブジェクトのどの部分を変更するかを指定するためのパス式も必要です。 これらの関数の出力は、変更が適用された有効なJSONオブジェクトです。

まず、JSONフィールドを次のように更新します JSON_INSERT 新しいを追加するには chipset すべての携帯電話の値が「Qualcomm」のキー:

UPDATE `e_store`.`products`
SET `attributes` = JSON_INSERT(
	`attributes` ,
	'$.chipset' ,
	'Qualcomm'
)
WHERE
	`category_id` = 2;

The $.chipset パス式は、の位置を識別します chipset オブジェクトのルートにあるプロパティ。

次のクエリを使用して、更新された携帯電話のカテゴリを調べます。

SELECT
	*
FROM
	`e_store`.`products`
WHERE
	`category_id` = 2

「Qualcomm」は現在、すべての携帯電話に搭載されています。

次に、JSONフィールドを更新します JSON_REPLACE 既存のものを変更するには chipset すべての携帯電話で「QualcommSnapsdragon」という値のキー:

UPDATE `e_store`.`products`
SET `attributes` = JSON_REPLACE(
	`attributes` ,
	'$.chipset' ,
	'Qualcomm Snapdragon'
)
WHERE
	`category_id` = 2;

「Qualcomm」は、すべての携帯電話で「QualcommSnapdragon」に置き換えられました。

最後に、JSONフィールドを次のように更新します JSON_SET 新しいを追加するには body_color すべてのテレビの値が「赤」のキー:

UPDATE `e_store`.`products`
SET `attributes` = JSON_SET(
	`attributes` ,
	'$.body_color' ,
	'red'
)
WHERE
	`category_id` = 1;

「赤」の色がすべてのテレビに適用されるようになりました。

これらの機能はすべて同じように見えますが、動作に違いがあります。

The JSON_INSERT 関数は、プロパティがまだ存在しない場合にのみ、プロパティをオブジェクトに追加します。

The JSON_REPLACE 関数は、プロパティが見つかった場合にのみプロパティを置き換えます。

The JSON_SET 関数は、プロパティが見つからない場合はプロパティを追加し、見つからない場合はプロパティを置き換えます。

これで、JSONフィールドからデータを更新できます。

ステップ5—JSONフィールドからのデータの削除

JSONフィールドのデータは JSON_REMOVE 機能と DELETE.

JSON_REMOVE JSON列から特定のキー/値を削除できます。

使用する JSON_REMOVE 機能、削除することが可能です mount_type すべてのカメラのキー/値ペア:

UPDATE `e_store`.`products`
SET `attributes` = JSON_REMOVE(`attributes` , '$.mount_type')
WHERE
	`category_id` = 3;

The JSON_REMOVE 関数は、パス式に基づいて指定されたキーを削除した後、更新されたJSONを返します。

または、 DELETE JSON列を使用した行全体。

使用する DELETEJSON_EXTRACTLIKE、Androidオペレーティングシステムの「Jellybean」バージョンを搭載したすべての携帯電話を削除することが可能です。

DELETE FROM `e_store`.`products`
WHERE `category_id` = 2
AND JSON_EXTRACT(`attributes` , '$.os') LIKE '%Jellybean%';

このクエリは、携帯電話の「Desire」モデルと「Passion」モデルを削除します。

特定の属性を操作するには、 JSON_EXTRACT 関数。 まず、 os 携帯電話の特性を抽出します。 そして、 LIKE 演算子はに適用されます DELETE 文字列を含むすべてのレコード Jellybean.

これで、JSONフィールドからデータを削除できます。

ステップ6—移行の作成

次に、新しいLaravelプロジェクトを作成します。

警告:このWebアプリケーションはチュートリアルのみを目的としており、本番環境では使用しないでください。

ターミナルウィンドウを開き、次のコマンドを実行します。

  1. composer create-project laravel/laravel estore-example

新しく作成されたプロジェクトディレクトリに移動します。

  1. cd estore-example

MySQLデータベースを使用するようにLaravelアプリケーションを構成します。

あなたはあなたを修正する必要があるかもしれません .env 設定するファイル DB_DATABASE, DB_USERNAME、 と DB_PASSWORD.

の3つの移行を作成します brands, categories、 と products それぞれ。

作る create_brands 移行:

  1. php artisan make:migration create_brands

を変更します create_brands.php 次のコード行を使用した移行:

database / migrations /(…)create_brands.php
<?php

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

class CreateBrands extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('brands', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

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

作る create_categories 移行:

  1. php artisan make:migration create_categories

を変更します create_categories.php 次のコード行を使用した移行:

database / migrations /(…)create_categories.php
<?php

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

class CreateCategories extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('categories', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->timestamps();
        });
    }

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

The create_products 移行には、インデックスと外部キーのディレクティブも含まれます。

  1. php artisan make:migration create_products

を変更します create_products.php 次のコード行を使用した移行:

database / migrations /(…)create_products.php
<?php

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

class CreateProducts extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('products', function(Blueprint $table){
            $table->engine = 'InnoDB';
            $table->increments('id');
            $table->string('name');
            $table->unsignedInteger('brand_id');
            $table->unsignedInteger('category_id');
            $table->json('attributes');
            $table->timestamps();
            // foreign key constraints
            $table->foreign('brand_id')->references('id')->on('brands')->onDelete('restrict')->onUpdate('cascade');
            $table->foreign('category_id')->references('id')->on('categories')->onDelete('restrict')->onUpdate('cascade');
            // indexes
            $table->index('brand_id');
            $table->index('category_id');
        });
    }

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

に注意してください $table->json('attributes'); 移行のステートメント。

注:これは、JSONデータ型をサポートするデータベースエンジンでのみ機能します。

古いバージョンのMySQLなどのエンジンは、これらの移行を実行できません。

メソッドという名前の適切なデータ型を使用して他のタイプのテーブルフィールドを作成するのと同様に、 json 名前のメソッド attributes.

ステップ7—モデルの作成

次の3つのモデルを作成します brands, categories、 と products それぞれ。

作成する Brand モデル:

  1. php artisan make:model Brand

を変更します Brand.php 次のコード行を含むファイル:

app / Models / Brand.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Brand extends Model
{
    use HasFactory;

    // A brand has many products
    public function products(){
        return $this->hasMany('Product')
    }
}

作成する Category モデル:

  1. php artisan make:model Category

を変更します Category.php 次のコード行を含むファイル:

app / Models / Category.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    // A category has many products
    public function products(){
        return $this->hasMany('Product')
    }
}

作成する Product モデル:

  1. php artisan make:model Product

を変更します Product.php 次のコード行を含むファイル:

app / Models / Product.php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    use HasFactory;

    public $timestamps = false;

    // Cast attributes JSON to array
    protected $casts = [
        'attributes' => 'array'
    ];

    // Each product has a brand
    public function brand(){
        return $this->belongsTo('Brand');
    }

    // Each product has a category
    public function category(){
        return $this->belongsTo('Category');
    }
}

The $casts キーを持つ配列 attributes に設定 array 製品がデータベースからフェッチされるときはいつでも、 attributes JSONは関連する配列に変換されます。 これにより、コントローラーアクションからレコードを更新できます。

ステップ8—製品の作成

このチュートリアルの残りの部分では、カメラ製品のカテゴリに焦点を当てます。

カメラに固有のフィールドを持つフォームを使用してビューを作成します。 簡潔にするために、テレビと携帯電話の製品カテゴリはカバーされませんが、デザインは非常に似ています。

カメラ製品カテゴリのコントローラーを作成します。

  1. php artisan make:controller CameraController

を変更します CameraController.php 次のコード行を使用します。

app / Http / Controller / CameraController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class CameraController extends Controller
{
    // creates product in database
    // using form fields
    public function store(Request $request){
        // create object and set properties
        $camera = new \App\Models\Product();
        $camera->name = $request->name;
        $camera->brand_id = $request->brand_id;
        $camera->category_id = $request->category_id;
        $camera->attributes = [
            'processor' => $request->processor,
            'sensor_type' => $request->sensor_type,
            'monitor_type' => $request->monitor_type,
            'scanning_system' => $request->scanning_system,
        ];
        // save to database
        $camera->save();
        // show the created camera
        return view('product.camera.show', ['camera' => $camera]);
    }
}

これで完了です store カメラの機能。

を作成してビューを作成します new.blade.php のファイル resources/views/product/camera ディレクトリツリー:

resources / views / product / camera / new.blade.php
<form method="POST" action="/product/camera/store">
    @csrf
    <table>
        <tr>
            <td><label for="name">Name</label></td>
            <td><input id="name" name="name" type="text"></td>
        </tr>
        <tr>
            <td><label for="brand-id">Brand ID</label></td>
            <td>
                <select id="brand-id" name="brand_id">
                    <option value="1">Samsung</option>
                    <option value="2">Nokia</option>
                    <option value="3">Canon</option>
                </select>
            </td>
        </tr>
        <tr>
            <td><label for="attributes-processor">Processor</label></td>
            <td><input id="attributes-processor" name="processor" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-sensor-type">Sensor Type</label></td>
            <td><input id="attributes-sensor-type" name="sensor_type" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-monitor-type">Monitor Type</label></td>
            <td><input id="attributes-monitor-type" name="monitor_type" type="text"></td>
        </tr>
        <tr>
            <td><label for="attributes-scanning-system">Scanning System</label></td>
            <td><input id="attributes-scanning-system" name="scanning_system" type="text"></td>
        </tr>
    </table>
    <input name="category_id" value="3" type="hidden">
    <button type="submit">Submit</button>
</form>

The brand_id ハードコードされたものとして表示されます select 以前に作成された3つのブランドの要素 options。 The category_id に設定されたハードコードされた非表示の入力値として表示されます id カメラ用。

のルートを変更します routes/web.php カメラを表示するには:

ルート/web.php
// ...

use App\Http\Controllers\CameraController;

Route::get('/product/camera/new', function() {
    return view('product/camera/new');
});

Route::post(
    '/product/camera/store',
    [CameraController::class, 'store']
);

次のコマンドを使用してアプリケーションを提供します。

  1. php artisan serve

次に、 localhost:8000/product/camera/new)Webブラウザを使用します。 新しいカメラを追加するためのフォームが表示されます。

ステップ9—製品の取得

The $casts 以前に宣言された配列 Product モデルは、属性を連想配列として扱うことにより、製品の読み取りと編集に役立ちます。

を変更します CamerasController 次のコード行を使用します。

app / Http / Controller / CameraController.php
<?php

// ...

class CameraController extends Controller
{
    // ... store ...

    // fetches a single product
    // from database
    public function show($id){
        $camera = \App\Models\Product::find($id);
        return view('product.camera.show', ['camera' => $camera]);
    }
}

これで完了です show カメラの機能。

を作成してビューを作成します show.blade.php のファイル resources/views/product/camera ディレクトリツリー:

resources / views / product / camera / show.blade.php
<table>
    <tr>
        <td>Name</td>
        <td>{{ $camera->name }}</td>
    </tr>
    <tr>
        <td>Brand ID</td>
        <td>{{ $camera->brand_id }}</td>
    </tr>
    <tr>
        <td>Category ID</td>
        <td>{{ $camera->category_id }}</td>
    </tr>
    <tr>
        <td>Processor</td>
        <td>{{ $camera->attributes['processor'] }}</td>
    </tr>
    <tr>
        <td>Sensor Type</td>
        <td>{{ $camera->attributes['sensor_type'] }}</td>
    </tr>
    <tr>
        <td>Monitor Type</td>
        <td>{{ $camera->attributes['monitor_type'] }}</td>
    </tr>
    <tr>
        <td>Scanning System</td>
        <td>{{ $camera->attributes['scanning_system'] }}</td>
    </tr>
</table>

のルートを変更します routes/web.php カメラを表示するには:

ルート/web.php
// ...

Route::get(
    '/product/camera/show/{id}',
    [CameraController::class, 'show']
);

次のコマンドを使用してアプリケーションを提供します。

  1. php artisan serve

次に、有効な id カメラ製品の場合(例: localhost:8000/product/camera/show/11)Webブラウザを使用します。 製品のカメラ情報の表が表示されます。 id 「11」の。

ステップ10—製品の編集

のための技術の組み合わせを使用することによって storeshow、ビューを作成できます edit 既存の製品。

次のようなフォームを作成できます new.blade.php. 次に、で使用されているものと同様の製品変数を事前に入力します show.blade.php:

<tr>
    <td><label for="attributes-processor">Processor</label></td>
    <td><input id="attributes-processor" name="processor" type="text" value="{{ $camera->attributes['processor'] }}"></td>
</tr>

これで、フォームに既存の値が表示され、ユーザーは更新が必要なものを簡単に確認できるようになります。

まず、 id モデルを取得するために使用されます。 次に、リクエストの値が適用されます。 最後に、新しい値がデータベースに保存されます。

ステップ11—JSON属性に基づく検索

EloquentORMを使用してJSON列をクエリすることもできます。

ユーザーが興味のある属性に基づいてカメラを検索できる検索ページについて考えてみます。

public function search(Request $request){
    $cameras = \App\Models\Product::where([
        ['attributes->processor', 'like', $request->processor],
        ['attributes->sensor_type', 'like', $request->sensor_type],
        ['attributes->monitor_type', 'like', $request->monitor_type],
        ['attributes->scanning_system', 'like', $request->scanning_system]
    ])->get();
    return view('product.camera.search', ['cameras' => $cameras]);
}

取得したレコードは、 product.camera.search として見る $cameras コレクション。 これにより、結果をループして、ユーザーの検索要求の条件を満たすカメラを表示できます。

ステップ12—製品の削除

非JSON列属性を使用すると、次のように指定して製品を削除できます。 where 句を呼び出してから、 delete 方法。

たとえば、 id.

\App\Models\Product::where('id', $id)->delete();

JSON列の場合、 where 単一または複数の属性を使用する句を使用してから、 delete 方法。

\App\Models\Product::where('attributes->sensor_type', 'CMOS')->delete();
}

この例では、このコードは、 sensor_type 属性は「CMOS」に設定されています。

結論

この記事では、JSONデータ型を使用してMySQLデータベースを設計し、LaravelWebアプリケーションを使用してデータベースに接続しました。

データをキーと値のペアとして別のテーブルに保存したり、エンティティの柔軟な属性を操作したりする必要がある場合は、データベース設計の圧縮に大きく貢献する可能性があるため、代わりにJSONデータ型フィールドの使用を検討する必要があります。

さらに深く掘り下げることに興味がある場合は、 MySQLドキュメントは、JSONの概念をさらに探求するための優れたリソースです。

Laravelの詳細については、 Get Started withLaravelに関するテクニカルトークを参照してください。