Files
2026-02-06 20:52:59 +07:00

209 lines
9.7 KiB
PHP

<?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'],
'thumbnail' => ['nullable', 'string', 'max:255'],
'gallery' => ['nullable', 'array'],
'gallery.*' => ['nullable', 'string', 'max:255'],
'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) - for VPS
'os_ids' => ['nullable', 'array'],
'os_ids.*' => ['exists:os,id'],
// Control Panels (pivot table product_control_panel) - for Hosting
'control_panel_ids' => ['nullable', 'array'],
'control_panel_ids.*' => ['exists:control_panels,id'],
'control_panel_price_modifiers' => ['nullable', 'array'],
'control_panel_price_modifiers.*' => ['nullable', 'numeric', 'min:0'],
'control_panel_default' => ['nullable', 'exists:control_panels,id'],
// Images
'thumbnail' => ['nullable', 'string', 'max:255'],
'gallery' => ['nullable', 'array'],
'gallery.*' => ['nullable', 'string', 'max:255'],
// 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ự.',
'thumbnail.string' => 'Đường dẫn ảnh đại diện phải là chuỗi ký tự.',
'thumbnail.max' => 'Đường dẫn ảnh đại diện không được vượt quá 255 ký tự.',
'gallery.array' => 'Thư viện ảnh không hợp lệ.',
'gallery.*.string' => 'Đường dẫn ảnh trong thư viện phải là chuỗi ký tự.',
'gallery.*.max' => 'Đường dẫn ảnh trong thư viện không được vượt quá 255 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.',
'control_panel_ids.array' => 'Danh sách Control Panel không hợp lệ.',
'control_panel_ids.*.exists' => 'Control Panel được chọn không tồn tại.',
'control_panel_price_modifiers.array' => 'Danh sách phụ phí Control Panel không hợp lệ.',
'control_panel_price_modifiers.*.numeric' => 'Phụ phí Control Panel phải là số.',
'control_panel_price_modifiers.*.min' => 'Phụ phí Control Panel không được âm.',
'control_panel_default.exists' => 'Control Panel mặc định 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.',
];
}
}