Juergen Posted January 24, 2017 Share Posted January 24, 2017 Hello @ all, I have studied several posts in this forum (fe. https://processwire.com/talk/topic/969-input-validation-in-back-end-fields/?do=findComment&comment=58931), but I am not able to perform a custom validation of one of my input fields Goal: Check if the date of one field is higher than the other, output an error if the condition doesnt match Here is what I have tried so far: $form->addHookAfter("processInput",function($event) { $form = $event->object; $firstdate = $form->get("date_end");//date field 1 $lastdate = $form->get("recurringdateend");// date field 2 if($firstdate >= $lastdate){ //check if the date of field 1 is higher than the value of field 2 $lastdate->error("Last date must be higher than the first date"); } }); I have tried to insert it in init.php and also in ready.php but without success Has somebody tried to achieve something similar and know how to deal with this situation? Best regards Link to comment Share on other sites More sharing options...
LostKobrakai Posted January 24, 2017 Share Posted January 24, 2017 Where's $form from? Link to comment Share on other sites More sharing options...
Juergen Posted January 24, 2017 Author Share Posted January 24, 2017 $form is the form of the current page beeing loaded in the backend. Link to comment Share on other sites More sharing options...
Juergen Posted January 24, 2017 Author Share Posted January 24, 2017 Oh sorry there was a copying mistake. First line must be $pages->addHookAfter("processInput",function($event) { but this doesnt work too. Link to comment Share on other sites More sharing options...
LostKobrakai Posted January 24, 2017 Share Posted January 24, 2017 Read up on this part of the hooks docs: https://processwire.com/api/hooks/#all_or_one Link to comment Share on other sites More sharing options...
Juergen Posted January 24, 2017 Author Share Posted January 24, 2017 Ok, I have read the docs and I put the following code into ready.php $pages->addHookBefore('saveReady', function($event){ $form = $event->object; $page = $event->arguments[0]; if($page->template == "events"){ $lastdate = $page->getUnformatted("recurringdateend"); $firstdate = $page->getUnformatted("date_end"); if($firstdate > $lastdate){ $form->error("First date could not be after the last date."); } } }); The validation works, but: The page will be saved, but it should not because there is an error The error message is not next at the form field. It is only at the top bar. I know that form->error has no relation with the field itself but using fe $form->get("recurringdateend")->error.... doesnt work. Link to comment Share on other sites More sharing options...
LostKobrakai Posted January 24, 2017 Share Posted January 24, 2017 That was the wrong way. Do not try to use a hook on Pages, but rather look at ProcessPageEdit and it's hooks. Link to comment Share on other sites More sharing options...
Juergen Posted January 24, 2017 Author Share Posted January 24, 2017 I took a look at https://github.com/ryancramerdesign/ProcessWire/blob/master/wire/modules/Process/ProcessPageEdit/ProcessPageEdit.module My code is $pages->addHookAfter("ProcessPageEdit::processInput", function($event){ $form = $event->object; $firstdate = $form->get("date_end");//date field 1 $lastdate = $form->get("recurringdateend");//field 2 $this->message("Datum: {$firstdate} "); }); But if I add this to my init.php or ready.php I dont get the value of date field 1 in the message. So it seems that after submission of the form the value will not be grabed. I only put the message line to the code to see if the hook works. I dont know what I should do: I use a hook after, I use processInput which is for processing input data, I use the form as my object and I try to grab the values of the form fields with the get() method - In my opinion this should be the correct way. Link to comment Share on other sites More sharing options...
kongondo Posted January 24, 2017 Share Posted January 24, 2017 (edited) Try Hooking into the inputfield instead like so. Note, example is from module context. See also notes below: public function init() { $this->pages->addHookAfter("InputfieldDatetime::processInput", $this, "hookAfter"); } public function hookAfter(HookEvent $event) { $field = $event->object; if($field->name == 'recurringdateend'){// @note: just a quick example; you might want to hook into 'date_end' as well $page = $this->wire('modules')->ProcessPageEdit->getPage(); #$this->message($field->name);// @note: testing only #$this->message($page->id);// @note: testing only /* // @note: testing only + FOR INFO $oldDate = $page->get($field->name);// existing DB value $newDate = $field->value;// incoming/NEW data $this->message("old date is: $oldDate");// returns timestamp $this->message("new date is: $newDate");// returns timestamp */ // @note: quick example, not useful to you but a good to know 'how-to' // @note: what you really want I suppose is to compare incoming values (from input) // @see above for that ($oldDate and $newDate) $firstdate = $page->get("date_end");//date field 1 $lastdate = $page->get("recurringdateend");// date field 2 if($firstdate >= $lastdate){ //check if the date of field 1 is higher than the value of field 2 // error here; @note $lastdate is not a field! // error example: $field->error('Some Error'); } } } @Note that in my example we are comparing existing DB values (not very useful). In your case, you want to compare incoming values, right? See the $oldDate and $newDate example. However, you would need to Hook into both 'date_end' and 'recurringdateend' in that case (i.e. run your Hook code if either is the field name). Edit2: See complete answer in next post. Edited January 24, 2017 by kongondo more info 2 Link to comment Share on other sites More sharing options...
kongondo Posted January 24, 2017 Share Posted January 24, 2017 (edited) Played a bit more with this. The following works for me. I am not sure if it is foolproof and/or if there are better approaches. Please note: From what I can tell, when saving a page, inputs (fields) will be sent in the order they appear in a template. date1 refers to the first date and date2 to the last date. We need to temporarily store the value of date1 in order to later compare it to date2. I stored the value in a session. You could do it differently, e.g. cache it. The code doesn't prevent a save of the 2 date fields. You can do that if you wish. This code is necessarily verbose in places; just to make things clear. public function init() { $this->pages->addHookAfter("InputfieldDatetime::processInput", $this, "hookAfter"); } public function hookAfter(HookEvent $event) { $field = $event->object; if($field->name == 'date1' || $field->name == 'date2') { // @note: from what I can tell, inputs are sent in the order of the fields in the page // ...so, date1 will be hooked into before date2 $date1 = '';// first date $date2 = '';// last date $session = $this->wire('session'); // save date1 to session; we'll retrieve it to compare against date2 if($field->name == 'date1') { $date1 = $field->value; $session->set('date1', $date1); } elseif($field->name == 'date2') { $date2 = $field->value; $date1 = $session->get('date1'); // compare the first and last dates if($date1 >= $date2) $field->error("Last date must be greater than the first date"); //else $this->message("Those dates are fine matey!");// @note: for testing only // delete the temporary session date1 $session->remove('date1'); } } } Edited January 24, 2017 by kongondo 3 Link to comment Share on other sites More sharing options...
Juergen Posted January 25, 2017 Author Share Posted January 25, 2017 Thanks @kongondo, I will give it a try. To me it seems a bit complex using sessions for this. I guess there will be another way, which works like an $input->post validation, but I am struggeling with this problem for a while. Its also unclear to me why $form->get("date_end"); doesn´t grab the value of the input field "date_end" after form submission Many thanks for your solution. Link to comment Share on other sites More sharing options...
kongondo Posted January 27, 2017 Share Posted January 27, 2017 On 25/01/2017 at 5:53 AM, Juergen said: Its also unclear to me why $form->get("date_end"); doesn´t grab the value of the input field "date_end" after form submission If it's not grabbing it and the fact that we know there is a value sent, it means it is not the correct implementation. In other words, that is not how you grab it . On 25/01/2017 at 5:53 AM, Juergen said: To me it seems a bit complex using sessions for this. I guess there will be another way, which works like an $input->post validation, but I am struggeling with this problem for a while. The session is only temporary (it is deleted immediately after) since you need to be able to store the value of the first date somewhere in order to do the comparison. Below is a different approach. We directly check the values in the $input->post. Seems a bit hackish (?) but it works. Here, we make sure 'erroneous' values are not saved. We revert to older values in the database or blanks if there were no saved dates and show a field error on the first date input. Also note that if no first date is input, the field error will be displayed. You can play around with the if() logic to change the behaviour. public function init() { $this->pages->addHookAfter("InputfieldDatetime::processInput", $this, "hookAfter"); } public function hookAfter(HookEvent $event) { $post = $this->wire('input')->post; // change date string to timestamp $firstdate = strtotime($post->date); $lastdate = strtotime($post->date2); // if first date greater than or equal to last date if($firstdate >= $lastdate) { $field = $event->object; if($field->name == 'date' || $field->name == 'date2') { // get the page being edited $page = $this->wire('modules')->ProcessPageEdit->getPage(); // ensure we don't save 'erroneous' dates; we revert to older saved (db) dates or blank $oldDate = $page->get($field->name); $field->value = $oldDate; // only show error on first date field if($field->name == 'date') $field->error("Last date must be greater than the first date"); } } } 1 Link to comment Share on other sites More sharing options...
Juergen Posted January 27, 2017 Author Share Posted January 27, 2017 Hello @kongondo thanks for the explanation. I have implemented your first version and it works well, means that it shows the error message at the top bar and next to the field. My previous attempts showed only the error message at the top bar and not next to the field. So using a session is not a problem. I only thought that the same process could be achived with simplier methods. Anyway, it works very well. I think it will be a good feature for PW if every field has a custom validation field in its field settings where you can add additional php code for validation and you will have access to the api variables. Link to comment Share on other sites More sharing options...
eangulo Posted August 3, 2017 Share Posted August 3, 2017 Hello, I'm trying to make users email unique and for this I try the following code: Can someone tell me why the form still saving values even if the form display the email error "Email already in use" ? I need to block form submission until the user enter a valid email. <?php namespace ProcessWire; /** * * ProcessWire 3.x * */ class GeneralTools extends WireData implements Module { public function init() { // Ensure Uniqueness of Emails across site $this->pages->addHookAfter("InputfieldEmail::processInput", $this, "validEmail"); } /** * Ensure Uniqueness of Emails across site * * @param \ProcessWire\HookEvent $event * */ public function validEmail(HookEvent $event) { $field = $event->object; if($field->name == 'email') { // for test: always display field error $field->error("Email already in use"); } } } Thanks Guys :D! Link to comment Share on other sites More sharing options...
Robin S Posted August 3, 2017 Share Posted August 3, 2017 1 hour ago, Elkin Angulo said: Can someone tell me why the form still saving values even if the form display the email error "Email already in use" ? Your module should be autoload in order to use that hook. See the autoload options for the getModuleInfo() method: https://processwire.com/api/ref/module/ 2 Link to comment Share on other sites More sharing options...
eangulo Posted August 3, 2017 Share Posted August 3, 2017 Hi Robin thanks for your answer, Actually my module is on autoload mode (below my module GeneralTools.info.php): <?php /** * GeneralTools.info.php * * Return information about this module. * */ $info = array( // Your module's title 'title' => "General Tools", // A 1 sentence description of what your module does 'summary' => "General helper functions.", // Module version number: use 1 for 0.0.1 or 100 for 1.0.0, and so on 'version' => 100, // Icon to accompany this module (optional), uses font-awesome icon names, minus the "fa-" part 'icon' => 'cogs', // URL to more info: change to your full modules.processwire.com URL (if available), or something else if you prefer 'href' => '', 'autoload' => true, 'singular' => false, 'requires' => "ProcessWire>=3.0", ); The after hook "InputfieldEmail::processInput" is triggered well because when I submit the User add/edit form I can see the error message "Email already in use" set by the hook, that part work fine, the problem is that even if the form contains errors (this one ---> $field->error("Email already in use") it update the values. I expect that when I set the field error the form will display the error and the it will not update User values because an error is displayed and the it must be resolved. Have you a idea why the form is not waiting until the error "$field->error("Email already in use")" is resolve before updating User values? Thank you so much :). Link to comment Share on other sites More sharing options...
Robin S Posted August 3, 2017 Share Posted August 3, 2017 9 minutes ago, Elkin Angulo said: Have you a idea why the form is not waiting until the error "$field->error("Email already in use")" is resolve before updating User values? This is deliberate and Ryan has talked about it but I don't have a link handy. Probably because there can be situations where a field contains a lot of data in which case the user should be notified of a problem but not have all their work thrown out. See this example from @Soma where he shows how you can set a field back to its previous value when you want to reject a submission: 3 Link to comment Share on other sites More sharing options...
eangulo Posted August 4, 2017 Share Posted August 4, 2017 Processwire never stop to impress me! I love it ! Thank you @Robin S for your explications and your time! 1 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