I have this snippet (I want to get elements from .xml file):
$movies = simplexml_load_file('http://www.example.com/example.xml');
$out = "";
foreach ($movies as $movie) {
$properties = array(
'photo' => $movie->image,
'title' => $movie->title,
'desc' => $movie->teaser,
'channel' => $movie->channel,
'date' => $movie->date);
$out .= $modx->getChunk('tpl_movies-item', $properties);
}
return $out;
And chunk tpl_movies-item:
<article>
[[+photo]]
[[+title]]
[[+desc]]
[[+date]]
[[+channel]]
aaa
</article>
It shows only "aaa" (but when I add "echo $properties['photo']" it prints right value), when I change values in array to strings, eg. 'desc' => "lololololol" it works right. Could u help me what should I do?
The problem is probably in your foreach statement since simplexml_load_file() needs ->children() to loop through the different childs of the object at hand. Try:
foreach ($movies->children() as $movie) {
$properties = array(
'photo' => (string)$movie->image,
'title' => (string)$movie->title,
'desc' => (string)$movie->teaser,
'channel' => (string)$movie->channel,
'date' => (string)$movie->date);
$out .= $modx->getChunk('tpl_movies-item', $properties);
}
UPDATE
Try casting the properties as string as in the example above since they could be returned as objects.
Related
I have an array like the following:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'user_identifier' => "{'user_id':87,'username':'some username','email':'someuseremail.com','first_name':'Some','last_name':'User','company':'Company'}",
'request_uri' => '/'
);
And I would like to convert it to the following:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'user_id' => 87,
'username' => 'some username',
'email' => 'someuseremail.com',
'first_name' => 'Some',
'last_name' => 'User',
'company' => 'Company',
'request_uri' => '/'
);
Which means I am decoding the JSON at user_identifier key and I am making part of the initial array $original and then I am removing the user_identifier key.
So far this is what I have done:
foreach ($original as $key => $log) {
$original[$key] = (array) $log;
}
foreach ($original as $key => $log) {
foreach($log as $k => $v) {
if ($k === 'user_identifier') {
$original['decoded'] = (array) json_decode($v);
}
}
}
Which is giving me an array like this one:
array(
'session_id' => 'ea29e7ae5c976794896b4c256f455dd5',
'request_uri' => '/',
'user_identifier' => "{'user_id':87,'username':'some username','email':'someuseremail.com','first_name':'Some','last_name':'User','company':'Company'}",
'decoded' => array(
'user_id' => 87,
'username' => 'some username',
'email' => 'someuseremail.com',
'first_name' => 'Some',
'last_name' => 'User',
'company' => 'Company'
)
);
As you may notice this is not even the array I am looking for and I have already one foreach loop to convert the initial result to an array - it's coming as and stdClass object - and then a nested foreach loop for decode the JSON and try to make it part of the initial array.
In such case I will need to add another loop to linearize the array. My concern is this array is just an example but the one I need to convert is a big one.
Is there any better way to achieve this?
I am using PHP 5.3.3
I would do it like this:
Extract the JSON from the original into an array. (Be sure to set the second argument of json_decode so you end up with an array instead of an object.)
$identifier = json_decode($your_array['user_identifier'], true);
merge the extracted array with the original.
$your_array = array_merge($your_array, $identifier);
Unset the now-redundant JSON
unset($your_array['user_identifier']);
Let's say you have an array like this:
$list = array(
'name' => 'foobar',
'id' => '12302',
'group' => array(array(
'name' => 'teamA',
'members' => array(
array(
'ID' => 'OAHSJLASJ8888'
'name' => 'eric',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'OKZ8JJLJYYH6'
'name' => 'franz',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'OKOIYHJKKK'
'name' => 'Amr',
'fname' => 'ok',
'age' => '13'
)
)
),
array(
'name' => 'teamB',
'members' => array(
array(
'ID' => 'FGZ9ILKA'
'name' => 'Evan',
'fname' => 'lu',
'age' => '22'
),
array(
'ID' => 'KMLML2039KKK'
'name' => 'Michel',
'fname' => 'as',
'age' => '33'
),
array(
'ID' => 'AAA2039KKK'
'name' => 'Nickr',
'fname' => 'ok',
'age' => '13'
)
)
)
)
);
You want to add a value to the associative array named Amr which is the third element of the member key of the group key $list[group][0][members][2][newKey] = B
Using recursive function and foreach, I'm able to find anything I'm aiming at. Using array_walk_recursive I can also find the targeted key value and modify it.
Using RecursiveIteratorIterator and foreach, I can also find the element and modify it's value.
My issue is that I can not replace the modified object within the tree. I can follow the path down, but I'm not able to climb the tree back. I could maintain a index of each array I traverse and then recalculate the path to the key, but it looks culprit to me.
I can not modify the data structure, the dataset I have is as is.
Thanks for any help you could bring.
Code for Iterator:
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($list));
foreach($iterator as $key=>$value) {
if ($key === 'ID') {
$metas = get_relatedmeta_objects($value),true));
//metas key should be added to the current array
}
}
Recursive method:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as &$key => $value ) {
if ($key === "ID") {
$metas = get_relatedmeta_objects($value);
//metas key should be added to the current array
} else if (is_array($value)) {
searchObject($value);
}
}
}
}
array_walk_recursive method:
function alterArray(&$item, $key, &$parentRec) {
if (is_array($item) || is_object($item)) {
searchObject($item);
}
if ($key === 'ID') {
$parentRec = json_decode(json_encode($parentRec), true);
$parentRec['metas'] = get_field_objects($item);
// the current array is modified but the value does not go back to the $list initial array.
}
}
function searchObject( &$element, &$parent) {
array_walk_recursive($element, 'alterArray', $element);
}
The data set could be anything. You do not know the key, you just know that some nested object can have ID key and when they do you want to add more content to this object.
The recursive function can do it, but you should use the & prefix on $value instead of $key:
function searchKeyAndAdd( &$element) {
if(is_array($element) || is_object($element)){
foreach ( $element as $key => &$value ) {
if ($key === "ID") {
$element['meta'] = get_relatedmeta_objects($value);
} else {
searchKeyAndAdd($value);
}
}
}
}
searchKeyAndAdd($list);
The other two methods offer no reference to the parent, although in the case of array_walk_recursive you tried it with the third argument, but there things get messy: to make it work on each recursive depth, you call array_walk_recursive recursively... but array_walk_recursive already visits all the key/value pairs recursively. So this will lead to many calls to alterArray with the same key/value, but with a different ancestor as third argument for each of them.
Furthermore, with this line:
$parentRec = json_decode(json_encode($parentRec), true);
... you lose the reference to the original $parentRec, and so any modification you make to $parentRec will no longer have an effect on the original array.
I want to print HTML table (30+ cols, 500+ rows) but before that I have to apply different function on almost each column values.
$sampleData = array(
0 => array(
'date' => '2015-10-20',
'time' => '12:30:00',
'price' => 500,
'currency' => 'EUR'
),
1 => array(
'date' => '2015-10-21',
'time' => '08:10:00',
'price' => 250,
'currency' => 'USD'
),
2 => array(
'date' => '2015-10-22',
'time' => '21:45:00',
'price' => 300,
'currency' => 'EUR'
)
);
So far I solved it with array of columns and names of functions that are called with call_user_func.
$cols = array(
'date' => 'self::formatDate',
'time' => 'self::formatTime'
);
And then create table like this:
$htmlRows = '';
foreach ($sampleData as $row) {
$htmlRows .= '<tr>';
foreach ($cols as $th => $function) {
$htmlRows .= '<td>'.call_user_func($cols[$th], $row[$th]).'</td>';
}
$htmlRows .= '</tr>';
}
To me it looked like quite elegant solution, but than I stuck on how to call function with two or more params, e.g. formatPrice($price, $currency); on third column while its values are stored under third and fourth key. Or is there a better way how to format table columns?
Thanks, sorry for english
Proposition:
Incorporate all fields you want to use for a specific column in a way you can split them easily, iterate over them and pass the collected values.
$cols = array(
'date' => 'self::formatDate',
'time' => 'self::formatTime',
'price,currency' => 'self::formatPrice',
);
and
$htmlRows = '';
foreach ($sampleData as $row) {
$htmlRows .= '<tr>';
foreach ($cols as $group => $function) {
$data = array();
$fields = explode(',', $group);
foreach ($fields as $field) {
$data[$field] = $row[$field];
}
$htmlRows .= '<td>'.call_user_func($cols[$th], $data).'</td>';
}
$htmlRows .= '</tr>';
}
You will have to adapt your formatXxx() functions to interpret the different representation.
It is actually a good idea to use the actual class name instead of self in the $cols array, because if you declare the functions as private and extend the class, the functions will not be found.
One way of doing it is to do another foreach and prepare a second array that is used for the HTML formatting.
foreach ($sampleData as $row) {
$newSampleData['date'] = self::formatDate($row['date']);
$newSampleData['something'] = self::formatDate($row['something'],'argument1','argument2');
... and so on
Then you use $newSampleData for your HTML code.
I have an array with a data structure like below
$array = array(
'someKey' => array(
'id' => 1,
'string' => 'some key',
'someKey2' => array(
'id' => 1,
'string' => 'some key two',
'someKeyThree' => array(
'id' => 1,
'string' => 'some key three',
,
),
),
'someOtherKey' => array(
),
);
What I would like to do is out every array as a nested div p structure,
<div>
<p>someKey</p> // key of first array value
<div>
<p>someKey2</p>
<div>
<p>SomeKeyThree</p>
</div>
</div>
</div>
I have tried using new RecursiveIteratorIterator(new RecursiveArrayIterator($this->getData(), RecursiveIteratorIterator::CHILD_FIRST));
and using that but I am having trouble as the end tag for div never ends up right. Also once the iterator reaches the bottom of an array with no array to go into I want it to stop iterating completely.
THanks
You have to call a function recursively.
function printUl($arr){
$output = "<ul>";
foreach ($arr as $key => $val){
if (is_array($val)){
$output .= printUl($val);
continue;
}
else{
$output .= "<li>".$val."</li>"
}
$output .= "</ul>";
return $output;
}
}
I'm checking to make sure an array of arrays does not contain certain strings before adding any new child arrays to the parent array
I want to make sure that if an array with the same website and condition exists a new child array will not be added to the parent array.
e.g. in this example the $newArr must not be inserted in to the array $arr because their already exists an array with the same website and condition.
$arr = array(
array(
'website' => 'amazon',
'price' => 20,
'location' => 'uk',
'link' => '...',
'condition' => 'new'
),
array(
'website' => 'abe',
'price' => 20,
'location' => 'uk',
'link' => '...',
'condition' => 'new'
)
);
$newArr = array(
'website' => 'amazon',
'price' => 60,
'location' => 'uk',
'link' => '...',
'condition' => 'new'
)
I'm looking for an easy solution as using the function in_array on the parent array is not enough.
code so far
$arr = array();
foreach($table->find('tr.result') as $row){
if(($website = $row->find('a img',0))
&& ($price = $row->find('span.results-price a',0))
&& ($location = $row->find('.results-explanatory-text-Logo'))
&& ($link = $row->find('a',0))){
$website = str_replace( array('.gif','.jpg','.png'), '', basename($website->src));
$price = floatval(trim(str_replace(',', '', $price->innertext), "£"));
$location = "uk";
$link = $link->href;
$arr[] = array(
'website' => $website,
'price' => $price,
'location' => $location,
'link' => $link,
'condition' => 'new'
);
}
}
You loop over $arr each time to look for $website and $condition (always 'new'?) or you can keep a secondary array of the found keys. If you're starting with an empty $arr each time, the second approach will work and be faster.
$arr = array();
$keys = array();
foreach($table->find('tr.result') as $row){
if(...){
...
$condition = 'new'; // set as needed
// track seen keys
$key = $website . '|' . $condition; // assumes neither field contains '|'
if (!isset($keys[$key])) {
$keys[$key] = true;
$arr[] = array(...);
}
}
}
I hope the comments in the below code speak for themselves... I'm not a PHP pro, and this is probably not the most elegant way, but I believe the logic makes sense. Obviously the $new_array object has some variables that aren't declared but it's for example only.
I hope that helps and that no one down votes me :)
<?php
// Original array
$arr = array();
foreach($result as $row) {
// Get the new array as an object first so we can check whether to add to the loop
$new_array = array(
'website' => $website,
'price' => $price,
'location' => $location,
'link' => $link,
'condition' => 'new'
);
// If the original array is empty there's no point in looping through it
if(!empty($arr)) {
foreach($arr as $child) {
// Check through each item of the original array
foreach($new_array as $compare) {
// Compare each item in the new array against the original array
if(in_array($compare, $child)) {
// if there's a match, the new array will not get added
continue;
}
}
}
}
// If there's no match, the new array gets added
$arr[] = $new_array;
}
?>