Deploy Ladill Woo Manager / deploy (push) Successful in 1m16s
Stop stripping tags on sync so paragraphs and bold book titles round-trip to Woo. Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
3.6 KiB
PHP
101 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Woo;
|
|
|
|
use App\Models\WooProduct;
|
|
use App\Models\WooStore;
|
|
use App\Support\WooHtml;
|
|
use Carbon\Carbon;
|
|
|
|
class ProductIngestService
|
|
{
|
|
/** @param array<string, mixed> $payload */
|
|
public function ingest(WooStore $store, array $payload): WooProduct
|
|
{
|
|
$externalId = (int) ($payload['id'] ?? 0);
|
|
if ($externalId === 0) {
|
|
throw new \InvalidArgumentException('WooCommerce product id missing.');
|
|
}
|
|
|
|
$regular = $this->priceMinor($payload['regular_price'] ?? null);
|
|
$sale = $this->priceMinor($payload['sale_price'] ?? null);
|
|
|
|
$images = collect((array) ($payload['images'] ?? []))
|
|
->map(fn (array $image, int $index) => [
|
|
'id' => (int) ($image['id'] ?? 0),
|
|
'src' => (string) ($image['src'] ?? ''),
|
|
'alt' => (string) ($image['alt'] ?? ''),
|
|
'position' => (int) ($image['position'] ?? $index),
|
|
])
|
|
->filter(fn (array $image) => $image['src'] !== '' || $image['id'] > 0)
|
|
->sortBy('position')
|
|
->values()
|
|
->all();
|
|
|
|
$categoryIds = collect((array) ($payload['categories'] ?? []))
|
|
->map(fn (array $category) => (int) ($category['id'] ?? 0))
|
|
->filter(fn (int $id) => $id > 0)
|
|
->values()
|
|
->all();
|
|
|
|
return WooProduct::query()->updateOrCreate(
|
|
[
|
|
'woo_store_id' => $store->id,
|
|
'external_id' => $externalId,
|
|
],
|
|
[
|
|
'name' => (string) ($payload['name'] ?? 'Product'),
|
|
'slug' => (string) ($payload['slug'] ?? 'product-'.$externalId),
|
|
'sku' => $this->nullableString($payload['sku'] ?? null),
|
|
'status' => (string) ($payload['status'] ?? WooProduct::STATUS_DRAFT),
|
|
'type' => (string) ($payload['type'] ?? 'simple'),
|
|
'regular_price_minor' => $regular,
|
|
'sale_price_minor' => $sale,
|
|
'stock_quantity' => isset($payload['stock_quantity']) ? (int) $payload['stock_quantity'] : null,
|
|
'manage_stock' => (bool) ($payload['manage_stock'] ?? false),
|
|
'category_external_ids' => $categoryIds,
|
|
'images' => $images,
|
|
'short_description' => WooHtml::sanitize($payload['short_description'] ?? null),
|
|
'description' => WooHtml::sanitize($payload['description'] ?? null),
|
|
'wc_updated_at' => $this->updatedAt($payload),
|
|
'metadata' => [
|
|
'permalink' => $payload['permalink'] ?? null,
|
|
'featured' => (bool) ($payload['featured'] ?? false),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
public function delete(WooStore $store, int $externalId): void
|
|
{
|
|
WooProduct::query()
|
|
->where('woo_store_id', $store->id)
|
|
->where('external_id', $externalId)
|
|
->delete();
|
|
}
|
|
|
|
private function priceMinor(mixed $value): ?int
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
return (int) round(((float) $value) * 100);
|
|
}
|
|
|
|
/** @param array<string, mixed> $payload */
|
|
private function updatedAt(array $payload): ?Carbon
|
|
{
|
|
$raw = $payload['date_modified_gmt'] ?? $payload['date_modified'] ?? null;
|
|
|
|
return is_string($raw) && $raw !== '' ? Carbon::parse($raw) : null;
|
|
}
|
|
|
|
private function nullableString(mixed $value): ?string
|
|
{
|
|
$text = trim((string) $value);
|
|
|
|
return $text === '' ? null : $text;
|
|
}
|
|
}
|