Page 1 of 1

Traversing Object

Posted: Tue Apr 24, 2007 7:04 pm
by victorsk
Hello,

I am really quite new to PHP and have a question about efficient access and retrieval of object data. Because I don't know all the shortcut functions that can do this, I've got this awkward way of accessing object elements. First, here is an excerpt of object whose data I"m trying to access when calling print_r($profile):

Code: Select all

stdClass Object
(
    [clientProfile] => stdClass Object
        (
            [passenger] => stdClass Object
                (
                    [firstName] => Joe
                    [middleName] => 
                    [lastName] => User
                    [birthday] => 2007-03-02T00:00:00
                    [passengerType] => Adult
                    [gender] => Male
                    [passengerTitle] => Mr
                    [parentIndex] => -1
                )
            [agentId] => 0
            [arrayOfAddresses] => stdClass Object
                (
                    [Address] => stdClass Object
                        (
                            [streetNumber] => 735
                            [subAddressType] => None
                            [subAddressNumber] => 
                            [streetName] => Hello St
                            [streetDirection] => None
                            [cityName] => Moon
                            [stateCode] => BC
                            [countryCode] => CA
                            [postalCode] => 12345
                        )

              )
I've altered real values for security reasons, but this is an example of Objects inside Objects and Arrays of Objects and this is my way of accessing elements:

Code: Select all

$data = Array();
        $personal_data = Array();
        $address_data = Array();
        $phone_data = Array();
        $i = 0;
        foreach($profile as $key => $value)
        {
                $data[$i] = $value;
                $i++;
        }
        $i = 0;
        foreach($data[0] as $key => $value)
        {
                $personal_data[$i] = $value;
                $i++;
        }
        $i = 0;
        //Acquire ADDRESS information about users       

        foreach($personal_data[2] as $key => $value)
        {
                $address_data[$i] = $value;
                 $i++;
        }

        //Display address information about users
        foreach($address_data[0] as $key => $value)
        {
                echo "$key $value\n";
        }
This is clearly a time-consuming approach of accessing objects and objects inside objects. My question is:

Is there a more efficient way of doing this? Perhaps recursion or some PHP accessor methods that can access objects inside objects and retrieve values?

Please help,
Thank you,
Victor.

Posted: Tue Apr 24, 2007 10:26 pm
by alex.barylski
You would likely only want to use recursion on data which is arbitrary in depth and complexity. Otherwise using a fixed number of loops like you have works fine and is likely most efficient.

You may also investigate using a single while loop to traverse a tree structure, but you would only use this technique in a framework where speed was more important than readability as the code is quite a bit more complex than recursion.

The idea behind recursion:

1) Get array
2) Iterate each element inside that array
3) Each element that is another array should iterated as well - so we use recursion

Code: Select all

$arr = get_array(); // Return some arbitrary depth array

function someFunc($arr)
{
  $cnt = count($arr);
  for($i=0; $i<$cnt; $i++){
    $tmp = $arr[$i]; // get element in array

    // TODO: Process array element, like dump to screen, etc 

    // Check to make sure element is an other arraym, then recursively enter than array
    if(is_array($tmp)
      someFunc($tmp);
  }
}

Posted: Wed Apr 25, 2007 9:16 am
by Begby
You can also encapsulate this access into a class instead of just using arbitrary objects as nested arrays....

To do this you could implement the IteratorAggregate interface along with the magic method __toString().


This would be a way to iterate through all the profiles and print out the passengers and addresses

Code: Select all

$profiles  = someFunctionToFetchProfiles() ;

foreach( $profiles as $profile )
{
  foreach( $profile->passengers as $passenger )
  {
     echo $passenger ;
     foreach( $passenger->addresses as $address )
     {
        echo $address ;
     }
  }
}

Implementing ArrayAccess would let you do this

Code: Select all

// print the second address for the first passenger
echo $profile->passenger[0]->address[2] ;
There are a lot of ways to accomplish what you want it to do and at the same time have your code be very readable and easy to write.

Posted: Wed Apr 25, 2007 9:51 am
by victorsk
Great! Thank you so much for the replies. I think I got it :)

Will need to switch from Java to PHP for the time being :lol:

Posted: Wed Apr 25, 2007 10:27 am
by pickle
I think the ideal solution would be to change the way you're storing the data. Don't store it all in one big array. Create object variables for the agent id, passenger and addresses

Code: Select all

class Profile
{
   public $Passenger;
   public $Agent;
   public $addresses;

  ...
}
Make $Passenger and $Agent objects in their own right, as well as individual elements of $addresses. Then, if you wanted to get the passenger's last name for example, you could call:

Code: Select all

$Profile->Passenger->last_name
To loop through the addresses, you could do this:

Code: Select all

foreach($Profile->addresses as $AddressObject){
...
}
Unless you've got no choice in how the data is stored, I'd strongly suggest going to this methodology. It will help make your code much easier to read, as well as abstracting out how different data is accessed (rather than having to know the complicated array structure).

Posted: Wed Apr 25, 2007 10:45 am
by victorsk
Hi,

Thank you for this advice, however I have no control over the way data is stored because I'm acquiring it from webservices using SOAP requests. One thing I've noticed as I am now implementing the foreach loops is a lot of error messages saying "Invalid argument supplied for foreach()" when the syntax is correct. I think it's a good idea to add error-checking is_object() method before each loop and I came up a new approach to doing this:

Code: Select all

//data to store array of objects
        $data = Array();
        $index = 0;
        foreach($profiles as $profile)
        {
                if (is_object($profile))
                foreach($profile as $clientProfiles)
                {
                        $data[$index] = $clientProfiles;
                        $index++;
                }
        }
traverseObjects($data);
I haven't written traverseObjects but I think it should have a recursive way as was suggested earlier of going through an array.

Thank you,
Victor.

Posted: Wed Apr 25, 2007 2:20 pm
by pickle
Well, if you're stuck with the data format then ya - deal with that. is_array() will also be useful in certain places here. Also, I'm not seeing any reason for you to need recursion. You should still be able to access the array information very similarly to how you would if objects were used:

Code: Select all

//to access the clients last name
$passenger_last_name = $profile['clientProfile']['passenger']['lastName'];

//to loop through addresses:
foreach($profile['clientProfile']['array_ofAddresses'] as $address=>$properties)
{
   $cityName = $properties['cityName'];
}