Skip to content

Adapters, Requests and Data Providers

Martin Büttner edited this page Mar 4, 2014 · 5 revisions

This article describes how the classes in \FACTFinder\Core\Server work together to provide the adapters (in \FACTFinder\Adapter) with data from the server.

Adapters

The classes in the Adapter namespace form the main chunk of the library's public interface. All the classes you will use are really just dependencies of the adapters. Each adapter gives you high-level access to one FACT-Finder feature, such as the search itself, the tag cloud, product comparison or suggestions.

Among an adapter's dependencies there is only one for communication with the server: a Core\Server\Request object. Each adapter holds its own Request, which represents parameters and the target of the request and hides the details of the server connection.

Requests

A Request is more than just a data object containing parameters and a request target. It's actually a Facade which hides a Core\Server\ConnectionData object and a Core\Server\DataProvider. It only provides access to the relevant fields in the ConnectionData:

  • It gives you read/write access to the parameters and HTTP header fields of the request.
  • It gives lets you set the action to be requested on the server (e.g. Search.ff).
  • It lets you configure timeouts for the request.
  • It gives you access to the server's response.

This interface completely hides the actual execution of the request to the server. All it guarantees is that the server data is available when you call getResponse().

The DataProvider object hidden behind the Request determines how the data is actually obtained (from the server, or otherwise). Details on data providers follow below.

Because, we don't want users to wire up their requests manually, we provide request factories.

Request Factories

For each type of data provider, there is an implementation of Core\Server\RequestFactoryInterface. The factory takes care of setting up the right data provider and wiring up the Request objects for you. For instance, if you want your requests to be performed via cURL's multi-interface (parallel HTTP requests), all you need to do is:

$requestFactory = FF::getInstance('Core\Server\MultiCurlRequestFactory',
                                  $loggerClass,
                                  $configuration,
                                  $requestParameters);

$request = $requestFactory->getRequest();

Of course, you need the last line for each adapter you want to create. Note that the request factory's third parameter is $requestParameters. This should be a Util\Parameters object representing the parameters from the request to the Client, so that requests to the Server can be pre-populated with these. (You can obtain such a Parameters object from Core\Client\RequestParser.)

These two classes, Request and RequestFactory, are the only ones you have to deal with to set up an adapter. The remaining classes are implementation details and therefore mostly of interest to contributors.

Data Providers

Implementations of AbstractDataProvider are responsible for the actual logic for turning a request into a response. There will usually be only one data provider per application (maintained by the single request factory in use) and it will handle all requests. When data for a request is obtained is at the discretion of the data provider, as long as a call to loadResponse() which actually provide the response.

The library comes with three data provider implementations:

  • FileSystemDataProvider turns the request parameters and target into a file name which it simply tries to load from the file system. This is very useful for unit testing (which is exactly where it is used): you can just save the server responses you want to test to individual files with the correct names in order to test adapters without ever connecting to the server.
  • EasyCurlDataProvider establishes the connection to the server via cURL (through PHP's curl_* functions) using the "easy interface". That means that it sends each request sequentially at the time its response is requested.
  • MultiCurlDataProvider also uses cURL, but the "multi interface". That means this data provider can set up multiple connections in parallel, which greatly reduces the bottleneck that server requests cause. In order to utilize this, this data provider makes use of its freedom of when to send out requests: as soon as the first response is requested, all requests will be processed in parallel and the responses are stored until they are actually needed. Therefore, to make maximum use of this data provider, you should always configure all adapters (and therefore requests) before requesting the first response.

Other implementations are conceivable as well, such a using sockets.

Connection Data

As described above Request objects don't actually store anything - they are just facades. The actual configuration data for each request (or connection) is stored in a ConnectionData object. This includes the action, parameters, HTTP header fields, and a generic set of "connection options" which can be interpreted by the data provider as necessary (e.g. as options for PHP's cURL module). There is 1:1 relationship between Request and ConnectionData objects.

Each ConnectionData object will also hold the corresponding server response once it's available. The data provider is responsible for filling those responses upon request, and the Request object will then read the response directly from the ConnectionData.

Responses

Last but not least there is the Response object which represents all data pertaining to a server response. This includes the actual response content, the HTTP code, a generic connection error code (e.g. a cURL error) and a generic connection error string. This is the object that can be obtained by an adapter, although most of them will only use the response content.

The Big Picture

TODO: Add class diagram of this corner of the library.

Clone this wiki locally