Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you convert third party API data to a collection resource in Laravel 5.6?

Tags:

I've been working on creating a clean interface for our various web application and I've run into a snag with Laravel's API Resources not properly converting the incoming json array into laravel collections.

I can do it with a single resource:

    namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;
use App\Product;

class ProductResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'name' => $this->resource['product']['name'],
            'description' => $this->resource['product']['description'],
            'price' => $this->resource['product']['rental_rate']['price']
        ];

        //return parent::toArray($request);
    }
}

print this response outputs:

{"name":"Arri Skypanel S60-C","description":"Arri Sky Panel S60-C 450w input with a 2000w tungsten equivalent & Combo Stand","price":"260.0"}

However trying to take this single item and turn it into a collection of items isn't going anywhere.

Anybody got a clue what I'm missing?

Pulling the API data looks like this:

namespace App;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;

class ThirPartyAPI
{
    private $url = 'https://api.third-party.com/api/v1/';

    public function pull($query, $additionalParams) {
        $client = new Client;
        $result = $client->get($this->url . $query . $additionalParams, [
            'headers' => [
                'Content-Type' => 'application/json',
                'X-AUTH-TOKEN' => env('CURRENT-AUTH-TOKEN'),
                'X-SUBDOMAIN' => env('CURRENT-SUBDOMAIN')
            ]
        ]);
        $array = json_decode($result->getBody()->getContents(), true);

        return $array;
    }
}

The API returns a lot of json data.

This is the Product model:

public function getAllProducts() {
        try {

            $productData = [];  
                $query = "/products?page=1&per_page=3&filtermode=active";
                $additionalParams = "";
                $productData = new ThirdPartyAPI;
                $productData = $productData->pull($query, $additionalParams);

                $productData = $productData['products'];    

        return ProductsResource::make($productData);
    } catch (\Exception $ex) {
        return $ex;
    } catch (\Throwable $ex) {
        return $ex;
    }

} 

Right now I'm trying something this to convert all the returned arrays into something I can control more:

namespace App\Http\Resources;

use Illuminate\Http\Resources\Json\Resource;

class ProductsResource extends Resource
{
    /**
     * Transform the resource into an array.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return array
     */
    public function toArray($request)
    {
        return [
            'products' => $this->collection->mapInto(function($request) {
               return[ 'name' => $this->resource['name'],
                       'description' => $this->resource['description'],
                       'price' => $this->resource['rental_rate']['price']
                ];
            })
        ];

    }

However var_dumping the data just returns this:

object(App\Http\Resources\ProductsResource)[200]
  public 'resource' => 
    array (size=3)
      0 => 
        array (size=37)
          'id' => int 164
          'name' => string '10A Dimmer' (length=10)
          [Lots of data]
              ...
          'sale_rates' => 
            array (size=0)
              ...
      1 => .....
      [cont]
 public 'with' => 
    array (size=0)
      empty
  public 'additional' => 
    array (size=0)
      empty

I've tried various forms of data conversion on the return json info and haven't had a lot of results except errors and confusing business. I'm a little shady on how Laravel handles API Resource handling.

like image 241
Mugluck Avatar asked Jul 04 '18 07:07

Mugluck


1 Answers

Ok after some investigation into Laravel's 'make', 'mapinto' and 'map' methods for collections, I eventually got a working result from this conversion here:

$productData = ThirdPartyAPI;
$productData = $productData->pull($query, $additionalParams);
$productData = $productData['products'];

$products = collect($productData)->map(function($row){
      return ProductsResource::make($row)->resolve();
});
var_dump($products);

That var_dump returns this:

object(Illuminate\Support\Collection)[228]
  protected 'items' => 
    array (size=3)
      0 => 
        array (size=3)
          'name' => string '10A Dimmer' (length=10)
          'description' => string '10amp Dimmer (Max 2.4k)' (length=23)
          'price' => string '5.0' (length=3)
      ....
        [And so on]

The initial information that was returned was a multidimensional array.

$returnedArray = array(
    ['array' => 1, 'name' => 'name', etc],
    ['array' => 2, 'name' => 'name, etc]
);

Laravel's default collection method only turns the top array into a collection. In order to properly be able to control the results via the Resource models we have to convert the whole set of arrays to collections, which means we have to iterate through the returned data to convert it to something laravel can read properly. That's what the map method does.

According to the docs it, 'The map method iterates through the collection and passes each value to the given callback. The callback is free to modify the item and return it, thus forming a new collection of modified items'

the make method creates a new collection instance. I don't know what the resolve function does except that the docs mention that it 'resolves a given class or interface name to its instance using the service container'. I'm going to assume it means that it makes sure passes through the class properly?

Anyway I hope that helps people in the future.

like image 135
Mugluck Avatar answered Oct 12 '22 01:10

Mugluck