Files
ladill-woo-manager/app/Http/Controllers/Woo/ProductController.php
T
isaaccladandCursor e4b6c2c1ba
Deploy Ladill Woo Manager / deploy (push) Successful in 55s
Add WooCommerce product and category management.
Sync catalog via plugin webhooks and REST proxy, with list/create/edit UI and updated logo from monolith assets.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-06 23:40:14 +00:00

154 lines
5.4 KiB
PHP

<?php
namespace App\Http\Controllers\Woo;
use App\Http\Controllers\Controller;
use App\Models\WooProduct;
use App\Models\WooStore;
use App\Services\Woo\CatalogSyncService;
use App\Services\Woo\CatalogWriteService;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
class ProductController extends Controller
{
public function __construct(
private CatalogWriteService $write,
private CatalogSyncService $sync,
) {}
public function index(Request $request): View
{
$user = $request->user();
$search = trim((string) $request->query('q', ''));
$storeFilter = $request->query('store');
$statusFilter = $request->query('status');
$storeIds = WooStore::query()->where('user_id', $user->id)->pluck('id');
$products = WooProduct::query()
->whereIn('woo_store_id', $storeIds)
->when($storeFilter, fn ($q) => $q->where('woo_store_id', $storeFilter))
->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();
$stores = WooStore::query()->where('user_id', $user->id)->orderBy('site_name')->get();
return view('woo.products.index', compact('products', 'stores', 'search', 'storeFilter', 'statusFilter'));
}
public function create(Request $request): View
{
$stores = $this->activeStores($request);
return view('woo.products.form', [
'product' => new WooProduct(['status' => WooProduct::STATUS_DRAFT]),
'stores' => $stores,
'categories' => collect(),
]);
}
public function store(Request $request): RedirectResponse
{
$validated = $this->validateProduct($request);
$store = $this->authorizedStore($request, (int) $validated['woo_store_id']);
$this->write->createProduct($store, $validated);
return redirect()->route('woo.products.index', ['store' => $store->id])
->with('success', 'Product saved.');
}
public function edit(Request $request, WooProduct $product): View
{
$product->load('store');
abort_if($product->store?->user_id !== $request->user()->id, 403);
$stores = $this->activeStores($request);
$categories = $product->store
? $product->store->categories()->orderBy('name')->get()
: collect();
return view('woo.products.form', compact('product', 'stores', 'categories'));
}
public function update(Request $request, WooProduct $product): RedirectResponse
{
$product->load('store');
abort_if($product->store?->user_id !== $request->user()->id, 403);
$validated = $this->validateProduct($request, $product);
$this->write->updateProduct($product, $validated);
return redirect()->route('woo.products.index', ['store' => $product->woo_store_id])
->with('success', 'Product updated.');
}
public function sync(Request $request): RedirectResponse
{
$validated = $request->validate([
'store' => ['required', 'integer'],
]);
$store = $this->authorizedStore($request, (int) $validated['store']);
$counts = $this->sync->syncAll($store);
return back()->with('success', sprintf(
'Synced %d categories and %d products from %s.',
$counts['categories'],
$counts['products'],
$store->site_name ?? $store->site_url,
));
}
/** @return array<string, mixed> */
private function validateProduct(Request $request, ?WooProduct $product = null): array
{
return $request->validate([
'woo_store_id' => ['required', 'integer'],
'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'],
]);
}
private function authorizedStore(Request $request, int $storeId): WooStore
{
return WooStore::query()
->where('user_id', $request->user()->id)
->whereKey($storeId)
->where('status', WooStore::STATUS_ACTIVE)
->firstOrFail();
}
/** @return \Illuminate\Support\Collection<int, WooStore> */
private function activeStores(Request $request)
{
return WooStore::query()
->where('user_id', $request->user()->id)
->where('status', WooStore::STATUS_ACTIVE)
->orderBy('site_name')
->get();
}
}