In order to make your life easy you should try to get the data as is through your api-client classes. Then push it through an appropriate transformer which will always return the data in the same format you want to use in your app. Then your controllers will only call the internal interface class in order to always get the same data format. When you have a working structure, then you can start playing around with a config, a driver and service providers.
Where to translate data from external API to Laravel model
I'm building an app that requests employee data from an external API. The data can come from two different sources right now, but could be more in the future. All data sources return all of the information needed about employees, but they do it in different formats (xml, json, etc.). Even when formats are the same, the structure can vary. I've set everything up so that I can easily request the data from the APIs and even swap out which service I'm calling to request the data at runtime.
The data that is returned comprises what we will call a "report" and each item on the report is called a "record". So, the data from the external services needs to mapped to a collection of "record" models that are tied to a new "report" model and then stored in my database.
My question is where/how should this mapping be done and then stored?
Here is the simplified code for what I have so far:
The interface that each external API request must implement
interface EmployeeDataRequestor {
function getData (Employee $employee);
}
Each service will have a concrete implementation
class Service1EmployeeDataRequestor implements EmployeeDataRequestor {
function getData (Employee $employee) {
//use Guzzle to fetch the data from the api
return $fetchedEmployeeData; //should I return raw employee data here
// as fetched from the service, or is this where I would also process the data
//and return and object? IE $this->processData($fetchedEmployeeData)
}
//function processData($data) {
//map data to standard object for my app
}
}
The controller where the data is requested
class EmployeeDataController extends Controller {
function __construct (EmployeeDataRequestorRegistry $registry) {
$this->EmployeeDataRequestorRegistry = $registry;
}
function requestData (Request $request, Employee $employee) {
$data = $this->EmployeeDataRequestorRegistry->get($request->get('dataRequestor'))
->requestData($employee);
//or, is this the place where I would process the data. Seems like no here because the mapping is going to be service specific.
//create a "mapper" class for each sercie I.E. class Service1DataMapper()
//then map the data here choosing the correct map using the info in $request->get('dataRequestor')
}
}
This is how I dynamically select my service provider on each request. I used the information in this excellent article about the registry pattern to set it up.
class DataRequestorRegistry {
protected $requestors = [];
function register ($name, DataRequestor $instance) {
$this->requestors[$name] = $instance;
return $this;
}
function get($name) {
if (in_array($name, $this->requestors)) {
return $this->requestors[$name];
} else {
throw new Exception("Invalid data service.");
}
}
}
class EmployeeDataServiceProvider extends ServiceProvider {
function register () {
$this->app->singleton(DataRequestorRegistry::class);
}
function boot () {
$this->app->make(DataRequestorRegistry::class)
->register("service1", new Service1EmployeeDataRequestor(Config::get('service1.api_key')));
$this->app->make(DataRequestorRegistry::class)
->register("service2", new Service2EmployeeDataRequestor(Config::get('service2.api_key')));
//...etc. for each external API that provides data
}
}
Please or to participate in this conversation.