Traversing through deeply nested JSON object












0















When interacting with an API used for building forms, I make an API call to get all the response values associated with my form. The API returns a deeply nested JSON object with all my form values.



One of the many response objects looks like this:



{
"title":{
"plain":"Send Money"
},
"fieldset":[
{
"label":{
"plain":"Personal Info Section"
},
"fieldset":[
{
"field":[
{
"label":{
"plain":"First Name"
},
"value":{
"plain":"Bob"
},
"id":"a_1"
},
{
"label":{
"plain":"Last Name"
},
"value":{
"plain":"Hogan"
},
"id":"a_2"
}
],
"id":"a_8"
}
],
"id":"a_5"
},
{
"label":{
"plain":"Billing Details Section"
},
"fieldset":{
"field":{
"choices":{
"choice":{
"label":{
"plain":"Gift"
},
"id":"a_17",
"switch":""
}
},
"label":{
"plain":"Choose a category:"
},
"value":{
"plain":"Gift"
},
"id":"a_14"
},
"fieldset":{
"label":{
"plain":""
},
"field":[
{
"choices":{
"choice":{
"label":{
"plain":"Other"
},
"id":"a_25",
"switch":""
}
},
"label":{
"plain":"Amount"
},
"value":{
"plain":"Other" //(This could also be a dollar amount like 10.00)
},
"id":"a_21"
},
{
"label":{
"plain":"Other Amount"
},
"value":{
"plain":"200"
},
"id":"a_20"
}
],
"id":"a_26"
},
"id":"a_13"
},
"id":"a_12"
}
]
}


The goal here is to run a report of all responses and print the data out in a readable way (e.g. "Bob Hogan - $200, Chad Smith - $100").



I'm thinking I'll have to use some sort of map-reduce algorithm because simply nesting a bunch of loops can be unscalable as well as computationally expensive given the increasing time complexity if its a large dataset. Maybe I have to write a recursive function that maps through my dataset, checks the id value, reduces it down to an array if it finds a matching id?



Additionally, I'd like to avoid using a 3rd party library. PHP has enough native functions to facilitate what I'm trying to accomplish.










share|improve this question





























    0















    When interacting with an API used for building forms, I make an API call to get all the response values associated with my form. The API returns a deeply nested JSON object with all my form values.



    One of the many response objects looks like this:



    {
    "title":{
    "plain":"Send Money"
    },
    "fieldset":[
    {
    "label":{
    "plain":"Personal Info Section"
    },
    "fieldset":[
    {
    "field":[
    {
    "label":{
    "plain":"First Name"
    },
    "value":{
    "plain":"Bob"
    },
    "id":"a_1"
    },
    {
    "label":{
    "plain":"Last Name"
    },
    "value":{
    "plain":"Hogan"
    },
    "id":"a_2"
    }
    ],
    "id":"a_8"
    }
    ],
    "id":"a_5"
    },
    {
    "label":{
    "plain":"Billing Details Section"
    },
    "fieldset":{
    "field":{
    "choices":{
    "choice":{
    "label":{
    "plain":"Gift"
    },
    "id":"a_17",
    "switch":""
    }
    },
    "label":{
    "plain":"Choose a category:"
    },
    "value":{
    "plain":"Gift"
    },
    "id":"a_14"
    },
    "fieldset":{
    "label":{
    "plain":""
    },
    "field":[
    {
    "choices":{
    "choice":{
    "label":{
    "plain":"Other"
    },
    "id":"a_25",
    "switch":""
    }
    },
    "label":{
    "plain":"Amount"
    },
    "value":{
    "plain":"Other" //(This could also be a dollar amount like 10.00)
    },
    "id":"a_21"
    },
    {
    "label":{
    "plain":"Other Amount"
    },
    "value":{
    "plain":"200"
    },
    "id":"a_20"
    }
    ],
    "id":"a_26"
    },
    "id":"a_13"
    },
    "id":"a_12"
    }
    ]
    }


    The goal here is to run a report of all responses and print the data out in a readable way (e.g. "Bob Hogan - $200, Chad Smith - $100").



    I'm thinking I'll have to use some sort of map-reduce algorithm because simply nesting a bunch of loops can be unscalable as well as computationally expensive given the increasing time complexity if its a large dataset. Maybe I have to write a recursive function that maps through my dataset, checks the id value, reduces it down to an array if it finds a matching id?



    Additionally, I'd like to avoid using a 3rd party library. PHP has enough native functions to facilitate what I'm trying to accomplish.










    share|improve this question



























      0












      0








      0


      1






      When interacting with an API used for building forms, I make an API call to get all the response values associated with my form. The API returns a deeply nested JSON object with all my form values.



      One of the many response objects looks like this:



      {
      "title":{
      "plain":"Send Money"
      },
      "fieldset":[
      {
      "label":{
      "plain":"Personal Info Section"
      },
      "fieldset":[
      {
      "field":[
      {
      "label":{
      "plain":"First Name"
      },
      "value":{
      "plain":"Bob"
      },
      "id":"a_1"
      },
      {
      "label":{
      "plain":"Last Name"
      },
      "value":{
      "plain":"Hogan"
      },
      "id":"a_2"
      }
      ],
      "id":"a_8"
      }
      ],
      "id":"a_5"
      },
      {
      "label":{
      "plain":"Billing Details Section"
      },
      "fieldset":{
      "field":{
      "choices":{
      "choice":{
      "label":{
      "plain":"Gift"
      },
      "id":"a_17",
      "switch":""
      }
      },
      "label":{
      "plain":"Choose a category:"
      },
      "value":{
      "plain":"Gift"
      },
      "id":"a_14"
      },
      "fieldset":{
      "label":{
      "plain":""
      },
      "field":[
      {
      "choices":{
      "choice":{
      "label":{
      "plain":"Other"
      },
      "id":"a_25",
      "switch":""
      }
      },
      "label":{
      "plain":"Amount"
      },
      "value":{
      "plain":"Other" //(This could also be a dollar amount like 10.00)
      },
      "id":"a_21"
      },
      {
      "label":{
      "plain":"Other Amount"
      },
      "value":{
      "plain":"200"
      },
      "id":"a_20"
      }
      ],
      "id":"a_26"
      },
      "id":"a_13"
      },
      "id":"a_12"
      }
      ]
      }


      The goal here is to run a report of all responses and print the data out in a readable way (e.g. "Bob Hogan - $200, Chad Smith - $100").



      I'm thinking I'll have to use some sort of map-reduce algorithm because simply nesting a bunch of loops can be unscalable as well as computationally expensive given the increasing time complexity if its a large dataset. Maybe I have to write a recursive function that maps through my dataset, checks the id value, reduces it down to an array if it finds a matching id?



      Additionally, I'd like to avoid using a 3rd party library. PHP has enough native functions to facilitate what I'm trying to accomplish.










      share|improve this question
















      When interacting with an API used for building forms, I make an API call to get all the response values associated with my form. The API returns a deeply nested JSON object with all my form values.



      One of the many response objects looks like this:



      {
      "title":{
      "plain":"Send Money"
      },
      "fieldset":[
      {
      "label":{
      "plain":"Personal Info Section"
      },
      "fieldset":[
      {
      "field":[
      {
      "label":{
      "plain":"First Name"
      },
      "value":{
      "plain":"Bob"
      },
      "id":"a_1"
      },
      {
      "label":{
      "plain":"Last Name"
      },
      "value":{
      "plain":"Hogan"
      },
      "id":"a_2"
      }
      ],
      "id":"a_8"
      }
      ],
      "id":"a_5"
      },
      {
      "label":{
      "plain":"Billing Details Section"
      },
      "fieldset":{
      "field":{
      "choices":{
      "choice":{
      "label":{
      "plain":"Gift"
      },
      "id":"a_17",
      "switch":""
      }
      },
      "label":{
      "plain":"Choose a category:"
      },
      "value":{
      "plain":"Gift"
      },
      "id":"a_14"
      },
      "fieldset":{
      "label":{
      "plain":""
      },
      "field":[
      {
      "choices":{
      "choice":{
      "label":{
      "plain":"Other"
      },
      "id":"a_25",
      "switch":""
      }
      },
      "label":{
      "plain":"Amount"
      },
      "value":{
      "plain":"Other" //(This could also be a dollar amount like 10.00)
      },
      "id":"a_21"
      },
      {
      "label":{
      "plain":"Other Amount"
      },
      "value":{
      "plain":"200"
      },
      "id":"a_20"
      }
      ],
      "id":"a_26"
      },
      "id":"a_13"
      },
      "id":"a_12"
      }
      ]
      }


      The goal here is to run a report of all responses and print the data out in a readable way (e.g. "Bob Hogan - $200, Chad Smith - $100").



      I'm thinking I'll have to use some sort of map-reduce algorithm because simply nesting a bunch of loops can be unscalable as well as computationally expensive given the increasing time complexity if its a large dataset. Maybe I have to write a recursive function that maps through my dataset, checks the id value, reduces it down to an array if it finds a matching id?



      Additionally, I'd like to avoid using a 3rd party library. PHP has enough native functions to facilitate what I'm trying to accomplish.







      json mapreduce associative-array php-7






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 22 '18 at 11:44









      Flimzy

      40.2k1367100




      40.2k1367100










      asked Nov 21 '18 at 23:12









      lsimonettilsimonetti

      4651616




      4651616
























          1 Answer
          1






          active

          oldest

          votes


















          1














          Actually there 's no need for a magic algorithm. Just a bit of php magic in form of entites, hydrators and filters.



          In this answer you 'll get an object orientated php approach, which will hydrate the json api response into objects, which you can easily filter. Just keep in mind, that in this oop apporach everything is an object.



          The data object - data entity



          First of all you have to know, how your data is structured. From this structury you can build php objects. From the given JSON structure you can use the following objects.



          namespace ApplicationEntity;

          // Just for recognizing entities as entities later
          interface EntityInterface
          {

          }

          class Title implements EntityInterface, JsonSerializable
          {
          public $plain;

          public function getPlain() : ?string
          {
          return $this->plain;
          }

          public function setPlain(string $plain) : Title
          {
          $this->plain = $plain;
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }

          class Fieldset implements EntityInterface, JsonSerializable
          {
          /**
          * Label object
          * @var Label
          */
          public $label;

          /**
          * Collection of Field objects
          * @var ArrayObject
          */
          public $fieldset;

          // implement getter and setter methods here
          }

          class Section implements EntityInterface, JsonSerializable
          {
          public $title;

          public $fieldsets;

          public function getTitle() : ?Title
          {
          return $this->title;
          }

          public function setTitle(Title $title) : Section
          {
          $this->title = $title;
          return $this;
          }

          public function getFieldsets() : ArrayObject
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          return $this->fieldsets;
          }

          public function setFieldsets(Fieldset $fieldset) : Section
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          $this->fieldsets->append($fieldset);
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }


          Well, this class depicts the properties of the very first json object given in your example. Why this class implements the JsonSerializable interface? With this implementation you are able to transform the class structure back into a well formed json string. I 'm not sure, if you need that. But for sure it is safe, while communicating with a rest api. The only thing you have to do now is programming entities for every expected complex data structure / json object. You need the title object with a plin property and a fieldset object with label and fieldset properties and so on.



          How to get the json data into a php object - hydration



          Of course, your given json structure is a string. When we talk about hydration we actually mean converting a json string into an object structure. The above mentioned entites are needed for this approach.



          But first the hydrator class itself.



          namespace ApplicationHydrator;
          use ApplicationEntityEntityInterface;

          class ClassMethodsHydrator
          {
          protected $strategies;

          public function hydrate(array $data, EntityInterface $entity) : EntityInterface
          {
          foreach ($data as $key => $value) {
          if (!method_exists($entity, 'set' . ucfirst($key)) {
          throw new InvalidArgumentException(sprintf(
          'The method %s does not exist in %s',
          get_class($entity)
          ));
          }

          if ($this->strategies[$key]) {
          $strategy = $this->strategies[$key];
          $value = $strategy->hydrate($value);
          }

          $entity->{'set' . ucfirst($key)}($value);
          }

          return $entity;
          }

          public function addStrategy(string $name, StrategyInterface $strategy) : Hydrator
          {
          $this->strategies[$name] = $strategy;
          return $this;
          }
          }


          Well, this is the class where all the magic happens. I guess this is what you mentioned as algorithm. The hydrator takes your data from the json response and pushes it into your entities. When having the entites hydrated, you can easily access the given data by calling the get methods of the entities.
          As the json data is complex and nested, we have to use hydrator strategies. A common pattern in hydration concerns. A strategy can be hooked into a object property and executes another hydrator. So we make sure that we represent the nested data in an identical object structure.



          Here 's an example of a hydrator strategy.



          namespace ApplicationHydratorStrategy;
          use ApplicationEntityEntityInterface;

          interface HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface;
          }

          use ApplicationEntityTitle;
          class TitleHydratorStrategy implements HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface
          {
          $value = (new ClassMethods())->hydrate($value, new Title);
          return $value;
          }
          }

          // Use case of a strategy
          $data = json_decode$($response, true);
          $section = (new ClassMethods())
          ->addStrategy('title', new TitleHydratorStrategy())
          ->hydrate($data, new Section());


          So what a hydrator strategy actually does? While iterating over our json api response, there are severel elements, which are an object or contain objects. To hydrate this multidimensional structure correctly, we use strategies.



          To stay with your example of the JSON response I 've added a simple use case. First we decode the json response into an assiciative, multidimensional array. After that we use our entities, hydrators and hydrator strategies to get an object, which contains all the data. The use case knows, that the title property in the JSON response is an object that should hydrated into our title entity, which contains the plain property.



          At the end our hydrated object has a structure like this ...



          ApplicationEntitySection {
          public:title => ApplicationEntityTitle [
          public:plain => string 'Send Money'
          }
          ...
          }


          Actually you can access the properties with the getter methods of our entities.



          echo $section->getTitle()->getPlain(); // echoes 'Send money'


          Knowing how to hydrate our classes leads us to the next step. Aggregation!



          Getting the full string with aggregation



          Actually aggregation is a common design pattern in modern object orientated programming. Aggregation means no more and no less than the allocation of data. Let 's have a look at your posted JSON response. As we can see the fieldset property of our root object contains a collection of fieldset objects, that we can access via our getter and setter methods. With this in mind, we can create additional getter methods in our section entity. Let us expand our section entity with a getFullName method.



          ...
          public function getFullName() : string
          {
          $firstname = $lastname = '';

          // fetch the personal info section
          if ($this->getFieldsets()->offsetExists(0)) {
          $personalInfoFieldset = $this->getFieldsets()->offsetGet(0)->getFiedlset()->offsetGet(0);
          $firstname = $personalInfoFieldset->getField()->offsetGet(0)->getValue();
          $lastname = $personalInfoFieldset->getField()->offsetGet(1)->getValue();
          }

          return $this->concatenate(' ', $firstname, $lastname);
          }

          public function concatenate(string $filler, ...$strings) : string
          {
          $string = '';
          foreach ($strings as $partial) {
          $string .= $partial . $filler;
          }

          return trim($string);
          }


          This example assumes, that both the first name and the last name are available in the very first item of the fieldset collection of the section entity. So we get Bob Hogan as return value. The concatenate method is just a little helper, which concatenates a number of strings with a filler (space).



          Filter data using our entities and the FilterIterator class



          Further you mentioned, that you have to find specific data by id. One possible solution coould be filtering our entities by a specific item with the Filter Iterator class.



          namespace ApplicationFilter;

          class PersonIdFilter extends FilterIterator
          {
          protected $id;

          public function __construct(Iterator $iterator, string $id)
          {
          parent::__construct($iterator);
          $this->id = $id;
          }

          public function accept()
          {
          $person = $this->getInnerIterator()->current();
          return ($person->getId() == $this->id) ? true : false;
          }
          }


          Because of using ArrayObject classes for our collections we are able to use iterators to filter for a specific argument. In this case we filter for the id in our personal info fieldsets.



          Starting from our hydration example we it could be something like the following code.



          $personalIterator = $section->getFieldsets()->offsetGet(0)->getFieldset()->getIterator();
          $filter = new PersonIdFilter($personalIterator, 'a_8');
          foreach ($filter as $result) {
          var_dump($result); // will output the first fieldset with the personal data
          }


          Too complex? Absolutely not!



          As you said you want a scalable solution without nested iterations in a huge loop. In my eyes it makes sense not writing just a huge single function, wich iterates the json response and returns the data you want. Working with objects in this case makes mch more sense because of the high scalability. You can access all the data you want in a glimpse by calling the right getter methods. Furthermore the code is mich more readable than a huge function which is recursivly iterating again and again. In the above shown approach you only code once and reuse all the objects again and again.



          Please keep in mind, the the above shown code is just a theoretical suggestion. It is not tested.






          share|improve this answer
























          • Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

            – lsimonetti
            Nov 27 '18 at 13:59











          • I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

            – Marcel
            Nov 27 '18 at 19:39












          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421756%2ftraversing-through-deeply-nested-json-object%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Actually there 's no need for a magic algorithm. Just a bit of php magic in form of entites, hydrators and filters.



          In this answer you 'll get an object orientated php approach, which will hydrate the json api response into objects, which you can easily filter. Just keep in mind, that in this oop apporach everything is an object.



          The data object - data entity



          First of all you have to know, how your data is structured. From this structury you can build php objects. From the given JSON structure you can use the following objects.



          namespace ApplicationEntity;

          // Just for recognizing entities as entities later
          interface EntityInterface
          {

          }

          class Title implements EntityInterface, JsonSerializable
          {
          public $plain;

          public function getPlain() : ?string
          {
          return $this->plain;
          }

          public function setPlain(string $plain) : Title
          {
          $this->plain = $plain;
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }

          class Fieldset implements EntityInterface, JsonSerializable
          {
          /**
          * Label object
          * @var Label
          */
          public $label;

          /**
          * Collection of Field objects
          * @var ArrayObject
          */
          public $fieldset;

          // implement getter and setter methods here
          }

          class Section implements EntityInterface, JsonSerializable
          {
          public $title;

          public $fieldsets;

          public function getTitle() : ?Title
          {
          return $this->title;
          }

          public function setTitle(Title $title) : Section
          {
          $this->title = $title;
          return $this;
          }

          public function getFieldsets() : ArrayObject
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          return $this->fieldsets;
          }

          public function setFieldsets(Fieldset $fieldset) : Section
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          $this->fieldsets->append($fieldset);
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }


          Well, this class depicts the properties of the very first json object given in your example. Why this class implements the JsonSerializable interface? With this implementation you are able to transform the class structure back into a well formed json string. I 'm not sure, if you need that. But for sure it is safe, while communicating with a rest api. The only thing you have to do now is programming entities for every expected complex data structure / json object. You need the title object with a plin property and a fieldset object with label and fieldset properties and so on.



          How to get the json data into a php object - hydration



          Of course, your given json structure is a string. When we talk about hydration we actually mean converting a json string into an object structure. The above mentioned entites are needed for this approach.



          But first the hydrator class itself.



          namespace ApplicationHydrator;
          use ApplicationEntityEntityInterface;

          class ClassMethodsHydrator
          {
          protected $strategies;

          public function hydrate(array $data, EntityInterface $entity) : EntityInterface
          {
          foreach ($data as $key => $value) {
          if (!method_exists($entity, 'set' . ucfirst($key)) {
          throw new InvalidArgumentException(sprintf(
          'The method %s does not exist in %s',
          get_class($entity)
          ));
          }

          if ($this->strategies[$key]) {
          $strategy = $this->strategies[$key];
          $value = $strategy->hydrate($value);
          }

          $entity->{'set' . ucfirst($key)}($value);
          }

          return $entity;
          }

          public function addStrategy(string $name, StrategyInterface $strategy) : Hydrator
          {
          $this->strategies[$name] = $strategy;
          return $this;
          }
          }


          Well, this is the class where all the magic happens. I guess this is what you mentioned as algorithm. The hydrator takes your data from the json response and pushes it into your entities. When having the entites hydrated, you can easily access the given data by calling the get methods of the entities.
          As the json data is complex and nested, we have to use hydrator strategies. A common pattern in hydration concerns. A strategy can be hooked into a object property and executes another hydrator. So we make sure that we represent the nested data in an identical object structure.



          Here 's an example of a hydrator strategy.



          namespace ApplicationHydratorStrategy;
          use ApplicationEntityEntityInterface;

          interface HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface;
          }

          use ApplicationEntityTitle;
          class TitleHydratorStrategy implements HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface
          {
          $value = (new ClassMethods())->hydrate($value, new Title);
          return $value;
          }
          }

          // Use case of a strategy
          $data = json_decode$($response, true);
          $section = (new ClassMethods())
          ->addStrategy('title', new TitleHydratorStrategy())
          ->hydrate($data, new Section());


          So what a hydrator strategy actually does? While iterating over our json api response, there are severel elements, which are an object or contain objects. To hydrate this multidimensional structure correctly, we use strategies.



          To stay with your example of the JSON response I 've added a simple use case. First we decode the json response into an assiciative, multidimensional array. After that we use our entities, hydrators and hydrator strategies to get an object, which contains all the data. The use case knows, that the title property in the JSON response is an object that should hydrated into our title entity, which contains the plain property.



          At the end our hydrated object has a structure like this ...



          ApplicationEntitySection {
          public:title => ApplicationEntityTitle [
          public:plain => string 'Send Money'
          }
          ...
          }


          Actually you can access the properties with the getter methods of our entities.



          echo $section->getTitle()->getPlain(); // echoes 'Send money'


          Knowing how to hydrate our classes leads us to the next step. Aggregation!



          Getting the full string with aggregation



          Actually aggregation is a common design pattern in modern object orientated programming. Aggregation means no more and no less than the allocation of data. Let 's have a look at your posted JSON response. As we can see the fieldset property of our root object contains a collection of fieldset objects, that we can access via our getter and setter methods. With this in mind, we can create additional getter methods in our section entity. Let us expand our section entity with a getFullName method.



          ...
          public function getFullName() : string
          {
          $firstname = $lastname = '';

          // fetch the personal info section
          if ($this->getFieldsets()->offsetExists(0)) {
          $personalInfoFieldset = $this->getFieldsets()->offsetGet(0)->getFiedlset()->offsetGet(0);
          $firstname = $personalInfoFieldset->getField()->offsetGet(0)->getValue();
          $lastname = $personalInfoFieldset->getField()->offsetGet(1)->getValue();
          }

          return $this->concatenate(' ', $firstname, $lastname);
          }

          public function concatenate(string $filler, ...$strings) : string
          {
          $string = '';
          foreach ($strings as $partial) {
          $string .= $partial . $filler;
          }

          return trim($string);
          }


          This example assumes, that both the first name and the last name are available in the very first item of the fieldset collection of the section entity. So we get Bob Hogan as return value. The concatenate method is just a little helper, which concatenates a number of strings with a filler (space).



          Filter data using our entities and the FilterIterator class



          Further you mentioned, that you have to find specific data by id. One possible solution coould be filtering our entities by a specific item with the Filter Iterator class.



          namespace ApplicationFilter;

          class PersonIdFilter extends FilterIterator
          {
          protected $id;

          public function __construct(Iterator $iterator, string $id)
          {
          parent::__construct($iterator);
          $this->id = $id;
          }

          public function accept()
          {
          $person = $this->getInnerIterator()->current();
          return ($person->getId() == $this->id) ? true : false;
          }
          }


          Because of using ArrayObject classes for our collections we are able to use iterators to filter for a specific argument. In this case we filter for the id in our personal info fieldsets.



          Starting from our hydration example we it could be something like the following code.



          $personalIterator = $section->getFieldsets()->offsetGet(0)->getFieldset()->getIterator();
          $filter = new PersonIdFilter($personalIterator, 'a_8');
          foreach ($filter as $result) {
          var_dump($result); // will output the first fieldset with the personal data
          }


          Too complex? Absolutely not!



          As you said you want a scalable solution without nested iterations in a huge loop. In my eyes it makes sense not writing just a huge single function, wich iterates the json response and returns the data you want. Working with objects in this case makes mch more sense because of the high scalability. You can access all the data you want in a glimpse by calling the right getter methods. Furthermore the code is mich more readable than a huge function which is recursivly iterating again and again. In the above shown approach you only code once and reuse all the objects again and again.



          Please keep in mind, the the above shown code is just a theoretical suggestion. It is not tested.






          share|improve this answer
























          • Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

            – lsimonetti
            Nov 27 '18 at 13:59











          • I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

            – Marcel
            Nov 27 '18 at 19:39
















          1














          Actually there 's no need for a magic algorithm. Just a bit of php magic in form of entites, hydrators and filters.



          In this answer you 'll get an object orientated php approach, which will hydrate the json api response into objects, which you can easily filter. Just keep in mind, that in this oop apporach everything is an object.



          The data object - data entity



          First of all you have to know, how your data is structured. From this structury you can build php objects. From the given JSON structure you can use the following objects.



          namespace ApplicationEntity;

          // Just for recognizing entities as entities later
          interface EntityInterface
          {

          }

          class Title implements EntityInterface, JsonSerializable
          {
          public $plain;

          public function getPlain() : ?string
          {
          return $this->plain;
          }

          public function setPlain(string $plain) : Title
          {
          $this->plain = $plain;
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }

          class Fieldset implements EntityInterface, JsonSerializable
          {
          /**
          * Label object
          * @var Label
          */
          public $label;

          /**
          * Collection of Field objects
          * @var ArrayObject
          */
          public $fieldset;

          // implement getter and setter methods here
          }

          class Section implements EntityInterface, JsonSerializable
          {
          public $title;

          public $fieldsets;

          public function getTitle() : ?Title
          {
          return $this->title;
          }

          public function setTitle(Title $title) : Section
          {
          $this->title = $title;
          return $this;
          }

          public function getFieldsets() : ArrayObject
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          return $this->fieldsets;
          }

          public function setFieldsets(Fieldset $fieldset) : Section
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          $this->fieldsets->append($fieldset);
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }


          Well, this class depicts the properties of the very first json object given in your example. Why this class implements the JsonSerializable interface? With this implementation you are able to transform the class structure back into a well formed json string. I 'm not sure, if you need that. But for sure it is safe, while communicating with a rest api. The only thing you have to do now is programming entities for every expected complex data structure / json object. You need the title object with a plin property and a fieldset object with label and fieldset properties and so on.



          How to get the json data into a php object - hydration



          Of course, your given json structure is a string. When we talk about hydration we actually mean converting a json string into an object structure. The above mentioned entites are needed for this approach.



          But first the hydrator class itself.



          namespace ApplicationHydrator;
          use ApplicationEntityEntityInterface;

          class ClassMethodsHydrator
          {
          protected $strategies;

          public function hydrate(array $data, EntityInterface $entity) : EntityInterface
          {
          foreach ($data as $key => $value) {
          if (!method_exists($entity, 'set' . ucfirst($key)) {
          throw new InvalidArgumentException(sprintf(
          'The method %s does not exist in %s',
          get_class($entity)
          ));
          }

          if ($this->strategies[$key]) {
          $strategy = $this->strategies[$key];
          $value = $strategy->hydrate($value);
          }

          $entity->{'set' . ucfirst($key)}($value);
          }

          return $entity;
          }

          public function addStrategy(string $name, StrategyInterface $strategy) : Hydrator
          {
          $this->strategies[$name] = $strategy;
          return $this;
          }
          }


          Well, this is the class where all the magic happens. I guess this is what you mentioned as algorithm. The hydrator takes your data from the json response and pushes it into your entities. When having the entites hydrated, you can easily access the given data by calling the get methods of the entities.
          As the json data is complex and nested, we have to use hydrator strategies. A common pattern in hydration concerns. A strategy can be hooked into a object property and executes another hydrator. So we make sure that we represent the nested data in an identical object structure.



          Here 's an example of a hydrator strategy.



          namespace ApplicationHydratorStrategy;
          use ApplicationEntityEntityInterface;

          interface HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface;
          }

          use ApplicationEntityTitle;
          class TitleHydratorStrategy implements HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface
          {
          $value = (new ClassMethods())->hydrate($value, new Title);
          return $value;
          }
          }

          // Use case of a strategy
          $data = json_decode$($response, true);
          $section = (new ClassMethods())
          ->addStrategy('title', new TitleHydratorStrategy())
          ->hydrate($data, new Section());


          So what a hydrator strategy actually does? While iterating over our json api response, there are severel elements, which are an object or contain objects. To hydrate this multidimensional structure correctly, we use strategies.



          To stay with your example of the JSON response I 've added a simple use case. First we decode the json response into an assiciative, multidimensional array. After that we use our entities, hydrators and hydrator strategies to get an object, which contains all the data. The use case knows, that the title property in the JSON response is an object that should hydrated into our title entity, which contains the plain property.



          At the end our hydrated object has a structure like this ...



          ApplicationEntitySection {
          public:title => ApplicationEntityTitle [
          public:plain => string 'Send Money'
          }
          ...
          }


          Actually you can access the properties with the getter methods of our entities.



          echo $section->getTitle()->getPlain(); // echoes 'Send money'


          Knowing how to hydrate our classes leads us to the next step. Aggregation!



          Getting the full string with aggregation



          Actually aggregation is a common design pattern in modern object orientated programming. Aggregation means no more and no less than the allocation of data. Let 's have a look at your posted JSON response. As we can see the fieldset property of our root object contains a collection of fieldset objects, that we can access via our getter and setter methods. With this in mind, we can create additional getter methods in our section entity. Let us expand our section entity with a getFullName method.



          ...
          public function getFullName() : string
          {
          $firstname = $lastname = '';

          // fetch the personal info section
          if ($this->getFieldsets()->offsetExists(0)) {
          $personalInfoFieldset = $this->getFieldsets()->offsetGet(0)->getFiedlset()->offsetGet(0);
          $firstname = $personalInfoFieldset->getField()->offsetGet(0)->getValue();
          $lastname = $personalInfoFieldset->getField()->offsetGet(1)->getValue();
          }

          return $this->concatenate(' ', $firstname, $lastname);
          }

          public function concatenate(string $filler, ...$strings) : string
          {
          $string = '';
          foreach ($strings as $partial) {
          $string .= $partial . $filler;
          }

          return trim($string);
          }


          This example assumes, that both the first name and the last name are available in the very first item of the fieldset collection of the section entity. So we get Bob Hogan as return value. The concatenate method is just a little helper, which concatenates a number of strings with a filler (space).



          Filter data using our entities and the FilterIterator class



          Further you mentioned, that you have to find specific data by id. One possible solution coould be filtering our entities by a specific item with the Filter Iterator class.



          namespace ApplicationFilter;

          class PersonIdFilter extends FilterIterator
          {
          protected $id;

          public function __construct(Iterator $iterator, string $id)
          {
          parent::__construct($iterator);
          $this->id = $id;
          }

          public function accept()
          {
          $person = $this->getInnerIterator()->current();
          return ($person->getId() == $this->id) ? true : false;
          }
          }


          Because of using ArrayObject classes for our collections we are able to use iterators to filter for a specific argument. In this case we filter for the id in our personal info fieldsets.



          Starting from our hydration example we it could be something like the following code.



          $personalIterator = $section->getFieldsets()->offsetGet(0)->getFieldset()->getIterator();
          $filter = new PersonIdFilter($personalIterator, 'a_8');
          foreach ($filter as $result) {
          var_dump($result); // will output the first fieldset with the personal data
          }


          Too complex? Absolutely not!



          As you said you want a scalable solution without nested iterations in a huge loop. In my eyes it makes sense not writing just a huge single function, wich iterates the json response and returns the data you want. Working with objects in this case makes mch more sense because of the high scalability. You can access all the data you want in a glimpse by calling the right getter methods. Furthermore the code is mich more readable than a huge function which is recursivly iterating again and again. In the above shown approach you only code once and reuse all the objects again and again.



          Please keep in mind, the the above shown code is just a theoretical suggestion. It is not tested.






          share|improve this answer
























          • Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

            – lsimonetti
            Nov 27 '18 at 13:59











          • I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

            – Marcel
            Nov 27 '18 at 19:39














          1












          1








          1







          Actually there 's no need for a magic algorithm. Just a bit of php magic in form of entites, hydrators and filters.



          In this answer you 'll get an object orientated php approach, which will hydrate the json api response into objects, which you can easily filter. Just keep in mind, that in this oop apporach everything is an object.



          The data object - data entity



          First of all you have to know, how your data is structured. From this structury you can build php objects. From the given JSON structure you can use the following objects.



          namespace ApplicationEntity;

          // Just for recognizing entities as entities later
          interface EntityInterface
          {

          }

          class Title implements EntityInterface, JsonSerializable
          {
          public $plain;

          public function getPlain() : ?string
          {
          return $this->plain;
          }

          public function setPlain(string $plain) : Title
          {
          $this->plain = $plain;
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }

          class Fieldset implements EntityInterface, JsonSerializable
          {
          /**
          * Label object
          * @var Label
          */
          public $label;

          /**
          * Collection of Field objects
          * @var ArrayObject
          */
          public $fieldset;

          // implement getter and setter methods here
          }

          class Section implements EntityInterface, JsonSerializable
          {
          public $title;

          public $fieldsets;

          public function getTitle() : ?Title
          {
          return $this->title;
          }

          public function setTitle(Title $title) : Section
          {
          $this->title = $title;
          return $this;
          }

          public function getFieldsets() : ArrayObject
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          return $this->fieldsets;
          }

          public function setFieldsets(Fieldset $fieldset) : Section
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          $this->fieldsets->append($fieldset);
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }


          Well, this class depicts the properties of the very first json object given in your example. Why this class implements the JsonSerializable interface? With this implementation you are able to transform the class structure back into a well formed json string. I 'm not sure, if you need that. But for sure it is safe, while communicating with a rest api. The only thing you have to do now is programming entities for every expected complex data structure / json object. You need the title object with a plin property and a fieldset object with label and fieldset properties and so on.



          How to get the json data into a php object - hydration



          Of course, your given json structure is a string. When we talk about hydration we actually mean converting a json string into an object structure. The above mentioned entites are needed for this approach.



          But first the hydrator class itself.



          namespace ApplicationHydrator;
          use ApplicationEntityEntityInterface;

          class ClassMethodsHydrator
          {
          protected $strategies;

          public function hydrate(array $data, EntityInterface $entity) : EntityInterface
          {
          foreach ($data as $key => $value) {
          if (!method_exists($entity, 'set' . ucfirst($key)) {
          throw new InvalidArgumentException(sprintf(
          'The method %s does not exist in %s',
          get_class($entity)
          ));
          }

          if ($this->strategies[$key]) {
          $strategy = $this->strategies[$key];
          $value = $strategy->hydrate($value);
          }

          $entity->{'set' . ucfirst($key)}($value);
          }

          return $entity;
          }

          public function addStrategy(string $name, StrategyInterface $strategy) : Hydrator
          {
          $this->strategies[$name] = $strategy;
          return $this;
          }
          }


          Well, this is the class where all the magic happens. I guess this is what you mentioned as algorithm. The hydrator takes your data from the json response and pushes it into your entities. When having the entites hydrated, you can easily access the given data by calling the get methods of the entities.
          As the json data is complex and nested, we have to use hydrator strategies. A common pattern in hydration concerns. A strategy can be hooked into a object property and executes another hydrator. So we make sure that we represent the nested data in an identical object structure.



          Here 's an example of a hydrator strategy.



          namespace ApplicationHydratorStrategy;
          use ApplicationEntityEntityInterface;

          interface HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface;
          }

          use ApplicationEntityTitle;
          class TitleHydratorStrategy implements HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface
          {
          $value = (new ClassMethods())->hydrate($value, new Title);
          return $value;
          }
          }

          // Use case of a strategy
          $data = json_decode$($response, true);
          $section = (new ClassMethods())
          ->addStrategy('title', new TitleHydratorStrategy())
          ->hydrate($data, new Section());


          So what a hydrator strategy actually does? While iterating over our json api response, there are severel elements, which are an object or contain objects. To hydrate this multidimensional structure correctly, we use strategies.



          To stay with your example of the JSON response I 've added a simple use case. First we decode the json response into an assiciative, multidimensional array. After that we use our entities, hydrators and hydrator strategies to get an object, which contains all the data. The use case knows, that the title property in the JSON response is an object that should hydrated into our title entity, which contains the plain property.



          At the end our hydrated object has a structure like this ...



          ApplicationEntitySection {
          public:title => ApplicationEntityTitle [
          public:plain => string 'Send Money'
          }
          ...
          }


          Actually you can access the properties with the getter methods of our entities.



          echo $section->getTitle()->getPlain(); // echoes 'Send money'


          Knowing how to hydrate our classes leads us to the next step. Aggregation!



          Getting the full string with aggregation



          Actually aggregation is a common design pattern in modern object orientated programming. Aggregation means no more and no less than the allocation of data. Let 's have a look at your posted JSON response. As we can see the fieldset property of our root object contains a collection of fieldset objects, that we can access via our getter and setter methods. With this in mind, we can create additional getter methods in our section entity. Let us expand our section entity with a getFullName method.



          ...
          public function getFullName() : string
          {
          $firstname = $lastname = '';

          // fetch the personal info section
          if ($this->getFieldsets()->offsetExists(0)) {
          $personalInfoFieldset = $this->getFieldsets()->offsetGet(0)->getFiedlset()->offsetGet(0);
          $firstname = $personalInfoFieldset->getField()->offsetGet(0)->getValue();
          $lastname = $personalInfoFieldset->getField()->offsetGet(1)->getValue();
          }

          return $this->concatenate(' ', $firstname, $lastname);
          }

          public function concatenate(string $filler, ...$strings) : string
          {
          $string = '';
          foreach ($strings as $partial) {
          $string .= $partial . $filler;
          }

          return trim($string);
          }


          This example assumes, that both the first name and the last name are available in the very first item of the fieldset collection of the section entity. So we get Bob Hogan as return value. The concatenate method is just a little helper, which concatenates a number of strings with a filler (space).



          Filter data using our entities and the FilterIterator class



          Further you mentioned, that you have to find specific data by id. One possible solution coould be filtering our entities by a specific item with the Filter Iterator class.



          namespace ApplicationFilter;

          class PersonIdFilter extends FilterIterator
          {
          protected $id;

          public function __construct(Iterator $iterator, string $id)
          {
          parent::__construct($iterator);
          $this->id = $id;
          }

          public function accept()
          {
          $person = $this->getInnerIterator()->current();
          return ($person->getId() == $this->id) ? true : false;
          }
          }


          Because of using ArrayObject classes for our collections we are able to use iterators to filter for a specific argument. In this case we filter for the id in our personal info fieldsets.



          Starting from our hydration example we it could be something like the following code.



          $personalIterator = $section->getFieldsets()->offsetGet(0)->getFieldset()->getIterator();
          $filter = new PersonIdFilter($personalIterator, 'a_8');
          foreach ($filter as $result) {
          var_dump($result); // will output the first fieldset with the personal data
          }


          Too complex? Absolutely not!



          As you said you want a scalable solution without nested iterations in a huge loop. In my eyes it makes sense not writing just a huge single function, wich iterates the json response and returns the data you want. Working with objects in this case makes mch more sense because of the high scalability. You can access all the data you want in a glimpse by calling the right getter methods. Furthermore the code is mich more readable than a huge function which is recursivly iterating again and again. In the above shown approach you only code once and reuse all the objects again and again.



          Please keep in mind, the the above shown code is just a theoretical suggestion. It is not tested.






          share|improve this answer













          Actually there 's no need for a magic algorithm. Just a bit of php magic in form of entites, hydrators and filters.



          In this answer you 'll get an object orientated php approach, which will hydrate the json api response into objects, which you can easily filter. Just keep in mind, that in this oop apporach everything is an object.



          The data object - data entity



          First of all you have to know, how your data is structured. From this structury you can build php objects. From the given JSON structure you can use the following objects.



          namespace ApplicationEntity;

          // Just for recognizing entities as entities later
          interface EntityInterface
          {

          }

          class Title implements EntityInterface, JsonSerializable
          {
          public $plain;

          public function getPlain() : ?string
          {
          return $this->plain;
          }

          public function setPlain(string $plain) : Title
          {
          $this->plain = $plain;
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }

          class Fieldset implements EntityInterface, JsonSerializable
          {
          /**
          * Label object
          * @var Label
          */
          public $label;

          /**
          * Collection of Field objects
          * @var ArrayObject
          */
          public $fieldset;

          // implement getter and setter methods here
          }

          class Section implements EntityInterface, JsonSerializable
          {
          public $title;

          public $fieldsets;

          public function getTitle() : ?Title
          {
          return $this->title;
          }

          public function setTitle(Title $title) : Section
          {
          $this->title = $title;
          return $this;
          }

          public function getFieldsets() : ArrayObject
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          return $this->fieldsets;
          }

          public function setFieldsets(Fieldset $fieldset) : Section
          {
          if (!$this->fieldsets) {
          $this->fieldsets = new ArrayObject();
          }

          $this->fieldsets->append($fieldset);
          return $this;
          }

          public function jsonSerialize() : array
          {
          return get_object_vars($this);
          }
          }


          Well, this class depicts the properties of the very first json object given in your example. Why this class implements the JsonSerializable interface? With this implementation you are able to transform the class structure back into a well formed json string. I 'm not sure, if you need that. But for sure it is safe, while communicating with a rest api. The only thing you have to do now is programming entities for every expected complex data structure / json object. You need the title object with a plin property and a fieldset object with label and fieldset properties and so on.



          How to get the json data into a php object - hydration



          Of course, your given json structure is a string. When we talk about hydration we actually mean converting a json string into an object structure. The above mentioned entites are needed for this approach.



          But first the hydrator class itself.



          namespace ApplicationHydrator;
          use ApplicationEntityEntityInterface;

          class ClassMethodsHydrator
          {
          protected $strategies;

          public function hydrate(array $data, EntityInterface $entity) : EntityInterface
          {
          foreach ($data as $key => $value) {
          if (!method_exists($entity, 'set' . ucfirst($key)) {
          throw new InvalidArgumentException(sprintf(
          'The method %s does not exist in %s',
          get_class($entity)
          ));
          }

          if ($this->strategies[$key]) {
          $strategy = $this->strategies[$key];
          $value = $strategy->hydrate($value);
          }

          $entity->{'set' . ucfirst($key)}($value);
          }

          return $entity;
          }

          public function addStrategy(string $name, StrategyInterface $strategy) : Hydrator
          {
          $this->strategies[$name] = $strategy;
          return $this;
          }
          }


          Well, this is the class where all the magic happens. I guess this is what you mentioned as algorithm. The hydrator takes your data from the json response and pushes it into your entities. When having the entites hydrated, you can easily access the given data by calling the get methods of the entities.
          As the json data is complex and nested, we have to use hydrator strategies. A common pattern in hydration concerns. A strategy can be hooked into a object property and executes another hydrator. So we make sure that we represent the nested data in an identical object structure.



          Here 's an example of a hydrator strategy.



          namespace ApplicationHydratorStrategy;
          use ApplicationEntityEntityInterface;

          interface HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface;
          }

          use ApplicationEntityTitle;
          class TitleHydratorStrategy implements HydratorStrategy
          {
          public function hydrate(array $value) : EntityInterface
          {
          $value = (new ClassMethods())->hydrate($value, new Title);
          return $value;
          }
          }

          // Use case of a strategy
          $data = json_decode$($response, true);
          $section = (new ClassMethods())
          ->addStrategy('title', new TitleHydratorStrategy())
          ->hydrate($data, new Section());


          So what a hydrator strategy actually does? While iterating over our json api response, there are severel elements, which are an object or contain objects. To hydrate this multidimensional structure correctly, we use strategies.



          To stay with your example of the JSON response I 've added a simple use case. First we decode the json response into an assiciative, multidimensional array. After that we use our entities, hydrators and hydrator strategies to get an object, which contains all the data. The use case knows, that the title property in the JSON response is an object that should hydrated into our title entity, which contains the plain property.



          At the end our hydrated object has a structure like this ...



          ApplicationEntitySection {
          public:title => ApplicationEntityTitle [
          public:plain => string 'Send Money'
          }
          ...
          }


          Actually you can access the properties with the getter methods of our entities.



          echo $section->getTitle()->getPlain(); // echoes 'Send money'


          Knowing how to hydrate our classes leads us to the next step. Aggregation!



          Getting the full string with aggregation



          Actually aggregation is a common design pattern in modern object orientated programming. Aggregation means no more and no less than the allocation of data. Let 's have a look at your posted JSON response. As we can see the fieldset property of our root object contains a collection of fieldset objects, that we can access via our getter and setter methods. With this in mind, we can create additional getter methods in our section entity. Let us expand our section entity with a getFullName method.



          ...
          public function getFullName() : string
          {
          $firstname = $lastname = '';

          // fetch the personal info section
          if ($this->getFieldsets()->offsetExists(0)) {
          $personalInfoFieldset = $this->getFieldsets()->offsetGet(0)->getFiedlset()->offsetGet(0);
          $firstname = $personalInfoFieldset->getField()->offsetGet(0)->getValue();
          $lastname = $personalInfoFieldset->getField()->offsetGet(1)->getValue();
          }

          return $this->concatenate(' ', $firstname, $lastname);
          }

          public function concatenate(string $filler, ...$strings) : string
          {
          $string = '';
          foreach ($strings as $partial) {
          $string .= $partial . $filler;
          }

          return trim($string);
          }


          This example assumes, that both the first name and the last name are available in the very first item of the fieldset collection of the section entity. So we get Bob Hogan as return value. The concatenate method is just a little helper, which concatenates a number of strings with a filler (space).



          Filter data using our entities and the FilterIterator class



          Further you mentioned, that you have to find specific data by id. One possible solution coould be filtering our entities by a specific item with the Filter Iterator class.



          namespace ApplicationFilter;

          class PersonIdFilter extends FilterIterator
          {
          protected $id;

          public function __construct(Iterator $iterator, string $id)
          {
          parent::__construct($iterator);
          $this->id = $id;
          }

          public function accept()
          {
          $person = $this->getInnerIterator()->current();
          return ($person->getId() == $this->id) ? true : false;
          }
          }


          Because of using ArrayObject classes for our collections we are able to use iterators to filter for a specific argument. In this case we filter for the id in our personal info fieldsets.



          Starting from our hydration example we it could be something like the following code.



          $personalIterator = $section->getFieldsets()->offsetGet(0)->getFieldset()->getIterator();
          $filter = new PersonIdFilter($personalIterator, 'a_8');
          foreach ($filter as $result) {
          var_dump($result); // will output the first fieldset with the personal data
          }


          Too complex? Absolutely not!



          As you said you want a scalable solution without nested iterations in a huge loop. In my eyes it makes sense not writing just a huge single function, wich iterates the json response and returns the data you want. Working with objects in this case makes mch more sense because of the high scalability. You can access all the data you want in a glimpse by calling the right getter methods. Furthermore the code is mich more readable than a huge function which is recursivly iterating again and again. In the above shown approach you only code once and reuse all the objects again and again.



          Please keep in mind, the the above shown code is just a theoretical suggestion. It is not tested.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Nov 25 '18 at 14:16









          MarcelMarcel

          2,1401816




          2,1401816













          • Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

            – lsimonetti
            Nov 27 '18 at 13:59











          • I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

            – Marcel
            Nov 27 '18 at 19:39



















          • Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

            – lsimonetti
            Nov 27 '18 at 13:59











          • I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

            – Marcel
            Nov 27 '18 at 19:39

















          Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

          – lsimonetti
          Nov 27 '18 at 13:59





          Brilliant. I like the use of the Hydrator Strategy pattern. That makes a lot of sense. Do you think that having a huge number of responses being kept in objects would have a considerable memory impact? I'm just trying to think in terms of performance.

          – lsimonetti
          Nov 27 '18 at 13:59













          I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

          – Marcel
          Nov 27 '18 at 19:39





          I guess this kind of traversing through deeply nested data sets would affect the memory usage. As php7 optimized a lot concerning memory while handling objects and object iteration this approach would use a minimum of more memory. Actually this approach is used by zend framework and works very well. If you want to optimize the memory usage a bit, you can try to use generators when it comes to filtering the objects. PHP generators implement automatically the iterator interface while using fewer memory. I haven 't tried to yield a generator from an array object. Might be a possible solution.

          – Marcel
          Nov 27 '18 at 19:39




















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53421756%2ftraversing-through-deeply-nested-json-object%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Biblatex bibliography style without URLs when DOI exists (in Overleaf with Zotero bibliography)

          ComboBox Display Member on multiple fields

          Is it possible to collect Nectar points via Trainline?