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>
223 lines
8.0 KiB
PHP
223 lines
8.0 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Woo;
|
|
|
|
use App\Models\WooCategory;
|
|
use App\Models\WooProduct;
|
|
use App\Models\WooStore;
|
|
use App\Support\WooHtml;
|
|
|
|
class CatalogWriteService
|
|
{
|
|
public function __construct(
|
|
private PluginClient $client,
|
|
private ProductIngestService $productIngest,
|
|
private CategoryIngestService $categoryIngest,
|
|
) {}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
public function createProduct(WooStore $store, array $input): WooProduct
|
|
{
|
|
$payload = $this->productPayload($input);
|
|
$response = $this->client->post($store, 'products', $payload);
|
|
|
|
if (is_array($response) && isset($response['id'])) {
|
|
return $this->productIngest->ingest($store, $response);
|
|
}
|
|
|
|
return $this->createLocalProduct($store, $payload);
|
|
}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
public function updateProduct(WooProduct $product, array $input): WooProduct
|
|
{
|
|
$product->loadMissing('store');
|
|
$store = $product->store;
|
|
abort_if(! $store, 404);
|
|
|
|
$payload = $this->productPayload($input);
|
|
$response = $this->client->put($store, 'products/'.$product->external_id, $payload);
|
|
|
|
if (is_array($response) && isset($response['id'])) {
|
|
return $this->productIngest->ingest($store, $response);
|
|
}
|
|
|
|
$product->update($this->localProductAttributes($payload));
|
|
|
|
return $product->fresh();
|
|
}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
public function createCategory(WooStore $store, array $input): WooCategory
|
|
{
|
|
$payload = $this->categoryPayload($input);
|
|
$response = $this->client->post($store, 'categories', $payload);
|
|
|
|
if (is_array($response) && isset($response['id'])) {
|
|
return $this->categoryIngest->ingest($store, $response);
|
|
}
|
|
|
|
return $this->createLocalCategory($store, $payload);
|
|
}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
public function updateCategory(WooCategory $category, array $input): WooCategory
|
|
{
|
|
$category->loadMissing('store');
|
|
$store = $category->store;
|
|
abort_if(! $store, 404);
|
|
|
|
$payload = $this->categoryPayload($input);
|
|
$response = $this->client->put($store, 'categories/'.$category->external_id, $payload);
|
|
|
|
if (is_array($response) && isset($response['id'])) {
|
|
return $this->categoryIngest->ingest($store, $response);
|
|
}
|
|
|
|
$category->update($this->localCategoryAttributes($payload));
|
|
|
|
return $category->fresh();
|
|
}
|
|
|
|
public function deleteCategory(WooCategory $category): bool
|
|
{
|
|
$category->loadMissing('store');
|
|
$store = $category->store;
|
|
abort_if(! $store, 404);
|
|
|
|
if ($this->client->delete($store, 'categories/'.$category->external_id)) {
|
|
$category->delete();
|
|
|
|
return true;
|
|
}
|
|
|
|
$category->delete();
|
|
|
|
return true;
|
|
}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
private function productPayload(array $input): array
|
|
{
|
|
$categories = collect($input['category_external_ids'] ?? [])
|
|
->map(fn ($id) => ['id' => (int) $id])
|
|
->filter(fn (array $row) => $row['id'] > 0)
|
|
->values()
|
|
->all();
|
|
|
|
return array_filter([
|
|
'name' => (string) ($input['name'] ?? ''),
|
|
'sku' => $input['sku'] ?? null,
|
|
'status' => (string) ($input['status'] ?? WooProduct::STATUS_DRAFT),
|
|
'regular_price' => $this->majorPrice($input['regular_price'] ?? null),
|
|
'sale_price' => $this->majorPrice($input['sale_price'] ?? null),
|
|
'manage_stock' => (bool) ($input['manage_stock'] ?? false),
|
|
'stock_quantity' => isset($input['stock_quantity']) ? (int) $input['stock_quantity'] : null,
|
|
'short_description' => WooHtml::sanitize($input['short_description'] ?? null),
|
|
'description' => WooHtml::sanitize($input['description'] ?? null),
|
|
'categories' => $categories,
|
|
'images' => $input['images'] ?? null,
|
|
], fn ($value) => $value !== null && $value !== '');
|
|
}
|
|
|
|
/** @param array<string, mixed> $input */
|
|
private function categoryPayload(array $input): array
|
|
{
|
|
$parent = (int) ($input['parent_external_id'] ?? 0);
|
|
|
|
return array_filter([
|
|
'name' => (string) ($input['name'] ?? ''),
|
|
'slug' => $input['slug'] ?? null,
|
|
'description' => WooHtml::sanitize($input['description'] ?? null),
|
|
'parent' => $parent > 0 ? $parent : 0,
|
|
], fn ($value) => $value !== null && $value !== '');
|
|
}
|
|
|
|
/** @param array<string, mixed> $payload */
|
|
private function createLocalProduct(WooStore $store, array $payload): WooProduct
|
|
{
|
|
$externalId = (int) (WooProduct::query()->where('woo_store_id', $store->id)->max('external_id') ?? 0) + 1;
|
|
|
|
return WooProduct::query()->create(array_merge(
|
|
$this->localProductAttributes($payload),
|
|
[
|
|
'woo_store_id' => $store->id,
|
|
'external_id' => $externalId,
|
|
'slug' => \Illuminate\Support\Str::slug((string) ($payload['name'] ?? 'product')).'-'.$externalId,
|
|
],
|
|
));
|
|
}
|
|
|
|
/** @param array<string, mixed> $payload */
|
|
private function createLocalCategory(WooStore $store, array $payload): WooCategory
|
|
{
|
|
$externalId = (int) (WooCategory::query()->where('woo_store_id', $store->id)->max('external_id') ?? 0) + 1;
|
|
$parent = (int) ($payload['parent'] ?? 0);
|
|
|
|
return WooCategory::query()->create(array_merge(
|
|
$this->localCategoryAttributes($payload),
|
|
[
|
|
'woo_store_id' => $store->id,
|
|
'external_id' => $externalId,
|
|
'parent_external_id' => $parent > 0 ? $parent : null,
|
|
'slug' => (string) ($payload['slug'] ?? \Illuminate\Support\Str::slug((string) ($payload['name'] ?? 'category')).'-'.$externalId),
|
|
],
|
|
));
|
|
}
|
|
|
|
/** @param array<string, mixed> $payload */
|
|
private function localProductAttributes(array $payload): array
|
|
{
|
|
$categoryIds = collect($payload['categories'] ?? [])
|
|
->map(fn (array $row) => (int) ($row['id'] ?? 0))
|
|
->filter(fn (int $id) => $id > 0)
|
|
->values()
|
|
->all();
|
|
|
|
return [
|
|
'name' => (string) ($payload['name'] ?? 'Product'),
|
|
'sku' => $payload['sku'] ?? null,
|
|
'status' => (string) ($payload['status'] ?? WooProduct::STATUS_DRAFT),
|
|
'regular_price_minor' => $this->minorPrice($payload['regular_price'] ?? null),
|
|
'sale_price_minor' => $this->minorPrice($payload['sale_price'] ?? null),
|
|
'manage_stock' => (bool) ($payload['manage_stock'] ?? false),
|
|
'stock_quantity' => isset($payload['stock_quantity']) ? (int) $payload['stock_quantity'] : null,
|
|
'short_description' => $payload['short_description'] ?? null,
|
|
'description' => $payload['description'] ?? null,
|
|
'category_external_ids' => $categoryIds,
|
|
'images' => $payload['images'] ?? null,
|
|
];
|
|
}
|
|
|
|
/** @param array<string, mixed> $payload */
|
|
private function localCategoryAttributes(array $payload): array
|
|
{
|
|
$parent = (int) ($payload['parent'] ?? 0);
|
|
|
|
return [
|
|
'name' => (string) ($payload['name'] ?? 'Category'),
|
|
'slug' => (string) ($payload['slug'] ?? \Illuminate\Support\Str::slug((string) ($payload['name'] ?? 'category'))),
|
|
'description' => $payload['description'] ?? null,
|
|
'parent_external_id' => $parent > 0 ? $parent : null,
|
|
];
|
|
}
|
|
|
|
private function majorPrice(mixed $value): ?string
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
return number_format(((float) $value), 2, '.', '');
|
|
}
|
|
|
|
private function minorPrice(mixed $value): ?int
|
|
{
|
|
if ($value === null || $value === '') {
|
|
return null;
|
|
}
|
|
|
|
return (int) round(((float) $value) * 100);
|
|
}
|
|
}
|