Jump to content

foreach iteration to create nested fieldset items


Federico
 Share

Recommended Posts

Hi all,

I have to create a form in admin by populating inputfield from multiple pages. It's all fine now, except how multilanguage inputfields are displayed, because no text field is rendered. This is the tree structure

- parent page

  -- children_01

    --- Grandchildren_01_1 (this page has inputfields to be displayed in the fieldset form)

    --- Grandchildren_01_2 (same as above for all following grandchildren pages)

    --- Grandchildren_01_3

  -- children_02

    --- Grandchildren_02_1

    --- Grandchildren_02_2

    --- Grandchildren_02_3

    --- Grandchildren_02_4

    --- Grandchildren_02_5

 

This is a sketch to illustrate how the final result looks like:

nestedInputfields-01.thumb.jpg.85488eb36e1f4c0bf32df775d67ddf1f.jpg

nestedInputfields-02.thumb.jpg.41ef6230e92e3b3d6836651e859459a0.jpg
 

My code so far look like this:

public function ___execute() {

        $page_01_reports   = $this->pages->get(1038);
 
        $wrapper = new InputfieldWrapper();

        foreach($page_01_reports->children() as $childs) { // loop of three pages

            $fieldset = wire('modules')->get('InputfieldFieldset');
            $fieldset->label = __("$childs->name");

            $wrapper->append($fieldset);

            foreach($childs->children() as $child) { // loop of final/targeted grandchildren pages

                $fieldset2 = wire('modules')->get('InputfieldFieldset');
                $fieldset2->label = __("$child->name");

                $fields = $child->getInputfields();
                foreach($fields as $field) {
                    $fieldset2->append($field);
                }

                $btn = $this->modules->get("InputfieldButton"); 
                $btn->href = "{$this->wire('config')->urls->admin}/reports/custom";
                $btn->icon = 'plus-circle';
                $btn->value = "Export for $child->name";
                $btn->aclass = "classeAnchor";
                $btn->addClass = "pw-modal";
                $btn->id = "ID";
                $fieldset2->append($btn);

                $fieldset->append($fieldset2);

            }            

        }  

        return $wrapper->render();
}

and this is the weird multilanguage inputfield as rendered in the form:

5a7d53219c1a5_ScreenShot2018-02-09at8_51_25AM.png.0e7ae6426ffa34c40b7c009de0a91c24.png

 

 

Do you have any hint to give? Maybe with this?

$this->page->of(false);

thank you a lot

 

Link to comment
Share on other sites

The bare bones are:

$form = $this->modules->InputfieldForm;
$parent = $this->pages(1234); // Get the parent page
foreach($parent->children as $child) {
    if(!$child->hasChildren) continue;
    $fs_level_1 = $this->modules->InputfieldFieldset;
    $fs_level_1->label = $child->title;
    $fs_level_1->collapsed = Inputfield::collapsedYes;
    foreach($child->children as $grandchild) {
        $inputfields = $grandchild->getInputfields();
        $fs_level_2 = $this->modules->InputfieldFieldset;
        $fs_level_2->label = $grandchild->title;
        $fs_level_2->collapsed = Inputfield::collapsedYes;
        $fs_level_2->add($inputfields);
        $fs_level_1->add($fs_level_2);
    }
    $form->add($fs_level_1);
}
// Add submit button or anything else here
// ...
return $form->render();

 

  • Like 1
Link to comment
Share on other sites

Thanks Robin, works properly - my code was too dirty with redundant parts while yours look pretty neat.

I had to change it a little in order to ignore some specific fields, such as title:

        $form = $this->modules->InputfieldForm;
        $page_01_reports = $this->pages->get(1038);
        $ignorefields = array("title");

        foreach($page_01_reports->children as $child) {
            if(!$child->hasChildren) continue;
            $fs_level_1 = $this->modules->InputfieldFieldset;
            $fs_level_1->label = $child->title;
            $fs_level_1->collapsed = Inputfield::collapsedNever;
            foreach($child->children as $grandchild) {
                $inputfields = $grandchild->getInputfields();

                // new fieldswrapper needed to get rid of selected fields in $ingorefields
                $fieldswrapper = new InputfieldWrapper();
                foreach($inputfields as $field) {
                    if(in_array($field->name, $ignorefields)) continue;
                    $fieldswrapper->add($field); // rebuilding the final fieldswrapper(s) to feed the InputfieldFieldset
                }

                $fs_level_2 = $this->modules->InputfieldFieldset;
                $fs_level_2->label = $grandchild->title;
                $fs_level_2->collapsed = Inputfield::collapsedYes;
                $fs_level_2->add($fieldswrapper);

                // add a submit button to the form
                $submit = wire('modules')->get('InputfieldSubmit');
                $submit->attr("value","$grandchild->title");
                $submit->attr("id+name","submit_$grandchild->id"); //1061
                $fs_level_2->add($submit);

                $fs_level_1->add($fs_level_2);
            }
            $form->add($fs_level_1);
        }
        // Add submit button or anything else here
        //....
        // render final form output
        return $form->render();

 

Link to comment
Share on other sites

@Robin S maybe (for sure) you know why I can't get input vars from the above form, this code is located in module init() 

if($this->input->post->submit) {
	echo "There are " . count($this->input->post->submit) . " post variables <br /> <hr>";
	foreach($this->input->post->submit as $key => $value)     echo htmlentities("$key = $value") . "<br />";
}

I see the submit button actually submitting the form, but no vars are rendered from the above code, it just says there are 1 post variables regardless the real number of values submitted for several inputfields

+ Tracydebbuger says that the $value is an invalid argument

Invalid argument supplied for foreach()

 

Thank you very much

Link to comment
Share on other sites

25 minutes ago, Federico said:

I see the submit button actually submitting the form, but no vars are rendered from the above code

$this->input->post->submit is a single post variable with the name "submit". To get all your post variables you want to use $this->input->post.

But I think you will find a problem in that if two or more of your grandchildren pages share the same the same template then the inputfields on those pages will share the same names. Meaning that the post variables will be overwriting each other when the form is submitted.

So you will need to find a way to rename the inputfields in your form so each inputfield has a unique name. Something like this might work, which prepends the page ID to the inputfield name:

// ...
foreach($inputfields as $field) {
	if(in_array($field->name, $ignorefields)) continue;
	$field->name =  $grandchild->id . '_' . $field->name; // make name unique by prepending the page ID
	$fieldswrapper->add($field);
}
// ...

 

Link to comment
Share on other sites

I've also implemented pretty much the same conf to make each inputfields name unique, many thanks for this hint. Fortunately, In may case I have submit buttons for each grandchildren inputfields wrappers, so once a user submit the form by clicking one grandchildren form button, only the related form vars will be taken into account with this standard conditional

if($this->input->post->submit_1061) // only inputfields for the grandchildren form with ID 1061

therefore there shouldn't be any issue related to overwriting fields I guess. However, the major issue I am facing is that I cannot retrieve vars from the post submission :(

 

 

Link to comment
Share on other sites

9 hours ago, Federico said:

Fortunately, In may case I have submit buttons for each grandchildren inputfields wrappers, so once a user submit the form by clicking one grandchildren form button, only the related form vars will be taken into account

No, a form doesn't work that way. The way you have it set up now you have a single form with multiple submit buttons. It doesn't matter which submit button you click, the submitted form data will be the same. The thing is that the name of each post variable is determined by the name of the input element it corresponds to. Where you have multiple inputs with the same name (which you will have unless you rename them using something like what I suggested before) only the last input of each name will actually make it into post.

Although you could change to having multiple forms (one for each grandchild page) I don't think the idea of multiple submit buttons is a good one. Where users see multiple fields from multiple pages in the same module interface the expectation will be that they can edit all of these fields at once. But in fact all changes they make besides those inside the form they click the submit button for will be lost. Users might not notice that which could cause problems, or they might notice it and report it to you as a bug. You don't really want either of those.

A single form with a single submit button would be better, then you loop through the submitted form data and use the added page ID prefix to connect it to the relevant page. But remember you need to sanitize all the submitted data. It looks like you're sort of recreating ProcessPageEdit with your module here - have a look at ProcessPageEdit::processInput to get an idea of the kinds of things to look out for.

 

9 hours ago, Federico said:

However, the major issue I am facing is that I cannot retrieve vars from the post submission

If the action of your form points to your Process module page (which it will do by default unless you changed it) then all the post data will be accessible in the init() method of your module, as you are already trying. But as I said above, the array of data is in $this->input->post. You mentioned you have Tracy installed - so do...

bd($this->input->post);

...in your init() method and you will see the post data you have available.

  • Like 3
Link to comment
Share on other sites

39 minutes ago, Robin S said:

You mentioned you have Tracy installed - so do...


bd($this->input->post);

...in your init() method and you will see the post data you have available.

You don't even need to do that :)

If you have the Request Info panel running, you will see the POST variables automatically populated - depending on your scenario it might be in the main bar, or the "redirect" bar.,

  • Like 4
Link to comment
Share on other sites

13 hours ago, Robin S said:

No, a form doesn't work that way. The way you have it set up now you have a single form with multiple submit buttons. It doesn't matter which submit button you click, the submitted form data will be the same. The thing is that the name of each post variable is determined by the name of the input element it corresponds to.

I do not know why I didn't think about that, in fact regardless the number of submit buttons (equal to the number of grandchildren in my case), the form is only one and therefore the submitted data will be always the same (involving all inputfields from all grandchildren pages). [thanks Robin]

Dealing with this, my scenario is to see whether the form (submit_form_report) is submitted from the init()

if($this->input->post->submit_form_reports) {
	$this->executeFormInputsProcessing();
}

If yes, then I have to check every single inputfield to sanitize and whitelist them, like this 

protected function executeFormInputsProcessing() {
	foreach($this->input->post->submit_form_reports as $key => $value){
		if(empty($value)) continue; // discard empty values

		// processing inputfields as a single item - is it the most appropriate way doing this one by one...?
		if ($key == '1061_reports_manager') { // page array
    		$p = $this->pages->get($value); $pname = $p->name; // grab the page and its name
    		$var_array = array();
    		foreach ($value as $a) {
    			$var = $this->sanitizer->pageName($a); // sanitize all values (page reference array)
    			$var_array[] = $var;
    		}
    	$this->input->whitelist('something', $var_array);
    	//$input->whitelist('expertise', implode(',', $var_array));
    	}
	}
}

not sure though if I have to duplicate every conditional if($key == 'somethingAgainstToCheck') for each inputfield, as I have like 50 inputfields overall and it will not really handy when scaling

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...