2

My data arrives in the form of associative arrays. This works, but my code is a mess of nested arrays and I would like to start using proper OO/Objects to make this data easier to work with. I cannot change the way I receive this data, so I would like to find a clean way to convert the associative arrays into instances of my classes below.

I have these classes to model People in my application:

class Person {
  /**
   * @var string
   */
  private $_name;

  /**
   * @var array[Dog]
   */
  private $_dogs = array();

  /**
   * @var array[Hobby]
   */
  private $_hobbies = array();
}

class Dog {
  /**
   * @var string
   */
  private $_color;

  /**
   * @var string
   */
  private $_breed;
}

class Hobby {
  /**
   * @var string
   */
  private $_description;
  /**
   * @var int
   */
  private $_cost;
}

My data (in JSON) looks like this:

'person': {
  'name': 'Joe',
  'dogs': [
     {'breed': 'Husky', 'color': 'Black'},
     {'breed': 'Poodle', 'color': 'Yellow'}
   ]
  'hobbies': [
     {'description': 'Skiing', 'cost': 500},
     {'description': 'Running', 'cost': 0}
   ]
}

I can easily convert this JSON to an associative array using json_decode, but the difficulty comes in converting each of the nested Hobby and Pet objects into the appropriate classes, and then when I want the associative array back, converting these objects into associative arrays again.

I can do all this by writing a to/from array function in each of my classes but that seems rather messy and prone to error. Is there a more straightforward way that I can quickly hydrate/dehydrate these objects?

aw crud
  • 8,791
  • 19
  • 71
  • 115

3 Answers3

3

Add this to Hobby

public function fromArray($array){

if(isset($array['description'])){
    $this->_description = $array['description'];
}
if(isset($array['cost'])){
    $this->_cost = $array['cost'];
}
}

public function toArray(){

$array = array();
$array['description'] = $this->_description;
$array['cost'] = $this->_cost;
return $array;
}

And this to Dog:

public function fromArray($array){

if(isset($array['breed'])){
    $this->_breed = $array['breed'];
}
if(isset($array['cost'])){
    $this->_cost = $array['color'];
}
}

public function toArray(){

$array = array();
$array['breed'] = $this->_breed;
$array['color'] = $this->_color;
return $array;
}

and in Person class:

public function fromArray($array){

if(isset($array['name'])){
    $this->_name = $array['name'];
}
if(isset($array['dogs'])){
    foreach($array['dogs'] as $dogArray){
        $dog = new Dog();
        $dog->fromArray($dogArray);
        $this->_dogs[] = $dog;
    }
}

if(isset($array['hobbies'])){
    foreach($array['hobbies'] as $hobbyArray){
        $hobby = new Hobby();
        $hobby->fromArray($hobbyArray);
        $this->_hobbies[] = $hobby;
    }
}
}

public function toArray(){

$array = array();
$array['name'] = $this->_name;
foreach($this->_dogs as $dogObj){
    $array['dogs'][] = $dogObj->toarray();;
}
foreach($this->_hobbies as $hobbyObj){
    $array['hobbies'][] = $hobbyObj->toarray();;
}
return $array;
}
1

This may not work perfectly (since i cant test it at the moment), but try something like this:

$data = json_decode($jsonData, TRUE);
$myObj = new Object();
foreach($data as $key => $val) {
  $myObj->$key = $val;
}

I passed TRUE for second param because otherwise json_decode will return stdClass object. I want to handle it as an associative array with foreach.

I realize that I haven't given a name of a design pattern, but I can't answer that with any confidence. Here's another response of a similar question: PHP Design Pattern

Community
  • 1
  • 1
Kristian
  • 21,204
  • 19
  • 101
  • 176
0

If you are doing a lot of decoding/encoding with JSON (as you mention you need to go back and forth between associative arrays and objects), I would make sure there is a legitimate reason to convert to custom classes.

Example:

$person = json_decode($json);

echo $person->name;
echo $person->dogs[0]->breed;

$person->name = "Jim";

$json = json_encode($person);

If you want to continue with a custom class, it will take some work. See this question.

Community
  • 1
  • 1
Matt Beckman
  • 5,022
  • 4
  • 29
  • 42