Homepage
Main navigation
Main content
Additional information
Login / Sign up
Quality Guidelines
About
GitHub
Propose new content
Winter CMS resources and help articles
Simple and to the point. Optimized by the community.
×
Login / Sign up
Quality Guidelines
About
GitHub
Propose new content
Edit trick
Changes will be published after manual review
Title
Give your trick a describing title. Do
not
start with «How to...».
Your trick
Keep it short and concise! Markdown is supported.
If you live in Switzerland its daily business that you need frontend forms with 2 to 4 localizations. The received data has to be sent to the your client and their customer, a database and some export functions are nice too. Sounds easy but is quite a journey. And the documentation is not complete enough for dummies and far too complicated. In forums you find a lot of different advices, impossible for dummies like me too. I'm glad I have a good friend coaching me through this. I hope I can help with some challenges I had and have cost me too much lifetime. Lets say we make kind of contact form: **Affected files overviews** ``` - plugins - - YourName - - - your Plugin - - - - components - - - - models - - - - etc - themes - - your Theme - - - assets - - - - bootstraps css combined with your own css - - - pages - - - - layout - - - - - your layout - - - - - your form - - - - partials - - - - - your scripts ``` **Used WinterCMS Setting Pages** - Translations (input labels, validation messages, flash message) - Mail Templates (mail template and contents) - Mail Settings (SMTP connection) - Administrators (setting the rights to edit data) **Dependencies in this example** - Translate Plugin - Pages Plugin - Bootstrap 5 - (Builder Plugin: recommend for easy plugin construction) - jquery if not used from the WinterCMS itself) **The page, where the form is located** ``` title = "Contact" url = "/contact" layout = "default" is_hidden = 0 // NOTE: Its easier not make localized urls. Urls like /de/contact, /en/contact, /fr/contact is not such an issue and its practical as the marketing can use the shortcut /contact and the user lands automatically on the form in his browser language. Nice! == <?php use \YourName\YourPlugin\Models\Contact; use RainLab\Translate\Models\Message; use Mail //////// Ajax request through data-request="onHandleForm" // Function has to start with "on", the rest is free but camelcase is needed function onHandleForm() { //////// Fill one entry of your model Contact with all form fields and save it into your Model. // Tip: use same code for database field, input name, input id and translations keys to keep overview and minimize typos $item = new Item(Input::all()); $item->save(); // define hidden fields here before save() and not in the form f.e.: // $item->created_at = Carbon\Carbon::now(); // $item->editstate_id = 1; etc... // if something is wrong here you get an exception // define validation fields also before save() here like this: // translate them late in the corresponding system settings page of WinterCMS $item->customMessages = [ 'firstname.required' => Message::trans('contactform.errors.firstname_required'), 'lastname.required' => Message::trans('contactform.errors.lastname_required'), 'email.required' => Message::trans('contactform.errors.email_required'), 'email.email' => Message::trans('contactform.email_email'), etc... ]; //////// Send Mails to client and perhaps his customer // First get the fields you want to use in your mail template, f.e. // the key of the mail template ('yourname.forms:...etc.') is totally free. // use your logical labeling system: $vars = [ 'created_at' => Carbon\Carbon::now(), 'firstname' => Input::get('firstname'), 'lastname' => Input::get('lastname'), 'email' => Input::get('email') ]; Mail::send('yourname.forms::yourplugin.data', $vars, function($message) { $message->to('yourclient@hiscompany.com', 'CompanyName'); $message->from('noreply@hiscompany.com', 'Website System'); }); Mail::send('yourname.forms::yourplugin', $vars, function($message) { $message->to(Input::get('email'), ''); $message->from('noreply@hiscompany.com', 'CompanyName'); }); $this['sent'] = true; ////// Flash Message. The user should now that submitting worked. // direct or localized, translation key according to your own logic: Flash::success('Hey User, your did it!'); or: Flash::success(Message::trans('contactform.flashmessage')); // want to send the user to another page after submitting? Then: return Redirect::to('/whateverpageyoulike'); } ?> == ////// Your client perhaps wants to edit the intro text in all languages.... {% content 'contact-intro' %} /////// The Form: put o the Ajax Functions you want with data-xx <form class="" novalidate data-request="onHandleForm" data-request-files data-request-validate data-request-flash> // you can show everywhere the overview of validation error messages like this: // at the same time the field with error gets red and has the message underneath it to here. <div class="alert alert-danger" data-validate-error> <p data-message></p> </div> // just some examples <div class="col-12 col-md-6 mb-3"> <label for="firstname">{{ 'contactform.label.firstname'|_ }} *</label> <input type="text" id="firstname" name="firstname" class="form-control"> <div class="form-text text-danger" data-validate-for="firstname"></div> </div> <div class="col-12 col-md-6 mb-3"> <label for="lastname">{{ 'contactform.label.lastname'|_ }} *</label> <input type="text" id="lastname" name="lastname" class="form-control"> <div class="form-text text-danger" data-validate-for="lastname"></div> </div> <div class="col-12 col-md-6 mb-3"> <label for="email">{{ 'contactform.label.email'|_ }} *</label> <input type="text" id="email" name="email" class="form-control"> <div class="form-text text-danger" data-validate-for="email"></div> </div> <p> {{ 'contactform.label.fileuploads'|_ }} </p> <div class="col-12 py-4"> <input id="files" type="file" name="files[]" multiple accept="application/pdf"> <div class="form-text text-danger" data-validate-for="files"></div> </div> <div class="col-12 text-center pt-3"> <button id="submit" name="submit" type="submit" class="btn btn-lg btn-primary" data-attach-loading> {{ 'contactform.label.submit'|_ }} </button> </div> </form> ////// here we have to add the script for the validation magic // attention, every Bootstrap version can have different class names here... this 5. {% put scripts %} <script> $(window).on('ajaxInvalidField', function(event, fieldElement, fieldName, errorMsg, isFirst) { $(fieldElement).closest('.form-control').addClass('is-invalid'); }); $(document).on('ajaxPromise', '[data-request]', function() { $(this).closest('form').find('.form-control.is-invalid').removeClass('is-invalid'); }); </script> {% endput %} ``` I hope you still can follow till here. As a dummy I still understand whats happening in this Laravel Code, wuu... **Layout Files and Scripts** I order to have the flash message working you need to add this code right before your body end tag. jQuery can be loaded from the WinterCMS directly too. Attention: The order is crucial here!!! ``` {% flash %} <p data-control="flash-message" class="flash-message fade {{ type }}" data-interval="10"> {{ message }} </p> {% endflash %} <script src="{{ 'assets/.../jquery.js'|theme }}"></script> or <script src="{{ 'assets/vendor/jquery.js'|theme }}"></script> {% framework extras %} ``` **Layout Files and Scripts** If your not familiar how to create a plugin, learn that first. Here whats crucial for Model.php f.e. Contact.php in our example ``` <?php namespace YourName\YourPlugin\Models; use Model; use System\Models\File; use DB; use YourName\YourPlugin\Models\SomeSecondModelforCheckboxesOrDropwdowns; class Contact extends Model { use \Winter\Storm\Database\Traits\Validation; public $table = 'yourname_yourplugin_contact'; //////// Validation // first the hidden fields with guarded // then the validation rules //max:number helps against bots that want to flood our databse public $guarded = ['id', 'updated_at', 'created_at', 'editstate_id']; // thats the hidden field public $rules = [ 'firstname' => 'required|max:80', 'lastname' => 'required|max:20', 'email' => 'email|required|max:30', ]; //thats needed to, don't know why public $customMessages = []; //Relations public $belongsToMany = [ 'yoursecondmodels' => [ YoursecondModel::class, 'table' => 'yourname_yourplugin_contact_somethingelse' ] ]; public $attachMany = [ 'files' => File::class, ]; } ``` **Dropdown with selection from a second model** Can easily be done with building a component. The default.htm would be like this. Also see the Twig documentation for sorting or use the reorder functionality of the Plugin. ``` {% set yoursecondmodels = __SELF__. yoursecondmodels %} <div class="mb-3"> <label for="something" class="form-label"><strong>{{ 'contactform.label.something'|_ }}</strong></label> <select class="form-select" aria-label="select something" name="something" > <option selected value="0">{{ 'contactform.select.pleasechoose' |_ }}</option> {% for yoursecondmodel in yoursecondmodels|sort((a, b) => a.name <=> b.name) %} {% if assurance.type == 'kk' %} <option value="{{ yoursecondmodel.id }}">{{ yoursecondmodel.name }}</option> {% endif %} {% endfor %} </select> </div> ``` **Checkboxes with selection from a second model** No one is understanding multiselects - - so its getting complicating with checkboxes. Use again a component for that. In the default.htm: ``` {% set yourthirdmodels = __SELF__.yourthirdmodels %} <div class="mb-3"> <strong>{{ 'contactform.label.yourthirdmodels' |_ }}</strong><br> {% for yourthirdmodel in yourthirdmodels %} <div class="form-check form-check-inline"> <input class="form-check-input" type="checkbox" id="yourthirdmodel{{ yourthirdmodel.id }}" value="{{ yourthirdmodel.id }}" name="yourthirdmodel{{ yourthirdmodel.id }}" > <label class="form-check-label" for="yourthirdmodel{{ yourthirdmodel.id }}" > {{ yourthirdmodel.name }} </label> </div> {% endfor %} </div> ``` In the function onHandleForm() we have to add this: (and now I'm getting a the limit of my skills, you can surely do this more elegantly with some Laravel knowledge...) ``` $checkboxes = [ Input::get('yourthirdmodel1'), Input::get('yourthirdmodel2'), etc., ]; $checkboxesfiltered = array_filter($checkboxes); // and then $contact = new Contact(Input::except('yourthirdmodel1', 'yourthirdmodel2',etc.',etc')); f.e..... $contact->created_at = Carbon\Carbon::now(); f.e. ...... $contact->customMessages = []; etc... $contact->yourthirdmodels = $checkboxesfiltered; $contact->save(); ``` I know. But it works. **Mail Contents** You can pass field contents into the sent mails with simple twig tags {{ created_at }}. If you need multilanguage mail contents try to work with case 'de' {} case 'en' {}' in the send mail code and do different mail templates. I'm not sure if the Translation Plugin works in the mail templates.... - - - For our OctoberCMS Friends and the Google Index: Multi-language Bootstrap 5 Frontend October Form with File Upload, Checkboxes, Ajax Validation, Mails and editable Backend.
References
Add additional online resources to your trick
×
Name
URL
×
Name
URL
×
Name
URL
×
Name
URL
×
Name
URL
×
Name
URL
+ Add reference
Topics
If your trick fits more than one topic select multiple. Select at least one.
Backend
Plugin Development
CMS
Twig
Themes
Deployment
Tags
You can use existing tags or create new ones. Add at least one.
Submit for review
Cancel
We use cookies to measure the performance of this website. Do you want to accept these cookies?
Accept
Decline