Weble / ZohoBooksApi

40 stars 39 forks source link

Handle bulk Item updates in single API Request - PUT #100

Open mfoolio opened 1 year ago

mfoolio commented 1 year ago

Attempting to update multiple ZohoBooks "Item" records with the "update" method quickly leads to API rate limits.

Using a PUT to the bare "/items" with a JSON object of many Item records will update them all with a single API call. The reply data is a JSON array with single top-level array of "items" with each record being the full updated individual Item and also the response "Code" and "Message" that would normally be returned from a single Item's update/PUT call.

While this ZohoBooksApi module allows to send an update to the bare "/items" by providing "null" as the id to the ->update() method (which in turns sends it to ->put()), the current release does not properly handle the resulting data response from the Zoho API.

Two issues presented:

To resolve for immediate use I modified code as follows:

        if (!$result) {
            // All ok, probably not json, like PDF?
            if ($response->getStatusCode() >= 200 && $response->getStatusCode() <= 299) {
                return (string)$response->getBody();
            }

            throw new ErrorResponseException($response->getReasonPhrase(), $response->getStatusCode());
        }

        if (isset($result['code']))
        {
            if ($result['code'] != 0)
            {
                throw new ErrorResponseException('Response from Zoho is not success. Message: ' . $result['message'], $result['code'] ?? $response->getStatusCode());
            } else
            {
                // All OK
                return $result;
            }
        }

        // All OK, JSON and likely bulk update with individual response codes
        return $result;
    }
    public function update($id, $data, $params = [])
    {
        $data = $this->client->put($this->getUrl(), $id, $data, $params);

        if ($id != null)
        {
            // Updated single object
            $data = $data[$this->inflector->singularize($this->getResourceItemKey())];
            return $this->make($data);
        } else
        {
            // Updated muliple objects, return collection
            // case for bulk ie. update PUT to /items

            // body is top level of module name i.e. 'items'
            // array of each object that was submitted, 'code' and 'message' is in each sub object

            return $data[$this->getResourceKey()];
        }
    }

Would it be more desirable to create another Module method such as updateMany() instead of passing in a null id? Are there consequences or other cases where processResult() shouldn't just return the JSON data object?

Skullbock commented 1 year ago

We deal with this in our CRM module, so if you feel like it you can check that implementation (https://github.com/Weble/ZohoCRMApi and let me know if that make sense in this use case, and we'll accept a PR for this gladly :)