1

I have a somewhat complex nested repeater in WordPress (using ACF), and I am building a frontend form that lets users submit posts.

Let's say I have a repeater field (just an array) Spaces, and another nested repeater field Walls.

A user can add spaces (main repeater), which all have multiple walls (nested repeater). In the frontend, the main repeater fields look somewhat like this:

<input type="text" name="spaces[1][space]">
<input type="text" name="spaces[2][space]">

Via AJAX, I add this field to the database in PHP with the following code:

if(isset($_POST['spaces'])) {
    $spaces = array();
    foreach ($_POST['spaces'] as $value) {
        $spaces[] = array(
            "space" => $value['space'],
        )
    }
}

This is working fine. Now the probems starts when I try to nest another array for the different walls a user has added to this space.

The input fields look like this:

<input type="text" name="spaces[1][walls][1][orientation]">
<input type="text" name="spaces[1][walls][2][orientation]">

I know the following PHP code works and correctly puts the right fields in the database:

if(isset($_POST['spaces'])) {
    $spaces = array();
    foreach ($_POST['spaces'] as $value) {
        $spaces[] = array(
            "space"   => $value['space'],
            "walls"   => array(
                            array(
                                "orientation" => 'North',
                            ),
                            array(
                                "orientation" => 'South',
                            ),
                        )
        )
    }
}

But of course this is just a static example and I have no idea how to change the above snippet so it takes the correct values through the $_POST variable... Obviously, a user can select more or less than 2 walls, so I need to adjust it for multiple arrays.

For submitting the data via AJAX I'm using a simple call like this:

var formData = form.serialize();
$.ajax({ type: 'POST', url: ajaxurl, data: formData })

Any help would be highly appreciated, as I have been breaking my head over this the past 7 hours.

Update 1: I edited my code like below, which seems to be working (altough it's obivously not the best approach in terms of good practice):

if(isset($_POST['spaces'])) {
    $spaces = array();
    foreach ($_POST['spaces'] as $value) {
        if($value['walls']) {
            $walls_array = array();
            foreach ($value['walls'] as $wall) {
                $walls_array[] = array( "orientation" => $wall['orientation'] );
            }
        } else {
            $walls_array = '';
        }
        $spaces[] = array(
            "space"             => $value['space'],
            "walls"             => $walls_array,
            "extra_information" => $value['extra_information'],
        );
    }
}

Update 2: Based on the answer of @mickmackusa, I was able to edit to edit my code as follows, which works great:

$spaces = array_map(
    fn($space) => [
        "space"             => $space['space'],
        "walls"             => array_map(
                                fn($wall) => [
                                    "orientation" => $wall['orientation'],
                                    "comments" => $wall['comments']
                                ], $space['walls']),
        "extra_information" => $space['extra_information'],
    ],
    $_POST['spaces'] ?? []
);

Update 3: As users will be able to add and reorder spaces and walls, it's almost impossible to keep the indexes in the HTML input fields. So instead of this:

<input type="text" name="spaces[1][walls][1][orientation]">
<input type="text" name="spaces[1][walls][2][orientation]">

I would like to have it like this:

<input type="text" name="spaces[][walls][][orientation]">
<input type="text" name="spaces[][walls][][orientation]">

In another post on SO, I found this code, which works as long as I don't add any walls to a space. But as soon as I add walls, the fields are incorrectly put into the database with extra empty spaces and walls:

$spaces_temp = [];
foreach($_POST['spaces'] as $k=>$v){
    $val = intdiv($k,2);
    $spaces_temp[$val][key($v)]=$v[key($v)];
}
$_POST['spaces']=$spaces_temp;
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • 1. It is not good practice to conditionally declare `$walls_array` as an empty string or a populated array (that is going to cause somebody a headache). 2. Stack Overflow questions must never contain the resolving "answer" to the initial question. – mickmackusa Mar 03 '23 at 03:23
  • Thanks, I edited my question to indicate it was just a step in the right direction, rather than an answer. – Senne Vandenputte Mar 03 '23 at 09:25
  • Please provide a realistic and troublesome $_POST array and your exact desires result. – mickmackusa Mar 03 '23 at 20:58

1 Answers1

1

It seems that you are not bothering to retain the numeric keys in the payload, so my untested suggestion is to build a nested set of iterators using two array_map() calls, then $spaces should be ready for you to insert in your database.

The walls element in the result array's rows will either be an empty or populated array. I don't know if you are saving the walls subset as an encoded or delimited string, but I will encourage you to obey 1NF (database normalization) when storing this data.

$spaces = array_map(
    fn($space) => [
        "space"             => $space['space'],
        "walls"             => array_map(fn($wall) => ["orientation" => $wall['orientation']], $space['walls']),
        "extra_information" => $space['extra_information'],
    ],
    $_POST['spaces'] ?? []
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • Hi Mick, appreciate you taking the time to answer my question and provide suggestions on how to improve my code. I replaced my code with yours, and added the extra fields beside 'orientation', and all seems to be working fine. Thanks a lot! – Senne Vandenputte Mar 03 '23 at 09:24
  • I have updated my question with some more obstacles I'm facing, would be great if you could provide your thoughts on that :) – Senne Vandenputte Mar 03 '23 at 11:09