This is a PHP library for communicating with the izi.TRAVEL API.
All requirements are resolved through Composer. After
installing composer, go to the project root and run composer install
to
install all dependencies.
The library provides a client to communicate with the API. It handles all requests, errors, and converts all API output to classes PHP objects. Three preparatory steps must be taken before API calls can be made:
- Create a
\GuzzleHttp\Client
instance. - Create a
\Triquanta\IziTravel\Client\*RequestHandler
instance and inject the Guzzle client from step 1 and an API key. - Create a
Triquanta\IziTravel\Client\Client
instance and inject the request handler from step 2.
Now you can call any of the methods on the client from step 3 and get the API's output as (arrays of) classed PHP objects.
Because working with raw, untyped data in PHP is bad developer experience (DX),
every data type in the API has corresponding interfaces and classes in this
library. All classes come with factory methods
(\Triquanta\IziTravel\DataType\FactoryInterface
)
to instantiate an object based on the API's raw JSON response.
Symfony EventDispatcher
is used for dispatching system events. Event names and classes are documented in
\Triquanta\IziTravel\Event\IziTravelEvents
.
All development takes places on the master
branch. Versions are released based
on Semantic Versioning.
Each API endpoint is represented by a request class in
\Triquanta\IziTravel\Request
and it must implement
\Triquanta\IziTravel\Request\RequestInterface
.
It can optionally implement any of the other
interfaces in the same
namespace and use any of the
traits to quickly build a
configurable request. Any object data returned by an endpoint must be converted
to classed objects before being returned by
\Triquanta\IziTravel\Request\RequestInterface::execute()
implementations.
The existing data types in Triquanta\IziTravel\DataType
can
be re-used.
\Triquanta\IziTravel\ClientInterface
is a
convenience layer for easily requesting data from different endpoints in the
same code. When adding a new request class, a method for it must be added to
this interface as well.
A new endpoint foo
was added for retrieving Foo content by UUID.
Additionally the content can be returned in a specific language and format
(full/compact). The first step is to create a class that implements the correct
interface (which is inherited from
RequestBase
):
<?php
/**
* @file Contains \Triquanta\IziTravel\Request\FooByUuid.
*/
namespace Triquanta\IziTravel\Request;
/**
* Returns a Foo object by UUID.
*/
class FooByUuid extends RequestBase {
/**
* @return \Triquanta\IziTravel\DataType\FooInterface
*/
public function execute() {
}
}
Now we have the basis for any request. Because this endpoint supports forms and is multilingual, we can easily add support for this by implementing two interfaces and using two traits:
<?php
class FooByUuid extends RequestBase implements FormInterface, MultilingualInterface, UuidInterface {
use FormTrait;
use MultilingualTrait;
use UuidTrait;
/**
* @return \Triquanta\IziTravel\DataType\FooInterface
*/
public function execute() {
}
}
The class will now feature additional methods and properties for setting and storing the form, language, and UUID of the content that should be returned by the API.
Now we are able to configure instances of this class, we need to use this configuration to execute the actual request and get the JSON from the response:
<?php
public function execute() {
$json = $this->requestHandler->request('/foo', [
'form' => $this->form,
'languages' => $this->languageCodes,
'uuid' => $this->limit,
]);
}
The request is made to the endpoint, and we pass along the values of the three endpoint parameters (form, language, UUID). As you can see, we use the traits' properties for this.
What remains is the conversion of this JSON to a classed object. We don't have to worry about NULL values, because the API returns HTTP error response in case the requested content is not available. That means by the time this code is executed, we can be sure the response is positive.
<?php
public function execute() {
// ...
return Foo::createFromJson($json, $this->form);
}
All data object classes implement
FactoryInterface
and must be
instantiated by using the interface's methods. By calling ::createFromJson()
,
we also validate the JSON against the available schemas.
The request class in its entirety now looks like this:
<?php
/**
* @file Contains \Triquanta\IziTravel\Request\FooByUuid.
*/
namespace Triquanta\IziTravel\Request;
/**
* Returns a Foo object by UUID.
*/
class FooByUuid extends RequestBase implements FormInterface, MultilingualInterface, UuidInterface {
use FormTrait;
use MultilingualTrait;
use UuidTrait;
/**
* @return \Triquanta\IziTravel\DataType\FooInterface
*/
public function execute() {
$json = $this->requestHandler->request('/foo', [
'form' => $this->form,
'languages' => $this->languageCodes,
'uuid' => $this->limit,
]);
return Foo::createFromJson($json, $this->form);
}
}
For convenience we also add a factory method to
ClientInterface
. Because the language and
UUID are the only required request parameters, they are also the only parameters
to the factory method. Any remaining optional parameters can be configured using
the setters on the returned request object.
<?php
namespace Triquanta\IziTravel\Client;
interface ClientInterface
{
// ...
/**
* Gets a request to get a Foo object by its UUID.
*
* @param string[] $languageCodes
* An array of ISO 639-1 alpha-2 language codes.
* @param string $uuid
*
* @return \Triquanta\IziTravel\Request\FooByUuid
*/
public function getFooByUuid(array $languageCodes, $uuid);
}
The method's implementation in Client
would look
like this:
<?php
namespace Triquanta\IziTravel\Client;
use namespace Triquanta\IziTravel\Request\FooByUuid;
class Client implements ClientInterface
{
// ...
public function getFooByUuid(array $languageCodes, $uuid)
{
return FooByUuid::create($this->requestHandler)
->setLanguageCodes($languageCodes)
->setUuid($uuid);
}
}
The newly supported endpoint can then be used:
<?php
use Triquanta\IziTravel\Client\Client;
$client = new Client(/* ... */);
/** @var \Triquanta\IziTravel\DataType\FooInterface $foo */
$foo = $client->getFooByUuid(['en'], 'de305d54-75b4-431b-adb2-eb6b9e546014')->setForm(FormInterface::FORM_COMPACT)->execute();
All code must be written according the PSR-2 guidelines.
Class and interface autoloading is done using PSR-4 using the following namespace mappings:
The library comes with PHPUnit-based tests that can be
run using ./phpunit.xml.dist. All tests are located in
\Triquanta\IziTravel\Tests
.
Some tests require configuration. Copy
./test_configuration.example.yml to
./test_configuration.local.yml
and fill out the values.