accountUser($request); $store = $this->currentStore($request); $search = trim((string) $request->query('q', '')); $statusFilter = $request->query('status'); $products = WooProduct::query() ->when($store, fn ($q) => $q->where('woo_store_id', $store->id)) ->when(! $store, fn ($q) => $q->whereRaw('1 = 0')) ->when($statusFilter, fn ($q) => $q->where('status', $statusFilter)) ->when($search !== '', function ($query) use ($search) { $like = '%'.$search.'%'; $query->where(function ($inner) use ($like) { $inner->where('name', 'like', $like) ->orWhere('sku', 'like', $like); }); }) ->with('store') ->latest('updated_at') ->paginate(20) ->withQueryString(); return view('woo.products.index', [ 'products' => $products, 'currentStore' => $store, 'search' => $search, 'statusFilter' => $statusFilter, 'productCount' => $this->subscriptions->productCount($user), 'productLimit' => $this->subscriptions->isPro($user) ? null : (int) config('woo.free.max_products', 20), ]); } public function create(Request $request): View|RedirectResponse { $user = $this->accountUser($request); $store = $this->currentStoreOrFail($request); if (! $this->subscriptions->canCreateProduct($user)) { return redirect()->route('woo.pro.index') ->with('upsell', 'You have reached the free plan limit of '.(int) config('woo.free.max_products', 20).' products. Upgrade to Pro for unlimited products.'); } return view('woo.products.form', [ 'product' => new WooProduct(['status' => WooProduct::STATUS_DRAFT, 'woo_store_id' => $store->id]), 'currentStore' => $store, 'categories' => $store->categories()->orderBy('name')->get(), ]); } public function store(Request $request): RedirectResponse { $user = $this->accountUser($request); $store = $this->currentStoreOrFail($request); if (! $this->subscriptions->canCreateProduct($user)) { return redirect()->route('woo.pro.index') ->with('upsell', 'You have reached the free plan limit of '.(int) config('woo.free.max_products', 20).' products. Upgrade to Pro for unlimited products.'); } $validated = $this->validateProduct($request, $store); $validated['images'] = $this->media->buildImagesFromRequest($request, $store); $this->write->createProduct($store, $validated); return redirect()->route('woo.products.index') ->with('success', 'Product saved.'); } public function edit(Request $request, WooProduct $product): View { $store = $this->currentStoreOrFail($request); abort_if($product->woo_store_id !== $store->id, 404); return view('woo.products.form', [ 'product' => $product, 'currentStore' => $store, 'categories' => $store->categories()->orderBy('name')->get(), ]); } public function update(Request $request, WooProduct $product): RedirectResponse { $store = $this->currentStoreOrFail($request); abort_if($product->woo_store_id !== $store->id, 404); $validated = $this->validateProduct($request, $store, $product); $validated['images'] = $this->media->buildImagesFromRequest($request, $store); $this->write->updateProduct($product, $validated); return redirect()->route('woo.products.index') ->with('success', 'Product updated.'); } public function sync(Request $request): RedirectResponse { $store = $this->currentStoreOrFail($request); $user = $this->accountUser($request); $counts = $this->sync->syncAll($store, $user); $message = sprintf( 'Synced %d categories and %d products from %s.', $counts['categories'], $counts['products'], $store->site_name ?? $store->site_url, ); if (! $this->subscriptions->isPro($user) && $this->subscriptions->productCount($user) >= (int) config('woo.free.max_products', 20)) { $message .= ' Free plan is limited to '.(int) config('woo.free.max_products', 20).' products — upgrade to Pro to sync your full catalog.'; } return back()->with('success', $message); } /** @return array */ private function validateProduct(Request $request, $store, ?WooProduct $product = null): array { return array_merge($request->validate([ 'name' => ['required', 'string', 'max:255'], 'sku' => ['nullable', 'string', 'max:120'], 'status' => ['required', Rule::in(array_keys(WooProduct::STATUSES))], 'regular_price' => ['nullable', 'numeric', 'min:0'], 'sale_price' => ['nullable', 'numeric', 'min:0'], 'manage_stock' => ['sometimes', 'boolean'], 'stock_quantity' => ['nullable', 'integer', 'min:0'], 'short_description' => ['nullable', 'string', 'max:5000'], 'description' => ['nullable', 'string', 'max:20000'], 'category_external_ids' => ['nullable', 'array'], 'category_external_ids.*' => ['integer', 'min:1'], 'product_images' => ['nullable', 'string'], 'featured_image_upload' => ['nullable', 'image', 'max:10240'], 'featured_image_src' => ['nullable', 'url', 'max:2000'], 'featured_image_alt' => ['nullable', 'string', 'max:255'], 'gallery_uploads' => ['nullable', 'array'], 'gallery_uploads.*' => ['image', 'max:10240'], 'gallery_image_srcs' => ['nullable', 'array'], 'gallery_image_srcs.*' => ['url', 'max:2000'], ]), [ 'woo_store_id' => $store->id, ]); } }