update
This commit is contained in:
@@ -117,16 +117,19 @@ class OsVersion extends Controller
|
||||
return DataTables::eloquent($query)
|
||||
->editColumn('os_group', function ($d) {
|
||||
return $d->osGroup ? $d->osGroup->name . ' - ' . $d->osGroup->host->name : '';
|
||||
})
|
||||
->editColumn('status', function ($d) {
|
||||
return $d->getStatus();
|
||||
})
|
||||
})
|
||||
->addColumn('action', function ($d) {
|
||||
return view('administrator.pages.package.version.datatable.action', [
|
||||
'id' => $d->id,
|
||||
'version' => $d->version
|
||||
])->render();
|
||||
})
|
||||
->addColumn(
|
||||
'status',
|
||||
function ($d) {
|
||||
return view('administrator.pages.package.version.datatable.status', ['row' => $d])->render();
|
||||
}
|
||||
)
|
||||
->filterColumn('host', function ($query, $keyword) {
|
||||
$query->whereHas('host', function ($q) use ($keyword) {
|
||||
$q->where('host', 'like', "%{$keyword}%");
|
||||
|
||||
@@ -4,8 +4,7 @@ namespace App\Http\Controllers\admin\product;
|
||||
|
||||
use App\Enums\Product\ProductTypeEnums;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Product\StoreProductRequest;
|
||||
use App\Http\Requests\Product\UpdateProductRequest;
|
||||
use App\Http\Requests\Product\ProductRequest;
|
||||
use App\Http\Services\ProductService;
|
||||
use App\Repositories\Group\GroupRepositoryInterface;
|
||||
use App\Repositories\Admin\Product\ProductRepositoryInterface;
|
||||
@@ -42,11 +41,15 @@ class ProductController extends Controller
|
||||
{
|
||||
$step = $request->get('step') ?? 1;
|
||||
$title = 'Add new product';
|
||||
$group = $this->groupRepository->getAll();
|
||||
// Fetch Product Categories instead of generic groups
|
||||
$group = \App\Models\ProductCategories::where('is_active', \App\Enums\Status::ACTIVE->value)->get();
|
||||
$type = $this->typeRepository->getAll();
|
||||
|
||||
|
||||
// Fetch OS versions with their group for the OS selector component
|
||||
$osVersions = \App\Models\Os::with('osGroup')->where('status', 1)->get();
|
||||
|
||||
if ($step === 1) {
|
||||
return view("administrator.pages.product.create", compact('group', 'type', 'title'));
|
||||
return view("administrator.pages.product.create", compact('group', 'type', 'title', 'osVersions'));
|
||||
}
|
||||
|
||||
if (!$request->has(['step', 'type'])) {
|
||||
@@ -58,59 +61,62 @@ class ProductController extends Controller
|
||||
|
||||
$view = "administrator.pages.product.create";
|
||||
|
||||
$data = compact('type_id', 'title', 'group', 'type', 'product_name');
|
||||
$data = compact('type_id', 'title', 'group', 'type', 'product_name', 'osOptions');
|
||||
|
||||
return view($view, $data);
|
||||
}
|
||||
|
||||
public function store(StoreProductRequest $request)
|
||||
public function store(ProductRequest $request)
|
||||
{
|
||||
try {
|
||||
$this->productService->store($request->validated());
|
||||
|
||||
return redirect()->route('product.index')->with('success', 'Add new product success');
|
||||
return redirect()->route('products.index')->with('success', 'Add new product success');
|
||||
} catch (Exception $e) {
|
||||
return back()->withErrors($e->getMessage())->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
public function edit(Request $request)
|
||||
public function edit(Request $request, $id)
|
||||
{
|
||||
$product = $this->productRepository->find($request->product_id);
|
||||
$product = $this->productRepository->find($id);
|
||||
|
||||
if (!$product) {
|
||||
return back()->withErrors("Can't find product.");
|
||||
}
|
||||
|
||||
$title = 'View product item';
|
||||
$group = $this->groupRepository->getAll();
|
||||
// Load relationships
|
||||
$product->load(['pricings', 'osVersions', 'category']);
|
||||
|
||||
$title = 'Edit product: ' . $product->name;
|
||||
$group = \App\Models\ProductCategories::where('is_active', \App\Enums\Status::ACTIVE->value)->get();
|
||||
$type = $this->typeRepository->getAll();
|
||||
|
||||
$productType = ProductTypeEnums::tryFrom($product->product_type);
|
||||
$type_id = $this->mapEnumToTypeId($productType);
|
||||
// Fetch OS versions for select
|
||||
$osVersions = \App\Models\Os::with('osGroup')->where('status', 1)->get();
|
||||
|
||||
$view = "administrator.pages.product.edit";
|
||||
|
||||
$data = compact('product', 'title', 'group', 'type', 'type_id');
|
||||
$data = compact('product', 'title', 'group', 'type', 'osVersions');
|
||||
|
||||
return view($view, $data);
|
||||
}
|
||||
|
||||
public function update(UpdateProductRequest $request)
|
||||
public function update(ProductRequest $request, $id)
|
||||
{
|
||||
try {
|
||||
$this->productService->update($request->product_id, $request->validated());
|
||||
return redirect()->route('product.index')->with('success', 'Update product success');
|
||||
$this->productService->update((int) $id, $request->validated());
|
||||
return redirect()->route('products.index')->with('success', 'Update product success');
|
||||
} catch (Exception $e) {
|
||||
return back()->withErrors($e->getMessage())->withInput();
|
||||
}
|
||||
}
|
||||
|
||||
public function destroy(Request $request)
|
||||
public function destroy(Request $request, $id)
|
||||
{
|
||||
try {
|
||||
$this->productService->delete($request->product_id);
|
||||
return redirect()->route('product.index')->with('success', 'Deleted successfully');
|
||||
$this->productService->delete((int) $id);
|
||||
return redirect()->route('products.index')->with('success', 'Deleted successfully');
|
||||
} catch (Exception $e) {
|
||||
return back()->withErrors($e->getMessage());
|
||||
}
|
||||
@@ -120,19 +126,33 @@ class ProductController extends Controller
|
||||
{
|
||||
$query = $this->productRepository->filter(
|
||||
$request->all(),
|
||||
[],
|
||||
['category'],
|
||||
[],
|
||||
['*'],
|
||||
true
|
||||
);
|
||||
|
||||
return DataTables::eloquent($query)
|
||||
->addColumn('category_name', function ($d) {
|
||||
return $d->category?->name ?? '-';
|
||||
})
|
||||
->addColumn('product_type_label', function ($d) {
|
||||
$type = ProductTypeEnums::tryFrom($d->product_type);
|
||||
return $type ? $type->label() : '-';
|
||||
})
|
||||
->addColumn('specs', function ($d) {
|
||||
return view('administrator.pages.product.datatable.specs', ['row' => $d])->render();
|
||||
})
|
||||
->addColumn('status', function ($d) {
|
||||
return view('administrator.pages.product.datatable.status', ['row' => $d])->render();
|
||||
})
|
||||
->addColumn(
|
||||
'action',
|
||||
function ($d) {
|
||||
return view('administrator.pages.product.datatable.action', ['row' => $d])->render();
|
||||
}
|
||||
)
|
||||
->rawColumns(['specs', 'status', 'action'])
|
||||
->make(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Product;
|
||||
|
||||
use App\Enums\Product\ProductBillingCycle;
|
||||
use App\Models\Product;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class ProductRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$productId = $this->route('id');
|
||||
|
||||
$rules = [
|
||||
'product_type' => ['required', 'string', 'max:50'],
|
||||
'product_categories_id' => ['required', 'exists:product_categories,id'],
|
||||
'name' => [
|
||||
'required',
|
||||
'string',
|
||||
'max:255',
|
||||
function ($attribute, $value, $fail) use ($productId) {
|
||||
$slug = Str::slug($value);
|
||||
$query = Product::where('slug', $slug);
|
||||
|
||||
// Exclude current product when updating
|
||||
if ($productId) {
|
||||
$query->where('id', '!=', $productId);
|
||||
}
|
||||
|
||||
if ($query->exists()) {
|
||||
$fail('Tên sản phẩm đã tồn tại. Vui lòng chọn tên khác.');
|
||||
}
|
||||
},
|
||||
],
|
||||
'short_description' => ['nullable', 'string', 'max:255'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'stock' => ['nullable', 'integer', 'min:0'],
|
||||
'is_unlimited' => ['boolean'],
|
||||
'cpu' => ['nullable', 'string', 'max:255'],
|
||||
'ram' => ['nullable', 'string', 'max:255'],
|
||||
'gpu' => ['nullable', 'string', 'max:255'],
|
||||
'disk' => ['nullable', 'string', 'max:255'],
|
||||
'bandwidth' => ['nullable', 'string', 'max:255'],
|
||||
'features' => ['nullable', 'json'],
|
||||
'metadata' => ['nullable', 'json'],
|
||||
'setup_fee' => ['nullable', 'numeric', 'min:0'],
|
||||
'trial_days' => ['nullable', 'integer', 'min:0'],
|
||||
'currency' => ['nullable', 'string', 'max:10'],
|
||||
'is_active' => ['boolean'],
|
||||
'sort_order' => ['nullable', 'integer'],
|
||||
'published_at' => ['nullable', 'date'],
|
||||
|
||||
// OS versions (pivot table os_product)
|
||||
'os_ids' => ['nullable', 'array'],
|
||||
'os_ids.*' => ['exists:os,id'],
|
||||
|
||||
// Pricing cycles (table product_pricings)
|
||||
'pricing' => ['nullable', 'array'],
|
||||
];
|
||||
|
||||
// Add dynamic validation for each billing cycle
|
||||
foreach (ProductBillingCycle::cases() as $cycle) {
|
||||
$cycleKey = $cycle->value;
|
||||
|
||||
$rules["pricing.{$cycleKey}.enabled"] = ['nullable', 'boolean'];
|
||||
$rules["pricing.{$cycleKey}.price"] = ['nullable', 'numeric', 'min:0'];
|
||||
$rules["pricing.{$cycleKey}.price_sale"] = [
|
||||
'nullable',
|
||||
'numeric',
|
||||
'min:0',
|
||||
"required_with:pricing.{$cycleKey}.price_sale_check",
|
||||
function ($attribute, $value, $fail) use ($cycleKey) {
|
||||
$price = $this->input("pricing.{$cycleKey}.price");
|
||||
|
||||
// price_sale requires price to be set first
|
||||
if ($value !== null && $value !== '' && (empty($price) || $price <= 0)) {
|
||||
$fail("Bạn phải nhập giá gốc trước khi nhập giá khuyến mãi cho {$cycleKey}.");
|
||||
return;
|
||||
}
|
||||
|
||||
// price_sale must be less than price
|
||||
if ($value !== null && $value !== '' && $price !== null && $value >= $price) {
|
||||
$fail("Giá khuyến mãi phải nhỏ hơn giá gốc cho {$cycleKey}.");
|
||||
}
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the error messages for the defined validation rules.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'product_type.required' => 'Vui lòng chọn loại sản phẩm.',
|
||||
'product_type.string' => 'Loại sản phẩm phải là chuỗi ký tự.',
|
||||
'product_type.max' => 'Loại sản phẩm không được vượt quá 50 ký tự.',
|
||||
|
||||
'product_categories_id.required' => 'Vui lòng chọn danh mục sản phẩm.',
|
||||
'product_categories_id.exists' => 'Danh mục sản phẩm không hợp lệ.',
|
||||
|
||||
'name.required' => 'Vui lòng nhập tên sản phẩm.',
|
||||
'name.string' => 'Tên sản phẩm phải là chuỗi ký tự.',
|
||||
'name.max' => 'Tên sản phẩm không được vượt quá 255 ký tự.',
|
||||
|
||||
'short_description.string' => 'Mô tả ngắn phải là chuỗi ký tự.',
|
||||
'short_description.max' => 'Mô tả ngắn không được vượt quá 255 ký tự.',
|
||||
|
||||
'description.string' => 'Mô tả phải là chuỗi ký tự.',
|
||||
|
||||
'stock.integer' => 'Số lượng tồn kho phải là số nguyên.',
|
||||
'stock.min' => 'Số lượng tồn kho không được âm.',
|
||||
|
||||
'is_unlimited.boolean' => 'Giá trị không giới hạn không hợp lệ.',
|
||||
|
||||
'cpu.string' => 'CPU phải là chuỗi ký tự.',
|
||||
'cpu.max' => 'CPU không được vượt quá 255 ký tự.',
|
||||
|
||||
'ram.string' => 'RAM phải là chuỗi ký tự.',
|
||||
'ram.max' => 'RAM không được vượt quá 255 ký tự.',
|
||||
|
||||
'gpu.string' => 'GPU phải là chuỗi ký tự.',
|
||||
'gpu.max' => 'GPU không được vượt quá 255 ký tự.',
|
||||
|
||||
'disk.string' => 'Dung lượng ổ cứng phải là chuỗi ký tự.',
|
||||
'disk.max' => 'Dung lượng ổ cứng không được vượt quá 255 ký tự.',
|
||||
|
||||
'bandwidth.string' => 'Băng thông phải là chuỗi ký tự.',
|
||||
'bandwidth.max' => 'Băng thông không được vượt quá 255 ký tự.',
|
||||
|
||||
'features.json' => 'Tính năng phải là định dạng JSON hợp lệ.',
|
||||
'metadata.json' => 'Metadata phải là định dạng JSON hợp lệ.',
|
||||
|
||||
'setup_fee.numeric' => 'Phí cài đặt phải là số.',
|
||||
'setup_fee.min' => 'Phí cài đặt không được âm.',
|
||||
|
||||
'trial_days.integer' => 'Số ngày dùng thử phải là số nguyên.',
|
||||
'trial_days.min' => 'Số ngày dùng thử không được âm.',
|
||||
|
||||
'currency.string' => 'Đơn vị tiền tệ phải là chuỗi ký tự.',
|
||||
'currency.max' => 'Đơn vị tiền tệ không được vượt quá 10 ký tự.',
|
||||
|
||||
'is_active.boolean' => 'Trạng thái kích hoạt không hợp lệ.',
|
||||
|
||||
'sort_order.integer' => 'Thứ tự sắp xếp phải là số nguyên.',
|
||||
|
||||
'published_at.date' => 'Ngày xuất bản không hợp lệ.',
|
||||
|
||||
'os_ids.array' => 'Danh sách hệ điều hành không hợp lệ.',
|
||||
'os_ids.*.exists' => 'Hệ điều hành được chọn không tồn tại.',
|
||||
|
||||
'pricing.array' => 'Thông tin giá không hợp lệ.',
|
||||
'pricing.*.price.numeric' => 'Giá phải là số.',
|
||||
'pricing.*.price.min' => 'Giá không được âm.',
|
||||
'pricing.*.price_sale.numeric' => 'Giá khuyến mãi phải là số.',
|
||||
'pricing.*.price_sale.min' => 'Giá khuyến mãi không được âm.',
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Product;
|
||||
|
||||
use App\Enums\Product\ProductTypeEnums;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreProductRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
$rules = [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'price' => ['required', 'numeric', 'min:0'],
|
||||
'price_sale' => ['nullable', 'numeric', 'min:0'],
|
||||
'description' => ['nullable', 'string'],
|
||||
'status' => ['required', 'integer'], // Value from Status Enum
|
||||
'type_id' => ['required', 'integer'],
|
||||
];
|
||||
|
||||
// Infer product type from type_id (Legacy mapping needs to be consistent with Controller)
|
||||
// Controller map: 1=>Addon, 2=>Domain, 3=>Hosting, 4=>VPS
|
||||
// We can access input 'type_id'
|
||||
$typeId = (int) $this->input('type_id');
|
||||
|
||||
if ($typeId === 4) { // VPS
|
||||
$rules = array_merge($rules, [
|
||||
'cpu' => ['required'],
|
||||
'ram' => ['required'],
|
||||
'disk' => ['required'],
|
||||
'bandwidth' => ['required'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Product;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateProductRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
// Based on existing controller logic, update rules are similar but allow optional fields if strictly updating parameters
|
||||
// However, standard update often requires validating presence if they are being updated.
|
||||
// Controller used specific rules.
|
||||
|
||||
// We need to fetch the product to know its type if 'type_id' is not in request (usually it is in hidden field).
|
||||
// Assuming type_id is present in update form as per Controller logic.
|
||||
|
||||
$rules = [
|
||||
'name' => ['required', 'string', 'max:255'],
|
||||
'price' => ['required', 'numeric', 'min:0'],
|
||||
'product_id' => ['required', 'exists:products,id'],
|
||||
];
|
||||
|
||||
$typeId = (int) $this->input('type_id');
|
||||
|
||||
if ($typeId === 4) { // VPS
|
||||
$rules = array_merge($rules, [
|
||||
'cpu' => ['required'],
|
||||
'ram' => ['required'],
|
||||
'disk' => ['required'],
|
||||
]);
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
@@ -2,117 +2,143 @@
|
||||
|
||||
namespace App\Http\Services;
|
||||
|
||||
use App\Enums\Product\ProductTypeEnums;
|
||||
use App\Enums\Status;
|
||||
use App\Models\ProductPricings;
|
||||
use App\Repositories\Admin\Product\ProductRepositoryInterface;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Exception;
|
||||
|
||||
class ProductService
|
||||
{
|
||||
protected ProductRepositoryInterface $repository;
|
||||
|
||||
public function __construct(
|
||||
protected ProductRepositoryInterface $productRepository
|
||||
) {}
|
||||
ProductRepositoryInterface $repository
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
}
|
||||
|
||||
public function store(array $data)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$typeId = (int) ($data['type_id'] ?? 0);
|
||||
$productType = $this->mapTypeIdToEnum($typeId);
|
||||
|
||||
if (!$productType) {
|
||||
throw new Exception('Invalid Product Type');
|
||||
}
|
||||
// Prepare product data
|
||||
$productData = $this->prepareData($data);
|
||||
|
||||
$prepareData = [
|
||||
'uuid' => (string) Str::uuid(),
|
||||
'name' => $data['name'],
|
||||
'slug' => Str::slug($data['name']),
|
||||
'product_type' => $productType->value,
|
||||
'is_active' => isset($data['status']) && (int)$data['status'] === Status::ACTIVE->value,
|
||||
|
||||
'price' => $data['price'] ?? 0,
|
||||
'price_sale' => $data['price_sale'] ?? null,
|
||||
'description' => $data['description'] ?? null,
|
||||
|
||||
// VPS Columns
|
||||
'cpu' => $data['cpu'] ?? null,
|
||||
'ram' => $data['ram'] ?? null,
|
||||
'disk' => $data['disk'] ?? null,
|
||||
'bandwidth' => $data['bandwidth'] ?? null,
|
||||
'gpu' => $data['gpu'] ?? null, // if added later
|
||||
|
||||
// Extra fields
|
||||
'sort_order' => $data['sort_order'] ?? 0,
|
||||
'billing_cycle' => $data['billing_cycle'] ?? 'monthly',
|
||||
];
|
||||
// Create product
|
||||
$product = $this->repository->create($productData);
|
||||
|
||||
$product = $this->productRepository->create($prepareData);
|
||||
|
||||
DB::commit();
|
||||
return $product;
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
// Sync OS versions if provided
|
||||
if (!empty($data['os_ids'])) {
|
||||
$product->osVersions()->sync($data['os_ids']);
|
||||
}
|
||||
|
||||
// Save pricing cycles if provided
|
||||
if (!empty($data['pricing'])) {
|
||||
$this->savePricings($product, $data['pricing'], $data['currency'] ?? 'USD');
|
||||
}
|
||||
|
||||
return $product;
|
||||
}
|
||||
|
||||
public function update(int $id, array $data)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$prepareData = [];
|
||||
|
||||
if (isset($data['name'])) {
|
||||
$prepareData['name'] = $data['name'];
|
||||
$prepareData['slug'] = Str::slug($data['name']);
|
||||
}
|
||||
|
||||
if (isset($data['status'])) {
|
||||
$prepareData['is_active'] = (int)$data['status'] === Status::ACTIVE->value;
|
||||
}
|
||||
|
||||
// Map other fields that overlap with fillable
|
||||
$fillable = ['price', 'price_sale', 'billing_cycle', 'cpu', 'ram', 'gpu', 'disk', 'bandwidth', 'sort_order', 'description'];
|
||||
foreach ($fillable as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$prepareData[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
// Prepare product data
|
||||
$productData = $this->prepareData($data, false);
|
||||
|
||||
$result = $this->productRepository->update($id, $prepareData);
|
||||
|
||||
DB::commit();
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
// Update product
|
||||
$result = $this->repository->update($id, $productData);
|
||||
|
||||
// Get the product for relationships
|
||||
$product = $this->repository->find($id);
|
||||
|
||||
// Sync OS versions if provided
|
||||
if (isset($data['os_ids'])) {
|
||||
$product->osVersions()->sync($data['os_ids']);
|
||||
}
|
||||
|
||||
// Save pricing cycles if provided
|
||||
if (!empty($data['pricing'])) {
|
||||
$this->savePricings($product, $data['pricing'], $data['currency'] ?? 'USD');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function delete(int $id)
|
||||
{
|
||||
DB::beginTransaction();
|
||||
try {
|
||||
$this->productRepository->delete($id);
|
||||
DB::commit();
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
DB::rollBack();
|
||||
throw $e;
|
||||
return $this->repository->delete($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save pricing cycles for product
|
||||
*/
|
||||
private function savePricings($product, array $pricingData, string $currency): void
|
||||
{
|
||||
foreach ($pricingData as $cycle => $pricing) {
|
||||
// Only save if enabled and has price
|
||||
if (!empty($pricing['enabled']) && isset($pricing['price'])) {
|
||||
ProductPricings::updateOrCreate(
|
||||
[
|
||||
'product_id' => $product->id,
|
||||
'billing_cycle' => $cycle,
|
||||
],
|
||||
[
|
||||
'price' => $pricing['price'] ?? 0,
|
||||
'price_sale' => $pricing['price_sale'] ?? null,
|
||||
'currency' => $currency,
|
||||
'is_active' => true,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
// Remove pricing if not enabled
|
||||
ProductPricings::where('product_id', $product->id)
|
||||
->where('billing_cycle', $cycle)
|
||||
->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function mapTypeIdToEnum(int $typeId): ?ProductTypeEnums
|
||||
|
||||
/**
|
||||
* Prepare data for create/update
|
||||
*/
|
||||
private function prepareData(array $data, bool $isCreate = true): array
|
||||
{
|
||||
return match ($typeId) {
|
||||
1 => ProductTypeEnums::ADDON,
|
||||
2 => ProductTypeEnums::DOMAIN,
|
||||
3 => ProductTypeEnums::HOSTING,
|
||||
4 => ProductTypeEnums::VPS,
|
||||
default => null
|
||||
};
|
||||
$prepared = [];
|
||||
|
||||
// Generate UUID and slug only on create
|
||||
if ($isCreate) {
|
||||
$prepared['uuid'] = (string) Str::uuid();
|
||||
}
|
||||
|
||||
// Name and slug
|
||||
if (isset($data['name'])) {
|
||||
$prepared['name'] = $data['name'];
|
||||
$prepared['slug'] = Str::slug($data['name']);
|
||||
}
|
||||
|
||||
// Direct mappable fields
|
||||
$fillableFields = [
|
||||
'short_description',
|
||||
'description',
|
||||
'setup_fee',
|
||||
'trial_days',
|
||||
'cpu',
|
||||
'ram',
|
||||
'gpu',
|
||||
'disk',
|
||||
'bandwidth',
|
||||
'stock',
|
||||
'is_unlimited',
|
||||
'is_active',
|
||||
'sort_order',
|
||||
'product_type',
|
||||
'product_categories_id',
|
||||
'published_at',
|
||||
];
|
||||
|
||||
foreach ($fillableFields as $field) {
|
||||
if (array_key_exists($field, $data)) {
|
||||
$prepared[$field] = $data[$field];
|
||||
}
|
||||
}
|
||||
|
||||
return $prepared;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user