Уже немного поздно, но, возможно, это может быть полезно, я надеюсь.
Я думаю, что вам может быть полезно предложить какое-то хорошее решение для вашей проблемы, поскольку вы сказали, что ожидаете. В прошлый раз у меня была похожая проблема, и мой руководитель команды представил мне отличный пример для подобных ситуаций. Я постараюсь объяснить как можно проще. Итак, вот в чем дело:
Для начала, скажем, у нас есть 3 необходимые таблицы: «категории», «атрибуты», «продукты». С помощью этих 3 таблиц мы можем построить некоторую нормальную структуру для нашей системы. При этом у нас может быть продукт, который может иметь несколько атрибутов, который относится к какой-то категории. Это хорошо. В этом случае, если администратор изменит какой-либо атрибут (имя атрибута или что-то подобное), это повлияет на все продукты, которые имеют этот атрибут. Но что если администратор захочет изменить какое-либо свойство атрибута только для конкретного продукта? Мы застрянем на этом. Вот четвертая таблица: «attribute_templates». Здесь, в этой новой таблице, мы будем хранить все атрибуты вместо таблицы атрибутов, как раньше. Но в таблице «атрибуты» мы будем хранить атрибуты с назначением их конкретного продукта. В этом случае администратор может изменить некоторое значение свойства атрибута для конкретного продукта, и это не повлияет на другие продукты. Также любой атрибут может иметь различный тип, и он должен принадлежать к какой-то определенной категории. Например: продукт телефона (это категория) (Galaxy S9) может иметь некоторые атрибуты, такие как «bluetooth», который будет иметь логический тип (иметь / не иметь), или «заднюю камеру», который будет иметь тип smallInteger (7MP), 10MP, 12MP и т. Д.). Таким образом, каждый атрибут будет иметь свой тип (в таблице «attribute_templates») и значение (в таблице «attribute»). В случае, когда администратор хочет добавить / удалить некоторые новые атрибуты, система должна добавить / удалить эти атрибуты только в / из таблицы «attribute_templates». А в запросах мы будем использовать только таблицу атрибутов (ничего общего с таблицей шаблонов). Этот шаблон хорош также, когда вы хотите добавить новую функциональность в БД. Например, в одном из моих проектов у меня были квартиры вместо продуктов. У них были атрибуты, а также удобства. Для этого я также создал таблицы «amenity_templates» и «удобствами» и соединил их с таблицами «квартиры» и «категории» (как я это сделал для таблицы «attribute_templates» и «атрибуты»).
Здесь яЯ прикреплю изображение в качестве примера моего шаблона, который покажет соответствующую структуру БД этих 4 таблиц через диаграмму (извините за плохую покраску).
Если вы хотите, вы можете создать и запустить некоторые начальные числа для этих таблиц в следующей последовательности:
- создать категории
- создать начальные атрибуты в 'attribute_templates' (в этомтаблица case 'attribute' еще пуста)
- создать продукт. если продукт должен иметь, например, 3 атрибута, то он скопирует эти три записи из «attribute_templates» и импортирует их в таблицу «атрибутов», и назначит их все этому продукту с соответствующим значением атрибута.
Ниже я напишу коды миграций (с правильной последовательностью) для каждой модели.
2019_05_19_100000_create_categories_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCategoriesTable extends Migration
{
public function up(){
Schema::create('categories', function (Blueprint $table) {
// PRIMARY
$table->tinyIncrements('id');
// ADDITIONAL
$table->string('name', 250);
// TIME
$table->timestamps();
});
}
public function down(){
Schema::dropIfExists('categories');
}
}
2019_05_19_200000_create_attribute_templates.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateAttributeTemplatesTable extends Migration
{
public function up()
{
Schema::create('attribute_templates', function (Blueprint $table) {
// PRIMARY
$table->bigIncrements('id');
// FOREIGN
$table->unsignedTinyInteger('category_id')->nullable();
$table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade');
// ADDITIONAL
$table->string('name', 250);
$table->string('value_type', 20)->nullable();
// TIME
$table->timestamps();
});
}
public function down(){
Schema::dropIfExists('attribute_templates');
}
}
2019_05_19_300000_create_products_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductsTable extends Migration
{
public function up(){
Schema::create('products', function (Blueprint $table) {
// PRIMARY
$table->bigIncrements('id');
// FOREIGN
$table->unsignedTinyInteger('category_id')->nullable();
$table->foreign('category_id')->references('id')->on('categories')->onUpdate('cascade');
// ADDITIONAL
$table->string('title', 250);
$table->string('image', 250)->nullable();
// TIME
$table->timestamps();
});
}
public function down(){
Schema::dropIfExists('products');
}
}
2019_05_19_400000_create_attributes_table.php
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductAttributesTable extends Migration
{
public function up(){
Schema::create('product_attributes', function (Blueprint $table) {
// PRIMARY
$table->bigIncrements('id');
// FOREIGN
$table->unsignedBigInteger('product_id');
$table->foreign('product_id')->references('id')->on('products')->onUpdate('cascade')->onDelete('cascade');
$table->unsignedBigInteger('template_id');
$table->foreign('template_id')->references('id')->on('attribute_templates')->onUpdate('cascade');
// ADDITIONAL
$table->text('value')->nullable();
// TIME
$table->timestamps();
});
}
public function down(){
Schema::dropIfExists('product_attributes');
}
}
Ниже я напишу коды Моделей (с правильной последовательностью).
Category.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $table = 'categories';
protected $fillable = [
// PRIMARY
'id', // tinyIncrements
// ADDITIONAL
'name', // string 250
];
// RELATIONS
public function products(){
return $this->hasMany(Product::class, 'category_id', 'id');
}
public function productAttributeTemplates() {
$this->hasMany(ProductAttributeTemplate::class, 'category_id');
}
}
AttributeTemplate.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class AttributeTemplate extends Model
{
protected $table = 'attribute_templates';
protected $fillable = [
// PRIMARY
'id', // bigIncrements
// FOREIGN
'category_id', // unsignedTinyInteger nullable
// ADDITIONAL
'name', // string 250
'value_type', // string 20 nullable
];
// RELATIONS
public function attribute(){
return $this->hasOne(ProductAttribute::class, 'template_id');
}
public function category(){
return $this->belongsTo(Category::class, 'category_id');
}
}
Product.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
protected $table = 'products';
protected $fillable = [
// PRIMARY
'id', // bigIncrements
// FOREIGN
'category_id', // unsignedTinyInteger
// ADDITIONAL
'title', // string 250
'image', // string 250 nullable
];
// RELATIONS
public function category(){
return $this->belongsTo(Category::class, 'category_id');
}
public function attributes(){
return $this->hasMany(ProductAttribute::class, 'product_id');
}
}
Attribute.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Attribute extends Model
{
protected $table = 'product_attributes';
protected $fillable = [
// PRIMARY
'id', // bigIncrements
// FOREIGN
'product_id', // unsignedBigInteger
'template_id', // unsignedBigInteger
// ADDITIONAL
'value', // text
];
// RELATIONS
public function product(){
return $this->belongsTo(Product::class, 'product_id');
}
public function template(){
return $this->belongsTo(AttributeTemplate::class, 'template_id');
}
}
Теперь, скажем, мы хотимдобавить некоторый телефонный продукт (OnePlus 7T), который будет следоватьАтрибуты ing: OS (OxygenOS 10), bluetooth (true), наушники (Type-C).
- Если продукт «телефон» не существует, нам нужно создать его в таблице «категории».
- Создать новый продукт «OnePlus 7T» в таблице «продукты» и присвоить его таблице «категории». .
- Скопируйте атрибуты «OS», «Bluetooth», «наушники» из таблицы «attribute_templates», вставьте их в таблицу «атрибутов» и назначьте их в таблицу «категории» и таблицу «продукты» (для соответствующейкатегории «телефон» и «OnePlus 7T»). А также, если вам нужно, вы можете написать «значение» атрибутов для каждого шаблона. Для нас это будет: «OxygenOS 10» для ОС, «true» для Bluetooth, «Type-C» для наушников.