Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

robinsonryan's avatar

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
  }
}
0 likes
2 replies
Borisu's avatar

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.

robinsonryan's avatar

Borisu - thanks. My question is where is the appropriate place to call the transformer class. Your comment makes it sound like pushing through the transformer should hapen in the API client classes. Something like:

//Psuedocode

Class api-client() {
    Function fetchData(){
        $data = //fetch data with guzzle
        Return $this->transformData($data)
    }

    Function transformData($data){
        $transforner = new DataTransformer();
        Return $transformer->transform($data);
   }
}

Class my controller(){
   
    $reportData = $api-client->getData;
    $report = Report ::create($reportData);

}

So, question was where to do the transformation and it seems like the api-client seems like the right place. That way, each client can have it's specific data transformed to my app's standard format and then return a standardized object. Do you agree?

Please or to participate in this conversation.