skydiver / october-plugin-forms

Create easy (and almost magic) AJAX forms
https://octobercms.com/plugin/martin-forms
MIT License
60 stars 41 forks source link

Laravel Validation #254

Closed Kusdrae closed 2 years ago

Kusdrae commented 3 years ago

I'm trying to validate my form data, by making the field "name" and "phone" unique, i used the tutorial found here "https://skydiver.github.io/october-plugin-forms/docs/validation/validation/", but it simply doesn't work, i tried using the Key as "name" and the value as "unique:martin_forms_records,form_data" but it just allows the form data to be duplicated...

Kusdrae commented 3 years ago

@tfprado Hey, i've created this issue, just so you know..

tfprado commented 3 years ago

@Kusdrae sorry to hear that, I am moving right now so I have limited access to my computer, but I will try my best. I forgot all the form data gets saved as JSON in the same column, that's probably what is causing the validation to not trigger. You may be able to use this example

'form_data.name' => 'unique:martin_forms_records',

Another option would be to create your own validation rule The final and clunkier option would be to hook into the martin.forms.beforeSaveRecord event (triggered right after the component validation)

Kusdrae commented 3 years ago

@tfprado hmm, i tried using this way, still no success, but i'm still trying to figure this out. HUGE thanks for answering me in such a short time, and doing it while moving. Wish you the best man!

tfprado commented 3 years ago

@Kusdrae my pleasure! Glad to get you started, I believe the recent OctoberCMS update changed how validation works because I have had to modify a lot of my old form code. I am sure other people will have similar questions.

Here is an example of using the before-form event. You end up having to create your own plugin which is a little clunky. I have switched to using my own custom components for forms, so I am not sure how well the ajax exception will work from the plugin boot method but here is the documentation

<?php namespace Author\Name;

...
use Martin\Forms\Models\Record;
use Event;
use Flash;
...

class Plugin extends PluginBase
{
    ...
    public function boot()
    {
        Event::listen('martin.forms.beforeSaveRecord', function (&$formdata, $component) {
              // You can query for all the models here (maybe you are using a particular group?) 
              // and loop through their form data, return either ValidationException or AjaxException if 
              // you want to send response content and updated partials as well
              $target = $formdata['name'];
              $records = Record::where('group', 'yourFormGroup')->get();
              foreach($records as record) {
                  if ($record->formdata['name'] == $target) {
                      Flash::error('Username already taken!');
                      // My code from a component, I have a flash partial in the component folder, you would need to add the  
                      // markup either in your cms partials or the plugin
                      throw new \AjaxException([
                        '#flashMessages' => $this->renderPartial('@flash')
                      ]);
                      // OctoberCMS example
                      throw new AjaxException([
                          'error' => 'Not enough questions',
                          'questionsNeeded' => 2
                      ]);
                  }
              {
        }
}
Kusdrae commented 3 years ago

@tfprado I tried using the whole "form_data" as the key, not just the name inside the form_data, but still no success.. I used it like this: key: form_data value: unique:martin_forms_records,form_data

tfprado commented 3 years ago

@Kusdrae here is a better example. It's a little clunky but the only way I could get this to work. EDIT: closed one of the brackets, there may have been some minor typos when I copied the code over.

You are going to overwrite the component default markup and use the same flash messages as the backend.

In my example, I created a folder in CMS partial section called 'devForm' (should be the same name as your component alias) with two files, default.htm (your form markup) and flash.htm. I also created a custom plugin (you can use scaffolding commands) and added the event code to the boot() method

flash.htm code (you can increase how long the message will show up for in seconds with data interval)

{% flash %}
    <p data-control="flash-message" data-interval="5" class="{{ type }}">
        {{ message }}
    </p>
{% endflash %}

Plugin.php code

class Plugin extends PluginBase
{
    ...
    public function boot()
    {
        Event::listen('martin.forms.beforeSaveRecord', function (&$formdata, $component) {
               // Change devForm to your component alias
              if ($component->alias == 'devForm') { 
                $target = $formdata['name'];
                $records = Record::where('group', 'dev')->get();
                foreach($records as $record) {
                    // Use the function included in the authors model to get array of values instead of JSON
                    $data = $record->getFormDataArrAttribute();
                    if ($data['name'] == $target) {
                        // Add error message and render the partial to display it
                        \Flash::error('Name must be unique!');
                        throw new \AjaxException([
                            '#devForm_forms_flash' => $component->renderPartial('@flash'),
                        ]);
                    }
                }
            }
        }
   }
}
Kusdrae commented 3 years ago

Thank you very much for your help, i thought i could do this in a simpler way, because i'm still kinda new to October, so i don't think i'm going to use this solution, but man, HUGE THANKS to you, really, it means a lot to know that there are still people who are willing to go out of their way to help someone, really, thanks!

Kusdrae commented 3 years ago

@tfprado Hey, i used a kind of "cheat" to be able to validate the form_data. So, when the data from the form goes to the database, every field goes into a string that's called "form_data", so i created a js variable that receives all the values from the inputs, and formats this variable exactly like the "form_data" in the database is formated. This solves one of the problems. If there's already a record in the DB that matches this variable, the form won't be submited, because it's not unique.. But if i want to create a new form, that wasn't in the DB previously, this new form passes the variable that "mimics" the form_data and passes this variable within the form data, so it's kinda duplicated.. Do you know if there's a way to use this variable to validate the form, but DONT pass it to the database ?

tfprado commented 3 years ago

@Kusdrae Adding it to allowed fields in the component removes it before validation, so any solutions will involve a bit of extending the plugin.

I know you are getting started with OctoberCMS and it can seem overwhelming at first, but I would recommend getting the builder plugin so you can create your own plugin to extend magic forms using a visual editor. If you don't have terminal/ssh access to your webserver you can also get developer tools plugin to help edit it.

I use these personal plugins to extend the functionality of 3rd party all the time, as it allows you to change plugin behaviour without having to worry about your changes disappearing in the next plugin update. You can use this method to hook into plugin events, add new form fields in the backend, extend/create new validator rules, etc.

Kusdrae commented 3 years ago

Thank you very much for your help, really. You sir, are a god.

Kusdrae commented 3 years ago

@tfprado hey, dont know if you can help me again, but anyway.. I have this form that i want to use PHP instead of Magic Forms, and i have this in the markup:

<!DOCTYPE html>

Contact Form - PHP/MySQL Demo Code
Contact Form

 

and this on the code (in the same page):

function kkk(){

if(isset($_POST['submit'])){

$con = mysqli_connect('...', '...', '...','...');

$txtName = $_POST['txtName']; $txtEmail = $_POST['txtEmail']; $txtPhone = $_POST['txtPhone']; $txtMessage = $_POST['txtMessage']; $sql = "INSERT INTO tbl_contact (Id, fldName, fldEmail, fldPhone, fldMessage) VALUES ('0', '$txtName', '$txtEmail', '$txtPhone', '$txtMessage');";

$rs = mysqli_query($con, $sql);

} }

by the way i removed the db connection data for security reasons.. I don't know why it's not working..

tfprado commented 3 years ago

@Kusdrae I would have to see your form code to know exactly what is wrong. Off the top of my head, I would say your AJAX handler needs to be renamed (AJAX handlers need to start with 'on'), you need to make sure to call the correct handler on the form and you are using the wrong post variable.

Here is a simple example to get you started.

EDIT: The OctoberCMS documentation lists some other useful features here for Request & Input If you using your own AJAX handler you may also want to include Validation

Kusdrae commented 3 years ago

@tfprado bro, thank you so much, you helped me so much, really. Wish you the BEST man!!!!!!!!!!!