介绍
MySQL 5.7.8 版引入了一种 JSON 数据类型,允许您访问 JSON 文档中的数据。
SQL 数据库在设计上趋于僵化。就其性质而言,结构化查询语言强制执行数据类型和大小限制。
相比之下,NoSQL 数据库鼓励设计的灵活性。在这些无模式数据库中,没有强加的结构限制,只有要保存的数据。
MySQL 中的 JSON 数据类型赋予您这两种系统的优势。它允许您构建数据库的某些部分,而让其他部分保持灵活。
本文的前半部分将设计一个带有 JSON 字段的数据库。它将逐步使用 MySQL 可用的内置函数来创建、读取、更新和删除行。
本文的后半部分将利用 Eloquent ORM 和 Laravel 与数据库进行通信。您将构建一个支持显示产品、添加新产品、修改现有产品和删除产品的管理面板。
先决条件
如果你想跟随这篇文章,你需要:
- MySQL 5.7.8 或更高版本和 PHP 7.3.24 或更高版本。您可以参考我们关于安装 Linux、Apache、MySQL 和 PHP 的教程
- 对SQL 查询有一定的了解。
- 对编写 PHP有一定的了解。
- 对Laravel有一定的了解。
- 本教程通过 Composer使用Laravel 安装。您可以参考我们关于安装 Composer 的教程。
注意: Laravel 现在提供了一个名为Sail的工具来与 Docker 一起使用,该工具将使用 MySQL、PHP 和 Composer 配置环境。
如果您在设置本地环境时遇到困难,这可能是另一种选择。
本教程已通过 MySQL v8.0.23、PHP v7.3.24、Composer v2.0.9 和 Laravel v8.26.1 验证。
步骤 1 — 定义架构
出于本教程的目的,您将从定义销售各种电子产品的在线商店的库存的模式构建。
传统上,实体-属性-价值模型 (EAV)模式将用于允许客户比较产品的功能。
但是,对于 JSON 数据类型,可以以不同的方式处理此用例。
该数据库将被命名e_store
,并有三个表命名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;
在brands
与categories
表将各有一个id
和name
领域。
创建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_id
和category_id
字段指定外键约束,指定它们分别引用brands
和categories
表。此表定义还指定不应允许删除引用的行,如果更新,更改也应反映在引用中。
该attributes
字段的列类型已声明为 JSON,这是 MySQL 中现在可用的本机数据类型。这允许您在attributes
现场使用 MySQL 中的各种 JSON 相关构造。
下面是创建的数据库的实体关系图:
这种数据库设计在效率和准确性方面并不是最好的。有一些常见的现实世界用例没有被考虑在内。例如,products
表中没有价格列,也不支持一个产品属于多个类别。但是,本教程的目的不是教授数据库设计,而是教授如何使用 MySQL 的 JSON 功能在单个表中对不同性质的对象进行建模。
第 2 步 – 在 JSON 字段中创建数据
现在,您将使用INSERT INTO
和来创建要添加到数据库的产品VALUES
。
以下是一些示例电视,其中包含使用字符串化 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"}}'
);
此示例声明了五种不同的电视产品。
或者,您可以使用内置JSON_OBJECT
函数来创建 JSON 对象。
该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"
)
);
此示例声明了五种不同的手机产品。
它还利用了JSON_ARRAY
在传递一组值时返回 JSON 数组的函数。
如果多次指定单个键,则只会保留第一个键/值对。这在 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"}'
)
);
此示例声明了五种不同的相机产品。
请注意,只有对象被传递给JSON_MERGE_PRESERVE
函数。其中一些是使用该JSON_OBJECT
函数构建的。其他人已作为有效的 JSON 字符串传递。
在JSON_MERGE_PRESERVE
函数的情况下,如果一个键重复多次,它的值将作为数组保留在输出中。
例如,这是具有相同network
键的 JSON 对象的集合:
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
结果来表示所有产品 – 五台电视、五台手机和五台相机。
现在,您可以在 JSON 字段中创建数据。
第 3 步 – 从 JSON 字段读取数据
现在您在数据库中有一些产品可以使用,您可以尝试读取数据。
对于非 JSON 类型的典型 MySQL 值,您通常会依赖于WHERE
子句。启发式地,当使用 JSON 列时,这不起作用。
当您希望使用 JSON 字段选择行时,您应该熟悉路径表达式的概念。路径表达式使用美元符号 ( $
) 和目标对象键。
与JSON_EXTRACT
函数结合使用时,您可以检索指定列的值。
假设您对所有至少具有一个 USB 和一个 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
。该$
符号标记要使用的对象。该$.ports.usb
和$.ports.hdmi
路径表达式转换为“取USB项下口”和“走HDMI项下口”分别。
提取出您感兴趣的键后,您可以>
对它们使用 MySQL 运算符,例如大于号 ( )。
此查询将产生三个结果:
这三台电视至少有一个 USB 端口和一个 HDMI 端口。“Bravia”和“Proton”车型不满足这些条件。
或者,该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;
所述$.chipset
路径表达式的识别的位置chipset
特性是在该对象的根。
使用以下查询检查更新的手机类别:
SELECT
*
FROM
`e_store`.`products`
WHERE
`category_id` = 2
“高通”现在适用于所有手机:
现在,更新 JSON 字段JSON_REPLACE
以修改chipset
所有手机的值为“Qualcomm Snapsdragon”的现有键:
UPDATE `e_store`.`products`
SET `attributes` = JSON_REPLACE(
`attributes` ,
'$.chipset' ,
'Qualcomm Snapdragon'
)
WHERE
`category_id` = 2;
现在所有手机的“Qualcomm”都替换为“Qualcomm Snapdragon”:
最后,更新 JSON 字段,为所有电视JSON_SET
添加一个body_color
值为“red”的新键:
UPDATE `e_store`.`products`
SET `attributes` = JSON_SET(
`attributes` ,
'$.body_color' ,
'red'
)
WHERE
`category_id` = 1;
“红色”颜色现在适用于所有电视:
所有这些功能看似相同,但它们的行为方式却有所不同。
JSON_INSERT
如果该属性不存在,该函数只会将该属性添加到该对象中。
该JSON_REPLACE
函数仅在找到时才替换该属性。
JSON_SET
如果未找到该属性,则该函数将添加该属性,否则将替换它。
现在,您可以从 JSON 字段更新数据。
第 5 步 – 从 JSON 字段中删除数据
您可以使用JSON_REMOVE
函数 和删除 JSON 字段中的数据DELETE
。
JSON_REMOVE
允许您从 JSON 列中删除某个键/值。
使用JSON_REMOVE
函数,可以mount_type
从所有相机中删除键/值对:
UPDATE `e_store`.`products`
SET `attributes` = JSON_REMOVE(`attributes` , '$.mount_type')
WHERE
`category_id` = 3;
该JSON_REMOVE
函数根据路径表达式在删除指定的键后返回更新后的 JSON。
或者,您可以DELETE
使用 JSON 列来整行。
使用DELETE
andJSON_EXTRACT
和LIKE
,可以删除所有具有“Jellybean”版本Android操作系统的手机:
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 应用程序仅用于教程目的,不应在生产环境中使用。
打开终端窗口并运行以下命令:
- composer create-project laravel/laravel estore-example
导航到新创建的项目目录:
- cd estore-example
将 Laravel 应用程序配置为使用 MySQL 数据库。
您可能需要修改您的.env
文件来设置DB_DATABASE
,DB_USERNAME
和DB_PASSWORD
。
您将创建三个迁移brands
,categories
和products
分别。
进行create_brands
迁移:
- php artisan make:migration create_brands
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
迁移:
- php artisan make:migration create_categories
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');
}
}
该create_products
迁移也将有索引和外键的指示:
- php artisan make:migration create_products
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列json
名为方法attributes
。
第 7 步 – 创建模型
你要为创建三个模型brands
,categories
和products
分别。
创建Brand
模型:
- php artisan make:model Brand
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
模型:
- php artisan make:model Category
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
模型:
- php artisan make:model Product
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');
}
}
设置$casts
了键的数组可确保每当从数据库中获取产品时,其JSON 都会转换为关联的数组。这允许您从控制器操作更新记录。attributes
array
attributes
步骤 8 — 创建产品
本教程其余部分的重点将放在相机产品类别上。
您将使用具有特定于相机的字段的表单构建视图。为简洁起见,不会涵盖电视和手机产品类别 – 但在设计上会非常相似。
为相机产品类别创建控制器:
- php artisan make:controller CameraController
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
目录树中创建文件来创建视图:
<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>
brand_id
以硬编码select
元素的形式呈现,其中包含早先创建的三个品牌作为option
s。该category_id
是作为设置为一个硬编码隐藏输入值id
的相机。
修改路由routes/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']
);
使用以下命令为应用程序提供服务:
- php artisan serve
然后,localhost:8000/product/camera/new
使用 Web 浏览器访问)。它将显示用于添加新相机的表单。
步骤 9 — 获取产品
模型中$casts
先前声明的数组Product
将通过将属性视为关联数组来帮助您阅读和编辑产品。
CamerasController
使用以下代码行修改:
<?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
目录树中创建文件来创建视图:
<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
以显示摄像机:
// ...
Route::get(
'/product/camera/show/{id}',
[CameraController::class, 'show']
);
使用以下命令为应用程序提供服务:
- php artisan serve
然后,使用 Web 浏览器访问适用id
于相机产品的有效产品(例如localhost:8000/product/camera/show/11
)。它将显示带有id
“11”的产品的相机信息表。
步骤 10 — 编辑产品
By using a combination of the techniques for store
and show
, you can create a view to edit
an existing product.
You can create a form similar to the one in new.blade.php
. And then prepopulate it with a product variable similiar to the one used in 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>
Now, the form displays the existing values, making it easier for users to see what needs updating.
First, the id
is used to retrieve the model. Next, the values from the request are applied. Lastly, the new values are saved to the database.
Step 11 — Searching Based on JSON Attributes
You can also query JSON columns using the Eloquent ORM.
Consider a search page that allows users to search for cameras based upon attributes that they are interested in.
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]);
}
The retrieved records will now be available to the product.camera.search
view as a $cameras
collection. This will allow you to loop through the results and display the cameras that satisfy the conditions from the user’s search request.
Step 12 — Deleting a Product
Using a non-JSON column attribute, you can delete products by specifying a where
clause and then calling the delete
method.
For example, in the case of an id
.
\App\Models\Product::where('id', $id)->delete();
For JSON columns, specify a where
clause using a single or multiple attributes and then call the delete
method.
\App\Models\Product::where('attributes->sensor_type', 'CMOS')->delete();
}
In this example, this code will remove all products that have a sensor_type
attribute set to “CMOS”.
Conclusion
In this article, you designed a MySQL database with the JSON data type and connected to it with a Laravel web application.
每当您需要将数据作为键/值对保存在单独的表中或使用实体的灵活属性时,您应该考虑改用 JSON 数据类型字段,因为它可以极大地帮助压缩您的数据库设计。
如果您有兴趣深入研究,MySQL 文档是进一步探索 JSON 概念的绝佳资源。
有关 Laravel 的更多信息,您可以查阅我们关于 Laravel入门的技术讲座。