I am creating a PHP class that use a 3rd party API. The API has a method with this request URL structure:
https://api.domain.com/path/sales?page=x
Where "x" is the page number.
Each page return 50 sales and I need to return an undefined number of pages for each user (depending on the user sales) and store some data from each sale.
I have already created some methods that get the data from the URL, decode and create a new array with the desired data, but only with the first page request.
Now I want to create a method that check if is there another page, and if there is, get it and make the check again
How can I check if there is another page? And how to create a loop that get another page if there is one?
I have already this code, but it create an infinite loop.
require('classes/class.example_api.php');
$my_class = new Example_API;
$page = 1;
$sales_url = $my_class->sales_url( $page );
$url = $my_class->get_data($sales_url);
while ( !empty($url) ) {
$page++;
$sales_url = $my_class->sales_url( $page );
$url = $my_class->get_data($sales_url);
}
I don't use CURL, I use file_get_content. When I request a page out of range, I get this result:
string(2) "[]"
And this other after json_decode:
array(0) { }
From your input, in the while loop, you change the $url (which actually holds the data return by the API call) and this is checked for emptiness, if I'm correct.
$url = $my_class->get_data($sales_url);
If the above is just the original response (so in case of page out of range a string "[]"), it will never get empty("[]") to true. So my guess is that the return value from get_data is this string, while it should be the actual array/json even if the result is empty (ie I suspect that you perform the json_decode once you have collected the data e.g. outside the loop).
If this is the case, my suggestion would be to either check for "[]" in the loop (e.g. while ($url !== "[]")) or within the loop decode the response data ($url = json_decode($url)).
From my experience with several API's, the response returns the number of rows found, and x number per page starting with page 1.
In your case, if the response has the number of rows then just divide it by the x number page and loop through the results as page numbers.
$results = 1000;
$perPage = 50;
$pages = ceil($results/$perPage);
for (i=1; $i <= $pages; $i++){
// execute your api call and store the results
}
Hope this help.
From the responses you've shown, you get an empty array if there are no results. In that case, you could use the empty method in a loop to determine if there's anything to report:
// Craft the initial request URL
$page = 1;
$url = 'https://api.domain.com/path/sales?page=' . $page;
// Now start looping
while (!empty(file_get_contents($url)) {
// There's data here, do something with it
// And set the new URL for the next page
$url = 'https://api.domain.com/path/sales?page=' . ++$page;
}
That way it will keep looping over all the pages, until there is no more data.
Check http response headers for total number of items in set
Related
Im making a big APi call, i have over 6000 records. Out of those 6000 i get 200 per page. So if we divide 6000 by 200 we get 30 pages in total.
So if I would want to get all records I'd need to make 30 different requests. I can speficy the amount per page in my request url parameters. Right now it looks like this:
$getRequest = $CallMonkeyApi->makeApiCall('GET','/address/get-all?apiKey=secret&page=1&size=200');
In the url you see a parameter "page=1" I would like this number to be dynamic from a loop that stops at 30. But I dont know where to start.
You can do this with a "for" loop
//An array for results
$getRequests = array();
//For every occurance where `x` is less than or equal to 30
for( $x = 1; $x<=30; $x++ ){
//Build the path to API call
$path = "/address/get-all?apiKey=secret&page=" . $x . "&size=200";
//Make API call and store the output on the $getRequests array of results
$getRequests[] = $CallMonkeyApi->makeApiCall('GET', $path);
}
Then you can access your $getRequests array by using $getRequests[$pageNumber]. So, for example, if I wanted to see page 5, i'd do print_r( $getRequests[4] ).
Note that the page number will be one less in the array because arrays start at 0.
As a part of an assignment I am trying to pull some statistics from the Riot API (JSON data for League of Legends). So far I have managed to find summoner id (user id) based on summoner name, and I have filtered out the id's of said summoner's previous (20) games. However now I can't figure out how to get the right values from the JSON data. So this is when I'll show you my code I guess:
$matchIDs is an array of 20 integers (game IDs)
for ($i = 1; $i <= 1; $i++)
{
$this_match_data = get_match($matchIDs[$i], $server, $api);
$processed_data = json_decode($this_match_data, true);
var_dump($processed_data);
}
As shown above my for loop is set to one, as I'm just focusing on figuring out one before continuing with all 20. The above example is how I got the match IDs and the summoner IDs. I'll add those codes here for comparison:
for ($i = 0; $i <= 19; $i++)
{
$temp = $data['matches'][$i]['matchId'];
$matchIDs[$i] = json_decode($temp, true);
}
$data is the variable I get when I pull all the info from the JSON page, it's the same method I use to get $this_match_data in the first code block.
function match_list($summoner_id, $server, $api)
{
$summoner_enc = rawurlencode($summoner);
$summoner_lower = strtolower($summoner_enc);
$curl =curl_init('https://'.$server.'.api.pvp.net/api/lol/'.$server.'/v2.2/matchlist/by-summoner/'.$summoner_id.'?api_key='.$api.'');
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
$result = curl_exec($curl);
curl_close($curl);
return $result;
}
Now to the root of the problem, This is where I put the data I get from the site, so you can see what I am working with. Now by using the following code I can get the first value in that file, the match ID.
echo $processed_data['matchId'];
But I can't seem to lock down any other information from this .json file. I've tried typing stuff like ['region'] instead of ['matchId'] with no luck as well as inserting index numbers like $processed_data[0], but nothing happens. This is just how I get the right info from the first examples and I am really lost here.
Ok, so I think I've figured it out myself. By adding this to the code I can print out the json file in a way more human-friendly way, and that should make it much easier to handle the data.
echo ("<pre>");
var_dump($processed_data);
echo ("</pre>");
I have just discovered you can get pagination results through the api by passing in the page parameter like so:
$projects = $client->get('projects/147/time-records?page=3')->getJson();
Is there a way of knowing how many time records a project has so I know how many times I need to paginate?
Alternatively, how would I go about retrieving several pages worth of data - i'm struggling with the code!
I have created an issue on Github - will await a response.
For now, I do the following:
// Get all the projects
// Set the page number
$page = 1;
// Create an empty array
$project_records = array();
// Get the first page of results
$project_records_results = $client->get('projects?page=' . $page)->getJson();
// Merge the results with base array
$project_records = array_merge($project_records, $project_records_results);
// Get the next page of results,
// if it returns something merge with the base array and continue
while ($project_records_results = $client->get('projects?page=' . ++$page)->getJson()) {
$project_records = array_merge($project_records, $project_records_results);
}
Sure. All paginated results will include following headers:
X-Angie-PaginationCurrentPage - indicates current page
X-Angie-PaginationItemsPerPage - indicates number of items per page
X-Angie-PaginationTotalItems - indicates number of items in the entire data set.
When you get header values, simple:
$total_pages = ceil($total_items_header_value / $items_per_page_header_value);
will give you number of pages that are in the collection.
Alternative: You can iterate through pages (by starting with page GET parameter set to 1, and incrementing it) until you get an empty result (page with no records). Page that returns no records is the last page.
Please note, that the headers are now all lowercase (v1)!
So the answer above should be corrected.
To get them call:
$headers = $client->get($path)->getHeaders();
Working code example from /api/v1/:
$paginationCurrentPage = isset($headers['x-angie-paginationcurrentpage'][0]) ? $headers['x-angie-paginationcurrentpage'][0] : NULL;
$paginationItemsPerPage = isset($headers['x-angie-paginationitemsperpage'][0]) ? $headers['x-angie-paginationitemsperpage'][0] : NULL;
$paginationTotalItems = isset($headers['x-angie-paginationtotalitems'][0]) ? $headers['x-angie-paginationtotalitems'][0] : NULL;
I have a simple list of orders which a user can filter by status (open,dispatched,closed). The filter dropdown triggers a post to the server and sends the filter value through. Orders are listed out 10 to a page with pagination links for any results greater than 10. Problem is when I click the pagination links to view the next page of results the filter value in the post is lost.
public function filter_orders() {
$page = ($this->uri->segment(4)) ? $this->uri->segment(4) : 0;
$filter = $this->input->post('order_status_filter');
$config = array();
$config["base_url"] = base_url() . "control/orders/filter_orders";
$config["per_page"] = 10;
$config['next_link'] = 'Next';
$config["uri_segment"] = 4;
$config['total_rows'] = $this->model_order->get_all_orders_count($this->input->post('order_status_filter'));
}
How can I make the pagination and filter work together. I've thought about injecting a query string in to the pagination links but it doesn't seem like a great solution.
The answer is very simple, use $_GET. You can also use URI segments.
i.e, index.php/cars/list/5/name-asc/price-desc'
The main reason you'll want to use $_GET is so you can link other users so they see the same result set you see. I'm sure users of your web app will want this functionality if you can imagine them linking stuff to each other.
That said, it would be ok to ALSO store the filters in the session so that if the user navigates away from the result set and then goes back, everything isn't reset.
Your best bet is to start a session and store the POST data in the session. In places in your code where you check to see if the user has sent POST data, you can check for session data (if POST is empty).
In other words, check for POST data (as you already do). If you got POST data, store it in the session. If a page has no POST data, check to see if you have session data. If you do, proceed as if it was POSTed. If you have both, overwrite the session with POST. You'll want to use new data your user sent you to overwrite older data they previously sent.
You either put everything in $_GET or if the data is sensible, put it in $_SESSION. Then it travels between pages.
In your case there seem to be no reason to put your filter data anywhere else than in $_GET.
A query string does seem the best solution. You could store it in the session or in cookies as well, but it makes sense to also store it in the query string.
Store it in cookies or the session if you want to remember the user's choice. Which seems like a friendly solution. It allows the user to keep their settings for a next visit, or for another page.
Store it in the query string, because going to 'page 2' doesn't tell you anything if you don't know about filters, page size or sorting. So if a user wants to bookmark page 2 or send it by e-mail, let them be able to send a complete link that contains this meta information.
Long story short: Store it in both.
maybe its not a right answer but, give it a try
<?php
// example url
$url = "index.php?page=6&filter1=value1&filter2=value2";
// to get the current url
//$url = "http://".$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"];
// change the page to 3 without changing any other values
echo url_change_index( $url, "page", 3 );
// will output "index.php?page=3&filter1=value1&filter2=value2"
// remove page index from url
echo url_change_index( $url, "page" );
// will output "index.php?filter1=value1&filter2=value2"
// the function
function url_change_index( $url, $name = null, $value = null ) {
$query = parse_url( $url, PHP_URL_QUERY );
$filter = str_replace( $query, "", $url );
parse_str( $query, $parsed );
$parsed = ( !isset( $parsed ) || !is_array( $parsed ) ) ? array() : $parsed;
if ( empty( $value ) ) {
unset( $parsed[$name] );
}
else {
$parsed[$name] = $value;
}
return $filter.http_build_query( $parsed );
}
?>
I want to do a loop, normally it is done with while do for etc but when the process is big I came up with a solution to refresh the page by echoing a javascript to refresh the page for the next loop.
for example:
The page is http://localhost/index.php --> this preforms the first iteration with $i=1;
at the end of the script it will be redirected to http://localhost/index.php?i=$i++
if (!$_GET['i']){
$i = 1;
}else{
$i = $_GET['i'];
}
if ($i<500){
// proceed with $i = $_GET['i']
//then redirect to http://localhost/index.php?i=$i++
}else{
echo "done";
}
Now, consider a situation that the imput parameters come from a FORM to this script. (i.e. $parameter1 , $parameter2, $parameter3)
Then I have to pass them every time to new url (next iteration).
At normal work I can pass them as GET variable to new url but how can I pass them if I don't want the user be able to see the value of parameters in url?
At normal work I can pass them as GET variable to new url but how can I pass them if I don't want the user be able to see the value of parameters in url?
You can not with the bare redirect, but if you're talking about a specific user, you can do so by assigning those parameters as session variables Docs and then passing the session id as an additional parameter (or trust the user has cookies enabled).
function do_redirect($i, Array $parameters)
{
$i = (int) $i;
$parameters['i'] = $i; // save to session as well
$_SESSION['parameters'] = $parameters;
// redirect to http://localhost/index.php?i=$i&SID
}
if (is_form_request())
{
$parameters = get_form_parameters();
do_redirect(1, $parameters);
}
elseif (is_redirect_loop_request())
{
$parameters = $_SESSION['parameters'];
$i = $parameters['i'];
if ($i < 500)
{
do_redirect($i++, $parameters);
} else {
echo "done.";
}
}
Not to be rude, but both answers above are quite prone to security issues (but the session solution is the best one). As for the 'encryption' solution of #itamar: that's not exactly encryption... This is called 'Caesar cypher' (http://en.wikipedia.org/wiki/Caesar_cipher), which is indeed as safe as a paper nuclear bunker...
It can be much easier and safe as can be; do not save the iteration in the session, but in the database. For the next request, the only thing you have to do is get the iterator from the database and go on with whatever you want to do. Sessions can be stolen, meaning someone could let you iterate from, say, $i=10 a thousand times. It cannot be done when the iterator is stored in a secure database.