Peter Falkenberg Brown Posted June 25, 2013 Share Posted June 25, 2013 Dear All, I've been scouring the API, the forums, and Antti's Fredi module, looking for tips. (Thanks, Antti!) I'm building forms on the front end, for a membership site, and have worked out the permissions, etc. Now, I'm working on how to obtain field values (to edit) while preserving the field settings and attributes, as defined in the backend. My weak point is that I'm not conversant in OOP PHP. It's easy to pull the value of a text field. But, I need to figure out how to pull the fields with single and multiple selects, checkboxes and radio boxes. I'd rather not handcode all of that with list selects and loops, and am assuming (hoping) that PW has an "easy way", since PW is so cool. I'm also working on a method where I loop through a field list and grab the values and surrounding HTML (e.g. for checkboxes), and then, having placed that "field construct" in a variable, replace a tag in an html template with that variable, allowing me to separate the look and feel from the code (I've done that in Perl). Here's a sample of a code snippet, with two fields. The text field works, but the select field (account_type) does not. In the PW admin, 'account_type' would have a dropdown with three or four values. Here's the code. The lines in red don't work. I also realize that if I handcoded it, I'd have to replace the entire input line in red, with a select construct. The input line for that field is just to see what I got back from the code above. Anybody have any thoughts about how to do this? === elseif ( $a_ == 'e' ) { $out .= ":: Edit Mode ::<br>"; $page->setOutputFormatting(false); $e_account_name = $page->account_name; $field = fields->get("account_type"); $inputField = $field->getInputfield($page); $inputField->value = "account_type"; $out .= " <form action='$full_page_url' method='POST'> <input type='hidden' name='a_' value='u'><br> <input type='text' name='account_name' value='$e_account_name' size='50' maxlength='100'> <input type='text' name='account_type' value='$inputField'> <input type='submit' name='submit' value='Update Account Data'> </form> "; } === Thanks! Peter Link to comment Share on other sites More sharing options...
adrian Posted June 25, 2013 Share Posted June 25, 2013 Hi Peter, Have have had lots of success using this code from diogo as a starting point: http://processwire.com/talk/topic/59-module-want-form-builder/?p=11639 This takes care of formatting the form inputs to match the field type in the back end. I think this will do what you need if I have understood you correctly. There is a comment further down that page from myself that might also help. Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 25, 2013 Author Share Posted June 25, 2013 Dear Adrian, Thanks. I had actually read that page a couple of times, but noted that the method to get the field seemed to be dependent upon the form module: $form = $modules->get('InputfieldForm'); and used the line: $form->append($f); to append the fields to the form. I'd like to grab each field, in it's complete format, and place it in a variable that I can then replace a tag with, so that I can create a custom look and feel with the form. Do you know how I could get each field value, with all of the attendant attributes / html code etc? This would also need to apply to the page type select lists. Thanks! Peter Link to comment Share on other sites More sharing options...
adrian Posted June 25, 2013 Share Posted June 25, 2013 It doesn't require Ryan's pro form module, if that's what you mean. InputfieldForm is part of the PW core. A couple of options for styling the form. You could use jquery, or you can simply add a class to each field as it gets appended to the $form. See bold line below. if ($myfields){ foreach($fields as $f){ // Output all the fields minus the ones listed in the $myfields array // Instead, to output only the fields that are in the array, remove the (!) from the condition if (!in_array($f->name, $myfields)){ $f->attr('class', 'mycssclass'); $form->append($f); } } } Of course you could nest some conditional statements in here to apply different styles to different field types. Does that take care of what you need to do? Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 25, 2013 Author Share Posted June 25, 2013 Dear Adrian, Thanks... I didn't mean the ProForm. I meant that I wanted to just grab the fields and their formats without initializing a form through the PW API. The append function lays out the form in an inflexible way. For example, I might like to have 4 columns, etc. In my example above, I use a get to obtain the value of a text field. I'm hoping that there's a similar function to obtain the final values and formats of the more complex fields, like a select list, without any dependence on the core form function. Thanks, Peter Link to comment Share on other sites More sharing options...
adrian Posted June 25, 2013 Share Posted June 25, 2013 Oh I see - sorry for misunderstanding on the ProForm. I do think you could easily change the layout by using the class attr, but maybe I am still missing your point. Of course if you match the layout in the backend using the Input -> Column Width (to make your 4 columns) - that layout will be mirrored on the front-end using diogo's code. Different CSS styles for the inputfield classes on the front end could easily change the look too. Not sure if this helps or not, but I have also done the following before. Please keep in mind that this is only partial code, but hopefully it gives you an idea of the approach I am talking about. It still relies on the InputfieldForm, but lets you iterate through each field separately. $questions = $page->fields; //Option type fields (select, checkbox, radio etc) if($question->type == 'FieldtypePage'){ $inputfield = $question->getInputfield($page); $options_parent_id = $inputfield->parent_id; $options_parent = $pages->get($options_parent_id); $options = $options_parent->children(); foreach($options as $option){ $form->append($field); } } Maybe someone else has a better approach! Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 25, 2013 Author Share Posted June 25, 2013 Dear Adrian, Thanks... I'm hoping to be able to assemble all of that into the $inputfield variable. It seems from your code higher up, that there's a PW function that grabs off the "assembled" components of the field and preps them, and then appends them to the form. If it could be placed into a variable instead, that would be great. For example: $var = " <select name=xyz> <option value=123>123 <option value=456 selected>456 </select> "; without having to do all the coding. It seems that PW is doing that in some cases, so I'm hoping to borrow those functions. Anyone have an idea? Ryan? Thanks, Peter Link to comment Share on other sites More sharing options...
Soma Posted June 26, 2013 Share Posted June 26, 2013 Without further consideration on if what you do is really a good way to go building forms... Every inputfield has a render method to render the field specific markup then added to field wrappers and the form wrapper as used in the admin. It's possible to get the inputfield markup used my PW: $f = $fields->get("somefield")->getInputField($page, null); $f->attr("value",$page->somefield); echo $f->render(); Link to comment Share on other sites More sharing options...
adrian Posted June 26, 2013 Share Posted June 26, 2013 Hey Soma, You've probably noticed that I have been pointing several people to that form code from diogo Are you suggesting that you don't like that approach? It has been working great for me - makes form generation so simple. I am sure Ryan's form builder is amazing, but so far I have been able to do everything I need with this approach. Do you think I should stop suggesting it? Link to comment Share on other sites More sharing options...
Soma Posted June 26, 2013 Share Posted June 26, 2013 Huh? I didn't say anything? I'm just not sure what Peter is up to with and how he's approach to create front-end forms and for what task really are... as he says he don't wants to use form API. But it really depends what forms are they? There's nothing wrong with using form API PW uses itself in the backend and I'm the one that brought extensive and various form examples (with upload and page editing) in multiple threads about this subject. I've been using it since there's required field options and error messages. http://processwire.com/talk/topic/2089-create-simple-forms-using-api/ and my gist are full of it https://gist.github.com/somatonic Link to comment Share on other sites More sharing options...
adrian Posted June 26, 2013 Share Posted June 26, 2013 Hey Soma - sorry for the confusion - I just wasn't sure if this comment: "Without further consideration on if what you do is really a good way to go building forms" was directed at the approach I had proposed, or Peter's approach to his current problem. Sorry, I don't think I ever saw that post of yours on creating simple forms using the API - I'll have to start sending people to that! Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 26, 2013 Author Share Posted June 26, 2013 Dear Soma, Thanks very, very much for your code sample. Looks like we're getting closer. I added this to my page template php file: $f = $fields->get("account_type")->getInputField($page, null); $f->attr("value",$page->account_type); $account_type_field = $f->render(); I got this error: Error: Call to a member function get() on a non-object (line 93... Line 93 is this line: $f = $fields->get("account_type")->getInputField($page, null); I'm running this inside a typical php file, where I'm able to successfully pull values from $page, e.g. $e_account_name = $page->account_name; Does the variable "$page" inside the getInputField function need to be something else, or initialized in some other way? About the method of building forms: the main reason I've been exploring this is so that I can use an html template with tags, to customize the look and feel of the form. Also, I think it's a useful thing to be able to do: to render a field in all it's admin / edit version glory. By the way, Soma, where did you learn the above code? Did you pull it out of the admin scripts? I want to do as much investigation on my own as I can, but the documentation about this is sparse, especially for a non-OOP person like me. Thanks! Peter Link to comment Share on other sites More sharing options...
MatthewSchenker Posted June 26, 2013 Share Posted June 26, 2013 Greetings, If you want complete flexibility from any formatting, you can just create forms any way you want and run through the ProcessWire API to actually make pages. Soma has posted a lot of examples of this, and I started a discussion on it also (with additional details for file uploads). I use this all the time: http://processwire.com/talk/topic/3105-create-pages-with-file-upload-field-via-api/ More recently, I have been experimenting with Zebra forms, which allows very nice validations on forms and works nicely with the ProcessWire API. Thanks, Matthew Link to comment Share on other sites More sharing options...
Soma Posted June 26, 2013 Share Posted June 26, 2013 Dear Soma, Thanks very, very much for your code sample. Looks like we're getting closer. I added this to my page template php file: $f = $fields->get("account_type")->getInputField($page, null); $f->attr("value",$page->account_type); $account_type_field = $f->render(); I got this error: Error: Call to a member function get() on a non-object (line 93... Line 93 is this line: $f = $fields->get("account_type")->getInputField($page, null); I'm running this inside a typical php file, where I'm able to successfully pull values from $page, e.g. $e_account_name = $page->account_name; Does the variable "$page" inside the getInputField function need to be something else, or initialized in some other way? The error reveals that get() is called on a non existent object. In the example the $fields->get(), see? Means the $fields somehow doesn't exist... it can't call the member method get() on it. $fields is like $pages or $page available in all template files, if not, there something either wrong with your installation, or you're using $fields in a function or module. In the scope of a function you'd have to use the global function wire("fields")->get(). $page in the getInputField($page, null) is just a page and I think it can be any, PW requires it. The secon argument is for the context. Since fields can have template context settings, you could pass the template there I think. You can see the base classes wire/core/field.php and wire/core/inputfield.php or any wire/modules/Fieldtype/* and wire/modules/Inputfield/* module that extend those. But the more important part in the code is this: $f->attr("value",$page->somefield); Where you set the value of a page to the inputfield before rendering. About the method of building forms: the main reason I've been exploring this is so that I can use an html template with tags, to customize the look and feel of the form. Also, I think it's a useful thing to be able to do: to render a field in all it's admin / edit version glory. By the way, Soma, where did you learn the above code? Did you pull it out of the admin scripts? I want to do as much investigation on my own as I can, but the documentation about this is sparse, especially for a non-OOP person like me. This is a very broad, somewhat complex or versatile, advanced and individual topic, I always have a little hard time educate to someone with basic to none coding skills just through writing without knowing the whole scope of the project and context. Are those front end users, trusted or not. What data needs to be edited or created... uploads, datepickers or just simple selects and so on. I spent already sooo many hours coming up with examples and explanations that I hardly feel motivated to do it over and over again. No offense to you or anyone personally of course! Peace The thing is, it is an somewhat advanced topic and it also took me some time to find all out and learn by trying and reading source code in core, understand how it works and what are the limits. It's also good to know that the inputfields and fields used in the backend primary are built to be used in the admin context and not originally intended for front-end usage. But it's built so well by Ryan that it's possible to use them out of the admin context to some extend. It's just that some fields other than simple text, select, radio or checkboxes ... like files, images,ASM select, dates with datepicker, they require some more assets to also be included in your front-end namely js and css files. So you'd have to include them aswell. The admin theme templates uses the $config->scripts and $config->styles file arrays to render the styles and scripts loaded by a inputfield module. I also recommend: - Read that thread I linked to completely. Have a look at some examples (my gists with forms) to get a feeling. They're by no mean complete or the only way. It gives you enough flexiblity even with styling and markup. You can set the markup of InputfieldWrappers. ie. $form->setMarkup($options) you'll see in wire/core/InptfieldWrapper.php - Study the FormTemplateProcessor module by Ryan. http://modules.processwire.com/modules/form-template-processor/ As Matthew said, you can also just build your very own markup and forms, as you would normally and use the API to add values, validate and save values to pages. It's not that much of a magic. And maybe keep trying to make simple forms using the API first to get into it... you know start small, grow big. Try to learn from mistakes and experiments asking in the forums. 2 Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 27, 2013 Author Share Posted June 27, 2013 Dear Soma, Thanks for your answer. I certainly don't want to waste your time, which you've already given so generously on this forum. I understand where you're coming from, because I'm a long time coder myself -- I just don't know OOP yet. I'm fine with digging through the core code, as you said that you did. I was simply curious where you found your answers, and you answered that (by digging through the core code). I'm sure one day, PW will have a huge reference section and examples of how to do everything. Who knows, maybe I'll be a contributor to that as well. Thanks for your efforts, and I'll keep digging. Best regards, Peter Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 27, 2013 Author Share Posted June 27, 2013 Hi Soma, Your answer lead me to this line of code, which worked: $fields = $page->getInputfields(); $f = $fields->get("account_type")->getInputField($page, null); $f->attr("value",$page->account_type); $account_type_field = $f->render(); After doing that, $account_type_field contained this code: <select id="Inputfield_account_type" name="account_type"><option value=''></option> <option value='1106'>trial</option> <option value='1107'>paid</option> <option selected='selected' value='1108'>complimentary</option></select> which was exactly what I wanted. It contains not only the select options but also the value of the drop down that had been stored. Thanks for all the tips, everyone! ProcessWire rocks. Peter Link to comment Share on other sites More sharing options...
Soma Posted June 27, 2013 Share Posted June 27, 2013 Hi Soma, Your answer lead me to this line of code, which worked: $fields = $page->getInputfields(); $f = $fields->get("account_type")->getInputField($page, null); $f->attr("value",$page->account_type); $account_type_field = $f->render(); I'm sure it works in your case for now, but is "wrong"... - There's already an API var $fields! http://cheatsheet.processwire.com/#fields , as I said in the previous post. Now you overwrite it, and it may cause problems later if you would want to use $fields somewhere else. It's like if you would overwrite $page or $pages... (Apart from that you're getting inputfields and not fields here), anyway. So I'm wondering why your $fields template var isn't exisiting! You may already have it overwritten before that code somewhere? - The red code you posted isn't necessary in this code. - But if you want to go with $page->getInputfields() you already get the inputfields!, so there would be no need to get it again. - Then also with $page->getInputfields() , there's no need to set the $f->attr("value",$page->account_type) again as the inputfield already has the value set from the page you're getting them. So this would be correct if you want to take this route: $inputfields = $page->getInputfields(); $f = $inputfields->get("account_type"); echo $f->render(); Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 28, 2013 Author Share Posted June 28, 2013 Dear Soma, I really appreciate your help. Thank you! I do believe that your advice has made a few of my little grey cells go ding! My form seems to be working now. I'm using an array of field names (stored in $field_array) that has fewer fields than the page, so I only create the vars necessary for the form. This is my code, as of now (condensed): $field_template = file_get_contents('./member_account_fields.html'); $page->setOutputFormatting(false); $inputfields = $page->getInputfields(); foreach ( $field_array as $field_name ) { $field_tag = ':!:' . $field_name . ':!:'; $field = $inputfields->get("$field_name"); $field_value = $field->render(); $field_template = str_replace($field_tag, $field_value, $field_template); } $out .= " <form action='$full_page_url' method='POST'><br> <input type='hidden' name='a_' value='u'><br> $field_template <input type='submit' name='submit' value='Update Account Data'> </form> $cancel_form "; I think the fact that I can loop through an array of field names, and get all the fields, in their editable format, with the correct values, including Page type fields, with just these two lines: $field = $inputfields->get("$field_name"); $field_value = $field->render(); is really, really cool. (Ryan, I join with many others in saying, you've done a great job with ProcessWire! You can be proud. ) Soma, and Adrian and Matthew, thanks very much for your help! (Of course, if I've missed something above, or done it incorrectly, please feel free to send me hunting for more little grey cells.) Peter 1 Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted June 30, 2013 Author Share Posted June 30, 2013 Dear Ryan and All, I've been making great progress on my form system (which I will run by you all in this postwhen I think it's done, to get your feedback). For now, I'm stumped on how to save multi-checkbox fields via the api (or multi-select fields, for that matter). I've figured out how to display them and edit them, but when I check off one and hit Update, I haven't figured out the syntax. I know it passes an array, and in Perl, I would parse through the array by hand. But with PW, after looking at the data tables for the fields, I see that a checkbox field with multiple options will have zero or more rows attached to the page. Thus, when I click them off or on,it seems like I'm adding or deleting rows from that checkbox field table. Is there a PW way to do this, that I'm missing? Of course, $page->set("$field_name", "$field_value"); didn't work. The checkbox field in question is all set up as type page, and works perfectly in the admin. Also, I'm assuming that: $field_value = $sanitizer->text($input->post->$field_name); won't work. Thanks for anyone's help. Peter Link to comment Share on other sites More sharing options...
Soma Posted June 30, 2013 Share Posted June 30, 2013 A page field uses pages, so the array will contain the page id's. So you go through them and add them to the field. To make things easier with adding removing pages, you can just removeAll() and then add the selected values back. if($input->post->submit){ $page->of(false); $page->checkboxes->removeAll(); foreach($input->post->checkboxes as $pid) { $page->checkboxes->add((int) $pid); } $page->save(); } Edit: edited code a little Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted July 1, 2013 Author Share Posted July 1, 2013 Dear Soma, Thank you for your response. After I figured out that "checkboxes" was not a PW function word, and that it was pseudo code, to be replaced by the actual field object name, , I got it to work with a loop through all the fields in my custom field array. This modified code seems to work fine. The page saves, with the edits captured. The routines below are done during *updates*, not adding new records. Feedback would be lovely. #.......................................................... $field = $fields->get("$field_name"); ... I do some things, here, before setting the field values below... #.......................................................... # set field values if ( $field->inputfield == 'InputfieldCheckboxes' or $field->inputfield == 'InputfieldSelectMultiple' ) { $page->$field->removeAll(); foreach( $input->post->$field_name as $pid ) { $page->$field->add( (int) $pid ); } } else { $page->set("$field_name", "$field_value"); } #.......................................................... One question is: is there a system var that one can use to test for any type of multiple input fields, rather than having to create a set of tests for all the different types of multiples? Like, "$field->inputfield is of a 'multiple' type"? * Do you think the code above is correct and done in the "PW" way? * My next question is that I'm stuck on the routines to create the editable input fields based on the user selecting, or deselecting one or more of the multiple options. I've gotten the "single value" fields to correctly capture the user input, and then display the modified fields again, in an "error" version of the edit page (based on a method that Ryan suggested) - http://processwire.com/talk/topic/407-processing-contact-forms/?p=3208. My primary difference, and one of the reasons that I'm working on this method of creating forms, rather than using the $form method that others have mentioned, is that I'm replacing template tags in an html file, with the field values at run time, for a custom look and feel. I'm also doing a lot of pre and post processing, all done within the page's template.php file, and a variety of included code files. I found that using this method removes any necessity to use hooks. Here's my code for the multiple value fields, to create new input fields to "re-edit". I'm missing something. Not sure what. The screens come back with the original values still selected. #.......................................................... # assemble input fields again, based on user's new values # to bring user back to revised edit screen $field_edit = $inputfields->get("$field_name"); if ( $field->inputfield == 'InputfieldCheckboxes' ) { # multiple checkboxes; modify user input checks foreach( $page->$field_name as $field_value_element ) { foreach( $input->post->$field_name as $pid ) { if ( $pid == $field_value_element ) { $field_edit->attr('checked', 'checked' ); } else { $field_edit->attr('checked', '' ); } } } } elseif ( $field->inputfield == 'InputfieldSelectMultiple' ) { # multiple selects; modify user input selections foreach( $page->$field_name as $field_value_element ) { foreach( $input->post->$field_name as $pid ) { if ( $pid == $field_value_element ) { $field_edit->attr('selected', 'selected' ); } else { $field_edit->attr('selected', '' ); } } } } else { # The single value field types work fine $field_edit->attr("value", $field_value); if ( $field->type == 'FieldtypeCheckbox' ) { $field_edit->attr('checked', $field_value == '1' ? 'checked' : ''); } } $field_edit_value = $field_error_text . $field_edit->render(); $field_template = str_replace($field_tag, $field_edit_value, $field_template); #.......................................................... Thanks very much for your help, and anyone else's help, too! Peter Link to comment Share on other sites More sharing options...
Soma Posted July 1, 2013 Share Posted July 1, 2013 I'm not sure the best way to check for multiple, it depends a lot on the fieldtype. It may differs from type to type. In case of a page field. You could try checking for the settings derefAsPage if($inputfield->derefAsPage == 0) { //... multiple } Or maybe... if($inputfield->value instanceof PageArray) { // .. multiple } Or simply by checking if the post value is an array. I'm not sure what the "PW way" means here. There's not so much the PW way, if it works... I personally would say yes, but being picky I wouldn't do it that way to begin with. But if it works out for you. To populate the fields again after submit, you just would add the value from the post to the inputfield before rendering. $field = $fields->get("$field_name"); $field->attr("value", $input->post->$field_name); echo $field->render(); // will have the value set Or you could use the processInput() of the inputfields to process and populate it. This is what PW uses to process fields. It doesn't matter what field type. $field = $fields->get("$field_name"); $field->processInput($input->post); // process field echo $field->render(); // will have the value set 1 Link to comment Share on other sites More sharing options...
Peter Falkenberg Brown Posted July 2, 2013 Author Share Posted July 2, 2013 Dear Soma, Thank you very much for your tips. I'll examine the multiple field test. Based on your suggestions above, I tested some code, and was able to replace the entire code block above (the second block dealing with the modified input fields), with these lines: $field_edit = $inputfields->get("$field_name"); $field_edit->processInput( $input->post ); $field_edit_value = $field_error_text . $field_edit->render(); $field_template = str_replace($field_tag, $field_edit_value, $field_template); These lines seem to work for all types of fields, as far as I can tell, so I don't need the if statements I was using for the multiple value fields. Kudos once again to the power of ProcessWire! (Thanks, Ryan!) Just curious, Soma. You said that because you were picky, you wouldn't use the method I'm using above. Could you elaborate? I'm eager to learn how various programmers would do things. (Like they say in Perl, "there's more than one way to do it.") Thanks again! Peter Link to comment Share on other sites More sharing options...
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now