Jump to content

How do you save form data to an existing page?


digitex
 Share

Recommended Posts

Is there a walk through or tutorial on how to allow a user to update data to an existing page using a form?

I have a members area (password protected) in which data displays from the members own page. The data is from a repeater field since each entry has related fields asociated with that entry, (seemed the easiest approach). I need them to be able to log into the member page and access their content via a form that they can edit and save. I'd also love for them to be able to add/delete the repeatable fields in a similar way as you can in the admin. Has this been addressed before? I wouldn't know how to begin. I've read many similar posts regarding adding new pages by way of a form but not with repeatable fields and editing existing data.

I've learned so much from this forum about PHP and working with PW but I still have no idea how to approach this.

Link to comment
Share on other sites

Repeater fields aren't designed to be used outside of the trusted user context of the admin (at least, not yet). That's not to say it isn't possible, just that it would probably take some time to figure out how, so I don't know how to answer the question yet. Even if you can get it working on the front-end, you have to be concerned about scale. Someone could create some real problems in your site by adding a million repeater items. So you would at least want a max_items option before considering something like this outside of a trusted user environment (and that's something we'll add soon). On the other hand, if you are building your own forms and creating and using repeaters purely from the API, then I think that should be fine. In that context, you can tailor it to the fields you are working with (perhaps not having to worry about things like AJAX saves) and account for the maximum items you will allow, etc. So I think my suggestion would be that if you use repeaters outside of a trusted user context, then create and populate them via the API so that you can lock it down with some hard limits specific to your case.

Link to comment
Share on other sites

Hi Ryan, thanks for the reply.

It is a trusted user environment in that members don't register themselves, their membership status has to be set up by an admin, (most likely me) and it's monitored. The site owner actually speaks to and often meets the members beforehand so the scenario you describe isn't likely. However, having the option to add/delete repeater items falls into the "wouldn't that be a nice feature" category rather than a necessary one. I'm most concerned right now in providing an editable form where members can alter data and save.

So far I have been able to make the edit page display the repeater field contents in a form but it doesn't save. I have cobbled this together by reading other threads which were not specifically related to what I need so I'm stuck.

Link to comment
Share on other sites

Is it only the repeater fields that don't save, or all the fields? I'm assuming all fields, but let me know if I'm wrong.

Because you are doing this outside of ProcessPageEdit, you need to connect the values from your inputfields to the values in your page. Something like this:

function formToPage(InputfieldWrapper $form, Page $page) {
 foreach($form as $inputfield) {
   $name = $inputfield->attr('name');
   if($name && $inputfield->isChanged()) {
     $page->set($name, $inputfield->attr('value')); 
   }
   if($inputfield instanceof InputfieldWrapper && count($inputfield->getChildren())) {
     formToPage($inputfield, $page); // go recursive
   }
 }
}

formToPage($form, $page); 
$page->save();
  • Like 1
Link to comment
Share on other sites

Thanks Ryan.

Sorry I didn't get back to you sooner. I've spent the last 3 days getting my own site up so i haven't had a chance to look at what you posted. Once I wrap my meager lobes around it I'll let you know how I made out.

BTW yes it's all the fields. I expect that you're right about it not connecting the values to the proper fields. I'll report back.

Link to comment
Share on other sites

  • 3 weeks later...

Hi Ryan,

I have the same problem. My form contains text fields, a textarea with TinyMCE and a repeater field.

<input id="nachname" name="nachname" type="text" value="..." />
<input id="firma" name="firma" type="text" value="..." />
...
// Online-Profil = Plattform+URL -> Repeater
<select id="profil1_n" name="profil1_n" class="mask1">
<option value=""></option>
<option value='Xing' selected>Xing</option>
<option value='Linkedin'>Linkedin</option>
<option value='Facebook'>Facebook</option>
<option value='Google+'>Google+</option>
</select>
<input id="profil1" name="profil1" type="url" value="http://www.xing.com/profile/..." />
// Long Text with TinyMCE
<textarea id="body" name="body" cols="30" rows="9" class="editor">...</textarea>
// sanitize form values or create empty
$form = array(
 'nachname' => $sanitizer->email($input->post->nachname),
 'firma' => $sanitizer->text($input->post->firma),
...
 'profil1_n' => $sanitizer->text($input->post->profil1_n),
 'profil1' => $sanitizer->text($input->post->profil1),
...
 'body' => $sanitizer->textarea($input->post->body),
 );

How should I call your function?

formToPage($sanitizer->text($input->post), $pages->find("parent=1008,name=$nutzer"));

or formToPage($form, $pages->find("parent=1008,name=$nutzer")); does not work.

Can you please explain that a little more detail? Many thanks!

Link to comment
Share on other sites

I think it's because you use $pages->find() as a second argument. It returns PageArray, not a page object. Try to use $pages->get() or $pages->find("parent=1008,name=$nutzer")->first() instead.

Link to comment
Share on other sites

That formToPage function there is for dealing with a PW-generated from using Inputfields. It looks to me like your form is one you've created yourself, so there's no need to use formToPage. Instead, you would get your form values from $input->post, sanitize them, and then populate them to your page object, i.e.

$page->nachname = $sanitizer->email($input->post->nachname); 

Also slkwrm is correct that the formToPage function was expecting a single Page rather than a PageArray. But like I say, it looks to me like formToPage is not what you want.

Link to comment
Share on other sites

Hi Ryan,

I'm sorry it took me so long to get back to this. I was called away. I spent some time trying to figure out the formtopage you posted as well and couldn't. Coming back to this thread I see am in a similar boat to at least one other person. My form is one of my creation as well so I guess the formtopage isn't for me either.

This is what I have. I didn't include the sanitizer. Is that the problem? Am I a complete knucklehead? Is it a simple thing to get working or have I corked it up royally?

<form action='./' method='post'>
<?php
$hafhmember = $pages->find("template=editable_page, include=hidden");
foreach($hafhmember as $h) {
 if (count ($hafhmember) > 1) { echo "<h2>$h->title</h2>"; }
 echo "<table>";
 foreach ($h->rp as $r) {
  echo "<tr><td><input id='start' type='text' size='30' value='{$r->start}' name='start' /> to <input id='end' type='text' size='30' value='{$r->end}' name='end' /></td>
  <td>$ <input id='cost' type='text' size='30' value='{$r->cost}' name='cost' /></td>
  <td>$ <input id='down' type='text' size='30' value='{$r->down}' name='down' /></td>";
  if ($r->purchased) echo "<td><input id='purchased' type='checkbox' size='30' value='1' name='purchased' checked /></td>";
   else echo "<td><input id='purchased' type='checkbox' size='30' value='0' name='purchased'</td>";
  echo "</tr>\n";

  $r->setOutputFormatting(false);
  $r->start = $input->post->start;
  $r->end = $input->post->end;
  $r->cost = $input->post->cost;
  $r->down = $input->post->down;
  $r->purchased = $input->post->purchased;
  $r->save();
 $r->setOutputFormatting(true);

  }
 echo "</table>\n";
}
echo "<input type='submit' value='Save' name='submit' />";

?>
</form>
Link to comment
Share on other sites

1. Field names 'start' and 'end' are reserved words in ProcessWire selectors, so probably not good to use as field names. Actually I'm surprised it let you create fields with those names (I need to fix that, if those are real field names you put in here?). Still, I'm not sure if that would necessarily be a problem in your code example working, but it's worth renaming those fields to save any potential issues.

2. I don't see anything here that checks if the form was submitted, so it looks to me like this block of code would blank out everything on the $r page every time the form is viewed. The only time it would populate data is when it was submitted. But of course, as soon as it's viewed again, it would make it blank. So I think you need something like this, immediately after the code that displays the form:

if(!$input->post->submit) continue; 

That would prevent it from executing all the code that populates the page below it, unless the form was actually submitted.

3. If there is more than 1 item in $hahfmember or $h, then they are all going to end up with the same values after the form is submitted. That's because you are repeating the same field names in your form, with no way to differentiate them, so they are basically overwriting each other. I recommend doing something like this:

foreach($h->rp as $r) {
 // not that $r in a string resolves to the id, so it's the same as $r->id
 echo "<input type='text' name='cost_$r' value='{$r->cost}' />";
 // and do the same with your other fields

 if(!$input->post->submit) continue; // if form isn't submitted, this skips everything below
 $r->of(false); // turn off output formatting
 $r->cost = $input->post("cost_$r"); 
 // and do the same with your other fields
}

4. I think it's always good to sanitize any input, whether using PW's $sanitizer or something else. While it's true that setting values to a page also sanitizes them automatically for most fields, I think it's always good to do your own sanitization specific to the needs of your context. I don't know what your field types are so can't say exactly what to do. So here's a few examples:

// if cost is an integer
$r->cost = (int) $input->post("cost_$r"); 

// or this to ensure it's a positive integer
$r->cost = abs($input->post("cost_$r"); 

// or this if cost is a text field
$r->cost = $sanitizer->text($input->post("cost_$r")); 

// or this if it's a textarea field
$r->cost = $sanitizer->textarea($input->post("cost_$r)); 

You could go even further and validate that the values were within expected ranges, etc.

5. If you don't have something like an entities output formatter defined for all your fields that are getting populated in the form, you probably want to encode them with htmlentities() before outputting each in the form. This ensures that a quote character like " or ' wouldn't break the output. And of course ensures your field doesn't get used for malicious purposes:

echo "<input type='text' name='cost_$r' value='" . htmlentities($r->cost, ENT_QUOTES, 'UTF-8') . "' />";

If it's an integer fieldtype, or a text field with a textformatter on it that already does this, then you don't need it (as it would double encode it).

Link to comment
Share on other sites

Thanks for the help Ryan.

1. No need to fix anything. I didn't realize those field names were reserved but I'm not using them anyway. I edited the code to make the field names more descriptive and easier to read. probably didn't need to bother but being a novice, I find it easier when the fields are english words that tell me what they mean. My bad.

2. I added:

if(!$input->post->submit) continue;

and it gave me an error when I hit submit:

Cannot break/continue 1 level (line 30 of /home/pinkster/public_html/site/templates/members.php)

So I altered it to:

<?php   if($input->post->submit) {
echo "<p>thank you. Your edits have been saved.</p>";
} else {

echo "<form action='./' method='post'>";
$hafhmember = $pages->find("template=editable_page, include=hidden");
foreach($hafhmember as $h) { etc etc etc

And the error goes away, the existing data is retained and hitting submit returns the thank you message but the edits still don't save.

3. Done. Thanks, good advice. It doesn't solve the problem but will certainly avoids a new problem because there will definitely be more than one item.

4 & 5. Have not bothered with sanitizing anything yet but will once it manages to save.

Do you have any other suggestions? Should I post the entire unaltered page instead of just the form? There's not much else there but there may be something else I've done wrong that I don't see.

Link to comment
Share on other sites

Cannot break/continue 1 level (line 30 of /home/pinkster/public_html/site/templates/members.php)

I would expect to get this error if doing a "continue;" outside of a foreach. Did you place the "continue;" within the foreach? It should have been placed right before the line that has: $r->setOutputFormatting(false);

If that still doesn't work, then you can just use an if() instead:

if($input->post->submit) {

 $r->of(false); // turn off output formatting
 $r->cost = $input->post("cost_$r"); 
 // and do the same with your other fields
 $r->save();

}
  • Like 1
Link to comment
Share on other sites

You were right Ryan. I tried the "continue" inside the foreach at first but it didn't seem to work. The error I reported was from my desperate attempt to make it work by moving it. I discovered today that it did work just as you instructed but I didn't realize it. With the code as it is now, when I click submit it reloads the page and the entire form goes blank. But If I log out and log back in it displays the changes correctly. So it's working but you can't tell unless you logout and back in which woould be confusing for users. Maybe I need to redirect upon submission to a new page like a thank you page so that when the user returns to the edit page it displays their edits correctly.

Link to comment
Share on other sites

have you tried to write checked="checked" ? Also PW I think stores it using value 1 or 0, but for the input html field you either only need checked="checked" or nothing (no value) attribute and it will be in the post as 1 or not at all, depending on it.

  • Like 1
Link to comment
Share on other sites

Soma you are a genius. I removed the value attribute and inserted checked="checked" and voila. The checkbox retains the changes I make. I still have to logout and log back in to see the updated status but that's a different issue.

Just to keep the problem unclouded I removed that part from my previous post.

Thanks for the help. I'm almost there.

Link to comment
Share on other sites

I would go ahead and do the redirect that you are talking about. Most likely your output is getting generated before your $page->save(), so the redirect should solve that issue. It also prevents a 'reload/refresh' from re-submitting the form. Basically, there are a few good reasons to redirect after a save, and that's also what PW does in it's admin. You don't necessarily have to redirect to another page, you could just redirect back to the same page, right after you complete the $page->save(), i.e.

$page->save();
$session->redirect('./'); 
  • Like 1
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...