gnikyt/basic-shopify-api
Tested Shopify API wrapper for PHP using Guzzle. Supports REST and GraphQL (sync/async), OAuth and private apps, rate limiting, retries, pagination, and helpers for install/authorize URLs, HMAC validation, call limits, middleware, and storage.
Installation
composer require gnikyt/basic-shopify-api
Register the service provider in config/app.php:
'providers' => [
// ...
Gnikyt\ShopifyApi\ShopifyServiceProvider::class,
],
Configuration Publish the config file:
php artisan vendor:publish --provider="Gnikyt\ShopifyApi\ShopifyServiceProvider" --tag="config"
Update .env with your Shopify API credentials:
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SHOPIFY_STORE_DOMAIN=your-store.myshopify.com
SHOPIFY_ACCESS_TOKEN=your_access_token
First Use Case: Fetching Products (PHP 8.1 Iterables)
use Gnikyt\ShopifyApi\Facades\Shopify;
// Works with PHP 8.1 iterable signature
foreach (Shopify::products()->get() as $product) {
// $product is now properly typed as an iterable item
echo $product->title;
}
REST API Usage Use the fluent facade methods for common operations:
// Create a product
$product = Shopify::products()->create([
'title' => 'New Product',
'body_html' => '<strong>Description</strong>',
'vendor' => 'Acme Corp',
]);
// Update a product
Shopify::products()->update($product->id, ['title' => 'Updated Title']);
// Delete a product
Shopify::products()->delete($product->id);
GraphQL Queries (Private App Support) Execute custom GraphQL queries, including properly documented private app queries:
$query = '
query {
products(first: 10) {
edges {
node {
title
id
}
}
}
}
';
$result = Shopify::graphql()->query($query);
// Private app queries (now fully documented)
$privateQuery = '
query {
privateMetafields {
edges {
node {
key
}
}
}
}
';
$privateResult = Shopify::graphql()->query($privateQuery, ['isPrivate' => true]);
Pagination Handle paginated responses with iterable support:
$products = Shopify::products()->get(['limit' => 50]);
foreach ($products as $product) {
// Process each product
}
// Or use nextPage() for full pagination
while ($products->hasMorePages()) {
$products = $products->nextPage();
foreach ($products as $product) {
// Process next batch
}
}
Webhooks Register and manage webhooks with signature verification:
// Register a webhook
Shopify::webhooks()->register(
'orders/create',
'https://your-app.com/webhooks/shopify',
['topic' => 'orders']
);
// Verify webhook signatures
$isValid = Shopify::webhooks()->verify($request);
Http client or Queue for async operations.$products = Cache::remember('shopify_products', now()->addHours(1), function () {
return Shopify::products()->get();
});
try {
$product = Shopify::products()->get($id);
} catch (\Gnikyt\ShopifyApi\Exceptions\ShopifyException $e) {
Log::error($e->getMessage());
abort(500, 'Failed to fetch product');
}
foreach loops directly on responses:
foreach (Shopify::products()->get() as $product) {
// $product is now properly typed
}
Authentication Issues
SHOPIFY_ACCESS_TOKEN is correct and has the required permissions.SHOPIFY_API_KEY and SHOPIFY_API_SECRET with OAuth flow.Shopify::setDebug(true); // Logs raw API responses
Rate Limiting
use Gnikyt\ShopifyApi\Exceptions\RateLimitExceededException;
try {
$response = Shopify::products()->get();
} catch (RateLimitExceededException $e) {
sleep($e->getRetryAfter());
retry();
}
GraphQL Schema Changes
Shopify::graphql()->introspect() method to inspect the schema dynamically.isPrivate flag when needed.Webhook Delays
if (Shopify::webhooks()->verify($request) && $request->hasValidSignature()) {
$order = $request->json()->get('order');
// Process only if not already handled
if (!Order::where('shopify_id', $order['id'])->exists()) {
// Handle order
}
}
Enable Debug Mode:
Shopify::setDebug(true);
Check Laravel logs for raw API responses.
Log API Calls:
Use Laravel’s tap to log inputs/outputs:
Shopify::products()->get()->tap(function ($products) {
Log::debug('Fetched products:', $products->toArray());
});
PHP 8.1 Iterables Debugging: Ensure your loops are compatible with the new iterable signature:
// Works with PHP 8.1
foreach (Shopify::products()->get() as $product) {
Log::debug('Product:', $product->title);
}
Custom Endpoints Extend the API client for unsupported endpoints:
use Gnikyt\ShopifyApi\ShopifyClient;
$client = new ShopifyClient();
$response = $client->request('GET', '/admin/api/2023-07/custom_endpoint', []);
Middleware Add custom middleware to the Guzzle client:
Shopify::extend(function ($client) {
$client->getEmitter()->attach(
new \GuzzleHttp\Middleware::tap(function ($request) {
// Modify request
})
);
});
Model Bindings Create Eloquent models with accessors for Shopify data:
class ShopifyProduct extends Model
{
public function getTitleAttribute($value)
{
return ucfirst($value);
}
public static function fromShopify($data)
{
return static::create($data);
}
}
PHP 8.1 Iterables in Collections
$products = Shopify::products()->get();
foreach ($products as $product) {
// $product is now properly typed
}
->all() or ->toArray() if needed:$productsArray = Shopify::products()->get()->toArray();
How can I help you explore Laravel packages today?