商品属性功能后端代码

This commit is contained in:
TL 2023-01-06 10:45:18 +08:00 committed by pushuo
parent a91477bf25
commit 1613adc3fa
26 changed files with 859 additions and 2 deletions

View File

@ -0,0 +1,104 @@
<?php
/**
* AttributeController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-04 19:45:41
* @modified 2023-01-04 19:45:41
*/
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Http\Resources\AttributeDetailResource;
use Beike\Admin\Http\Resources\AttributeResource;
use Beike\Admin\Http\Resources\AttributeValueResource;
use Beike\Admin\Http\Resources\AutocompleteResource;
use Beike\Admin\Repositories\AttributeGroupRepo;
use Beike\Admin\Repositories\AttributeRepo;
use Illuminate\Http\Request;
class AttributeController extends Controller
{
public function index(Request $request)
{
$attributes = AttributeRepo::getList();
$data = [
'attribute_list' => $attributes,
'attribute_list_format' => AttributeResource::collection($attributes),
'attribute_group' => AttributeGroupRepo::getList(),
];
if ($request->expectsJson()) {
return json_success(trans('success'), $data);
}
return view('admin::pages.attributes.index', $data);
}
public function show(Request $request, int $id)
{
$data = [
'attribute' => (new AttributeDetailResource(AttributeRepo::find($id)))->jsonSerialize(),
'attribute_group' => AttributeGroupRepo::getList(),
];
return view('admin::pages.attributes.form', $data);
}
public function store(Request $request)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeRepo::create($requestData);
return json_success(trans('common.created_success'), $item);
}
public function update(Request $request, int $id)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeRepo::update($id, $requestData);
return json_success(trans('common.updated_success'), $item);
}
public function storeValue(Request $request, int $id)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeRepo::createValue(array_merge($requestData, ['attribute_id' => $id]));
return json_success(trans('common.created_success'), new AttributeValueResource($item));
}
public function updateValue(Request $request, int $id, int $value_id)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeRepo::updateValue($value_id, $requestData);
return json_success(trans('common.updated_success'), new AttributeValueResource($item));
}
public function destroyValue(Request $request, int $id, int $value_id)
{
AttributeRepo::deleteValue($value_id);
return json_success(trans('common.deleted_success'));
}
public function destroy(Request $request, int $id)
{
AttributeRepo::delete($id);
return json_success(trans('common.deleted_success'));
}
public function autocomplete(Request $request): array
{
$items = AttributeRepo::autocomplete($request->get('name') ?? '', 0);
return json_success(trans('common.get_success'), AutocompleteResource::collection($items));
}
public function autocompleteValue(Request $request, int $id): array
{
$items = AttributeRepo::autocompleteValue($id, $request->get('name') ?? '');
return json_success(trans('common.get_success'), AutocompleteResource::collection($items));
}
}

View File

@ -0,0 +1,47 @@
<?php
/**
* AttributeGroupController.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-04 19:45:41
* @modified 2023-01-04 19:45:41
*/
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Repositories\AttributeGroupRepo;
use Illuminate\Http\Request;
class AttributeGroupController extends Controller
{
public function index()
{
$data = [
'attribute_groups' => AttributeGroupRepo::getList(),
];
return view('admin::pages.attribute_group.index', $data);
}
public function store(Request $request)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeGroupRepo::create($requestData);
return json_success(trans('common.created_success'), $item);
}
public function update(Request $request, int $id)
{
$requestData = json_decode($request->getContent(), true);
$item = AttributeGroupRepo::update($id, $requestData);
return json_success(trans('common.updated_success'), $item);
}
public function destroy(Request $request, int $id)
{
AttributeGroupRepo::delete($id);
return json_success(trans('common.deleted_success'));
}
}

View File

@ -2,7 +2,9 @@
namespace Beike\Admin\Http\Controllers;
use Beike\Admin\Http\Resources\ProductAttributeResource;
use Beike\Models\Product;
use Beike\Models\ProductAttribute;
use Illuminate\Http\Request;
use Beike\Repositories\ProductRepo;
use Beike\Repositories\CategoryRepo;
@ -107,13 +109,14 @@ class ProductController extends Controller
if ($product->id) {
$descriptions = $product->descriptions->keyBy('locale');
$categoryIds = $product->categories->pluck('id')->toArray();
$product->load('brand');
$product->load('brand', 'attributes');
}
$data = [
'product' => $product,
'descriptions' => $descriptions ?? [],
'category_ids' => $categoryIds ?? [],
'product_attributes' => ProductAttributeResource::collection($product->attributes),
'languages' => LanguageRepo::all(),
'tax_classes' => TaxClassRepo::getList(),
'source' => [

View File

@ -0,0 +1,30 @@
<?php
namespace Beike\Admin\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AttributeDetailResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$data = [
'id' => $this->id,
'attribute_group_id' => $this->attribute_group_id,
'name' => $this->description->name ?? '',
'sort_order' => $this->sort_order,
'attribute_group_name' => $this->attributeGroup->description->name ?? '',
'created_at' => time_format($this->created_at),
'values' => AttributeValueResource::collection($this->values),
'descriptions' => $this->descriptions
];
return $data;
}
}

View File

@ -0,0 +1,27 @@
<?php
namespace Beike\Admin\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AttributeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$data = [
'id' => $this->id,
'name' => $this->description->name ?? '',
'sort_order' => $this->sort_order,
'attribute_group_name' => $this->attributeGroup->description->name ?? '',
'created_at' => time_format($this->created_at),
];
return $data;
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Beike\Admin\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AttributeValueResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$data = [
'id' => $this->id,
'attribute_id' => $this->attribute_id,
'name' => $this->description->name,
'description' => $this->description,
'descriptions' => $this->descriptions,
'created_at' => time_format($this->created_at),
];
return $data;
}
}

View File

@ -0,0 +1,24 @@
<?php
namespace Beike\Admin\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
class AutocompleteResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array|\Illuminate\Contracts\Support\Arrayable|\JsonSerializable
*/
public function toArray($request)
{
$data = [
'id' => $this->id,
'name' => $this->description->name ?? '',
];
return $data;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace Beike\Admin\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class ProductAttributeResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param Request $request
* @return array
* @throws \Exception
*/
public function toArray($request): array
{
$this->load('attribute', 'attributeValue');
$data = [
'attribute' => [
'id' => $this->attribute_id,
'name' => $this->attribute->description->name,
],
'attribute_value' => [
'id' => $this->attribute_value_id,
'name' => $this->attributeValue->description->name,
],
];
return $data;
}
}

View File

@ -0,0 +1,72 @@
<?php
/**
* AttributeGroupRepo.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-04 19:45:41
* @modified 2023-01-04 19:45:41
*/
namespace Beike\Admin\Repositories;
use Beike\Models\AttributeGroup;
class AttributeGroupRepo
{
public static function getList()
{
return AttributeGroup::query()->orderByDesc('id')->with('description', 'descriptions')->get();
}
public static function create($data)
{
$attributeGroup = AttributeGroup::query()->create([
'sort_order' => $data['sort_order'],
]);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attributeGroup->descriptions()->createMany($descriptions);
$attributeGroup->load('description', 'descriptions');
return $attributeGroup;
}
public static function update($id, $data)
{
$attributeGroup = AttributeGroup::query()->updateOrCreate(['id' => $id], [
'sort_order' => $data['sort_order'],
]);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attributeGroup->descriptions()->delete();
$attributeGroup->descriptions()->createMany($descriptions);
return $attributeGroup;
}
public static function find($id)
{
return AttributeGroup::query()->find($id);
}
public static function delete($id)
{
$group = AttributeGroup::query()->findOrFail($id);
$group->delete();
}
}

View File

@ -0,0 +1,138 @@
<?php
/**
* AttributeRepo.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-04 19:45:41
* @modified 2023-01-04 19:45:41
*/
namespace Beike\Admin\Repositories;
use Beike\Admin\Http\Resources\TaxClassDetail;
use Beike\Models\Attribute;
use Beike\Models\AttributeValue;
use Beike\Models\TaxClass;
class AttributeRepo
{
public static function getList()
{
return Attribute::query()->orderByDesc('id')->paginate()->withQueryString();
}
public static function create($data)
{
$attribute = Attribute::query()->create([
'attribute_group_id' => $data['attribute_group_id'],
'sort_order' => $data['sort_order'],
]);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attribute->descriptions()->createMany($descriptions);
return $attribute;
}
public static function update($id, $data)
{
$attribute = Attribute::query()->updateOrCreate(['id' => $id], [
'attribute_group_id' => $data['attribute_group_id'],
'sort_order' => $data['sort_order'],
]);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attribute->descriptions()->delete();
$attribute->descriptions()->createMany($descriptions);
return $attribute;
}
public static function createValue($data)
{
$attributeValue = AttributeValue::query()->create([
'attribute_id' => $data['attribute_id'],
]);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attributeValue->descriptions()->createMany($descriptions);
return $attributeValue;
}
public static function updateValue($id, $data)
{
$attributeValue = AttributeValue::query()->findOrFail($id);
$descriptions = [];
foreach ($data['name'] as $locale => $name) {
$descriptions[] = [
'locale' => $locale,
'name' => $name,
];
}
$attributeValue->descriptions()->delete();
$attributeValue->descriptions()->createMany($descriptions);
return $attributeValue;
}
public function deleteValue($id)
{
AttributeValue::query()->findOrFail($id)->delete();
}
public static function find($id)
{
return Attribute::query()->with('values.descriptions')->find($id);
}
public static function delete($id)
{
$attribute = Attribute::query()->findOrFail($id);
$attribute->values()->delete();
$attribute->delete();
}
public static function autocomplete($name)
{
$builder = Attribute::query()->with('description')
->whereHas('description', function ($query) use ($name) {
$query->where('name', 'like', "{$name}%");
});
return $builder->limit(10)->get();
}
public static function autocompleteValue($attributeId, $name)
{
$builder = AttributeValue::query()->with('description')
->where('attribute_id', $attributeId)
->whereHas('description', function ($query) use ($name) {
$query->where('name', 'like', "{$name}%");
});
return $builder->limit(10)->get();
}
}

View File

@ -20,6 +20,24 @@ Route::prefix($adminName)
->group(function () {
Route::get('/', [Controllers\HomeController::class, 'index'])->name('home.index');
// 属性
Route::middleware('can:attributes_update')->post('attributes/{id}/values', [Controllers\AttributeController::class, 'storeValue'])->name('attributes.values.store');
Route::middleware('can:attributes_show')->get('attributes/{id}/values/autocomplete', [Controllers\AttributeController::class, 'autocompleteValue'])->name('attributes.values.autocomplete');
Route::middleware('can:attributes_update')->put('attributes/{id}/values/{value_id}', [Controllers\AttributeController::class, 'updateValue'])->name('attributes.values.update');
Route::middleware('can:attributes_update')->delete('attributes/{id}/values/{value_id}', [Controllers\AttributeController::class, 'destroyValue'])->name('attributes.values.destroy');
Route::middleware('can:attributes_index')->get('attributes', [Controllers\AttributeController::class, 'index'])->name('attributes.index');
Route::middleware('can:attributes_show')->get('attributes/autocomplete', [Controllers\AttributeController::class, 'autocomplete'])->name('attributes.autocomplete');
Route::middleware('can:attributes_show')->get('attributes/{id}', [Controllers\AttributeController::class, 'show'])->name('attributes.show');
Route::middleware('can:attributes_create')->post('attributes', [Controllers\AttributeController::class, 'store'])->name('attributes.store');
Route::middleware('can:attributes_update')->put('attributes/{id}', [Controllers\AttributeController::class, 'update'])->name('attributes.update');
Route::middleware('can:attributes_delete')->delete('attributes/{id}', [Controllers\AttributeController::class, 'destroy'])->name('attributes.destroy');
// 属性组
Route::middleware('can:attribute_groups_index')->get('attribute_groups', [Controllers\AttributeGroupController::class, 'index'])->name('attribute_groups.index');
Route::middleware('can:attribute_groups_create')->post('attribute_groups', [Controllers\AttributeGroupController::class, 'store'])->name('attribute_groups.store');
Route::middleware('can:attribute_groups_update')->put('attribute_groups/{id}', [Controllers\AttributeGroupController::class, 'update'])->name('attribute_groups.update');
Route::middleware('can:attribute_groups_delete')->delete('attribute_groups/{id}', [Controllers\AttributeGroupController::class, 'destroy'])->name('attribute_groups.destroy');
// 商品品牌
Route::middleware('can:brands_index')->get('brands/names', [Controllers\BrandController::class, 'getNames'])->name('brands.names');
Route::middleware('can:brands_index')->get('brands/autocomplete', [Controllers\BrandController::class, 'autocomplete'])->name('brands.autocomplete');

View File

@ -34,6 +34,7 @@ class ProductService
if ($isUpdating) {
$product->skus()->delete();
$product->descriptions()->delete();
$product->attributes()->delete();
}
$descriptions = [];
@ -45,6 +46,8 @@ class ProductService
}
$product->descriptions()->createMany($descriptions);
$product->attributes()->createMany($data['attributes'] ?? []);
$skus = [];
foreach ($data['skus'] as $index => $sku) {
$sku['position'] = $index;

View File

@ -41,7 +41,7 @@ class Sidebar extends Component
foreach ($routes as $route) {
$this->addLink($route['route'], $route['icon'] ?? '', $this->equalRoute($route['route']), (bool)($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
}
} elseif (Str::startsWith($routeName, ['products.', 'categories.', 'brands.'])) {
} elseif (Str::startsWith($routeName, ['products.', 'categories.', 'brands.', 'attribute_groups.', 'attributes.'])) {
$routes = $this->getProductSubRoutes();
foreach ($routes as $route) {
$this->addLink($route['route'], $route['icon'] ?? '', $this->equalRoute($route['route']), (bool)($route['blank'] ?? false), $route['hide_mobile'] ?? 0);
@ -126,6 +126,8 @@ class Sidebar extends Component
['route' => 'categories.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'products.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'brands.index', 'icon' => 'fa fa-tachometer-alt','hide_mobile' => 1],
['route' => 'attribute_groups.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'attributes.index', 'icon' => 'fa fa-tachometer-alt'],
['route' => 'products.trashed', 'icon' => 'fa fa-tachometer-alt'],
];
return hook_filter('sidebar.product_routes', $routes);

View File

@ -0,0 +1,44 @@
<?php
/**
* Attribute.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Attribute extends Base
{
use HasFactory;
protected $fillable = ['attribute_group_id', 'sort_order'];
public function attributeGroup() : BelongsTo
{
return $this->belongsTo(AttributeGroup::Class);
}
public function values() :HasMany
{
return $this->hasMany(AttributeValue::Class);
}
public function description()
{
return $this->hasOne(AttributeDescription::class)->where('locale', locale());
}
public function descriptions() :HasMany
{
return $this->hasMany(AttributeDescription::Class);
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* AttributeDescription.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AttributeDescription extends Base
{
use HasFactory;
protected $fillable = ['attribute_id', 'locale', 'name'];
}

View File

@ -0,0 +1,38 @@
<?php
/**
* AttributeGroup.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AttributeGroup extends Base
{
use HasFactory;
protected $fillable = ['sort_order'];
public function description()
{
return $this->hasOne(AttributeGroupDescription::class)->where('locale', locale());
}
public function descriptions() :HasMany
{
return $this->hasMany(AttributeGroupDescription::Class);
}
public function attributes() :HasMany
{
return $this->hasMany(Attribute::Class);
}
}

View File

@ -0,0 +1,24 @@
<?php
/**
* AttributeGroupDescription.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AttributeGroupDescription extends Base
{
use HasFactory;
protected $fillable = ['attribute_group_id', 'locale', 'name'];
}

View File

@ -0,0 +1,39 @@
<?php
/**
* AttributeValue.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AttributeValue extends Base
{
use HasFactory;
protected $fillable = ['attribute_id'];
public function description()
{
return $this->hasOne(AttributeValueDescription::class)->where('locale', locale());
}
public function descriptions() :HasMany
{
return $this->hasMany(AttributeValueDescription::Class);
}
public function attribute() :BelongsTo
{
return $this->belongsTo(Attribute::Class);
}
}

View File

@ -0,0 +1,25 @@
<?php
/**
* AttributeValueDescription.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\HasMany;
class AttributeValueDescription extends Base
{
use HasFactory;
protected $fillable = ['attribute_value_id', 'locale', 'name'];
}

View File

@ -2,6 +2,7 @@
namespace Beike\Models;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Eloquent\Factories\HasFactory;
@ -44,6 +45,11 @@ class Product extends Base
return $this->hasMany(ProductSku::class);
}
public function attributes() : HasMany
{
return $this->hasMany(ProductAttribute::class);
}
public function master_sku()
{
return $this->hasOne(ProductSku::Class)->where('is_default', 1);

View File

@ -0,0 +1,34 @@
<?php
/**
* AttributeValueDescription.php
*
* @copyright 2023 beikeshop.com - All Rights Reserved
* @link https://beikeshop.com
* @author TL <mengwb@guangda.work>
* @created 2023-01-03 20:22:18
* @modified 2023-01-03 20:22:18
*/
namespace Beike\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class ProductAttribute extends Base
{
use HasFactory;
protected $fillable = ['product_id', 'attribute_id', 'attribute_value_id'];
public function attribute() : BelongsTo
{
return $this->belongsTo(Attribute::Class);
}
public function attributeValue() : BelongsTo
{
return $this->belongsTo(AttributeValue::Class);
}
}

View File

@ -34,6 +34,12 @@ class ProductDetail extends JsonResource
'thumb' => image_resize($image, 150, 150)
];
}, $this->images ?? []),
'attributes' => $this->attributes->map(function ($attribute) {
return [
'attribute' => $attribute->attribute->description->name,
'attribute_value' => $attribute->attributeValue->description->name,
];
})->toArray(),
'category_id' => $this->category_id ?? null,
'variables' => $this->decodeVariables($this->variables),
'skus' => SkuDetail::collection($this->skus)->jsonSerialize(),

View File

@ -0,0 +1,81 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('attributes', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('attribute_group_id')->comment('属性组 ID')->index('attribute_group_id');
$table->integer('sort_order')->comment('排序');
$table->timestamps();
});
Schema::create('attribute_descriptions', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('attribute_id')->comment('属性 ID')->index('attribute_id');
$table->string('locale')->default('')->comment('语言');
$table->string('name')->default('')->comment('名称');
$table->index(['attribute_id', 'locale'], 'attribute_id_locale');
$table->timestamps();
});
Schema::create('attribute_values', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('attribute_id')->comment('属性 ID')->index('attribute_id');
$table->timestamps();
});
Schema::create('attribute_value_descriptions', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('attribute_value_id')->comment('属性值 ID')->index('attribute_value_id');
$table->string('locale')->default('')->comment('语言');
$table->string('name')->default('')->comment('名称');
$table->index(['attribute_value_id', 'locale'], 'attribute_value_id_locale');
$table->timestamps();
});
Schema::create('attribute_groups', function (Blueprint $table) {
$table->id();
$table->integer('sort_order')->comment('排序');
$table->timestamps();
});
Schema::create('attribute_group_descriptions', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('attribute_group_id')->comment('属性组 ID')->index('attribute_group_id');
$table->string('locale')->default('')->comment('语言');
$table->string('name')->default('')->comment('名称');
$table->index(['attribute_group_id', 'locale'], 'attribute_group_id_locale');
$table->timestamps();
});
Schema::create('product_attributes', function (Blueprint $table) {
$table->id();
$table->unsignedInteger('product_id')->comment('商品 ID')->index('product_id');
$table->unsignedInteger('attribute_id')->comment('属性 ID')->index('attribute_id');
$table->unsignedInteger('attribute_value_id')->comment('属性值 ID')->index('attribute_value_id');
$table->index(['product_id', 'attribute_id'], 'product_id_attribute_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('attributes');
Schema::dropIfExists('attribute_descriptions');
Schema::dropIfExists('attribute_values');
Schema::dropIfExists('attribute_value_descriptions');
Schema::dropIfExists('attribute_groups');
Schema::dropIfExists('attribute_group_descriptions');
Schema::dropIfExists('product_attributes');
}
};

View File

@ -48,6 +48,8 @@ return [
'access_frontend' => 'Frontend',
// sidebar
'attribute_groups_index' => 'Attribute Group',
'attributes_index' => 'Attributes',
'settings_index' => 'Setting',
'admin_users_index' => 'Admin Users',
'plugins_index' => 'Plugins',

View File

@ -48,6 +48,8 @@ return [
'access_frontend' => '访问前台',
// sidebar
'attribute_groups_index' => '属性组',
'attributes_index' => '属性',
'settings_index' => '系统设置',
'admin_users_index' => '后台用户',
'plugins_index' => '插件列表',

View File

@ -47,6 +47,8 @@ return [
'access_frontend' => '訪問前台',
// sidebar
'attribute_groups_index' => '屬性組',
'attributes_index' => '屬性',
'settings_index' => '系統設置',
'admin_users_index' => '後台用戶',
'plugins_index' => '插件列表',