array_insert() to help inject items into a particular position in form array
| Project: | Drupal |
| Version: | 7.x-dev |
| Component: | forms system |
| Category: | feature request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | needs work |
One of the most difficult things about manipulating Drupal forms is the process of manipulating the arrays themselves. And one of the things that I commonly want to do is "stick this stuff in after that". But I have yet to find a PHP function to manipulate arrays in this way.
So I wrote this function called array_insert(). Perhaps there's a cleaner way to handle this, but it seems like a function that should be available in Drupal core. It would certainly help with a lot of common hook_form_alter() tasks.
For instance, this function (finally) offers a way to stick something into the node-type settings page (admin/settings/content-types/[node-type]) immediately before the "submit" buttons, you'd just create an array with your form items and then call
$form = array_insert($form, 'buttons', $my_stuff, TRUE);
<?php
/**
* Inserts values from $arr2 after (or before) $key in $arr1
* if $key is not found, $arr2 is appended to $arr1 using array_merge()
*
* @param $arr1
* array to insert into
* @param $key
* key of $arr1 to insert after
* @param $arr2
* array whose values should be inserted
* @param $before
* insert before the given key. defaults to inserting after
* @return
* merged array
*/
function array_insert($arr1, $key, $arr2, $before = FALSE){
$done = FALSE;
foreach($arr1 as $arr1_key => $arr1_val){
if(!$before){
$new_array[$arr1_key] = $arr1_val;
}
if($arr1_key == $key && !$done){
foreach($arr2 as $arr2_key => $arr2_val){
$new_array[$arr2_key] = $arr2_val;
}
$done = TRUE;
}
if($before){
$new_array[$arr1_key] = $arr1_val;
}
}
if(!$done){
$new_array = array_merge($arr1, $arr2);
}
return $new_array;
}
?>
#1
Seems like this would be useful for core. I'm no array expert but it looks good.
#2
The functin may be useful.
But, I think you are better off with a combination of array_keys, array_search and array_splice. timer it out.
#3
Per CHX's advice, here's a new version. It turns out that array_splice() actually destroys the keys of the inserted array, so I've had to go another route.
<?php
/**
* inserts values from $arr2 after (or before) $key in $arr1
* if $key is not found, values from $arr2 are appended to the end of $arr1
*
* This function uses array_merge() so be aware that values from conflicting keys
* will overwrite each other
*
* @param $arr1
* array to insert into
* @param $key
* key of $arr1 to insert after (or before)
* @param $arr2
* array whose values should be inserted
* @param $before
* boolean. insert before the given key. defaults to inserting after
* @return
* merged array
*/
function drupal_array_insert($arr1, $key, $arr2, $before = FALSE) {
$index = array_search($key, array_keys($arr1));
if($index === FALSE){
$index = count($arr1); // insert @ end of array if $key not found
}
else {
if(!$before){
$index++;
}
}
$end = array_splice($arr1, $index);
return array_merge($arr1, $arr2, $end);
}
// EXAMPLE //////////////////
$arr1 = array('one' => 1, 'two' => 2, 'three' => 3, 'four' => 4);
$arr2 = array('first' => 1, 'second' => 2, 'third' => 3, 'fourth' => 4);
$new_array = drupal_array_insert($arr1, 'three', $arr2);
print_r($new_array);
/**
Array
(
[one] => 1
[two] => 2
[three] => 3
[first] => 1
[second] => 2
[third] => 3
[fourth] => 4
[four] => 4
)
**/
?>
#4
sorry for not reading the manual page before :( but losing numberic keys is not good :( array_slice also resets numeric keys :(
#5
i wrote a different version for my big nodeapi submit handler patch. since that patch is dormant for now, i think this function should be considered for core under this issue. my version is very small.
#6
Moshe's version is shorter because it doesn't do the same thing as mine.
- Mine allows you to stick array key/value pairs into an array either before or after a given key.
- Moshe's just sticks stuff in at a given numeric position.
Both have their merits, but I think mine is a bit more useful for manipulating Drupal forms.
e.g.: Stick this stuff before the 'buttons' in the form.
#7
Aren't weights supposed to let us put things where we want?
#8
there are no weights in submit/validate handlers, for example.
#9
Nor in form_alter for most forms. For example, I was trying tonight to alter taxonomy_image so that the form would appear in the actual taxomomy edit page rather than on a separate tab. At the time the form is built, none of the fields are assigned weights, therefore the additions always appear /below/ the Submit/Delete buttons which is less than ideal.
However, this function didn't allow me to do that either. :P
<?php
function taxonomy_image_form_alter($form_id, &$original_form) {
if ($form_id == 'taxonomy_form_term') {
// $form definition...
$original_form = drupal_array_insert($original_form, 'submit', $form, TRUE);
return $original_form;
}
}
// still appears under the Submit/Delete buttons. ;(
?>
...either that or I'm just tired and being stupid, which is also very likely. ;) I just figured I'd mention it here since this function sounds like it should allow me to do that.
#10
@webchick - in your example you are accepting $original_form as a reference AND returning it. not good, though i dunno if thats bad enough to explain the failure. anyone available to review this some more?
#11
there is now a #weight element...
#12
huh? we have had #weight since fapi was born. how does that help with the stated problem?
#13
Feature requests go to 7.x-dev.
#14
I had a need for this functionality so did some testing on the two patches ... I was able to get what I wanted by using jjeff's version in comment 3 ... the following "code" is probably lengthier than it needs to be, but it illustrates that the function performs as promised. I was not able to get this functionality from moshe's patch as they indeed have different purposes.
// text keys
$before_submit = drupal_array_insert($form, '#submit', array('mykey' => 'test'), TRUE );
[mykey] => test
[#submit] => Array
(
[0] => user_register_submit
)
$after_submit = drupal_array_insert($form, '#submit', array('mykey' => 'test'), FALSE );
[#submit] => Array
(
[0] => user_register_submit
)
[mykey] => test
//numeric keys
$before_submit = drupal_array_insert($form, '#submit', array('test'), TRUE );
[0] => test
[#submit] => Array
(
[0] => user_register_submit
)
$after_submit = drupal_array_insert($form, '#submit', array('test'), FALSE );
[#submit] => Array
(
[0] => user_register_submit
)
[0] => test
// text keys
$before_submit = drupal_array_insert($form['#submit'], 0, array('mykey' => 'test'), TRUE );
Array
(
[mykey] => test
[0] => user_register_submit
)
$after_submit = drupal_array_insert($form['#submit'], 0, array('mykey' => 'test'), FALSE );
Array
(
[0] => user_register_submit
[mykey] => test
)
//numeric keys
$before_submit = drupal_array_insert($form['#submit'], 0, array('test'), TRUE );
Array
(
[0] => test
[1] => user_register_submit
)
$after_submit = drupal_array_insert($form['#submit'], 0, array('test'), FALSE );
Array
(
[0] => user_register_submit
[1] => test
)
Just for kicks I tried to break the above tests by setting the key of the array I was passing in to the same as the key I was trying to position before and after. This worked as promised with numbers. When I tried conflicting string keys like in the following it simply returned the original value of $form
$before_submit = drupal_array_insert($form, '#submit', array('#submit' => 'test'), TRUE );Hope that helps
#15
This is great! I did a google search for this functionality and the Drupal page was the first hit :).
#3 works as expected for me, and helps in the use case where no weights are assigned to elements. I'll plan on writing a patch + test for this soon.