latest(); if ($request->filled('q')) { $query->where('name', 'like', '%'.$request->q.'%'); } $products = $query->paginate(25); return view('admin.products.index', compact('products')); } public function create() { $categories = Category::orderBy('name')->get(); $attributes = Attribute::with('values')->get(); $variantsJson = '[]'; return view('admin.products.form', compact('categories', 'attributes', 'variantsJson')); } public function store(Request $request) { $data = $request->validate([ 'name' => 'required|string|max:255', 'category_id' => 'nullable|exists:categories,id', 'description' => 'nullable|string', 'base_price' => 'required|numeric|min:0', 'tax_rate' => 'required|numeric|in:7,19', 'weight' => 'nullable|numeric|min:0', 'active' => 'boolean', 'featured' => 'boolean', 'track_stock' => 'boolean', 'sort_order' => 'integer', ]); $data['slug'] = Str::slug($data['name']); $data['active'] = $request->boolean('active'); $data['featured'] = $request->boolean('featured'); $data['track_stock'] = $request->boolean('track_stock'); $product = Product::create($data); $this->syncImages($request, $product); $this->syncVariants($request, $product); return redirect()->route('admin.products.index')->with('success', 'Artikel erstellt.'); } public function edit(Product $product) { $product->load('images', 'variants.attributeValues.attribute'); $categories = Category::orderBy('name')->get(); $attributes = Attribute::with('values')->get(); $variantsJson = json_encode($product->variants->map(fn($v) => [ 'id' => $v->id, 'sku' => $v->sku, 'price_override' => $v->price_override, 'stock' => $v->stock, 'stock_warning' => $v->stock_warning, 'active' => (bool) $v->active, 'attribute_value_ids' => $v->attributeValues->pluck('id')->values()->all(), ])); return view('admin.products.form', compact('product', 'categories', 'attributes', 'variantsJson')); } public function update(Request $request, Product $product) { $data = $request->validate([ 'name' => 'required|string|max:255', 'category_id' => 'nullable|exists:categories,id', 'description' => 'nullable|string', 'base_price' => 'required|numeric|min:0', 'tax_rate' => 'required|numeric|in:7,19', 'weight' => 'nullable|numeric|min:0', 'active' => 'boolean', 'featured' => 'boolean', 'track_stock' => 'boolean', 'sort_order' => 'integer', ]); $data['slug'] = Str::slug($data['name']); $data['active'] = $request->boolean('active'); $data['featured'] = $request->boolean('featured'); $data['track_stock'] = $request->boolean('track_stock'); $product->update($data); $this->syncImages($request, $product); $this->syncVariants($request, $product); return redirect()->route('admin.products.index')->with('success', 'Artikel aktualisiert.'); } public function destroy(Product $product) { $product->delete(); return redirect()->route('admin.products.index')->with('success', 'Artikel gelöscht.'); } private function syncImages(Request $request, Product $product): void { if ($request->hasFile('images')) { foreach ($request->file('images') as $i => $file) { $path = $file->store('products', 'public'); ProductImage::create(['product_id' => $product->id, 'path' => $path, 'sort_order' => $i]); } } // Bild löschen if ($request->filled('delete_images')) { ProductImage::whereIn('id', $request->delete_images) ->where('product_id', $product->id) ->delete(); } } private function syncVariants(Request $request, Product $product): void { $variants = $request->input('variants', []); $existingIds = []; foreach ($variants as $v) { if (empty($v['sku'])) continue; $variant = isset($v['id']) ? ProductVariant::find($v['id']) : null; $variantData = [ 'product_id' => $product->id, 'sku' => $v['sku'], 'price_override' => filled($v['price_override']) ? $v['price_override'] : null, 'stock' => (int) ($v['stock'] ?? 0), 'stock_warning' => (int) ($v['stock_warning'] ?? 5), 'active' => isset($v['active']), ]; if ($variant) { $variant->update($variantData); } else { $variant = ProductVariant::create($variantData); } // Attributwerte synchronisieren $valueIds = array_filter((array) ($v['attribute_value_ids'] ?? [])); $variant->attributeValues()->sync($valueIds); $existingIds[] = $variant->id; } // Nicht mehr vorhandene Varianten löschen $product->variants()->whereNotIn('id', $existingIds)->delete(); // Sicherstellen dass jedes Produkt mindestens eine Standard-Variante hat if ($product->variants()->count() === 0) { ProductVariant::create([ 'product_id' => $product->id, 'sku' => 'DEFAULT-' . $product->id, 'price_override'=> null, 'stock' => 0, 'stock_warning' => 5, 'active' => true, ]); } } }