For a given PHP object (loaded from CouchDB) $obj:
class stdClass#1 (3) {
public $_id =>
string(10) "nochecksum"
public $_rev =>
string(34) "1-4f734a24465bf7ba2de316fe87ffa0c1"
public $rooms =>
class stdClass#2 (1) {
public $kitchen =>
class stdClass#3 (1) {
public $ceilingFan =>
bool(false)
}
}
}
And for a given multidimensional array of data $arr, consisting of changed or new values for properties:
array(1) {
'rooms' =>
array(1) {
'kitchen' =>
array(1) {
'needsCleaning' =>
bool(true)
}
}
}
How is it possible to set $obj's properties to be values from $arr?
The solution is simple for a single dimension array:
foreach ($arr as $k=>$v) {
$obj->{$k}=$v;
}
I tried with a recursive function, but I don't know how to reference the parent(s):
$obj = setObjectFromArray($obj, $arr);
function setObjectFromArray($obj, $arr, $tree=Array())
{
foreach ($arr as $k=>$v) {
if (is_array($v)) {
$tree[]=$k;
$obj = setObjectFromArray($obj, $v, $tree);
} else {
// Here $tree is Array('rooms','kitchen')
// I want to set $obj->rooms->kitchen->{$k}
}
}
return $obj;
}
I think passing a reference of the object's property to the recursive function might work - but I don't understand either enough to make an educated guess. Any help appreciated
You could do it like this:
function setObjectFromArray($obj, $arr) {
foreach ($arr as $k => $v) {
$obj->{$k} = is_array($v) ? setObjectFromArray($obj->{$k}, $v) : $v;
}
return $obj;
}
The major difference with your code is the assignment to $obj->{$k} instead of $obj: that way you rewrite (or create) the property at each level of the recursion tree.
Be aware that even if you call the function like this:
$result = setObjectFromArray($obj, $arr);
... $obj will still have been modified and be equal to $result.
This solution is not totally obvious and maybe can fail somewhere but still - it's pair of json functions:
$r = array(
'rooms' => array(
'kitchen' => array(
'needsCleaning' => true
)
)
);
echo '<pre>',print_r(json_decode(json_encode($r))), '</pre>';
Explanation: you encode array to json-string and then decode this string to object.
Maybe you can expand this code further.
Related
I've got object and I need list of it's keys.
for now I doing that in foreach
foreach($obj as $key => $attribute){
var_dump($key);
}
Is there some PHP built in function for getting object keys like array_keys for arrays?
trace
object(Solarium\QueryType\Select\Result\Document)#1383 (1) {
["fields":protected]=> array(31) { ["pdf_url"]=> string(51)
"xxxxxxxxxxxx" ["title"]=>
string(150) ......
class A
{
private $a = 1;
protected $b = 2;
public $c = 3;
}
$object = new A();
$fields = get_object_vars($object);
But by this method, you can only get public fields from your object,
i.e
print_r($fields);
Will output
Array ( [c] => 3 )
Problem is because it was
array in object.
I solve problem with this
array_keys($obj->getFields())
I really like the functional programming style of using array map to create an array of objects from another array of objects.
$newObjects = array_map(
function($oldObject) {
return new NewObject($oldObject);
},
$oldObjects
);
Which all works fine but I would really like to be able to set the indices of the array so that they are the ids of the original objects for easier search and retrieval from the array but I cannot think how to do it other then which is not as elegant.
$newObjects = array();
foreach ($oldObjects as $oldObject) {
$newObjects[$oldObject->getId()] = new NewObject($oldObject);
}
Is there a way I can do this?
That is - array_reduce() is exactly what you need:
class Bar
{
protected $id;
public function __construct($id)
{
$this->id = $id;
}
public function getId()
{
return $this->id;
}
}
class Foo
{
protected $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
}
$oldObjects = [new Bar('x'), new Bar('y'), new Bar('z')];
$newObjects = array_reduce($oldObjects, function($current, Bar $obj) {
$current[$obj->getId()] = new Foo($obj);
return $current;
}, []);
This will do all in-place without having to spend memory on additional arrays like for array_combine()
However, I would suggest to use such constructs when they're necessary. Using this just because it "looks better" might be not a good idea - as plain loops are in most cases just more readable.
What if you use array_walk and a temporary array with your new indices.
$array = ['A', 'B', 'C', 'D'];
$reIndexedTemp = [];
array_walk(
$array,
function ($item, $key) use (&$reIndexedTemp) {
// here you can have your logic to assemble your new index
$reIndexedTemp[$key + 100] = $item;
}
);
//$array = $reIndexedTemp;
var_dump($array, $reIndexedTemp);
output (without the commented line) :
array(4) {
[0] =>
string(1) "A"
[1] =>
string(1) "B"
[2] =>
string(1) "C"
[3] =>
string(1) "D"
}
array(4) {
[100] =>
string(1) "A"
[101] =>
string(1) "B"
[102] =>
string(1) "C"
[103] =>
string(1) "D"
}
I think a foreach is probably the most readable solution in this case, but you can use array_map() with array_combine() to achieve what you want. Something like:
// empty array to store the old object ids
$ids = [];
// map over old objects, inheriting $id
// from parent scope by reference
$objs = array_map(function($oldObject) use (&$ids) {
$ids[] = $oldObject->getId();
return new NewObject($oldObject);
}, $oldObjects);
// combine id and object arrays
$newObjects = array_combine($ids, $objs);
Hope this helps :)
Looking around - Looking for array_map equivalent to work on keys in associative arrays
Suggests it might work using array_combine
So I guess it would be
$newObjects = array_combine(
array_map(
function($oldObject) {
return $oldObject->getId();
},
$oldObjects
),
array_map(
function($oldObject) {
return new NewObject($oldObject);
},
$oldObjects
)
);
Hmm probably the best, just this side of overblown but definately a lot more complex than the foreach
This question already has answers here:
Convert a PHP object to an associative array
(33 answers)
Closed 6 months ago.
I'm using amazon product advertising api. Values are returned as a multidimensional objects.
It looks like this:
object(AmazonProduct_Result)#222 (5) {
["_code":protected]=>
int(200)
["_data":protected]=>
string(16538)
array(2) {
["IsValid"]=>
string(4) "True"
["Items"]=>
array(1) {
[0]=>
object(AmazonProduct_Item)#19 (1) {
["_values":protected]=>
array(11) {
["ASIN"]=>
string(10) "B005HNF01O"
["ParentASIN"]=>
string(10) "B008RKEIZ8"
["DetailPageURL"]=>
string(120) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/B005HNF01O?SubscriptionId=AKIAJNFRQCIJLTY6LDTA&tag=*********-20"
["ItemLinks"]=>
array(7) {
[0]=>
object(AmazonProduct_ItemLink)#18 (1) {
["_values":protected]=>
array(2) {
["Description"]=>
string(17) "Technical Details"
["URL"]=>
string(217) "http://www.amazon.com/Case-Logic-TBC-302-FFP-Compact/dp/tech-data/B005HNF01O%3FSubscriptionId%3DAKIAJNFRQCIJLTY6LDTA%26tag%*******-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3DB005HNF01O"
}
}
[1]=>
object(AmazonProduct_ItemLink)#17 (1) {
["_values":protected]=>
array(2) {
I mean it also has array inside objects. I would like to convert all of them into a multidimensional array.
I know this is old but you could try the following piece of code:
$array = json_decode(json_encode($object), true);
where $object is the response of the API.
You can use recursive function like below:
function object_to_array($obj, &$arr)
{
if (!is_object($obj) && !is_array($obj))
{
$arr = $obj;
return $arr;
}
foreach ($obj as $key => $value)
{
if (!empty($value))
{
$arr[$key] = array();
objToArray($value, $arr[$key]);
}
else {$arr[$key] = $value;}
}
return $arr;
}
function convertObjectToArray($data) {
if (is_object($data)) {
$data = get_object_vars($data);
}
if (is_array($data)) {
return array_map(__FUNCTION__, $data);
}
return $data;
}
Credit to Kevin Op den Kamp.
I wrote a function that does the job, and also converts all json strings to arrays too. This works pretty fine for me.
function is_json($string) {
// php 5.3 or newer needed;
json_decode($string);
return (json_last_error() == JSON_ERROR_NONE);
}
function objectToArray($objectOrArray) {
// if is_json -> decode :
if (is_string($objectOrArray) && is_json($objectOrArray)) $objectOrArray = json_decode($objectOrArray);
// if object -> convert to array :
if (is_object($objectOrArray)) $objectOrArray = (array) $objectOrArray;
// if not array -> just return it (probably string or number) :
if (!is_array($objectOrArray)) return $objectOrArray;
// if empty array -> return [] :
if (count($objectOrArray) == 0) return [];
// repeat tasks for each item :
$output = [];
foreach ($objectOrArray as $key => $o_a) {
$output[$key] = objectToArray($o_a);
}
return $output;
}
This is an old question, but I recently ran into this and came up with my own solution.
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
Now if $array is an object itself you can just cast it to an array before putting it in array_walk_recursive:
$array = (array)$object;
array_walk_recursive($array, function(&$item){
if(is_object($item)) $item = (array)$item;
});
And the mini-example:
array_walk_recursive($array,function(&$item){if(is_object($item))$item=(array)$item;});
In my case I had an array of stdClass objects from a 3rd party source that had a field/property whose value I needed to use as a reference to find its containing stdClass so I could access other data in that element. Basically comparing nested keys in 2 data sets.
I have to do this many times, so I didn't want to foreach over it for each item I need to find. The solution to that issue is usually array_column, but that doesn't work on objects. So I did the above first.
Just in case you came here as I did and didn't find the right answer for your situation, this modified version of one of the previous answers is what ended up working for me:
protected function objToArray($obj)
{
// Not an object or array
if (!is_object($obj) && !is_array($obj)) {
return $obj;
}
// Parse array
foreach ($obj as $key => $value) {
$arr[$key] = $this->objToArray($value);
}
// Return parsed array
return $arr;
}
The original value is a JSON string. The method call looks like this:
$array = $this->objToArray(json_decode($json, true));
Is it possible to set multiple properties at a time for an object in php?
Instead of doing:
$object->prop1 = $something;
$object->prop2 = $otherthing;
$object->prop3 = $morethings;
do something like:
$object = (object) array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
but without overwriting the object.
Not like the way you want. but this can be done by using a loop.
$map = array(
'prop1' => $something,
'prop2' => $otherthing,
'prop3' => $morethings
);
foreach($map as $k => $v)
$object->$k = $v;
See only 2 extra lines.
You should look at Object Oriented PHP Best Practices :
"since the setter functions return $this you can chain them like so:"
$object->setName('Bob')
->setHairColor('green')
->setAddress('someplace');
This incidentally is known as a fluent interface.
I would recommend you don't do it. Seriously, don't.
Your code is much MUCH cleaner the first way, it's clearer of your intentions, and you aren't obfocusing your code to the extent where sometime in the future someone would look at your code and think "What the hell was the idiot thinking"?
If you insist on doing something which is clearly the wrong way to go, you can always create an array, iterate it and set all the properties in a loop. I won't give you code though. It's evil.
You could write some setters for the object that return the object:
public function setSomething($something)
{
$this->something = $something;
return $this; //this will return the current object
}
You could then do:
$object->setSomething("something")
->setSomethingelse("somethingelse")
->setMoreThings("some more things");
You would need to write a setter for each property as a __set function is not capable of returning a value.
Alternatively, set a single function to accept an array of property => values and set everything?
public function setProperties($array)
{
foreach($array as $property => $value)
{
$this->{$property} = $value;
}
return $this;
}
and pass in the array:
$object->setProperties(array('something' => 'someText', 'somethingElse' => 'more text', 'moreThings'=>'a lot more text'));
I realise this is an old question but for the benefit of others that come across it, I solved this myself recently and wanted to share the result
<?php
//Just some setup
header('Content-Type: text/plain');
$account = (object) array(
'email' => 'foo',
'dob'=>((object)array(
'day'=>1,
'month'=>1,
'year'=>((object)array('century'=>1900,'decade'=>0))
))
);
var_dump($account);
echo "\n\n==============\n\n";
//The functions
function &getObjRef(&$obj,$prop) {
return $obj->{$prop};
}
function updateObjFromArray(&$obj,$array){
foreach ($array as $key=>$value) {
if(!is_array($value))
$obj->{$key} = $value;
else{
$ref = getObjRef($obj,$key);
updateObjFromArray($ref,$value);
}
}
}
//Test
updateObjFromArray($account,array(
'id' => '123',
'email' => 'user#domain.com',
'dob'=>array(
'day'=>19,
'month'=>11,
'year'=>array('century'=>1900,'decade'=>80)
)
));
var_dump($account);
Obviously there are no safeguards built in. The main caveat is that the updateObjFromArray function assumes that for any nested arrays within $array, the corresponding key in $obj already exists and is an object, this must be true or treating it like an object will throw an error.
Hope this helps! :)
I wouldn't actually do this....but for fun I would
$object = (object) ($props + (array) $object);
you end up with an stdClass composed of $objects public properties, so it loses its type.
Method objectThis() to transtypage class array properties or array to stdClass. Using direct transtypage (object) would remove numeric index, but using this method it will keep the numeric index.
public function objectThis($array = null) {
if (!$array) {
foreach ($this as $property_name => $property_values) {
if (is_array($property_values) && !empty($property_values)) {
$this->{$property_name} = $this->objectThis($property_values);
} else if (is_array($property_values) && empty($property_values)) {
$this->{$property_name} = new stdClass();
}
}
} else {
$object = new stdClass();
foreach ($array as $index => $values) {
if (is_array($values) && empty($values)) {
$object->{$index} = new stdClass();
} else if (is_array($values)) {
$object->{$index} = $this->objectThis($values);
} else if (is_object($values)) {
$object->{$index} = $this->objectThis($values);
} else {
$object->{$index} = $values;
}
}
return $object;
}
}
Background
Assume I have the following nested variable in PHP.
$data = Array(
Array('lname' => 'Simpson','fname' => 'Homer','age' => '35','motto' => '_blank_'),
Array('lname' => 'Simpson','fname' => 'Marge','age' => '34','motto' => '_blank_'),
Array('lname' => 'Flintstone','fname' => 'Fred','age' => '33','motto' => '_blank_'),
Array('lname' => 'Flintstone','fname' => 'Wilma','age' => '29','motto' => '_blank_')
);
Assume also the standard methods for accessing specific values:
print($data[0]['fname']); // Homer
print($data[1]['age']); // 34
Question
Is there an existing library or framework that would allow me to easily
acess specific values declaratively, without using foreach loops?
$test = $data->get_record_by_fname['Homer']
print $test['age'] //35
If you really wanted to overkill everything, you could try an approach using magical methods!
class Simpsons
{
protected $_data = array();
public function __construct(array $data)
{
$this->_data = array_map(function ($i) { return (object)$i; }, $data);
}
public function __call($method, $args)
{
if (count($args) == 0)
return NULL;
foreach ($this->_data as $row)
{
if (property_exists($row, $method) && $row->$method == $args[0])
{
return $row;
}
}
return NULL;
}
}
Usage:
$p = new Simpsons($data); // Stored in the format provided
var_dump($p->fname('Homer')); // Gets the record with fname = Homer
Is there a particular reason you don't want to use foreach loops? If it's merely for conciseness, you could just declare the function yourself, it's fairly trivial:
function get_record($set, $field, $value) {
foreach($set as $key => $val) {
if($val[$field] === $value) return $set[$key];
}
return NULL;
}
Then your example would become:
$test = get_record($data, 'fname', 'Homer');
print $test['age']; //35
class SomeClass{
// Stores the Array of Data
public $data;
// Sets up the object. Only accepts arrays
public function __construct(array $data)
{
$this->data = $data;
}
// Gets a record based on the key/value pair
public function getByKey($key, $value)
{
foreach($this->data as $array)
{
if(is_array($array)
{
if(array_key_exists($key, $array) && $array[$key] == $value)
{
return $array;
}
}
}
}
}
$array = array( 1 => array("Test" => "Hello"));
$obj = new SomeClass($array);
$record = $obj->getByKey('Test', 'Hello');
This lets you get a record based on what a key/value pair inside the array is. Note, the type hinting in the constructor is PHP 5.3(?)
BTW, No, there is no way to escape the foreach as even internal PHP functions (anything beginning with array_) uses a foreach or some other type of loop. However, if you encapsulate the loop into a class, you don't have to think about it.