PHP - Access an array using another array as the key - php

I'm creating a class that assists in building settings pages for WordPress plugins. The developer instantiates the class by passing in an array of settings. A basic example looks like:
Array
(
[textbox1] => Array
(
[title] => Textbox
[type] => text
[section] => general
[desc] => The text description
)
[a_nice_dropdown] => Array
(
[title] => Select me
[type] => select
[section] => general
[desc] => The select description
[choices] => Array
(
[red] => Red
[blue] => Blue
[green] => Green
)
)
)
This works fine. My class builds the options page and the inputs have HTML that looks like:
<input id="textbox1" type="text" name="options_slug[textbox1]" value="">
When "Save Changes" is clicked, my class grabs all the options tied to "options_slug" and stores them in a single wp_options entry as a nice serialized array, making it easy to grab later.
The New Challenge
I have a new project which requires multiple nested "repeater" fields, similar to the way Advanced Custom Fields handles it. I've created a new field type, to handle this, which can support "subfields". An example config output (from error_log) looks like:
Array
(
[subfields_container] => Array
(
[title] => Subfields
[type] => subfields
[section] => general
[desc] => This is the subfields description text
[subfields] => Array
(
[textbox2] => Array
(
[title] => Textbox
[type] => text
[section] => general
[desc] => The text description
)
[select1deep] => Array
(
[title] => Subfield Select
[type] => select
[choices] => Array
(
[1] => 1
[2] => 2
[3] => 3
)
[std] => 1
)
)
)
)
I've configured the HTML output so an input inside a "subfields" container now looks like:
<input id="textbox1" type="text" name="options_slug[subfields_container][textbox2]" value="">
The idea being that the end user can easily group fields: i.e.,
$options = get_option('options_slug');
foreach($options['subfield_container'] as $subfield) {
// etc...
}
The Problem
As I iterate through these multidimensional arrays, I need to continually update a $options variable at the current index so it can be saved to the DB. So where previously I was able to do:
$id = 'textbox1';
$options[$id] = $_POST['textbox1'];
Now I'm doing something like:
$id = array('subfields_container' => 'textbox2');
$options[$id] = $_POST['textbox2'];
But I get "illegal offset type" errors. Because I can't set an array property using another array.
I've considered just putting dashes in the ID's instead of creating a hierarchical array, something like:
<input id="textbox1" type="text" name="options_slug[subfields_container-textbox2]" value="">
But then I'll lose the ability to foreach over a specific part of the stored options.
The Question
What's the best way to dynamically set a value inside of a multidimensional array when the array is not fixed in structure?
Thank you

I'd suggest to just dynamically create a multi-leveled array:
$arr = array();
$arr['subfields_container']['textbox1'] = $_POST['textbox1'];
print_r($arr);
=>
Array
(
[subfields_container] => Array
(
[textbox1] => <POSTed value>
)
)
All of the non-existent keys will just be created on the fly, regardless of the number of nested levels you specify.
Update
Given that the user can arbitrarily specify any number of nesting levels as elucidated below, you probably want a recursive function that returns the values for all the elements of the current level, and calls itself to retrieve the values for any elements at the current level that contain sub elements.
Example:
function getNestedPostVars($config, $formName, $keys = array()) {
$output = [];
foreach ($config as $label => $fieldConfig) {
if (isset($config['subfields'])) {
$output[$label] = getNestedPostVars(
$config['subfields'],
$formName,
array_merge($keys, array($label))
);
continue;
}
$output[$label] = /* path to $_POST element using $keys/$label */ ;
}
return $output;
}

Related

php push an array into an existing array by it's index

I'm having trouble with putting this question to words so ill just use a simple example, hope the title sortof got my problem across.
I'm creating a blog site where I can create blogposts and people can post comments. This is all saved in JSON except for login details which are saved in MySQL.
Now saving the blogposts go fine but I'm now trying to save comments.
Lets say the blogpost array looks like this:
Array
(
[0] => Array
(
[id] => 0
[title] => first blogpost
[content] => blogpost text
)
[1] => Array
(
[id] => 1
[title] => second blogpost
[content] => blogpost 2 text
)
)
Now someone writes a comment on 'second blogpost', I save it into an array like this(user taken from MySQL):
Array
(
[user] => myusername
[comment] => first post was better!
)
Now I want to merge them like this:
Array
(
[0] => Array
(
[id] => 0
[title] => first blogpost
[content] => blogpost text
)
[1] => Array
(
[id] => 1
[title] => second blogpost
[content] => blogpost 2 text
[comments] => Array
(
[user] => myusername
[comment] => first post was better!
)
)
)
I tried searching for a while and I'd expect this to be somewhere on the site already but I can't find it. I tried a couple variations of array_push and array_merge but it always ended up replacing the relevant blogpost instead of adding onto it.
EDIT: Someone noted the new array can't just float around, I think it's better now.
If you had any related key between posts and comments ( like having post_id in comment array ) that would make more sense to merge/put them.
I assume that's your blogpost
Array
(
[0] => Array
(
[id] => 0
[title] => first blogpost
[content] => blogpost text
)
[1] => Array
(
[id] => 1
[title] => second blogpost
[content] => blogpost 2 text
)
)
And your comments should be like:
Array
(
[user] => myusername
[comment] => first post was better!
[post_id] => 1
)
That way, you would be able to find the matched blogpost.
But, outside of your data structure, here is an example to merge an item into an element of an array of array.
A nested loop example.
foreach($posts as &$post){
foreach($comments as $comment){
if($post['id'] == $comment['post_id']){
$post['comments'][] = $comment;
}
}
}
the key here is sending each reference of the element into loop by &$post and then just manipulate them in loop.
Working with indexed arrays. (Like you already have index names as post_id and a comments index as an empty array)
foreach($comments as $comment){
$posts[$comment['post_id']]['comments'][] = $comment;
}
When the blogpost is updated, I assume you can get the id of that blogpost.
Then you can check if your data structure already has a key "comments". If it does not, add the key and create an array containing the comment and the user as the first array.
If it already exists, add a new array with the user and the comment so that there can be multiple comments for each blogpost.
For example using array_map:
$blogPosts = array_map(function ($blogPost) use ($blogPostId, $comment) {
if ($blogPost["id"] === $blogPostId) {
isset($blogPost["comments"]) ? $blogPost["comments"][] = $comment : $blogPost["comments"] = [$comment];
return $blogPost;
}
return $blogPost;
}, $blogPosts);
Php demo
So I fixed it after a bit of thinking
This is the final structure:
Array
(
[0] => Array
(
[id] => 0
[title] => 1st post
[content] => 1st post works!
[date] => 21-01-2019
[comments] => Array
(
[0] => Array
(
[user] => Me
[comment] => hey 1
[date] => 12:02 21-01-2019
)
[1] => Array
(
[user] => Me
[comment] => hey 2
[date] => 12:03 21-01-2019
)
)
)
)
I added a timestamp because of a suggestion here. It's also a simplified version of what I actually use, I tried adding many more comments and on multiple posts which both work.
This is the code, I should mention the ID is in the URL and it's saved as JSON:
$filename = file.json;
$currentArray = json_decode(file_get_contents($filename), true);
$comment = $_POST['comment'];
$username = $_SESSION['username'];
$date = date("H:i d-m-Y");
$id = $_GET['id'];
Pretty straightforward so far, here is how the array is created:
$currentArray[$id]["comments"][] = array (
'user' => $username,
'comment' => $comment,
'date' => $date
);
[$id] saves it to the correct post, ["comments"] saves it to the comments key(or creates it) and the last [] gives every comment a different index inside the ["comments"].
$newJSON = json_encode($currentArray, JSON_PRETTY_PRINT);
file_put_contents($filename, $newJSON);
And lastly encoding it and saving it to JSON.
Hope this helps someone.

Reading Array and Getting Variable Output

By default, Gravity Form merge tags for checkboxes output the value of the selected option. If they choices for a question are English, Spanish, and Other, and the user selects English, the merge tag outputs:
english
My end goal is to be able to customize a Gravity Form merge tag output for checkboxes to include all of the options, checkbox-like format, and the selected option (checkbox is checked).
Right now, I'm trying to at least be able to return all of the options. Using this function,
`
add_action( 'gform_pre_submission_3', 'pre_submission_handler' );
function pre_submission_handler( $form ) {
foreach($form['fields'] as &$field) {
if($field['id'] === 13) {
//$selectData['inputs'] = $field['inputs'];
$selectData['selections'] = $field['choices'];
$the_fields = $selectData;
foreach ($the_fields as $fields) {
print_r($fields);
}
}
}
I can get an output:
Array (
[0] => Array (
[text] => English
[value] => Language: English
[isSelected] =>
[price] =>
)
[1] => Array (
[text] => Spanish
[value] => Language: Spanish
[isSelected] =>
[price] =>
)
[2] => Array (
[text] => Other (specify)
[value] => Other
[isSelected] =>
[price] =>
)
)
I think that I'm going to need to get the [text] for each of the sub arrays into a new array. What's the syntax like to do that?
With the new array, I will be able to do a foreach loop, wrapping each element in HTML, using CSS to style the output like a checkbox.
As shorty:
$new_array = array_map(function($a){return $a['text'];},$fields);
;)

Saving Array of Array in Mysql using Active Record Code Igniter

I am trying to save a Form Data in Mysql. But nested array is causing problem.
Array ( [name] => Custom Project
[number] => 08883
[key] => Array ( [0] => Server Cost
[1] => Domain Cost
[2] => Design Charges,
)
[value] => Array ( [0] => 098
[1] => 765
[2] => 787
)
)
I am using $this->db->insert('invoice',$array) and it is inserting data fine without the nested array.
So is there a way I can separate this nested array from above array and then save this nested array into another table having only two columns key and value
You could just use array_combine to create the key pair value:
function save_value($array)
{
$key_value = array_combine($array['key'], $array['value']);
$this->db->insert('table_name', $key_value);
}
I used the very simple approach, I pushed one array to another because the result was to be processed in Codeigniter insert method.
//Item description
$description=$_POST['key'];
//item amount
$amount=$_POST['value'];
$big_array=array();
for($i=0; $i<sizeof($description); $i++){
$small_array=array('description'=>$description[$i],'amount'=>$amount[$i]);
array_push($big_array,$small_array);
}
//adding data into model
$this->my_Model->InsertData($big_array);

jQuery post input with same names but in different arrays

I have various forms in a single page.
All the forms get submitted by the below jQuery function.
All is ok, but there is a specific form that has duplicated input names.
In fact in this form the inputs get cloned on request.
So for example, this form represents how many people attend an event.
The user can add as many people as he wants and adding details of each by filling the form.
The inputs get cloned on the fly and inserted inside the form.
The problem is that jQuery is sending them in the wrong way, like shown below, and my php code is not responding well. In fact it just sees one of the posted "people" instead that them all.
Example of jquery post:
protagonist_description zxcvvcbhcvbjfg
protagonist_description jfghjfghjh
protagonist_email
protagonist_email
protagonist_name zxcvzxcv
protagonist_name dfghdfgh
protagonist_phone
protagonist_phone
protagonist_role zxcvxzcv
protagonist_role hgfjgfhjhgh
protagonist_surname zcxvzxcv
protagonist_surname dfghd
protagonist_video
protagonist_video
protagonist_website
protagonist_website
Example of PHP response:
Array
(
[protagonist_name] => dfghdfgh
[protagonist_surname] => dfghd
[protagonist_role] => hgfjgfhjhgh
[protagonist_description] => jfghjfghjh
[protagonist_email] =>
[protagonist_phone] =>
[protagonist_website] =>
[protagonist_video] =>
)
As you see it just gets the last posted.
Here is my jQuery function:
$(".save").click(function() {
$.ajax({
type: 'POST',
data: $('form[name="wizard_form"]').serialize(),
success: function() {
$('#publish').modal('show');
}
});
});
Please note that all the cloned inputs are inside the same form.
I can only use one form. So please do not suggest using more cloned forms. It won't work for my code as it is.
I am looking for a way to correctly merge the inputs with the same name in specific arrays posted to the PHP code.
How to do that?
You first need to change your form inputs to be arrays by adding [] to the name like so:
<input name="protagonist_description[]" />
And then in your PHP it will look like this:
Array
(
[protagonist_name] => Array
(
[0] => zxcvzxcv,
[1] => dfghdfgh,
)
[protagonist_surname] => Array
(
[0] => zcxvzxcv,
[1] => dfghd,
)
[protagonist_role] => Array
(
[0] => zxcvxzcv,
[1] => hgfjgfhjhgh,
)
[protagonist_description] => Array
(
[0] => zxcvvcbhcvbjfg,
[1] => jfghjfghjh,
)
[protagonist_email] => Array
(
[0] => ,
[1] => ,
)
[protagonist_phone] => Array
(
[0] => ,
[1] => ,
)
[protagonist_website] => Array
(
[0] => ,
[1] => ,
)
[protagonist_video] => Array
(
[0] => ,
[1] => ,
)
)
At this point you can then loop through the values like this:
for ($i = 0, $max = count($_POST['protagonist_name']); $i < $max; $i++) {
$first_protagonist = array(
'protagonist_name' => $_POST['protagonist_name'][$i],
'protagonist_surname' => $_POST['protagonist_surname'][$i],
// ...
);
}
Per David's suggestion, here's the code for a foreach if you wish:
foreach ($_POST['protagonist_name'] as $key => $val) {
$first_protagonist = array(
'protagonist_name' => $val,
'protagonist_surname' => $_POST['protagonist_surname'][$key],
'protagonist_description' => $_POST['protagonist_description'][$key],
// ...
);
}
You need to add brackets to your input's name.
Example:
<input name="my_name[]" value="david" />
<input name="my_name[]" value="john" />
<input name="my_name[]" value="francis" />
$_POST['my_name'] will be an array containing all results.
Array
(
[my_name] => Array
(
[0] => david
[1] => john
[2] => francis
)
)
To handle arrays of input elements, in other words defined as having the same element name. If you define the name with two square brackets in the end it will be transformed to an array on the server side for php.
<input type='text' name='elmname[]' .....
<input type='text' name='elmname[]' .....
if you submit the form consisting these elements and then on the php side you will see that $_POST['elmname'] will be an array

Using jQuery UI Autocomplete with datasource and 3d array php/SQL query

I want to use jQuery UI Autocomplet to make a form field autocomplete grabbing the values from a database:
http://jqueryui.com/demos/autocomplete/#remote
I have copied the code accross, but the example (birds) is an associative array.
$items = array(
"Great Bittern"=>"Botaurus stellaris",
"Little Grebe"=>"Tachybaptus ruficollis",
"Black-necked Grebe"=>"Podiceps nigricollis",
"Little Bittern"=>"Ixobrychus minutus");
etc etc
I want to query my database taking two fields, id and author.
However the query returns an multidimensional array, where each returned row from the database is an array.
e.g. ( [0] => Array ( [ID] => 1 [Author] => Higgins ) ) ( [0] => Array ( [ID] => 2 [Author] => Darl) )( [0] => Array ( [ID] => 3 [Author] => Lewis) )
How can I return the query so it is in the format:
"1=>Higgins,
2=>Darl,
3=>etc etc,"
so that i can use the script?
Given that your database array is $dbresult, you can do it like this:
foreach($dbresult as &$arr) {
$completearray[$arr['ID']] = $arr['Author'];
}
var_dump($completearray);
above will output the following array:
1=>Higgins
2=>Darl
3=>Lewis
UPDATE: I've updated the code above so the resulting array is indexed by the ID field.

Categories