商品属性功能后端代码
This commit is contained in:
parent
a91477bf25
commit
1613adc3fa
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
@ -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'));
|
||||
}
|
||||
}
|
||||
|
|
@ -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' => [
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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');
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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'];
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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'];
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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'];
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
};
|
||||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ return [
|
|||
'access_frontend' => '访问前台',
|
||||
|
||||
// sidebar
|
||||
'attribute_groups_index' => '属性组',
|
||||
'attributes_index' => '属性',
|
||||
'settings_index' => '系统设置',
|
||||
'admin_users_index' => '后台用户',
|
||||
'plugins_index' => '插件列表',
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ return [
|
|||
'access_frontend' => '訪問前台',
|
||||
|
||||
// sidebar
|
||||
'attribute_groups_index' => '屬性組',
|
||||
'attributes_index' => '屬性',
|
||||
'settings_index' => '系統設置',
|
||||
'admin_users_index' => '後台用戶',
|
||||
'plugins_index' => '插件列表',
|
||||
|
|
|
|||
Loading…
Reference in New Issue