Magento 2 Development 22: Helper

0

 

One very important concept that is widely in Magento is Helper. So here we will learn what is helper and how to use them.

It’s always good idea to create all the functions which are used frequently in a single class. That’s why we use helper to group all frequently used or common methods together. It helps in implementing the DRY concept in coding.

By grouping I mean we create different helper which take care different things, if putting all the methods in a single class become too cumbersome. One such example of grouping that you frequently observe in magento is a helper to handle all email related tasks.

The helpers can be injected anywhere (i.e. in Blocks, Controllers, templates, etc) so that we can access the methods of that helper class. Injection of class is just a fancy term for “mentioning the class in the constructor” so that we can create object of that class.

In this blog, for now I will only create a single function to get the customer id. We can not get the customer id from session, if all the caches are enabled. So we need to do some hack, which we will do later. If you remember we have used the customer id in 3-4 places. And everywhere we have used the session to get it. Now if we have to implement the hack, we will have to update the code in 3-4 places. Which is not ideal. So we will create a function in helper to get the customer id. And we will call it everywhere else where the customer id is needed. So now we have to change in only one function, if we do some changes later. Note that we will and can keep adding more functions to the helper as we see fit.



Creating and Using the Helper

To create the helpers we use new folder named Helper inside the module directory. By default we create helper with name Data. But we can use any names. Let’s create the helper as Helper/Data.php

<?php
namespace Webkul\BlogManager\Helper;
use Magento\Customer\Model\Session;
class Data extends \Magento\Framework\App\Helper\AbstractHelper
{
public $customerSession;
public function __construct(
Session $customerSession,
\Magento\Framework\App\Helper\Context $context
) {
$this->customerSession = $customerSession;
parent::__construct($context);
}
public function getCustomerId()
{
$customerId = $this->customerSession->getCustomerId();
return $customerId;
}
}

Since this is a helper, we have extended the default helper class. And we have created a method to get the customer id.


Now let’s quickly update all the codes where we have used the customer id. We will have to edit Block/BlogList.php

<?php
namespace Webkul\BlogManager\Block;
class BlogList extends \Magento\Framework\View\Element\Template
{
public $blogCollection;
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Webkul\BlogManager\Model\ResourceModel\Blog\CollectionFactory $blogCollection,
\Webkul\BlogManager\Helper\Data $helper,
array $data = []
) {
$this->blogCollection = $blogCollection;
$this->helper = $helper;
parent::__construct($context, $data);
}
public function getBlogs()
{
$customerId = $this->helper->getCustomerId();
$collection = $this->blogCollection->create();
$collection->addFieldToFilter('user_id', $customerId)
->setOrder('updated_at', 'DESC');
return $collection;
}
}

As you can see here, we have removed the session codes. And injected the helper class in the constructor and called the getCustomerId function.


We also have to edit the Controller/Manage/Edit.php

<?php
namespace Webkul\BlogManager\Controller\Manage;
use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
class Edit extends AbstractAccount
{
public $resultPageFactory;
public $blogFactory;
public $helper;
public $messageManager;
public function __construct(
Context $context,
PageFactory $resultPageFactory,
\Webkul\BlogManager\Model\BlogFactory $blogFactory,
\Webkul\BlogManager\Helper\Data $helper,
\Magento\Framework\Message\ManagerInterface $messageManager
) {
$this->resultPageFactory = $resultPageFactory;
$this->blogFactory = $blogFactory;
$this->helper = $helper;
$this->messageManager = $messageManager;
parent::__construct($context);
}
public function execute()
{
$blogId = $this->getRequest()->getParam('id');
$customerId = $this->helper->getCustomerId();
$isAuthorised = $this->blogFactory->create()
->getCollection()
->addFieldToFilter('user_id', $customerId)
->addFieldToFilter('entity_id', $blogId)
->getSize();
if (!$isAuthorised) {
$this->messageManager->addError(__('You are not authorised to edit this blog.'));
return $this->resultRedirectFactory->create()->setPath('blog/manage');
}
$resultPage = $this->resultPageFactory->create();
$resultPage->getConfig()->getTitle()->set(__('Edit Blog'));
$layout = $resultPage->getLayout();
return $resultPage;
}
}


And also Controller/Manage/Save.php

<?php
namespace Webkul\BlogManager\Controller\Manage;
use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;
class Save extends AbstractAccount
{
public $blogFactory;
public $helper;
public $messageManager;
public function __construct(
Context $context,
\Webkul\BlogManager\Model\BlogFactory $blogFactory,
\Webkul\BlogManager\Helper\Data $helper,
\Magento\Framework\Message\ManagerInterface $messageManager
) {
$this->blogFactory = $blogFactory;
$this->helper = $helper;
$this->messageManager = $messageManager;
parent::__construct($context);
}
public function execute()
{
$data = $this->getRequest()->getParams();
$customerId = $this->helper->getCustomerId();
if (isset($data['id']) && $data['id']) {
$isAuthorised = $this->blogFactory->create()
->getCollection()
->addFieldToFilter('user_id', $customerId)
->addFieldToFilter('entity_id', $data['id'])
->getSize();
if (!$isAuthorised) {
$this->messageManager->addError(__('You are not authorised to edit this blog.'));
return $this->resultRedirectFactory->create()->setPath('blog/manage');
} else {
$model = $this->blogFactory->create()->load($data['id']);
$model->setTitle($data['title'])
->setContent($data['content'])
->save();
$this->messageManager->addSuccess(__('You have updated the blog successfully.'));
}
} else {
$model = $this->blogFactory->create();
$model->setData($data);
$model->setUserId($customerId);
$model->save();
$this->messageManager->addSuccess(__('Blog saved successfully.'));
}
return $this->resultRedirectFactory->create()->setPath('blog/manage');
}
}


One more file that we have to edit is Controller/Manage/Delete.php

<?php
namespace Webkul\BlogManager\Controller\Manage;
use Magento\Customer\Controller\AbstractAccount;
use Magento\Framework\App\Action\Context;
class Delete extends AbstractAccount
{
public $blogFactory;
public $helper;
public $jsonData;
public function __construct(
Context $context,
\Webkul\BlogManager\Model\BlogFactory $blogFactory,
\Webkul\BlogManager\Helper\Data $helper,
\Magento\Framework\Json\Helper\Data $jsonData
) {
$this->blogFactory = $blogFactory;
$this->helper = $helper;
$this->jsonData = $jsonData;
parent::__construct($context);
}
public function execute()
{
$blogId = $this->getRequest()->getParam('id');
$customerId = $this->helper->getCustomerId();
$isAuthorised = $this->blogFactory->create()
->getCollection()
->addFieldToFilter('user_id', $customerId)
->addFieldToFilter('entity_id', $blogId)
->getSize();
if (!$isAuthorised) {
$msg=__('You are not authorised to delete this blog.');
$success=0;
} else {
$model = $this->blogFactory->create()->load($blogId);
$model->delete();
$msg=__('You have successfully deleted the blog.');
$success=1;
}
$this->getResponse()->setHeader('Content-type', 'application/javascript');
$this->getResponse()->setBody(
$this->jsonData->jsonEncode(
[
'success' => $success,
'message' => $msg
]
));
}
}

Now we have created and used a helper. Please verify all the pages that these changes affect. And please make a habit of checking/verifying the changes even if it’s a minor change.


Cacheable

One more thing that I missed earlier is use of cacheable argument. By default magento cached all pages. So if you enable all caches and check the blog listing page you will see some confusing results.

Magento recommends that all public pages (pages which can be accessed without sign-in) must be cacheable. That means all pages such as category page, product page, cms page are cached. And it make sense because the public pages are get frequently accessed and the data on those pages do not change based on who the user is.

However for all non public page (those pages which can be accessed only after logging in), we should disable the cache if the data is not static for that page.

We have not yet created any public page, we will do in next blogs. So in our case the private pages are blog listing, blog edit page and blog add page. We do not care blog add page because it is static because we just show a blank form. But for the other two pages we will have to disable the cache. Which we can do from the layout xml file.

So let’s first edit the view/frontend/layout/blogmanager_manage_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="customer_account"/>
<body>
<referenceContainer name="content">
<block class="Webkul\BlogManager\Block\BlogList" name="blogmanager.blog.list" template="Webkul_BlogManager::list.phtml" cacheable="false" />
</referenceContainer>
</body>
</page>

Here we have done a very minor change. We have just added cacheable=”false” in the node. Now this page won’t be cached. Note that if there were multiple blocks then adding cacheable=”false” in any one of them will make the whole page non cacheable. That means we can not say that cache this block and not this in a page.


Let’s edit the other page too view/frontend/layout/blogmanager_manage_edit.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="customer_account"/>
<head>
<css src="Webkul_BlogManager::css/style.css"/>
</head>
<body>
<referenceContainer name="content">
<block class="Webkul\BlogManager\Block\Blog" name="blogmanager.blog.edit" template="Webkul_BlogManager::edit.phtml" cacheable="false" />
</referenceContainer>
</body>
</page>

Now it will work fine even if we enable all caches.


Show Few More Columns

It is not related to the previous topics. But it is a minor change so I will append it in this blog. As you can see their are various important details are missing from the blog list on seller side. We will do some changes to show status, created at and updated at on the grid.

First we will edit the phtml file view/frontend/templates/list.phtml

<div class="blog-list-table-container">
<table class="blog-list-table">
<tr class="blog-list-table-head">
<th class="blog-list-table-id">
<?= __("Id")?>
</th>
<th class="blog-list-table-title">
<?= __("Title")?>
</th>
<th class="blog-list-table-content">
<?= __("Content")?>
</th>
<th class="blog-list-table-status">
<?= __("Status")?>
</th>
<th class="blog-list-table-date">
<?= __("Updated At")?>
</th>
<th class="blog-list-table-date">
<?= __("Created At")?>
</th>
<th class="blog-list-table-action-edit">
<?= __("Edit")?>
</th>
<th class="blog-list-table-action-delete">
<?= __("Delete")?>
</th>
</tr>
<?php
$blogs = $block->getBlogs();
$statuses = $block->getStatuses();
foreach ($blogs as $blog) {?>
<tr class="blog-list-table-row">
<td class="blog-list-table-id">
<?= $blog->getId()?>
</td>
<td class="blog-list-table-title">
<?= $blog->getTitle()?>
</td>
<td class="blog-list-table-content">
<?= substr($blog->getContent(), 0, 20).'...'?>
</td>
<td class="blog-list-table-status">
<?= $statuses[$blog->getStatus()]?>
</td>
<td class="blog-list-table-date">
<?= $block->getFormattedDate($blog->getUpdatedAt())?>
</td>
<td class="blog-list-table-date">
<?= $block->getFormattedDate($blog->getCreatedAt())?>
</td>
<td class="blog-list-table-action blog-list-table-action-edit">
<a href="<?= $block->getUrl('blog/manage/edit', ['id'=>$blog->getId()])?>">
<?= __('Edit') ?>
</a>
</td>
<td class="blog-list-table-action blog-list-table-action-delete">
<a href="<?= $block->getUrl('blog/manage/delete', ['id'=>$blog->getId()])?>">
<?= __('Delete') ?>
</a>
</td>
</tr>
<?php } ?>
</table>
</div>
<script type="text/x-magento-init">
{
"*": {
"bloglist": ""
}
}
</script>

In the head section of the table we have added the three columns and similarly in the body section also.

We have called a method getStatuses which will return an associative array with integer (0 or 1) as the key and the status label as the value. Using this we can show the label based on the value.

One more thing is that we are not showing the date directly from the database. We have created getFormattedDate function in the block class which will format the date in a nice readable format.


Now let’s create these methods in Block/BlogList.php

<?php
namespace Webkul\BlogManager\Block;
class BlogList extends \Magento\Framework\View\Element\Template
{
public $blogCollection;
public $statuses;
public function __construct(
\Magento\Framework\View\Element\Template\Context $context,
\Webkul\BlogManager\Model\ResourceModel\Blog\CollectionFactory $blogCollection,
\Webkul\BlogManager\Helper\Data $helper,
\Webkul\BlogManager\Model\Blog\Status $blogStatus,
\Magento\Framework\Stdlib\DateTime\TimezoneInterface $date,
array $data = []
) {
$this->blogCollection = $blogCollection;
$this->helper = $helper;
$this->blogStatus = $blogStatus;
$this->date = $date;
parent::__construct($context, $data);
}
public function getBlogs()
{
$customerId = $this->helper->getCustomerId();
$collection = $this->blogCollection->create();
$collection->addFieldToFilter('user_id', $customerId)
->setOrder('updated_at', 'DESC');
return $collection;
}
public function getStatuses()
{
$statuses = [];
foreach ($this->blogStatus->toOptionArray() as $status) {
$statuses[$status['value']] = $status['label'];
}
return $statuses;
}
public function getFormattedDate($date)
{
return $this->date->date($date)->format('d/m/y H:i');
}
}

In getStatuses we have used the class that we created to get the statuses for the ui component. It is a good practice because if we want to add more statuses then we will have to change only in one file.

To get the formatted date we have used \Magento\Framework\Stdlib\DateTime\TimezoneInterface which will return the date in the mentioned format. If we want to convert then we have to pass the timestamp in date() and if we leave it empty then the current timestamp will be used. Note that this will return the date based on the locale.


Now you will see all the details like,

2021-08-03_10-36


Post a Comment

0Comments
Post a Comment (0)
To Top