Back to Top

Magento 2 Development 09: Form Validation and Saving Data

Updated 6 March 2024

Now that we have created a nice form let’s use it to save some data in the table.

Form Validation

Most probably you have used javascript or jquery for form validation. But Magento provides various form validation techniques out of the box by extending the jQuery Validation library. Let’s modify our phtml file view/frontend/templates/add.phtml

Updated code for view/frontend/templates/add.phtml file

<div class="blog-container">
    <form class="blog-form" data-mage-init='{"validation": {}}'>
        Title: <input type="text" name="title" class="required-entry"/>
        Content: <textarea name="content" class="required-entry validate-no-html-tags"></textarea>
        <button type="submit">Submit</button>
    </form>
</div>

We have data-mage-init to initialize the validation on this form. And we have added required-entry class in each field, which implies that this field is required while submitting the form. Also in the content field, we have added a “validate-no-html-tags” class which will check for HTML tags. Magento provides multiple ways to initialize the form validation and also multiple ways to add the validation rules, which you should explore on your own.

There are multiple rules present in Magento to validate email, number, URL, etc. which will cover most of our needs. You can find most of the available validation rules in vendor/magento/magento2-base/lib/web/mage/validation.js

Searching for an experienced
Magento 2 Company ?
Find out More

Now when we flush the cache and reload the page and try to submit the form. You should see the error message.

2021-02-08_13-37

During development, we can disable some of the caches so that we do not have to flush the cache every time when we make changes. Disabling the full page and blocking the HTML cache will do the trick.

php bin/magento cache:disable full_page block_html

But please keep in mind to enable the cache when you are testing your module.

Saving Data

To save the form-submitted data we need to create a new action. But before that let’s mention the form action in our form, view/frontend/templates/add.phtml

Updated code for view/frontend/templates/add.phtml file

<div class="blog-container">
    <form class="blog-form" method="post" action=<?= $escaper->escapeUrl($block->getUrl('blog/manage/save')); ?> data-mage-init='{"validation": {}}'>
        Title: <input type="text" name="title" class="required-entry"/>
        Content: <textarea name="content" class="required-entry validate-no-html-tags"></textarea>
        <button type="submit">Submit</button>
    </form>
</div>

Here we have mentioned the form submit type as post and the action. In the action attribute, we have called the getUrl method from the block. The getUrl method takes the URL which is frontName/controllerName/actionName and it will return the full URL by adding the base URL. So that if we install this module on another website, we do not have to make any manual changes.

You also noticed that we use the $escaper->escapeUrl() method. This method will apply the default HTML escaping which is used to remove HTML content with the data and additionally removes javascript:, vbscript: and data:. If you want to prevent URLs like this in the user-provided links, you can use this method. The $escaper is the parent attribute so we don’t need to define it in our phtml file.

Let’s create the new action to save the data, Controller/Manage/Save.php action file,

Code for Controller/Manage/Save.php file

<?php
namespace Webkul\BlogManager\Controller\Manage;

use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;

class Save extends AbstractAccount
{
    /**
     * @var \Webkul\BlogManager\Model\BlogFactory
     */
    protected $blogFactory;

    /**
     * Dependency Initilization
     *
     * @param Context $context
     * @param \Webkul\BlogManager\Model\BlogFactory $blogFactory
     */
    public function __construct(
        Context $context,
        \Webkul\BlogManager\Model\BlogFactory $blogFactory
    ) {
        $this->blogFactory = $blogFactory;
        parent::__construct($context);
    }

    /**
     * Provides content
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $data = $this->getRequest()->getParams();
        $model = $this->blogFactory->create();
        $model->setTitle($data['title']);
        $model->setContent($data['content']);
        $model->save();
        echo 'Saved';
    }
}

The $this->getRequest()->getParams() is used to get the form data, which is similar to $_GET and $_POST.

Factory

Now let’s talk about the \Webkul\BlogManager\Model\BlogFactory class a little bit. If you recall we created a class with the name Blog, not BlogFactory, so how can we use this class if it’s not created? Remember generated/code folder which created when we run the di compile command? Magento creates these factory classes for the Models (note that the factory classes are get created for models and collections only). You can find this BlogFactory class in generated/code/Webkul/BlogManager/Model/ folder after running the di compile command. Magento will not create these classes if it has not been used yet, which means you will not see the CommentFactory class there. This process of generating code automatically is known as code generation. And the factories are part of the Factory Design Pattern which is one of the many design patterns that Magento uses.

Why do we need these factory classes? and why it is required only for models and not other classes?

Dependency Injection

When we create objects by passing in the constructor, it does not create a new object every time. It first checks if there is an object has been already created for that class, if an object is available then it will use that object otherwise it will create a shareable object. This whole process is known as the Dependency Injection design pattern.

Back to Factory

If you recall the model class represents a single row in the table. We do not want to share a single object for all rows. It will be ideal if we create a new object for each row. That’s why we need factory classes for models and collections because these are related to the data of the tables. The Factory class will ensure that we get a new object every time we call the create() method.

Here we have created a new object for the model and assigned it to the $model. We can set data for each column by calling the set methods. These are called getter and setter, which follow the camel-case naming convention. Here we have called setTitle to set the title column and setContent to set the content column. Now to actually save this in the table, we need to call the save() method.

Now if you do di compile and submit the form, you will see the message “Saved” that we have printed at the end. And if you check the table you should see the new entry,

2021-02-08_15-27

We can also call the setData method and pass an associative array instead of calling the setter for each column. The key for the associative array will be the column name of the table.

Updated code for Controller/Manage/Save.php file

<?php
namespace Webkul\BlogManager\Controller\Manage;

use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;

class Save extends AbstractAccount
{
    /**
     * @var \Webkul\BlogManager\Model\BlogFactory
     */
    protected $blogFactory;

    /**
     * Dependency Initilization
     *
     * @param Context $context
     * @param \Webkul\BlogManager\Model\BlogFactory $blogFactory
     */
    public function __construct(
        Context $context,
        \Webkul\BlogManager\Model\BlogFactory $blogFactory
    ) {
        $this->blogFactory = $blogFactory;
        parent::__construct($context);
    }

    /**
     * Provides content
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $data = $this->getRequest()->getParams();
        $model = $this->blogFactory->create();
        $model->setData($data);
        $model->save();
        echo 'Saved';
    }
}

Here $data is an associative array, you can verify it by printing it with the print_r function of PHP.

Saving data in the user_id column

In the user_id column, we were supposed to insert the current customer’s id. We can get the logged-in customer’s id from the customer session.

Updated code for Controller/Manage/Save.php file

<?php
namespace Webkul\BlogManager\Controller\Manage;

use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;
use Magento\Customer\Model\Session;

class Save extends AbstractAccount
{
    /**
     * @var \Webkul\BlogManager\Model\BlogFactory
     */
    protected $blogFactory;

    /**
     * @var Magento\Customer\Model\Session
     */
    protected $customerSession;

    /**
     * Dependency Initilization
     *
     * @param Context $context
     * @param \Webkul\BlogManager\Model\BlogFactory $blogFactory
     */
    public function __construct(
        Context $context,
        \Webkul\BlogManager\Model\BlogFactory $blogFactory,
        Session $customerSession
    ) {
        $this->blogFactory = $blogFactory;
        $this->customerSession = $customerSession;
        parent::__construct($context);
    }

    /**
     * Provides content
     *
     * @return \Magento\Framework\View\Result\Page
     */
    public function execute()
    {
        $data = $this->getRequest()->getParams();
        $model = $this->blogFactory->create();
        $model->setData($data);

        $customer = $this->customerSession->getCustomer();
        $customerId = $customer->getId();
        $model->setUserId($customerId);

        $model->save();
        echo 'Saved';
    }
}

Here we have passed the customer session class to get the logged-in customer data. From the customer session, we have called getCustomer() which will return the customer model for the current customer. And from that, we can get the id by calling getId(). We have set the customer id in our model with the setUserId method because our column name is user_id.

Now if you again submit the form, you might get some exceptions like the below if you have not run di compile command,

2021-02-08_15-44

After compiling if we submit the form the user id will also be set in the table,

2021-02-08_15-53

Sessions

Magento has different types of sessions,
1. Magento\Backend\Model\Session– This session will use for Magento Admin.
2. Magento\Catalog\Model\Session– Catalog session will use for the frontend for product filters.
3. Magento\Checkout\Model\Session– The checkout session will use to store checkout-related information.
4. Magento\Customer\Model\Session– Customer session will use for logged-in customers.
It has a few more sessions like Message Session, Persistent Data Session, and Newsletter Session.

Please check out more blogs to know about factory design patterns and code generation and other design patterns.

https://webkul.com/blog/dependency-injection/

https://webkul.com/blog/magento2-code-generation-and-factory-design-pattern/

Folder Structure till now,

Selection_108
Folder Structure

Next Blog -> Magento 2 Development 10: Collection and Block

Previous Blog -> Magento 2 Development 08: Layout and Templates

. . .

Leave a Comment

Your email address will not be published. Required fields are marked*


Be the first to comment.

Back to Top

Message Sent!

If you have more details or questions, you can reply to the received confirmation email.

Back to Home