PHP loop through array of object gives unexpected result - php

I have the following PHP code to set parentId for each post.
The parentId of each data all become the last post ID.
What's wrong with my logic?
btw, if I change it to array, everythings becomes ok. Please help!
$data = array(
(object)array('name' => 'myname')
);
$posts = array(
(object)array('ID' => 1, 'data'=>$data),
(object)array('ID' => 2, 'data'=>$data),
(object)array('ID' => 3, 'data'=>$data)
);
foreach($posts as &$post){
$post->data[0]->parentId = $post->ID;
}
print '<pre>';print_r($posts);die;
die;
Results:
Array
(
[0] => stdClass Object
(
[ID] => 1
[data] => Array
(
[0] => stdClass Object
(
[name] => myname
[parentId] => 3 // expect to be 1
)
)
)
[1] => stdClass Object
(
[ID] => 2
[data] => Array
(
[0] => stdClass Object
(
[name] => myname
[parentId] => 3 // expect to be 2 !!!
)
)
)
[2] => stdClass Object
(
[ID] => 3
[data] => Array
(
[0] => stdClass Object
(
[name] => myname
[parentId] => 3
)
)
)
)

All things considered, the real issue here is, after a second glance at your code, the way you're setting the data property. Since PHP5, objects are passed/assigned by reference by default. Remember the PHP4 days? ($newInstance = &new SomeClass();), PHP5 uses references for objects now, so when you do:
$data = array(
(object)array('name' => 'myname')//create object
);
Then, all three objects are assigned the same object (By reference!), so if you change it for the first time around, all three instances will reflect the same change!
I've recently posted a lengthy answer on references in loops here, it might be worth a look, because looping by reference isn't the best way to go about your business.
Some code-review:
Instead of constructing all these arrays, and cast them to objects individually, I'd just do this:
$data = array(
array('name' => 'myname')
);
$posts = array(
array('ID' => 1, 'data'=>$data),
array('ID' => 2, 'data'=>$data),
array('ID' => 3, 'data'=>$data)
);
foreach($posts as $k => $post)
{
$posts[$k]['data'][0]['parentId'] = $posts[$k]['ID'];
}
$posts = json_decode(json_encode($posts));//turns everything into objects
print_r($posts);
At first, it might seem inefficient to json_encode something, just to json_decode it, but json_decode returns objects, instead of associative arrays by default. I've ran a few tests scripts not too long ago, as it turned out: the encode-decode approach was actually faster than casting each associative array...

Okay, i misunderstood your problem, due to the fact that you reuse the data object you end up with a reference problem, this can be avoided by using clone, as seen below
<?php
$data = (object) array('name' => 'myname');
$posts = array(
(object) array('ID' => 1, 'data'=> array(clone $data)),
(object) array('ID' => 2, 'data'=> array(clone $data)),
(object) array('ID' => 3, 'data'=> array(clone $data))
);
foreach($posts as $postKey => $post){
$posts[$postKey]->data[0]->parentId = $posts[$postKey]->ID;
}
print '<pre>';
print_r($posts);

Related

Array index stuck at [0]

I am trying to create an array out of looped data. The variables contain looped data. All works fine, but when array is outputted, the index gets stuck at 0 and doesn't move up from 0 to 1 etc. I wonder what the problem is and how I can get this fixed.
Thanks.
$productinfo = array(
array(
"Productname" => "$productname",
"StarRating" => "$starrating",
"AddedValue" => "$addedvalue",
"ProductImage" => "$image",
"TotalPrice" => "$totalprice",
"ProductLink" => "$link" )
);
$productinfojson= json_encode($productinfo);
$output = json_decode($productinfojson, TRUE);
echo "<pre>";
print_r( $output );
echo "</pre>";
The above outputs:
Array
(
[0] => Array
(
[Procuctname] => Pencil Stack
[StarRating] => 3
[AddedValue] => Free Delivery
[ProductImage] =>
[TotalPrice] => 5.50
[ProductLink] => http://---.net/product
)
)
Array
(
[0] => Array
(
[Procuctname] => Block Bundle
[StarRating] => 4
[AddedValue] => Free Delivery
[ProductImage] =>
[TotalPrice] => 15
[ProductLink] => http://---.net/product
)
)
UPDATE if only one array is used.
code:
$productinfo = array(
"Productname" => "$productname",
"StarRating" => "$starrating",
"AddedValue" => "$addedvalue",
"ProductImage" => "$image",
"TotalPrice" => "$totalprice",
"ProductLink" => "$link" );
OUTPUT
Array
(
[Procuctname] => Pencil Stack
[StarRating] => 3
[AddedValue] => Free Delivery
[ProductImage] =>
[TotalPrice] => 5.50
[ProductLink] => http://---.net/product
)
Array
(
[Procuctname] => Block Bundle
[StarRating] => 4
[AddedValue] => Free Delivery
[ProductImage] =>
[TotalPrice] => 15
[ProductLink] => http://---.net/product
)
The issue stems straight from your array creation. There doesn't seem to be a loop anywhere in your code either....
Anyways, lets sort out your array creation. I assume you're getting the data from a database/data source and assigning it to variables, inputting it into an array? Well the way you currently have it, it's overwriting the first index element in the array.
Disclaimer: The below is pseudo code, until you update with your actual loop code..
$productinfo = array();
while($row = FETCH_DATA_FROM_SQL()) {
// assign it to variables here...?
$productinfo[] = array(
"Productname" => "$productname",
"StarRating" => "$starrating",
"AddedValue" => "$addedvalue",
"ProductImage" => "$image",
"TotalPrice" => "$totalprice",
"ProductLink" => "$link"
);
}
That will add each element to the new $productinfo array and you should be able to loop through the elements correctly.
One more thing to note, you're decoding your json like this:
$output = json_decode($productinfojson, TRUE);
That second parameter (TRUE), turns it into an associative array, not an object. So when you loop it as an object like below:
foreach($response->products->product as $product) {...
It isn't going to work. If you want it as an object, remove the second parameter (TRUE), otherwise loop it as an array:
foreach($response['products']['product'] as $product) {...

CakePHP format find('all') to ('list') in view?

Is there a way to format the $this->find('all') array into the $this->find('list') in the view? The reason I ask is so that I can pass that new array into the form helper options and then use the $this->find('all') array to build some additional things I need?
Array (
[0] => Array ( [School] => Array ( [id] => 0 [name] => Nature [address] => 112 Main [max_students] => 25 [application_level] => 5 ) )
[1] => Array ( [School] => Array ( [id] => 1 [name] => Math [address] => 112 Smith [max_students] => 25 [application_level] => 0 ) )
[2] => Array ( [School] => Array ( [id] => 2 [name] => Art [address] => 112 Lane [max_students] => 25 [application_level] => 0 ) )
)
So this is the array I get when I do a find('all'). I want to build the array so it looks like:
Array (
[0] => 'Nature'
[1] => 'Math'
[2] => 'Art'
)
This is usually done by the $this->find('list') function. Though the reason I want the whole array is because I need to add the application_level into $this->Form->input() function. This is because I need to add the option of class with the application level attached so I show only the shows with the application level based on the previous selection.
EDIT: Can't I just do $this->find('list', [insert parameters here?]);? I just don't understand how you set up the additional parameters?
If your query isn't overly complicated and isn't going to return a excessive number of results, just run it twice (once for find all and once for find list).
Find all, list, first, whatever are all the same in terms of the paramaters you pass in. E.g.:
$this->Model->find('all', array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
));
... you literally replace all with list. In your case, probably easiest to do it twice, once for each. Like this:
$parameters = array(
'conditions' => array(
'field' => 500,
'status' => 'Confirmed'
),
'order' => 'id ASC'
);
$alldata = $this->Model->find('all', $parameters);
$listdata = $this->Model->find('list', $parameters);
Otherwise, you can loop through it and populate your own list:
$list = array();
foreach($findall as $row) {
$id = $row['id'];
$name = $row['name'];
$list[$id] = $name;
}
$this->set('listdata', $list);
Short answer to your question is that there's no quick, easy way to select all and list from the same query, but you can re use your parameters (conditions, order etc) by passing them in as predefined arrays, or populate your own list.
An alternative answer to creating the results formatted like find('list') from results from find('all') using CakePHP's hash utility:
//where $data is the result of find all
App::uses('Hash', 'Utility');
$ids = Hash::format($data, array('{n}.Model.id'), '{0}'); //ids in an array.
$names = Hash::format($data, array('{n}.Model.name'), '{0}'); //names in an array
$dataAsList = array_combine($ids, $names);
To improve on kai's answer. The Hash class has a method called combine that can do what you're trying to do in only one line
$list = Hash::combine($data,'{n}.Model.id','{n}.Model.name');
the $list will be a flat array like data from find('list');

What am I doing wrong in declaring an array of objects in php?

This might be something too obvious, but I am kind of brain dead at the moment.
$sizeArr = array({
"name"=>'18"x24"',"wd"=>18,"ht"=>24,"price"=>99.00
},
{
"name"=>'24"x36"',"wd"=>24,"ht"=>36,"price"=>189.00
});
i want it to be accessed like $sizeArr[0]->name, but I am getting an error saying Unexpected {.
What am I doing wrong?
You can't use {} to define objects in PHP like you would in JavaScript (which I'm assuming you're familiar with).
In PHP objects and arrays are both arrays - yes, this does give problems if you're serialising to JSON.
So:
$sizeArr = array(array(
"name"=>'18"x24"',"wd"=>18,"ht"=>24,"price"=>99.00
),
array(
"name"=>'24"x36"',"wd"=>24,"ht"=>36,"price"=>189.00
));
You must convert your array:
$yourArray = array (
'item1' => 'Apple',
'item2' => 'Peach'
);
$p = (object) $yourArray;
print $p->item1;
For multidimensional Arrays, have a look here: http://www.richardcastera.com/blog/php-convert-array-to-object-with-stdclass
you can't use {} for this but you can try this
(object) is for convert array to object
$sizeArr = array(
(object)array(
"name"=>'18"x24"',"wd"=>18,"ht"=>24,"price"=>99.00
),
(object)array(
"name"=>'24"x36"',"wd"=>24,"ht"=>36,"price"=>189.00
)
);
echo $sizeArr[0]->name;
$sizeArr structure print_r($sizeArr); output
Array
(
[0] => stdClass Object
(
[name] => 18"x24"
[wd] => 18
[ht] => 24
[price] => 99
)
[1] => stdClass Object
(
[name] => 24"x36"
[wd] => 24
[ht] => 36
[price] => 189
)
)
output:
18"x24"
$sizeArr = array(
(object)array("name"=>'18"x24"',"wd"=>18,"ht"=>24,"price"=>99.00),
(object)array("name"=>'24"x36"',"wd"=>24,"ht"=>36,"price"=>189.00)
);
$sizeArr[0]->name;

Cakephp using Set class to fetch data with key starting at zero

I'm using Set class of Cakephp to format the find returned array but cannot seem to find a way to get the counter starting at zero and auto-increment for array keys so it is like
[0] => 3
[1] => 6
[2] => 12
I'm currently using below query to get the data from my HasAndBelongsToMany table.
$interest_ids = Set::combine($this->User->Interestsub->find('threaded', array
(
'conditions' => array
(
'Interestsub.name' => $interests
),
//'fields' => array('Interestsub.id'),
'recursive' => -1
)
),
'{n}.Interestsub.id',
'{n}.Interestsub.id'
);
The reason why I need this is that I'm currently trying to get the returned array as part of bigger parent array preparing to be saved for SaveAll function. To be formatted properly, I need below nested array coming out:
[0] => Array
(
[interestssub_id] => 12
[user_id] => 2
)
[1] => Array
(
[interestssub_id] => 22
[user_id] => 2
)
[2] => Array
(
[interestssub_id] => 32
[user_id] => 2
)
Is there a way we can use Combine class to format the returned array like above?
There's no real reason to use the Set class in this case. Just use good old fashioned php:
$threaded = $this->User->Interestsub->find('threaded', array(
'conditions' => array(
'Interestsub.name' => $interests
),
'recursive' => -1
));
$interest_ids = array();
foreach ($threaded as $thread) {
$interest_ids[] = array(
'interestssub_id' => $thread['Interestsub.id'],
'interestssub_id' => $thread['Interestsub.user_id']
);
}

Creating arrays dynamically using PHP

Hey folks, please lend a hand to a PHP beginner. I'm trying to put a load of dynamically created variabled into an array to re-read later, reason is a SOAP message sent is a mess and im trying to create a less complicated array:
$placea = "place a";
$placeb = "place b";
$myarray = array();
echo "<pre>";
print_r($myarray);
echo "</pre>";
what i want to be able to do:
Array
(
[0] => [Place A] => Array
(
[0] => [Accommodation] => Array
(
[RoomId] => 001
[RoomAvail] => true
[Date] => 12.04.2011
)
[1] => [Accommodation] => Array
(
[RoomId] => 002
[RoomAvail] => true
[Date] => 12.04.2011
)
) Array
(
[1] => [Place B] => Array
(
[0] => [Accommodation] => Array
(
[RoomId] => 101
[RoomAvail] => true
[Date] => 12.04.2011
)
[1] => [Accommodation] => Array
(
[RoomId] => 102
[RoomAvail] => true
[Date] => 12.04.2011
)
)
)
how would i write that out in php? sorry if its bleek and/or the array structure is incorrect
So you just need to use the array initializer repetitively.
If you want to initialize an array in PHP with some values, say 1 through 4, you make a call like:
$foo = array(1, 2, 3, 4);
And if you want to make an associative array, where you store some key/value pairs, you make a call like:
$foo = array('key' => 'value', 'other key' => 'other value');
But you can of course nest calls, and mix and match layers of associative and non associative arrays to achieve something like your example, e.g.:
$foo = array(
'Place A' => array(
// note use of first array on the next line is
// to generate structure like [0] => 'Accomodation' => ...
array('Accomodation' => array(
'RoomId' => '001',
'RoomAvail' => true,
'Date' => '12.04.2011')
)),
array('Accomodation' => array(
'RoomId' => '002',
'RoomAvail' => true,
'Date' => '12.04.2011')
))
),
'Place B' => array(
array('Accomodation' => array(
'RoomId' => '101',
'RoomAvail' => true,
'Date' => '12.04.2011')
)),
array('Accomodation' => array(
'RoomId' => '102',
'RoomAvail' => true,
'Date' => '12.04.2011')
))
)
);
This will very nearly produce what you're looking for, to make it replicate exactly what you have you would wrap each 'Place A' with an array and each "place" would get its own assignment to some variable $foo (I assumed this wasn't actually what you wanted and wrote something maybe slightly more intuitive).
If you want to have a 'less complicated' array, you have a two arrays, one fore place a and one for place b and then merge them using array_merger() http://www.php.net/manual/en/function.array-merge.php.
Study up on the array functions control structures in the manual. Many different ways of achieving bloated arrays uglying up your code.
This would dynamically create an array.
foreach($soapResponse as $key1 => $value1){
foreach($value as $key2 => $value2){
// $key1 = Place A or B
// value1 = array of values
$arrayResponse[$key1][$key2] = $value2;
}
}

Categories