Skip to content

Commit 7c6f921

Browse files
author
kshitij verma
committed
added public app api kit
1 parent a1d3e15 commit 7c6f921

File tree

7 files changed

+354
-380
lines changed

7 files changed

+354
-380
lines changed

README.md

Lines changed: 113 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# SHOPIFY API PHP SDK
2-
PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/apps#custom-apps), [Public App](https://shopify.dev/concepts/apps#public-apps) and [Private App](https://shopify.dev/concepts/apps#private-apps) using [RESTApi](https://shopify.dev/docs/admin-api/rest/reference) and [Graphql](https://shopify.dev/docs/admin-api/graphql/reference).
2+
PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/apps#custom-apps), [Public App](https://shopify.dev/concepts/apps#public-apps) and [Private App](https://shopify.dev/concepts/apps#private-apps) using [REST Api](https://shopify.dev/docs/admin-api/rest/reference) and [Graphql](https://shopify.dev/docs/admin-api/graphql/reference).
33
* Call GET, POST, PUT and DELETE RestApi method.
44
* Process GraphQL Admin API for [Query root](https://shopify.dev/docs/admin-api/graphql/reference/queryroot) and [Mutations](https://shopify.dev/docs/admin-api/graphql/reference/mutation).
55
* Queryroot is used to get resources and mutations is used to update resources (products/orders/customers).
@@ -20,7 +20,7 @@ PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/
2020
## Getting started
2121
### Initialize the client
2222
#### 1. For Private App
23-
* To create instance of `Client` class, you need `shop`, `api_key`, `password` of private app, `api_params` is an array to pass api version with `YYYY-DD/unstable` format otherwise, Api latest version will be assigned.
23+
* To create instance of `Client` class, need `shop`, `api_key`, `password` of private app, `api_params` is an array to pass api version with `YYYY-DD/unstable` format otherwise, Api latest version will be assigned.
2424
2525
```
2626
<?php
@@ -30,18 +30,63 @@ PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/
3030
$api_params['version'] = '2019-10';
3131
$client = new Shopify\PrivateApp($shop, $api_key, $password, $api_params);
3232
```
33-
#### 2. For Public/Custom App (under development)
34-
* To create instance of `Client` class, you need `shop`, `api_key`, `api_secret_key` of private app, `api_params` is an array to pass api version with `YYYY-DD/unstable` format otherwise, Api latest version will be assigned.
33+
#### 2. For Public App
34+
* To create instance of `Client` class, need `shop`, `api_key`, `api_secret_key` of public app.
3535
3636
```
3737
<?php
3838
require(__DIR__ . '/../vendor/autoload.php');
3939
use Shopify\PuplicApp;
4040
4141
$api_params['version'] = '2019-10';
42-
$client = new Shopify\PrivateApp($shop, $api_key, $api_secret_key, $api_params);
42+
$client = new Shopify\PublicApp($shop, $api_key, $api_secret_key, $api_params);
4343
```
44+
* Prepare authorise url to install public app and get `access_token`
45+
```
46+
if(isset($_GET['code']))
47+
{
48+
//get access_token after authorization of public app
49+
if($access_token = $client->getAccessToken($_GET)){
50+
//set access_token for api call
51+
$client->setAccessToken($access_token);
52+
$response = $client->call('GET','products',['limit'=>250]);
53+
}
54+
}else{
55+
//$redirect_uri (mention in App URL in your public app)
56+
$redirect_url= isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"?'https://':'http://';
57+
if ($_SERVER["SERVER_PORT"] != "80") {
58+
$redirect_url .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
59+
} else {
60+
$redirect_url .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
61+
}
4462
63+
header('Location: '.urldecode($client->prepareAuthorizeUrl($redirect_url)));
64+
}
65+
```
66+
#### 3. For Custom App (Under Development)
67+
Note: Oauth process is similar to public app. To authenticate with Shopify by using a custom app, you need to generate an [installation link](https://shopify.dev/tutorials/authenticate-a-custom-app-with-oauth#create-a-custom-app-and-generate-an-installation-link) from your Partner Dashboard.
68+
* To create instance of `Client` class, need `shop`, `api_key`, `api_secret_key` of custom app.
69+
70+
```
71+
<?php
72+
require(__DIR__ . '/../vendor/autoload.php');
73+
use Shopify\PuplicApp;
74+
75+
$api_params['version'] = '2019-10';
76+
$client = new Shopify\PublicApp($shop, $api_key, $api_secret_key, $api_params);
77+
```
78+
* Copy link from `Merchant install link` section in Custom App and run in browser then it redirect to your url provied provided while creating custom app.
79+
```
80+
if(isset($_GET['code']))
81+
{
82+
//get access_token after authorization of public app
83+
if($access_token = $client->getAccessToken($_GET)){
84+
//set access_token for api call
85+
$client->setAccessToken($access_token);
86+
$response = $client->call('GET','products',['limit'=>250]);
87+
}
88+
}
89+
```
4590
### Call REST Api
4691
* Get Products with limit 250 with `call()` function
4792
```
@@ -85,7 +130,15 @@ PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/
85130
}
86131
}
87132
```
88-
133+
Note:
134+
* For single attribute and field
135+
```
136+
attribute('id','gid://shopify/Product/1432379031652') and field('title')
137+
```
138+
* For multiple attributes and fields
139+
```
140+
attributes(['product_type','fragrance','limit'=>250]) and `fields(['title','description'])
141+
```
89142
Prepare query:
90143
91144
```
@@ -95,13 +148,12 @@ PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/
95148
$query = Query::query("");
96149
$query->fields('product');
97150
$query->product->attribute('id', "gid://shopify/Product/1432379031652");
98-
$query->product->field('title');
99-
$query->product->field('description');
151+
$query->product->fields(['title','description']);
100152
$graphqlString = $query->build();
101153
102154
```
103155
104-
Call GraphQL qith `callGraphql()` function:
156+
Call GraphQL with `callGraphql()` function:
105157
106158
```
107159
$response = $client->callGraphql($graphqlString);
@@ -138,7 +190,59 @@ PHP SDK helps to connect with shopify [Custom App](https://shopify.dev/concepts/
138190
```
139191
$response = $client->callGraphql($graphqlString);
140192
```
193+
* Cursor Pagination with graphQL
141194
195+
```
196+
{
197+
products(first: 250) {
198+
edges {
199+
cursor
200+
node {
201+
id
202+
}
203+
}
204+
}
205+
}
206+
```
207+
Prepare Query and `$reserve_query` variable to store query for next page call:
208+
209+
```
210+
<?php
211+
use rdx\graphqlquery\Query;
212+
213+
$query = Query::query("");
214+
$query->fields('products');
215+
$query->products->attribute('first', 250);
216+
$query->products->field('edges');
217+
$query->products->edges->fields(['cursor','node']);
218+
$query->products->edges->node->fields(['title','description']);
219+
$reserve_query = $query;
220+
$graphqlString = $query->build();
221+
222+
```
223+
Call GraphQL qith `callGraphql()` function. And
224+
225+
```
226+
$response = $client->callGraphql($graphqlString);
227+
228+
```
229+
If you continue repeating this step with the cursor value of the last product in each response you get until your response is empty. So need to check `cursor` index available in last array of `$response` then repeat call by adding cursor value with `after` attribute in products field.
230+
231+
```
232+
if(isset($response['data']['products']['edges']) && $last_array = end($response['data']['products']['edges']))
233+
{
234+
if(isset($last_array['cursor']))
235+
{
236+
//assign cursor value in `last` attribute
237+
$query = $reserve_query->products->attribute('after', $last_array['cursor']);
238+
$graphqlString = $reserve_query->build();
239+
$next_response = $client->callGraphql($graphqlString);
240+
print_r($next_response);
241+
}
242+
}
243+
```
244+
245+
142246
### Error Handling
143247
144248
Below errors handled with `ApiException` Class

src/Client.php

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,13 @@ class Client implements ClientInterface
6969
* Shopify shared secret key for private app
7070
* @var string
7171
*/
72-
protected $shared_secret;
72+
protected $api_secret_key;
7373

74+
/**
75+
* access token for public app
76+
* @var string
77+
*/
78+
protected $access_token;
7479
/**
7580
* array('version')
7681
* @var array
@@ -141,7 +146,17 @@ public function call($method, $path , array $params = [])
141146
}else {
142147
$options['json'] = $params;
143148
}
144-
return $this->request($method,$url,$options);
149+
150+
$http_response = $this->request($method,$url,$options);
151+
if (strtoupper($method) === 'GET' && $http_response->getHeaderLine(self::PAGINATION_STRING)) {
152+
$this->next_page = $this->parseLinkString($http_response->getHeaderLine(self::PAGINATION_STRING),'next');
153+
$this->prev_page = $this->parseLinkString($http_response->getHeaderLine(self::PAGINATION_STRING),'previous');
154+
}
155+
if($http_response->getHeaderLine(self::API_CALL_RATE_LIMIT_HEADER)) {
156+
list($api_call_requested, $api_call_Limit) = explode('/', $http_response->getHeaderLine(self::API_CALL_RATE_LIMIT_HEADER));
157+
static::$wait_next_api_call = $api_call_requested / $api_call_Limit >= 0.8;
158+
}
159+
return \GuzzleHttp\json_decode($http_response->getBody()->getContents(),true);
145160
}
146161

147162
/**
@@ -158,18 +173,19 @@ public function callGraphql($query)
158173
if(isset($this->requestHeaders[self::GRAPHQL]) && is_array($this->requestHeaders[self::GRAPHQL])) {
159174
$options['headers'] = $this->requestHeaders[self::GRAPHQL];
160175
}
161-
162176
$options['body'] = $query;
163177
if(self::$wait_next_api_call)
164178
{
165179
usleep(1000000 * rand(3, 6));
166180
}
167-
$response = $this->request('POST', $url, $options);
181+
$http_response = $this->request('POST', $url, $options);
182+
$response = \GuzzleHttp\json_decode($http_response->getBody()->getContents(),true);
168183
if(isset($response['errors']))
169184
{
170185
$http_bad_request_code = 400;
171186
throw new ApiException(\GuzzleHttp\json_encode($response['errors']),$http_bad_request_code);
172187
}
188+
return $response;
173189
}
174190

175191
/**
@@ -185,32 +201,21 @@ public function request($method,$url,array $options)
185201
try
186202
{
187203
$client = new \GuzzleHttp\Client();
188-
$http_response = $client->request($method, $url, $options);
189-
$response_content = $http_response->getBody()->getContents();
190-
$response = [];
191-
if (strtoupper($method) === 'GET' && $http_response->getHeaderLine(self::PAGINATION_STRING)) {
192-
$this->next_page = $this->parseLinkString($http_response->getHeaderLine(self::PAGINATION_STRING),'next');
193-
$this->prev_page = $this->parseLinkString($http_response->getHeaderLine(self::PAGINATION_STRING),'previous');
194-
}
195-
196-
$response = \GuzzleHttp\json_decode($response_content,true);
197-
if($http_response->getHeaderLine(self::API_CALL_RATE_LIMIT_HEADER)) {
198-
list($api_call_requested, $api_call_Limit) = explode('/', $http_response->getHeaderLine(self::API_CALL_RATE_LIMIT_HEADER));
199-
static::$wait_next_api_call = $api_call_requested / $api_call_Limit >= 0.8;
200-
}
204+
return $client->request($method, $url, $options);
201205
}
202206
catch (RequestException $e)
203207
{
204-
$json_error = json_decode($e->getResponse()->getBody()->getContents(),true);
205-
if(isset($json_error['errors'])) {
206-
$error_message = $json_error['errors'];
208+
209+
if(!empty($e->getResponse()->getBody()->getContents()))
210+
{
211+
$json_error = json_decode($e->getResponse()->getBody()->getContents(),true);
212+
$error_message = isset($json_error['errors'])?$json_error['errors']:\GuzzleHttp\json_encode($json_error);
207213
}
208214
else {
209-
$error_message = $e->getResponse()->getBody()->getContents();
215+
$error_message = $e->getMessage();
210216
}
211217
throw new ApiException($error_message,$e->getCode());
212218
}
213-
return $response;
214219
}
215220

216221
/**

0 commit comments

Comments
 (0)