Files
SpiceManagementSystem/app/Filament/User/Resources/ProductResource/Pages/EditProduct.php
T
Malek TellissiandClaude Sonnet 4.6 74b47193d9
Deploy / deploy (push) Successful in 15s
Hide 'publish to Shopify' button if any variant already has a shopify_variant_id
Products imported before shopify_product_id column was added have null
there, but their variants already carry shopify_variant_id — that's
enough to know the product exists in Shopify. Running shopify:import
once will backfill shopify_product_id for all existing products.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-12 17:25:53 +02:00

201 lines
6.8 KiB
PHP

<?php
namespace App\Filament\User\Resources\ProductResource\Pages;
use App\Filament\User\Resources\ProductResource;
use App\Models\ProductSize;
use App\Models\Recipe;
use App\Services\ShopifyService;
use Filament\Actions;
use Filament\Notifications\Notification;
use Filament\Resources\Pages\EditRecord;
class EditProduct extends EditRecord
{
protected static string $resource = ProductResource::class;
protected function getHeaderActions(): array
{
return [
Actions\Action::make('shopify-create-product')
->label('In Shopify veröffentlichen')
->icon('heroicon-o-arrow-up-tray')
->color('warning')
->requiresConfirmation()
->modalHeading('Produkt in Shopify anlegen')
->modalDescription('Erstellt das Produkt mit allen Varianten in Shopify und verknüpft die IDs.')
->visible(fn () =>
! $this->record->shopify_product_id &&
! ProductSize::where('product_id', $this->record->id)
->whereNotNull('shopify_variant_id')
->exists()
)
->action(fn () => $this->createProductInShopify()),
Actions\Action::make('rezept-versionen')
->label('Rezept-Versionen')
->icon('heroicon-o-clock')
->color('gray')
->url(fn () => ProductResource::getUrl('recipe-history', ['record' => $this->record])),
Actions\DeleteAction::make(),
];
}
protected function afterSave(): void
{
$this->syncSizes();
$this->syncRecipe();
}
protected function syncSizes(): void
{
$product = $this->record;
$incoming = collect($this->data['sizes'] ?? []);
$keepIds = [];
foreach ($incoming as $size) {
if (empty($size['packing_id'])) {
continue;
}
$gtin = preg_replace('/\D/', '', (string) ($size['gtin'] ?? ''));
$ps = ProductSize::updateOrCreate(
[
'product_id' => $product->id,
'packing_id' => (int) $size['packing_id'],
],
[
'price' => $size['price'],
'sku' => $size['sku'] ?: null,
'gtin' => $gtin !== '' ? $gtin : null,
'description' => $size['description'] ?? null,
]
);
$keepIds[] = $ps->id;
}
// Only delete local-only sizes (no shopify_variant_id) that were removed
ProductSize::where('product_id', $product->id)
->whereNotIn('id', $keepIds)
->whereNull('shopify_variant_id')
->delete();
}
protected function syncRecipe(): void
{
$product = $this->record;
$spices = $this->data['recipe_spices'] ?? [];
if (empty($spices)) {
return;
}
$newPivot = collect($spices)->mapWithKeys(fn ($s) => [
(int) $s['spice_id'] => (float) $s['amount_in_percent'],
])->toArray();
$current = $product->recipe;
if ($current) {
$current->load('spices');
$existingPivot = $current->spices->mapWithKeys(fn ($s) => [
$s->id => (float) $s->pivot->amount_in_percent,
])->toArray();
if ($existingPivot == $newPivot) {
return;
}
}
$nextVersion = Recipe::where('product_id', $product->id)->max('version') + 1;
$recipe = Recipe::create([
'name' => $product->title,
'product_id' => $product->id,
'version' => $nextVersion,
]);
$recipe->spices()->sync(
collect($newPivot)->mapWithKeys(fn ($pct, $id) => [
$id => ['amount_in_percent' => $pct],
])->toArray()
);
$product->update(['recipes_id' => $recipe->id]);
}
public function createProductInShopify(): void
{
$product = $this->record;
if ($product->shopify_product_id) {
Notification::make()->title('Bereits in Shopify verknüpft')->warning()->send();
return;
}
$sizes = ProductSize::with('packing')->where('product_id', $product->id)->get();
try {
$result = app(ShopifyService::class)->createProduct($product, $sizes);
$product->update(['shopify_product_id' => $result['shopify_product_id']]);
foreach ($result['variants'] as $packingId => $variantId) {
ProductSize::where('product_id', $product->id)
->where('packing_id', $packingId)
->update(['shopify_variant_id' => $variantId]);
}
Notification::make()->title('Produkt in Shopify erstellt')->success()->send();
$this->redirect(ProductResource::getUrl('edit', ['record' => $product]));
} catch (\Throwable $e) {
Notification::make()->title('Shopify-Fehler')->body($e->getMessage())->danger()->send();
}
}
public function publishVariantToShopify(int $sizeId): void
{
$size = ProductSize::with('packing')->find($sizeId);
$product = $this->record;
if (! $size || $size->shopify_variant_id || ! $product->shopify_product_id) {
Notification::make()
->title('Nicht möglich')
->body($product->shopify_product_id
? 'Variante bereits in Shopify verknüpft.'
: 'Produkt hat keine Shopify-ID — zuerst shopify:import ausführen.')
->warning()
->send();
return;
}
try {
$variantId = app(ShopifyService::class)->createVariant($product->shopify_product_id, $size);
$size->update(['shopify_variant_id' => $variantId]);
Notification::make()->title('Variante in Shopify veröffentlicht')->success()->send();
$this->redirect(ProductResource::getUrl('edit', ['record' => $product]));
} catch (\Throwable $e) {
Notification::make()->title('Shopify-Fehler')->body($e->getMessage())->danger()->send();
}
}
public function syncVariantWithShopify(int $sizeId): void
{
$size = ProductSize::find($sizeId);
if (! $size || ! $size->shopify_variant_id) {
Notification::make()->title('Keine Shopify-Verknüpfung')->warning()->send();
return;
}
try {
app(ShopifyService::class)->updateVariant($size);
Notification::make()->title('Shopify synchronisiert')->success()->send();
} catch (\Throwable $e) {
Notification::make()->title('Shopify-Fehler')->body($e->getMessage())->danger()->send();
}
}
}