- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
380 lines
10 KiB
PHP
380 lines
10 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Infrastructure\Api;
|
|
|
|
use App\Framework\Api\ApiRequestTrait;
|
|
use App\Framework\Http\Method;
|
|
use App\Framework\HttpClient\AuthConfig;
|
|
use App\Framework\HttpClient\ClientOptions;
|
|
use App\Framework\HttpClient\ClientResponse;
|
|
use App\Framework\HttpClient\CurlHttpClient;
|
|
use App\Framework\HttpClient\HttpClient;
|
|
|
|
final class ShopifyClient
|
|
{
|
|
use ApiRequestTrait;
|
|
|
|
private string $apiVersion;
|
|
|
|
/**
|
|
* Erstellt einen neuen Shopify API-Client
|
|
*
|
|
* @param string $shopDomain Die Shopify-Domain (z.B. 'my-store.myshopify.com')
|
|
* @param string $accessToken Das Zugriffstoken für die API
|
|
* @param string $apiVersion Die API-Version (z.B. '2023-10')
|
|
* @param HttpClient|null $httpClient Ein optionaler HTTP-Client
|
|
*/
|
|
public function __construct(
|
|
string $shopDomain,
|
|
string $accessToken,
|
|
string $apiVersion = '2024-04',
|
|
?HttpClient $httpClient = null
|
|
) {
|
|
$this->baseUrl = "https://{$shopDomain}/admin/api/{$apiVersion}";
|
|
$this->apiVersion = $apiVersion;
|
|
|
|
$this->defaultOptions = new ClientOptions(
|
|
auth: AuthConfig::custom([
|
|
'headers' => ['X-Shopify-Access-Token' => $accessToken],
|
|
])
|
|
);
|
|
|
|
$this->httpClient = $httpClient ?? new CurlHttpClient();
|
|
}
|
|
|
|
/**
|
|
* Ruft eine Liste aller Produkte ab
|
|
*
|
|
* @param array $options Optionale Parameter (limit, since_id, usw.)
|
|
* @return array Die Produktliste
|
|
*/
|
|
public function getProducts(array $options = []): array
|
|
{
|
|
$queryParams = $this->buildQueryParams($options);
|
|
$endpoint = 'products.json' . $queryParams;
|
|
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: $endpoint
|
|
);
|
|
|
|
return $this->decodeJson($response)['products'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft ein einzelnes Produkt ab
|
|
*
|
|
* @param int $productId Die Produkt-ID
|
|
* @return array Die Produktdaten
|
|
*/
|
|
public function getProduct(int $productId): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: "products/{$productId}.json"
|
|
);
|
|
|
|
return $this->decodeJson($response)['product'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Erstellt ein neues Produkt
|
|
*
|
|
* @param array $productData Die Produktdaten
|
|
* @return array Das erstellte Produkt
|
|
*/
|
|
public function createProduct(array $productData): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::POST,
|
|
endpoint: 'products.json',
|
|
data: ['product' => $productData]
|
|
);
|
|
|
|
return $this->decodeJson($response)['product'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Aktualisiert ein bestehendes Produkt
|
|
*
|
|
* @param int $productId Die Produkt-ID
|
|
* @param array $productData Die zu aktualisierenden Produktdaten
|
|
* @return array Das aktualisierte Produkt
|
|
*/
|
|
public function updateProduct(int $productId, array $productData): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::PUT,
|
|
endpoint: "products/{$productId}.json",
|
|
data: ['product' => $productData]
|
|
);
|
|
|
|
return $this->decodeJson($response)['product'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Löscht ein Produkt
|
|
*
|
|
* @param int $productId Die Produkt-ID
|
|
* @return bool Erfolg oder Misserfolg
|
|
*/
|
|
public function deleteProduct(int $productId): bool
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::DELETE,
|
|
endpoint: "products/{$productId}.json"
|
|
);
|
|
|
|
return $response->status->value === 200;
|
|
}
|
|
|
|
/**
|
|
* Ruft eine Liste aller Bestellungen ab
|
|
*
|
|
* @param array $options Optionale Parameter (limit, status, usw.)
|
|
* @return array Die Bestellungsliste
|
|
*/
|
|
public function getOrders(array $options = []): array
|
|
{
|
|
$queryParams = $this->buildQueryParams($options);
|
|
$endpoint = 'orders.json' . $queryParams;
|
|
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: $endpoint
|
|
);
|
|
|
|
return $this->decodeJson($response)['orders'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft eine einzelne Bestellung ab
|
|
*
|
|
* @param int $orderId Die Bestellungs-ID
|
|
* @return array Die Bestellungsdaten
|
|
*/
|
|
public function getOrder(int $orderId): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: "orders/{$orderId}.json"
|
|
);
|
|
|
|
return $this->decodeJson($response)['order'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Erstellt eine neue Bestellung
|
|
*
|
|
* @param array $orderData Die Bestellungsdaten
|
|
* @return array Die erstellte Bestellung
|
|
*/
|
|
public function createOrder(array $orderData): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::POST,
|
|
endpoint: 'orders.json',
|
|
data: ['order' => $orderData]
|
|
);
|
|
|
|
return $this->decodeJson($response)['order'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft Informationen über den Shop ab
|
|
*
|
|
* @return array Die Shop-Informationen
|
|
*/
|
|
public function getShopInfo(): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: 'shop.json'
|
|
);
|
|
|
|
return $this->decodeJson($response)['shop'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft eine Liste aller Kunden ab
|
|
*
|
|
* @param array $options Optionale Parameter (limit, since_id, usw.)
|
|
* @return array Die Kundenliste
|
|
*/
|
|
public function getCustomers(array $options = []): array
|
|
{
|
|
$queryParams = $this->buildQueryParams($options);
|
|
$endpoint = 'customers.json' . $queryParams;
|
|
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: $endpoint
|
|
);
|
|
|
|
return $this->decodeJson($response)['customers'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Erstellt einen neuen Kunden
|
|
*
|
|
* @param array $customerData Die Kundendaten
|
|
* @return array Der erstellte Kunde
|
|
*/
|
|
public function createCustomer(array $customerData): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::POST,
|
|
endpoint: 'customers.json',
|
|
data: ['customer' => $customerData]
|
|
);
|
|
|
|
return $this->decodeJson($response)['customer'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft einen einzelnen Kunden ab
|
|
*
|
|
* @param int $customerId Die Kunden-ID
|
|
* @return array Die Kundendaten
|
|
*/
|
|
public function getCustomer(int $customerId): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: "customers/{$customerId}.json"
|
|
);
|
|
|
|
return $this->decodeJson($response)['customer'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Erstellt einen neuen Webhook
|
|
*
|
|
* @param string $topic Das Webhook-Thema (z.B. 'orders/create')
|
|
* @param string $address Die URL, die aufgerufen werden soll
|
|
* @param string $format Das Format (JSON oder XML)
|
|
* @return array Die Webhook-Daten
|
|
*/
|
|
public function createWebhook(string $topic, string $address, string $format = 'json'): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::POST,
|
|
endpoint: 'webhooks.json',
|
|
data: [
|
|
'webhook' => [
|
|
'topic' => $topic,
|
|
'address' => $address,
|
|
'format' => $format,
|
|
],
|
|
]
|
|
);
|
|
|
|
return $this->decodeJson($response)['webhook'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft alle Webhooks ab
|
|
*
|
|
* @return array Die Liste der Webhooks
|
|
*/
|
|
public function getWebhooks(): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: 'webhooks.json'
|
|
);
|
|
|
|
return $this->decodeJson($response)['webhooks'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Sucht Produkte anhand von Suchkriterien
|
|
*
|
|
* @param string $query Die Suchanfrage
|
|
* @param array $options Zusätzliche Optionen
|
|
* @return array Die gefundenen Produkte
|
|
*/
|
|
public function searchProducts(string $query, array $options = []): array
|
|
{
|
|
$options = array_merge($options, ['query' => $query]);
|
|
$queryParams = $this->buildQueryParams($options);
|
|
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: 'products/search.json' . $queryParams
|
|
);
|
|
|
|
return $this->decodeJson($response)['products'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Ruft Metafields für eine Ressource ab
|
|
*
|
|
* @param string $resourceType Der Ressourcentyp (product, customer, usw.)
|
|
* @param int $resourceId Die Ressourcen-ID
|
|
* @return array Die Metafields
|
|
*/
|
|
public function getMetafields(string $resourceType, int $resourceId): array
|
|
{
|
|
$response = $this->sendRequest(
|
|
method: Method::GET,
|
|
endpoint: "{$resourceType}s/{$resourceId}/metafields.json"
|
|
);
|
|
|
|
return $this->decodeJson($response)['metafields'] ?? [];
|
|
}
|
|
|
|
/**
|
|
* Verarbeitet Rate-Limiting-Informationen aus den Headern
|
|
*
|
|
* @param ClientResponse $response Die API-Antwort
|
|
* @return array Rate-Limiting-Informationen
|
|
*/
|
|
public function getRateLimitInfo(ClientResponse $response): array
|
|
{
|
|
$headers = $response->headers->all();
|
|
|
|
return [
|
|
'limit' => (int)($headers['X-Shopify-Shop-Api-Call-Limit'][0] ?? 0),
|
|
'remaining' => isset($headers['X-Shopify-Shop-Api-Call-Limit'][0])
|
|
? $this->parseRemainingCalls($headers['X-Shopify-Shop-Api-Call-Limit'][0])
|
|
: null,
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Baut Query-Parameter aus einem Options-Array
|
|
*
|
|
* @param array $options Die Optionen
|
|
* @return string Die Query-Parameter als String
|
|
*/
|
|
private function buildQueryParams(array $options): string
|
|
{
|
|
if (empty($options)) {
|
|
return '';
|
|
}
|
|
|
|
return '?' . http_build_query($options);
|
|
}
|
|
|
|
/**
|
|
* Parst die verbleibenden API-Aufrufe aus dem Header
|
|
*
|
|
* @param string $limitHeader Der Header-Wert
|
|
* @return int|null Die verbleibenden Aufrufe
|
|
*/
|
|
private function parseRemainingCalls(string $limitHeader): ?int
|
|
{
|
|
if (preg_match('/^(\d+)\/(\d+)$/', $limitHeader, $matches)) {
|
|
$current = (int)$matches[1];
|
|
$limit = (int)$matches[2];
|
|
|
|
return $limit - $current;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|