How to create pagination in shopify rest api using php - php

I created curl in php for use shopify product rest API. Now I want to create pagination in that.
How to create?
Link: "<https://{shop}.myshopify.com/admin/api/{version}/products.json?page_info={page_info}&limit={limit}>; rel={next}, <https://{shop}.myshopify.com/admin/api/{version}/products.json?page_info={page_info}&limit={limit}>; rel={previous}"
How to use Link ?
Thanks

Below function can help you. to fetch data using API in Php/Laravel
public function request($method,$url,$param = []){
$client = new \GuzzleHttp\Client();
$url = 'https://'.$this->username.':'.$this->password.'#'.$this->domain.'/admin/api/2019-10/'.$url;
$parameters = [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json'
]
];
if(!empty($param)){ $parameters['json'] = $param;}
$response = $client->request($method, $url,$parameters);
$responseHeaders = $response->getHeaders();
$tokenType = 'next';
if(array_key_exists('Link',$responseHeaders)){
$link = $responseHeaders['Link'][0];
$tokenType = strpos($link,'rel="next') !== false ? "next" : "previous";
$tobeReplace = ["<",">",'rel="next"',";",'rel="previous"'];
$tobeReplaceWith = ["","","",""];
parse_str(parse_url(str_replace($tobeReplace,$tobeReplaceWith,$link),PHP_URL_QUERY),$op);
$pageToken = trim($op['page_info']);
}
$rateLimit = explode('/', $responseHeaders["X-Shopify-Shop-Api-Call-Limit"][0]);
$usedLimitPercentage = (100*$rateLimit[0])/$rateLimit[1];
if($usedLimitPercentage > 95){sleep(5);}
$responseBody = json_decode($response->getBody(),true);
$r['resource'] = (is_array($responseBody) && count($responseBody) > 0) ? array_shift($responseBody) : $responseBody;
$r[$tokenType]['page_token'] = isset($pageToken) ? $pageToken : null;
return $r;
}
Example usage of this function
$product_ids = [];
$nextPageToken = null;
do{
$response = $shop->request('get','products.json?limit=250&page_info='.$nextPageToken);
foreach($response['resource'] as $product){
array_push($product_ids, $product['id']);
}
$nextPageToken = $response['next']['page_token'] ?? null;
}while($nextPageToken != null);
If you want to use graphQL api in php / laravel then below post can help you
How to request shopify graphql-admin-api from an api?

As of API version 2019-07 you do indeed need to use cursor based pagination.
First make a normal API call, include the header response.
Then in the header response you will see link : ... with rel next or previous.
Extract the page_info and then make another call with page_info.
The first call is something like this:
https://...:...#xyz.myshopify.com/admin/api/2019-10/products.json?limit=2&published_status=published
Then the second call is
https://...:...#xyz.myshopify.com/admin/api/2019-10/products.json?limit=2&page_info=asdfas1321asdf3as1f651saf61s3f1x32v1akjhfasdj
When you make the second call, remove any filers as the filters will be applied from the first call.
Btw: if your testing in a browser due to the way the link is in angle brackets, it will be hidden, so just view source code in your developer environment.
Reference: https://help.shopify.com/en/api/guides/paginated-rest-results

private $shop_url = '';
private $shop_user = '';
private $shop_password = '';
function __construct($shop) {
$this->shop_url = $shop->url;
$this->shop_user = $shop->user;
$this->shop_password = $shop->userpas;
$this->syncShopifyOrder($shop);
}
private function getOrderRequest($url){
$client = new \GuzzleHttp\Client();
$response = $client->request('GET', $url, [
'auth' => [$this->shop_user, $this->shop_password]
]);
if($response->getStatusCode() !== 200){
throw new OrderSyncException('Connection problem!');
}
$data = [];
$paginate_links = $response->getHeader('Link');
if($paginate_links){
$page_link = $paginate_links[0];
$links_arr = explode(",", $page_link);
if($links_arr){
$tobeReplace = ["<",">",'rel="next"',";",'rel="previous"'];
$tobeReplaceWith = ["","","",""];
foreach ($links_arr as $link) {
$link_type = strpos($link, 'rel="next') !== false ? "next" : "previous";
parse_str(parse_url(str_replace($tobeReplace, $tobeReplaceWith, $link), PHP_URL_QUERY), $op);
$data[$link_type] = trim($op['page_info']);
}
}
}
$order_data = $response->getBody()->getContents();
$data['all_orders'] = (json_decode($order_data))->orders;
return $data;
}
// Shopify Orders
private function syncShopifyOrder($shop)
{
$count = 0;
try{
if($shop){
$nextPageToken = null;
do{
$param = ($nextPageToken)? '&page_info='.$nextPageToken : '&status=any&fulfillment_status=any&order=created_at asc&created_at_min=2020-08-10T13:30:33+02:00';
$url = $this->shop_url . 'admin/api/2020-01/orders.json?limit=250'.$param;
$data = $this->getOrderRequest($url);
$all_orders = $data['all_orders'];
$nextPageToken = isset($data['next']) ? $data['next'] : null;
if($all_orders){
$count += count((array) $all_orders);
$this->bulkorderInsertShopify($shop, $all_orders);
}
}while($nextPageToken);
}else{
throw new OrderSyncException('You have not configured shop!');
}
$this->syncSuccessReport($shop, $count);
}catch(OrderSyncException $e) {
$this->syncErrorReport($shop, $count, $e->getMessage());
}catch(\Exception $e) {
$this->syncErrorReportAdmin($shop, $count, $e);
}
}

Related

Is it possible to update inventory_policy for all products using API in Shopify?

From my PHP application, I want to update the inventory_policy (continue/deny) of all products using API. Is there any way to do so without a loop?
I did not find any way to update it at once. Hence, I have updated it one by one. Please have the code below.
public function update_inventory_policy_for_all_item($inventory_policy, $page_info=null){
$response = $this->do_get("/admin/api/2021-04/products.json?limit=250".$page_info);
if ($response === FALSE)
{
return false;
}
$result_products = $response['body']['products'];
$headers = $response['headers'];
foreach($result_products as $shopify_product){
foreach($shopify_product['variants'] as $variant){
$variant_id = $variant['id'];
$data['variant'] = array(
'id' => $variant['id'],
'inventory_policy' => $inventory_policy,
);
$this->do_put("/admin/api/2021-04/variants/$variant_id.json", $data);
}
}
if(isset($headers['link'])) {
$links = explode(',', $headers['link']);
foreach($links as $link) {
$next_page = false;
if(strpos($link, 'rel="next"')) {
$next_page = $link;
}
}
if($next_page) {
preg_match('~<(.*?)>~', $next_page, $next);
$url_components = parse_url($next[1]);
parse_str($url_components['query'], $params);
$page_info = '&page_info=' . $params['page_info'];
$this->update_inventory_policy_for_all_item($inventory_policy, $page_info);
}
}
return true;
}

how to use dreamscape api in nodejs for check domain name

I have already work this in php. Here is my working code:
$request = array(
'DomainNames' => $domain_names
);
$response = dreamScapeAPI('DomainCheck', $request);
$available = false;
$alt_domains = array(); // Alternative to user's expected domain names
if (!is_soap_fault($response)) {
// Successfully checked the availability of the domains
if (isset($response->APIResponse->AvailabilityList)) {
$availabilityList = $response->APIResponse->AvailabilityList;
foreach ($availabilityList as $list) {
if ($list->Available){
if ($domain == $list->Item) {
$available = true; // user prefered domain found
}
else {
$alt_domains[] = $list->Item;
}
}
}
}
else {
$error = $response->APIResponse->Errors;
foreach ($error as $e) {
$api_error = $e->Message;
//echo $e->Item . ' - ' . $e->Message . '<br />';
}
}
}
function dreamScapeAPI($method, $data = null) {
$reseller_api_soap_client = "";
$soap_location = 'http://soap.secureapi.com.au/API-2.1';
$wsdl_location = 'http://soap.secureapi.com.au/wsdl/API-2.1.wsdl';
$authenticate = array();
$authenticate['AuthenticateRequest'] = array();
$authenticate['AuthenticateRequest']['ResellerID'] = '**';
$authenticate['AuthenticateRequest']['APIKey'] = '**';
//convert $authenticate to a soap variable
$authenticate['AuthenticateRequest'] = new SoapVar($authenticate['AuthenticateRequest'], SOAP_ENC_OBJECT);
$authenticate = new SoapVar($authenticate, SOAP_ENC_OBJECT);
$header = new SoapHeader($soap_location, 'Authenticate', $authenticate, false);
$reseller_api_soap_client = new SoapClient($wsdl_location, array('soap_version' => SOAP_1_2, 'cache_wsdl' => WSDL_CACHE_NONE));
$reseller_api_soap_client->__setSoapHeaders(array($header));
$prepared_data = $data != null ? array($data) : array();
try {
$response = $reseller_api_soap_client->__soapCall($method, $prepared_data);
} catch (SoapFault $response) { }
return $response;
}
I tried with this : https://github.com/dan-power/node-dreamscape
But domain search can not work properly. Mainly, I want it on nodejs using nodejs dreamscape api but this method is not available on nodejs.
Here is my working demo: click here

List Azure files and folders from File share snapshots with php

To list the files and folders from Azure Files can be done with this code:
function ListFolder($shareName, $path)
{
global $fileRestProxy;
$vMaxResultados = 5000;
$vNextMarker = "";
$listResult = null;
try
{
do
{
$options = new ListDirectoriesAndFilesOptions();
$options->setMaxResults($vMaxResultados);
$options->setMarker($vNextMarker);
$listResult = $fileRestProxy->ListDirectoriesAndFiles($shareName,$path,$options);
$vNextMarker = $listResult->getNextMarker();
} while ($vNextMarker != "");
}
catch (Exception $e)
{
$code = $e->getCode();
$error_message = $e->getMessage();
return "ERROR:$code:$error_message";
}
return $listResult;
}
But how is the sintaxis or method to the same with a snapshot from these Share?
This doesn't work:
function ListSnapshotFolder($shareName, $path, $snapshot)
{
global $fileRestProxy;
$vMaxResultados = 5000;
$vNextMarker = "";
$listResult = null;
try
{
do
{
$options = new ListDirectoriesAndFilesOptions();
$options->setMaxResults($vMaxResultados);
$options->setMarker($vNextMarker);
$shareFull = $shareName . "?snapshot=" . $snapshot;
$listResult = $fileRestProxy->ListDirectoriesAndFiles($shareFull,$path,$options);
$vNextMarker = $listResult->getNextMarker();
} while ($vNextMarker != "");
}
catch (Exception $e)
{
$code = $e->getCode();
$error_message = $e->getMessage();
return "ERROR:$code:$error_message";
}
return $listResult;
}
Is there any parameter in the $option object to add?
Or maybe the $shareFull must be created in some format?
$shareFull = $shareName . "?snapshot=" . $snapshot;
Thanks in advance.
I believe you have found a bug in the SDK. I looked up the source code here and there's no provision to provide sharesnapshot query string parameter in the options as well as the code does not even handle it.
public function listDirectoriesAndFilesAsync(
$share,
$path = '',
ListDirectoriesAndFilesOptions $options = null
) {
Validate::notNull($share, 'share');
Validate::canCastAsString($share, 'share');
Validate::canCastAsString($path, 'path');
$method = Resources::HTTP_GET;
$headers = array();
$postParams = array();
$queryParams = array();
$path = $this->createPath($share, $path);
if (is_null($options)) {
$options = new ListDirectoriesAndFilesOptions();
}
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_REST_TYPE,
'directory'
);
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_COMP,
'list'
);
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_PREFIX_LOWERCASE,
$options->getPrefix()
);
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_MARKER_LOWERCASE,
$options->getNextMarker()
);
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_MAX_RESULTS_LOWERCASE,
$options->getMaxResults()
);
$this->addOptionalQueryParam(
$queryParams,
Resources::QP_TIMEOUT,
$options->getTimeout()
);
$dataSerializer = $this->dataSerializer;
return $this->sendAsync(
$method,
$headers,
$queryParams,
$postParams,
$path,
Resources::STATUS_OK,
Resources::EMPTY_STRING,
$options
)->then(function ($response) use ($dataSerializer) {
$parsed = $dataSerializer->unserialize($response->getBody());
return ListDirectoriesAndFilesResult::create(
$parsed,
Utilities::getLocationFromHeaders($response->getHeaders())
);
}, null);
}
You may want to open up an issue here: https://github.com/Azure/azure-storage-php/issues and bring this to SDK team's attention.

502 Bad Gateway Foreach loop

hope for some help, basically i have a script that gets the latest posts from facebook users, and basically i check if there is any new post that is not available on my database, in case that this post is new, than i save it in my database along with the post id (this way i check if exist on DB).
But i have a issue with it, in my case i need to check a number of users, and this users keeps growing, in my case i have 400 users. If i go more thatn 100 users i get the 500 error of course, it is many requests.
So does someone have a ideia of how could i handle it?
My code: FarcebookParcer.php
public function facebook($id, $num) {
//Set your App ID and App Secret.
$appID = 'xxxxxxxxxx';
$appSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx';
//Create an access token using the APP ID and APP Secret.
$accessToken = $appID . '|' . $appSecret;
//Tie it all together to construct the URL
$url = "https://graph.facebook.com/$id/posts?fields=attachments,created_time&limit=$num&access_token=$accessToken";
if (Helper::get_http_response_code($url) != 200) {
return false;
}
//Make the API call
$opts = array(
'http' => array(
'method' => 'GET',
'timeout' => 120
)
);
$context = stream_context_create($opts);
$result = file_get_contents($url, false, $context);
//Decode the JSON result.
$dt = json_decode($result, true);
$posts = $dt;
return $posts;
}
CronController.php
public function socialfacebook() {
$facebook = SocialSnap::all();
$socialparser = new FacebookParser();
$appID = 'xxxxxxxxxxx';
$appSecret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
$accessToken = $appID . '|' . $appSecret;
set_time_limit(0);
foreach ($facebook as $key => $value) {
if ($value->facebook != NULL) {
$facebook_user = $socialparser->facebook_user(substr($value->facebook, 1));
$facebook_posts = $socialparser->facebook(substr($value->facebook, 1), 1);
//Check if valid url
if ($facebook_posts == false || $facebook_user == false) {
continue;
}
if (isset($facebook_posts['data'][0]['attachments']['data'][0]['target']['url']) && isset($facebook_posts['data'][0]['attachments']['data'][0]['description'])) {
SnapChat::where('facebook', $value->facebook)->update(['facebook_photo' => 'https://graph.facebook.com/'. substr($value->facebook, 1) . '/picture/?type=normal']);
$post_current = SocialSnap::where('id_social', $facebook_posts['data'][0]['id'])->first();
//return $post_current;
if ($post_current == NULL) {
$post = new SocialSnap;
$post->id_social = $facebook_posts['data'][0]['id'];
$post->id_snapchats = $value->id;
$post->date_social = isset($facebook_posts['data'][0]['created_time']) ? date("Y-m-d H:m:s", strtotime($facebook_posts['data'][0]['created_time'])) : "";
$post->type = 'facebook';
$post->url = $facebook_posts['data'][0]['attachments']['data'][0]['target']['url'];
$post->message = $facebook_posts['data'][0]['attachments']['data'][0]['description'];
if (isset($facebook_posts['data'][0]['attachments']['data'][0]['media']['image']['src'])) {
$post->image = $facebook_posts['data'][0]['attachments']['data'][0]['media']['image']['src'];
}
$post->save();
}
}
}
}
echo 'DONE';
}

Twitter access token request returns "Invalid or expired token"

I am using custom code to connect to Twitter and request an access token. For some reason when trying to post to "access_token" on the API it returns "invalid or expired token". The code is as follows (apart from a few external function calls and properties this should be sufficient to replicate the error):
public function authenticate($get,$return = false) {
session_start();
if (!isset($get['oauth_verifier'])){
// Step 1 - get a request token
$step1 = $this->processRequest('oauth/request_token',0,$this->pObj->getRedirectUrl().'?process=true');
parse_str($step1,$parts);
if ($parts['oauth_callback_confirmed'] !== 'true'){ die('Error with process'); }
$_SESSION['tw_secret'] = $parts['oauth_token_secret'];
// Step 2
$url = str_replace('1.1/','',$this->api_url);
header("Location: {$url}oauth/authenticate?oauth_token={$parts['oauth_token']}");
} else {
// Step 3
$this->o_token = $get['oauth_token'];
$this->o_secret = $_SESSION['tw_secret'];
$content['oauth_verifier'] = $get['oauth_verifier'];
$step3 = $this->processRequest('oauth/access_token',1,null,$content,'array');
}
}
// https://dev.twitter.com/docs/auth/creating-signature
private function generateSignature($oauth,$fullurl,$http_method,$content){
// Take params from url
$main_url = explode('?',$fullurl);
// Split the content
$contents = explode('&',$content);
$urls = array_merge(explode('&',$main_url[1]),$contents);
foreach ($urls as $param){
$bits = explode('=',$param);
if (strlen($bits[0])){
$oauth[$bits[0]] = rawurlencode($bits[1]);
}
}
ksort($oauth);
$string = http_build_query($oauth);
$new_string = strtoupper($http_method).'&'.urlencode($main_url[0]).'&'.urlencode(urldecode($string));
// The request_token request doesn't need a o_secret because it doesn't have one!
$sign_key = strstr($fullurl,'request_token') ? $this->c_secret.'&' : $this->c_secret.'&'.$this->o_secret;
return urlencode(base64_encode(hash_hmac('sha1',$new_string,$sign_key,true)));
}
public function processRequest($in_url,$test = false,$callback = null,$content = null, $content_type = 'json',$form_content_type = 'application/x-www-form-urlencoded'){
$method = 'GET';
// Twitter still uses Oauth1 (which is a pain)
$oauth = array(
'oauth_consumer_key'=>$this->c_key,
'oauth_nonce'=>$this->random(32),
'oauth_signature_method'=>'HMAC-SHA1',
'oauth_timestamp'=>time(),
'oauth_token'=>$this->o_token,
'oauth_version'=>'1.0'
);
$url = $this->api_url.$in_url;
if (strlen($callback)){
$oauth['oauth_callback'] = urlencode(urldecode($callback));
unset($oauth['oauth_token']);
$method = 'POST';
$url = str_replace('1.1/','',$url);
}
if (is_array($content) || strlen($content)){ $method = 'POST'; }
$oauth['oauth_signature'] = $this->generateSignature($oauth,$url,$method,'');
ksort($oauth);
foreach ($oauth as $k=>$v){
$auths[] = $k.'="'.$v.'"';
}
$stream = array('http' =>
array(
'method' => $method,
'ignore_errors'=>true, // http://php.net/manual/en/context.http.php - otherwise browser returns error not error content
'follow_location'=>false,
'max_redirects'=>0,
'header'=> array(
'Content-type: '.$form_content_type,
'Authorization: OAuth '.implode(', ',$auths),
'Connection: close'
)
)
);
if (is_array($content)){
$content = $content_type == 'json' ? json_encode($content) : http_build_query($content);
/* foreach ($content as $k=>$v){
$strs[] = "$k=".urlencode(urldecode($v));
}
// Just for now to keep things simple
$content = 'status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21';*/
}
if (!is_null($content)){
$stream['http']['content'] = $content;
}
// Tell streams to make a request
// Invalid key or 401 error tends to suggest an incorrect signing key / signature
$response = file_get_contents($url, false, stream_context_create($stream));
if ($test){
print'<pre>';print_r($oauth);print'</pre>';
print'<pre>';print_r($stream);print'</pre>';
//echo $callback.'<br>';
echo $url.'<br>';
//print'<pre>';print_r($http_response_header);print'</pre>';
print'<pre>[';print_r($response);print']</pre>';
}
if (!is_object(json_decode($response))){
// Content supplied is not json - just return it
return $response;
} else {
$response = json_decode($response);
}
return $this->pObj->convertObjectToArray($response);
}
The reason for the problems are:
1) The version of the Twitter API should not be included
2) The post is missing the Oauth_verifier in the signature
Thanks to twitteroauth for providing some guiding light.

Categories