Download the PHP package macsidigital/laravel-api-client without Composer
On this page you can find all versions of the php package macsidigital/laravel-api-client. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download macsidigital/laravel-api-client
More information about macsidigital/laravel-api-client
Files in macsidigital/laravel-api-client
Package laravel-api-client
Short Description Laravel API Client Builder package
License MIT
Homepage https://github.com/macsidigital/laravel-api-client
Informations about the package laravel-api-client
Laravel API Client
Laravel package for Building API Client's
An API Client Builder Library
Installation
You can install the package via composer:
Versions
1.0 - Laravel 5.5 - 5.8 - Deprecated and no longer maintained.
2.0 - Laravel 6.0 - Maintained, again feel free to create pull requests. This is open source which is a 2 way street.
3.0 - Laravel 7.0 - 8.0 - Maintained, again feel free to create pull requests. This is open source which is a 2 way street.
Usage
The main aim of this library is to add a common set of traits to models to be able to create, update, retrieve and delete records when accessing APIs. Obviously all APIs are different, so you should check documentation on how best to implement with these traits.
The basic concept is you build a client in the API library that you create which extends on the models in this library.
Entry Model
The first thing to do is to create an entry model and extend the MacsiDigital/API/Support/Entry model, this is what shapes how the API will work. It's the model that houses all the attributes, so if you want pagination or to customise something it will likely be here. Its an Abstract model so has to be extended in your implementation.
We recommend placing it in a Support folder within your src directory.
List of Attributes
Most of these are self-explanatory, there is a magic method attribute in there, if we return raw data than we receive the actual response without any error checking. if we set $throwExceptionsIfRaw = true, then we still receive raw data, but it will check to make sure the return was successful and not an error. If we receive an error it will throw a HttpException. You can also set this on teh fly by calling with Exceptions() on a query.
In your extended Entry model implementation you have to define a newRequest function, this is where the logic for how we 1connect will go and should return a MacsiDigital\API\Support\Factory object, which is better renamed to Client in your implementation, which will resolve the API Gateway Client.
If using OAuth we would have to input our OAuth Logic, or for OAuth2 something like this would work, this is xero implementation
As you can see all the initial logic is required on how we need to open up a Gateway, please note that the API does not handle the OAuth2 Authorisation, for this you will need to use an OAuth2 client like macsidigital/laravel-oauth2-client or league/oauth2-client. The macsidigital client handles the routing and saving of the data to either database or file.
There is also a function in the entry class that returns the builder class to use, if you roll your own builder class, further details below, this is where you need to set the class.
RESTful API by default
We by default use standard RESTful method calls:-
- get - retrieve records
- post - create a record
- patch - update a record
- put - replace a record
- delete - delete a record
We also add a find call, as most APIs have different endpoints for get and find methods.
- find - retrieve a single record
However, some APIs have different ideas on what methods to call (we are looking at you Xero).
So you override the default create and update methods by adding these attributes or methods to your models
Also, some implementations, looking at you again Xero, use update methods to create models and create methods for updating models, so you may need to override the end point functions in the resource model. We have added logic to try to automatically get around this but if you are having problems then set them manually in your model.
Xero uses a patch request for creating models and post request for updating models.
Retrieving models
You can retrieve a single model by either using the find method and passing an ID, or an array of IDs, or by running a query and returning first() or last();
You can also use get and all to retrieve many models, this will return a Result Set, which is an enhanced Laravel Collection.
Some APIs (Yes Xero) return single results wrapped in an array, like multi record searches. So you can override the hydrate() method on the builder model.
Result Sets
After we run a multi result return type like all() and get(), we are returned a result set. This will take care of any pagination and can be used to dip back into the Gateway to retrieve the next lot of results, houses information on what page we navigated, what is the next page etc. This is dependant on the API, and we try to utilise the APIs returned meta where possible.
all() return method
With the all() method we try to retrieve all results by recursively hitting the API endpoint, because this can use rate limits quite quickly there is a $maxQueries attribute in our entry model, once set this is the max amount of queries that can be hit in a recursive call.
If the max is hit and you require more results then you can call the retrieveNextPage() method which will add the next round of queries to the result set.
get() return method
This will return up to the max number of queries if its not set to paginate. When pagination is set then it will restrict records to the perPage total.
We use get() just like laravel, when we need to filter, order or paginate.
Result Set Functions
As noted result sets are just like laravel collections, with additional functions.
So if our API only returns 30 results per page, our all method will do its best to return as many results as it can. This will generally lead to some pagination. So to retrieve the next set of results we can do the following.
The previousPage() method will go back a page, we cache the results so going back will not make an api call but pull the cached results.
Sometimes you may also want to accumulate records, to do this you can call the getNextRecords() method. Please note not to mix this method with the next and previous page methods.
// Need to check this as think Xero doesn't return page counts. If you try to retrieve more records than there is available, then an exception will be thrown.
Links
At present there is no facility to use predefined links but it is something we may add in the future.
Raw Searches
If you would like to receive the raw response from the query then set raw on the query
Http Errors
If there are any errors returned from our call and the response is not set to raw then we will throw a new Http Exception. We have some default behaviour but different APIs have different responses to errors. So you can override the prepareHttpErrorMessage() method on the builder model to customise the Exception message.
Models
We have 2 base model types, resources and apiResources.
We utilise Laravel's hasAttributes trait in the models so you should be able to use any casts like Laravel in any model.
Resources
These are generally resources that are returned as relationships of other models but do not interact with the API directly. To create one extend the MacsiDigital/API/Support/Resource.
This should only be used on models that are returned as a sub array of a called model.
API Resources
These are models that will interact directly with an API, or indirectly through a parent model. To create one extend the MacsiDigital/API/Support/APIResource.
If you want to roll your own then you need to ensure you add the MacsiDigital\API\Traits\InteractsWithAPI trait. Also follow how our Support API resource works.
In these models we also house many variables, like primary key and api endpoints, these are the available attributes
The apiData and apiMultiple fields dictate what field in the response will house the main body data, this should be 'data' in a good api but it really does vary. Zoom uses 'users' for multiple records and '' for single.
If apiDataField is set to '' it will return the body direct.
Xero also uses a different method.
In the case of Xero it can be overwritten in the getApiMultipleDataField function so that we don't have to set on all models.
In a similar way we can override the primaryKey as some api's will keep the ID field as UserID by creating the following function
EndPoints
A quick note on endpoints, we can set an endpoint as 'users' but we can also include bound models similar to a laravel route when results are returned as part of a relationship.
We can also set customEndpoints on a model for specific endPoints, if the endPoints don't follow convention.
Relationships
We have tried to get the models as close to a Laravel model as we can, even down to how relationships work.
By default we will try to create relationship objects for any returned input if they are setup. However you can override this behaviour by setting LoadRaw to true in the model
There may be times where you want some auto-loading but not allow all connected models to autoload, in this case you can set any models that should not autoload by setting them like so:-
For each model we need to create a function, just like Laravel, so if there is a User Model which has a relationship with an Address Model you would set:-
This could also be a HasMany relationship and the reverse on the address would be a belongsTo method
A name and a field can be passed as a 2nd and 3rd argument. A 4th argument can also passed which will be any fields and values in an array that should be passed onto all relationship models. This is handy when you need to track a parent's id on teh child model.
We try to automatically work out the name and field attributes if not passed for you based on the function name, so we will look for a field 'user_id' in the array 'users' in the above method. However not all APIs use the same id naming so you can set the IDSuffix by adding this to your model.
With the name field we will check results for User and user
Its worth pointing out that this is case sensitive so User and user will give different results, UserID and userID. Again this is due to all APIs being different.
Its also worth pointing out that some resources don't interact directly with the API, in these cases the field is ignored.
At present we do not have the ability for Many to Many but will see if there is a need in our API building quests.
Now that the relationships are set we can use Laravel like syntax to retrieve and work with models and relationships
We utilise save, saveMany (has many only), create and createMany (has many only) functions to save existing models and create new models directly to the relation, as long as they are models that interact with the api.
When the relation models do not interact with the api then we expose new make() and attach() methods so they can be attached to persisting models for saving.
This works well when relationships are returned direct as part of an API call, which is common in APIs. However sometimes APIs don't send these items direct and therefore need different end point calls.
In these cases we use custom relationship models
Custom Relationship Models
First you need to extend either the HasOne or HasMany relationship models.
Within the extended model you need to create the logic for retrieving, creating, updating and deleting as is required by the API. Unfortunately as these are all case specific you will need to create CustomRelations models for any implementation required.
To call it we call the hasCustom method and pass the model class as the 2nd parameter. Parameters 3 and 4 can still be the name and field and 5 any fields and values to add to any new models.
The custom class will need a constructor, a save, saveMany (for hasMany), create, createMany (for hasMany) and getResults methods.
This really gives a blank canvas to be able to get those pain in the butt endpoints into your API.
Sometimes relationships are not returned as part of the request and have to be called separately. Sometimes this may mean hitting a different endpoint, which is set in the model, see note on endPoints above.
We can also override the default with customEndPoints on a per model basis.
You can set end points for find, get, create, update and delete.
Filters
Filters will vary from API to API, so we have tried to build an extendable filter functionality that can easily be implemented.
Filters use Laravel like syntax to apply with the where functions
And of course you can stack where clauses
At present we only cover the where and whereIn, the latter will only work with the ID field, but we intend to add more of the laravel where clauses, like whereBetween if we find there is support for this in APIs.
Now as noted all APIs are different so generally we have to customise the filter logic.
First of all you need to create a Builder model that extends the Support/Builder model.
In the Entry model you need to set the below function
In this builder class you would then create functions that are called when filters are added and when they are processed into the query string. So to modify we would modify 2 methods
As arrays cant use symbols as keys we do some translating, so if '=' is called we will change this to AddWhereEquals and processWhereEquals, the list below is of the translations we make.
The default is called for any custom filters, so lets say you call where('name', 'StartsWith', 'Bob') this will call addWhereStartsWith and processWhereStartsWith methods
Now each API will handle filtering differently so the logic in these methods are to suit the API, here is an example for Xero, who add all filters to a where query string. They allow the main ones and have 3 custom types, 'Contains' which is the same as a 'like' call, 'StartWith' and 'EndsWith'.
As we create custom filter methods, this makes filtering easy and extremely powerful, but it really does depend on what the API allows to be done.
So as an example in Xero we can apply a If-Modified-Since header to our request to retrieve only models modified since a specific date, this can be achieved like so.
Of course for these sorts of custom actions we have to update the allowed operands field within our Entry model to allow them to be applied.
Creating and Updating
Generally in APIs the attributes required for saving and updating are different, and they are in turn different to the attributes when a model is retrieved. We therefore utilise 'Persistence' models which will house validation logic and the attributes required to make a successful save. So in our models we need to Extend the InteractsWithAPI trait and add the following 2 protected attributes.
The Insert and Update resources should extend the MacsiDigital/API/Support/PersistResource. These models will have attributes for what fields to use and any validation.
As you can see we set the field and any regular Laravel validation logic in the persistAttribute, if there is no validation we can pass ''.
We can also extend this to include relationships. It may look like so:-
When utilising the relationships in this way it will dip into the linked resource model and use its validation and fields logic. So in this it will pull in the following logic
We can pass an array directly into the relatedResource attribute if we don't want to create a persistent resource.
We can require fields from relationships by using dot notation and not including the relationship:-
So in this instance we are only interested in street, town and postcode from the address model.
We can use a mix of the above, its not one or the other.
Mutating
Some APIs will mutate the data, a common case is wrapping the fields in a new array.
To achieve this we can use Mutators on the persistModel
This will now create the correct json for the api and allow validation to still take place.
Saving
To save its as simple as calling the save() function.
THe model will see if it already exists and call the correct insert or update method. If updating we will only pass dirty attributes to the persistence model.
You can also utilise other laravel methods like make, create and update directly in the model.
We pass back helpful exceptions if it fails due to the API rejecting it. For example some APIs will have unusual rules, in Xero you can only pass multiple contacts if you supply email addresses, in this case we would get back an exception:-
Deleting
To delete we would just call the delete function, this will return true if it deleted or throw a HttpException if there was an error.
Calling custom requests
Whilst we do our best to cover the main restful endPoints for models, there are going to be some cases where we have to create something custom.
We have tried to make that as easy as possible, each model has the API client injected in on instantiation, so we can create any function to call on the API. For example, zoom exposes an endPoint to upload a profile picture, we could create persistModels, but it may be overkill, so we can simply create a function like so
Nice and simple.
ToDo
- Proper Documentation
- Tests
Changelog
Please see CHANGELOG for more information on what has changed recently.
Contributing
Please see CONTRIBUTING for details.
Security
If you discover any security-related issues, please email [email protected] instead of using the issue tracker.
Credits
- Colin Hall
- MacsiDigital
- All Contributors
We use a lot of Laravel type functions taken directly from Laravel, or taken and modified so we also have to credit the Laravel team.
License
The MIT License (MIT). Please see License File for more information.
All versions of laravel-api-client with dependencies
nesbot/carbon Version ^1.26.3 || ^2.0
guzzlehttp/guzzle Version ~7.0|~6.0|~5.0|~4.0
guzzlehttp/oauth-subscriber Version ^0.6.0
firebase/php-jwt Version ^6.0
illuminate/support Version ^7.0|^8.0|^9.0|^10.0
macsidigital/laravel-oauth2-client Version ^1.2|^2.0