PHP - For each loop problems - php

In Wordpress I'm trying to create a metabox script from scratch to better understand both Wordpress and PHP.
I'm having some problems with a for each loop on a multidimensional array though. I'm using PHP5.
This is the array:
$meta_box = array();
$meta_box[] = array(
'id' => 'monitor-specs',
'title' => 'Monitor Specifications',
'context' => 'normal',
'priority' => 'default',
'pages' => array('monitors', 'products'),
'fields' => array(
array(
'name' => 'Brand',
'desc' => 'Enter the brand of the monitor.',
'id' => $prefix . 'monitor_brand',
'type' => 'text',
'std' => ''
)
)
);
And this is the for each loop:
foreach ($meta_box['pages'] as $post_type => $value) {
add_meta_box($value['id'], $value['title'], 'je_format_metabox', $post_type, $value['context'], $value['priority']);
}
What I'm trying to do is loop through the keys in the 'pages' array which is an array inside the 'meta_box' array and at the same time be able to use the key values of the 'meta_box' array.
Do I need to nest some for each loops?
Would be grateful for some pointers in the right direction so I can solve this.

foreach ($meta_box[0]['pages'] as $post_type => $value) {
or
$meta_box = array(...

Your foreach starts with $meta_box['pages'], but there is no $meta_box['pages'].
You do have a $meta_box[0]['pages'] though, so you need two loops:
foreach($meta_box as $i => $box)
foreach($box['pages'] as $page)
add_meta_box(.., ..); // do whatever
What were you expecting to be in your $value variable?

this here:
$meta_box = array();
$meta_box[] = array(......
suggests that there is no $meta_box['pages']. meta_box is an array with numerical indexes (check the [] operator) and each of its elements is an array that has the key 'pages'.
so you need to use foreach on $meta_box, and on each element you need to use the pages key.. id, title, context are elements on the same level as pages, as you can see

You are referencing to the wrong array key
$meta_box[] <-- $meta_box[0]
But, you refer using :-
foreach ($meta_box['pages'] as $post_type => $value) {
Add the array key will solve the problem :-
foreach ($meta_box[0]['pages'] as $post_type => $value) {

Maybe it could be nice to create some class to hold this information.
class Metabox
{
public $id, $title, $context, $priority, $pages, $fields;
public function __construct($id, $title, $pages, $fiels, $context='normal', $priority='default')
{
$this->id = $id;
$this->title = $title;
$this->pages = $pages;
$this->fields = $fields;
$this->context = $context;
$this->priority = $priority;
}
}
$meta_box = array();
$meta_box[] = new Metabox(
'monitor-specs',
'Monitor Specifications',
array('monitors', 'products'),
array(
'name' => 'Brand',
'desc' => 'Enter the brand of the monitor.',
'id' => $prefix . 'monitor_brand',
'type' => 'text',
'std' => ''
)
);
Now you can loop over the meta_box array like:
foreach ($meta_box as $box)
{
add_meta_box($box->id, $box->title, .. and more)
// This function could be placed in the metabox object
/* Say you want to access the pages array : */
$pages = $box->pages;
foreach ($pages as $page)
{
..
}
}
Now you still have a loop in a loop, but maybe helps seeing your problem more clearly.

Related

How to find a key in multidimensional array?

I have a multidimensional array and i need make a searchCategory($categories, $id) function, which have to return a value of 'title' properties.
it try this code, it work but only for one layer of multidimensional array.
Multidimensional array:
$categories = array( array(
"id" => 1,
"title" => "Обувь",
'children' => array(
array(
'id' => 2,
'title' => 'Ботинки',
'children' => array(
array('id' => 3, 'title' => 'Кожа'),
array('id' => 4, 'title' => 'Текстиль'),
),
),
array('id' => 5, 'title' => 'Кроссовки',),
)), array(
"id" => 6,
"title" => "Спорт",
'children' => array(
array(
'id' => 7,
'title' => 'Мячи'
)
) ), );
Code which i try solve problem:
function searchCategory($categories, $id) {
foreach($categories as $category) {
if($category['id'] == $id) {
echo $category['title'] . '<br>';
}
}
};
I need my function to look up the id value in all arrays and return the title in the case of the array found
Here is recursive iterator case for you, please go through inline doc for explanation
function searchCategory($categories, $id)
{
$arrayiter = new RecursiveArrayIterator($categories);
$iteriter = new RecursiveIteratorIterator($arrayiter);
foreach ($iteriter as $key => $value) {
// checking if iterator comes to point where key is id and value matched
if ($key == 'id' && $value == $id) {
// returning matched value with current iterator instance
return $iteriter->getInnerIterator()['title'];
}
}
return '';
}
echo searchCategory($categories, 2).'<br/>';
echo searchCategory($categories, 7);
Working demo.
RecursiveArrayIterator - This iterator allows to unset and modify values and keys while iterating over Arrays and Objects in the same way as the ArrayIterator. Additionally it is possible to iterate over the current iterator entry.
RecursiveIteratorIterator - Can be used to iterate through recursive iterators.
RecursiveIteratorIterator::getInnerIterator: Get inner iterator
you need to make your function recursively
Demo : https://3v4l.org/CR7CD
function searchCategory($categories, $id) {
foreach($categories as $category) {
if($category['id'] == $id)
echo $category['title'] . '<br>';
if(isset($category['children']))
searchCategory($category['children'],$id);
}
}

PHP foreach loop used in Wordpress wp_customize->add_control

I'm trying to make a list of Google Font choices to use in a dropdown in the WordPress Customizer but I am having difficulty getting this loop right:
$i = 0;
foreach ($items as $font_value => $item) {
$i++;
$str = $item['family'];
}
The string above need to be inside in the array below to generate a list of choices:
$wp_customize->add_control( new WP_Customize_Control( $wp_customize, 'ounox-fonts-display-control', array(
'label' => 'Fonts Section',
'section' => 'ounox-fonts-section',
'settings' => 'ounox-fonts-display',
'type' => 'select',
'choices' => $str
)));
The choices argument expects an array , and not a string, so you would need to save each $item['family'] into an array instead, and then add that array to the argument.
Hapstyx is also correct in pointing out that you don't need the $i++ to iterate your loop.
The array that choices expects for your drop down options should look something like this:
$choices = array(
'option-value-1' => 'Option Title 1',
'option-value-2' => 'Option Title 2',
'option-value-3' => 'Option Title 3'
);
We can build this type of array like this:
//predefine a blank array which we will fill with your options
$choices = array();
//loop through your items and that the values to the $choices array
foreach ($items as $font_value => $item) {
$choices[$item['slug']] = $item['family']; //I'm assuming your $item array contains some sort of slug to set as the value, otherwise, comment the above out, and uncomment the below:
// $choices[$item['family']] = $item['family']
}
//set your arguments
$args = array(
'label' => 'Fonts Section',
'section' => 'ounox-fonts-section',
'settings' => 'ounox-fonts-display',
'type' => 'select',
'choices' => $choices
);
//add the control
$wp_customize->add_control( new WP_Customize_Control( $wp_customize, 'ounox-fonts-display-control', $args));
A foreach loop doesn't need a counter so $i is redundant.
You're also overriding $str with each iteration.
$str = '';
foreach ($items as $font_value => $item) {
$str .= $item['family']; // . '##' in case you need a delimiter
}

Know the element level in multidimensional array

Well, I am here again dealing with arrays in php. I need your hand to guide me in the right direction. Suppose the following array:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
The - (hyphen) symbol only illustrates the deep level.
Well, I need to build another array (or whatever), because it should be printed as an HTML select as below:
-fruits
--green
---limon
---mango
--red
---apple
-cars
--ferrari
---enzo
----blue
----black
---318
--lamborg
---spider
---gallardo
----gallado-96
-----blue
-----red
-----gallado-98
Looks that for each level element, it should add a space, or hyphen to determinate that it belongs to a particular parent.
EDIT
The have provide an answer provideng my final code. The html select element will display each level as string (repeating the "-" at the begging of the text instead multi-level elements.
Here's a simple recursive function to build a select dropdown given an array. Unfortunately I'm not able to test it, but let me know if it works. Usage would be as follows:
function generateDropdown($array, $level = 1)
{
if ($level == 1)
{
$menu = '<select>';
}
foreach ($array as $a)
{
if (is_array($a))
{
$menu .= generateDropdown($a, $level+1);
}
else
{
$menu .= '<option>'.str_pad('',$level,'-').$a.'</option>'."\n";
}
}
if ($level == 1)
{
$menu = '</select>';
}
return $menu;
}
OK, I got it with the help of #jmgardhn2.
The data
This is my array:
$temp = array(
array(
'name' => 'fruits',
'sons' => array(
array(
'name' => 'green',
'sons' => array(
array(
'name' => 'mango'
),
array(
'name' => 'banana',
)
)
)
)
),
array(
'name' => 'cars',
'sons' => array(
array(
'name' => 'italy',
'sons' => array(
array(
'name' => 'ferrari',
'sons' => array(
array(
'name' => 'red'
),
array(
'name' => 'black'
),
)
),
array(
'name' => 'fiat',
)
)
),
array(
'name' => 'germany',
'sons' => array(
array(
'name' => 'bmw',
)
)
),
)
)
);
Recursive function
Now, the following function will provide an array with items like [level] => [name]:
function createSelect($tree, $items, $level)
{
foreach ($tree as $key)
{
if (is_array($key))
{
$items = createSelect($key, $items, $level + 1);
}
else
{
$items[] = array('level' => $level, 'text' => $key);
}
}
return $items;
}
Calling the funcion
Now, call the function as below:
$items = createSelect($temp, array(), 0);
Output
If you iterate the final $items array it will look like:
1fruits
2green
3mango
3banana
1cars
2italy
3ferrari
4red
4black
3fiat
2germany
3bmw

Check if an array of an array contains a certain string

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;
}
?>

Quick Recursive search of all indexes within an array

Ok, so say I have an array as follows:
$buttons = array(
'mlist' => array(
'title' => 'Members',
'href' => $scripturl . '?action=mlist',
'show' => $context['allow_memberlist'],
'sub_buttons' => array(
'mlist_view' => array(
'title' => 'View the Member List',
'href' => $scripturl . '?action=mlist',
'show' => true,
),
'mlist_search' => array(
'title' => 'Search for Members',
'href' => $scripturl . '?action=mlist;sa=search',
'show' => true,
'is_last' => true,
),
),
),
'home' => array(
'title' => 'Home',
'href' => $scripturl,
'show' => true,
'sub_buttons' => array(
),
'is_last' => $context['right_to_left'],
),
'help' => array(
'title' => 'Help',
'href' => $scripturl . '?action=help',
'show' => true,
'sub_buttons' => array(
),
),
);
I need to sort through this array and return all indexes of it in another array as an index, and the values of these arrays will be the title. So it should return an array as follows:
array(
'mlist' => 'Members',
'mlist_view' => 'View the Member List',
'mlist_search' => 'Search for Members',
'home' => 'Home',
'help' => 'Help',
);
How can this be achieved easily? Basically, need the key of each array if a title is specified and need to populate both within another array.
The following snippet loops over all of the arrays (recursively) to extract the key/title pairs.
$index = array();
$iterator = new RecursiveIteratorIterator(new ParentIterator(new RecursiveArrayIterator($buttons)), RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $key => $value) {
if (array_key_exists('title', $value)) {
$index[$key] = $value['title'];
}
}
var_dump($index);
How can this be achieved easily?
initialize an empty, new array
foreach the $buttons array with key and value
extract title from value
set the key in the new array with the title
done.
Edit: In case a recursive array iterator catches too much (identifying elements as children while they are not - just being some other array), and you don't want to write an extension of the recursive iterator class, stepping through all children can be solved with some "hand written" iterator like this:
$index = array();
$childKey = 'sub_buttons';
$iterator = $buttons;
while(list($key, $item) = each($iterator))
{
array_shift($iterator);
$index[$key] = $item['title'];
$children = isset($item[$childKey]) ? $item[$childKey] : false;
if ($children) $iterator = $children + $iterator;
}
This iterator is aware of the child key, so it will only iterate over childs if there are some concrete. You can control the order (children first, children last) by changing the order:
if ($children) $iterator = $children + $iterator;
- or -
if ($children) $iterator += $children;
I'm sure my answer is not most efficient, but using many foreach loops and if checks, it can be done. However, with my solution if you nested another array inside of say 'mlist_view' that you needed to get a title from, it would not work. My solution works for a max of 2 arrays inside of arrays within buttons. A better (and more general purpose solution) would probably involve recursion.
$result = array();
foreach($buttons as $field => $value) {
foreach($value as $nF => $nV) {
if($nF === 'title') {
$result[$field] = $nV;
}
if(is_array($nV)) {
foreach($nV as $name => $comp) {
if(is_array($comp)) {
foreach($comp as $nnF => $nnV) {
if($nnF === 'title') {
$result[$name] = $nnV;
}
}
}
}
}
}
}
foreach($result as $f => $v) {
echo $f.": ".$v."<br/>";
}
This works for your value of $buttons, fairly simple:
function get_all_keys($arr) {
if (!is_array($arr)) return array();
$return = array();
foreach (array_keys($arr) as $key) {
if (is_array($arr[$key])
&& array_key_exists('title', $arr[$key]))
$return[$key] = $arr[$key]['title'];
$return = array_merge($return, get_all_keys($arr[$key]));
}
return $return;
}
echo "<pre>";
print_r(get_all_keys($buttons));
echo "</pre>";
Which returns:
Array
(
[mlist] => Members
[mlist_view] => View the Member List
[mlist_search] => Search for Members
[home] => Home
[help] => Help
)

Categories