vgrem / phpSPO

Microsoft 365 Library for PHP.
MIT License
360 stars 117 forks source link

loading a lot of list items - troubles #98

Open agamayaservers opened 6 years ago

agamayaservers commented 6 years ago

when I try to load more than 100 records from list once, I get an issue that coming only 100 records per time. I've tried to use ->top(3000) and it was working for some short time, and then... stoped working Ok, I should be able to receive data part by part there is top and skip methods for it, but ->skip(100) doesn't make any sense. With any parameter given I anyway receive first records with count from 1 to ->top() parameter, but no more than 100. Is it any way to get items with more than 100 numbers?

techmind-dot commented 4 years ago

I've the same problem...

Used library version: 2.3.1

Code snippet: I use the following code to receive data from a SharePoint (Office 365/Microsoft 365):

$list = $this->web->getLists()->getByTitle('customers');

$items = $list->getItems()->skip(100)->top(100);

Expected behavoir: I want to receive the entries 101 to 200.

Actual behavoir: Instead of 101 to 200 I receive the first 1 to 100 entries of the list.

Analysis: The class Office365\PHP\Client\Runtime\ClientAction is used for all API requests (SharePoint, OneDrive, Graph) by generating the API request URL with the method buildRequest().

Code snippet from line 64 to 70:

   public function buildRequest(){
        $path = $this->getResourcePath();
        $resourceUrl = $this->getContext()->getServiceRootUrl() . $path->toUrl();
        if (!is_null($this->getQueryOptions())) {
            $resourceUrl .= '?' . $this->getQueryOptions()->toUrl();
        }
        $request = new RequestOptions($resourceUrl);

For the request parameters it is using the method getQueryOptions()->toUrl() of the class Office365\PHP\Client\Runtime\OData\ODataQueryOptions that generates the request URL.

The class ODataQueryOptions contains the parameters that are used in the request URL.

Code snipt from line 33 to 47:

    public $Select;

    public $Filter;

    public $Expand;

    public $OrderBy;

    public $Top;

    public $Skip;

    public $SkipToken;

    public $Search;

The attributes $Top and $Skip are set by the above method top() and skip().

So the request URL looks like this: https://{site_url}/_api/Web/Lists/getByTitle('customer')/items?$top=1000&$skip=100"

Issue:

The $skip query option does not work with queries for SharePoint list items.

The $skiptoken query option enables you to skip over items until the specified item is reached and return the rest.

see: https://docs.microsoft.com/en-us/sharepoint/dev/sp-add-ins/use-odata-query-operations-in-sharepoint-rest-requests

So the right request URL in my case shall look like this: https://{site_url}/_api/Web/Lists/getByTitle('customer')/items?$skiptoken=Paged=TRUE%26p_ID=535%26$top=100

Where 535 would be the last entry ID in the resultset 1 to 100.

Solution: You must use the method skiptoken() that set the attribute $Skiptoken.

This code works:

function getAllDataFromCustomer() {

        $list = $this->web->getLists()->getByTitle('customer'); //init List resource

        $end = false;
        $top = 100;
        $skiptoken = null;

        $result_items = [];

        do {

            $items = null;

            //prepare a query to retrieve from the
            $items = $list->getItems()->skiptoken($skiptoken)->top($top);

             //save a query to retrieve list items from the server
            $this->ctx->load($items); 
            $this->ctx->executeQuery();

            foreach ($items->getData() as $item) {
                $result_items[] = $item;
            }

            if ($items->getCount() == $top) {
                // Set skip token if the end of list isn't reached yet
                $skiptoken = 'Paged=TRUE&p_ID=' . end($result_items)->Id;
            } else {
                $end = true;
            }

        } while ($end == false);

        return $result_items;

}