This Magento2 FAQ module provides complete FAQ solution to display frequently asked questions grouped by categories. It will be displayed on separate FAQ page using a custom jQuery Accordion.
Let's start it by creating custom extension.
You can find complete module on Github at Magelearn_Categoryfaq
Create folder inside app/code/Magelearn/Categoryfaq
<?php /** * Copyright © All rights reserved. * See COPYING.txt for license details. */ use Magento\Framework\Component\ComponentRegistrar; ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Magelearn_Categoryfaq', __DIR__);Add composer.json file in it:
{
"name": "magelearn/module-categoryfaq",
"description": "Magento 2 Category FAQ Extension",
"type": "magento2-module",
"license": "GPL-3.0",
"authors": [
{
"name": "vijay rami",
"email": "vijaymrami@gmail.com"
}
],
"minimum-stability": "dev",
"require": {},
"autoload": {
"files": [
"registration.php"
],
"psr-4": {
"Magelearn\\Categoryfaq\\": ""
}
}
}<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Magelearn_Categoryfaq" setup_version="1.0.0"> <sequence> <module name = "Magento_GraphQl"/> </sequence> </module> </config>
<?xml version="1.0" ?>
<schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
<table name="magelearn_categoryfaq_category" resource="default" engine="innodb" comment="magelearn_categoryfaq_category Table">
<column name="id" nullable="false" xsi:type="int" comment="ID" identity="true"/>
<index referenceId="MAGELEARN_CATEGORYFAQ_CATEGORY_ID" indexType="btree">
<column name="id"/>
</index>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="id"/>
</constraint>
<column name="name" nullable="false" xsi:type="text" comment="Name"/>
<column name="description" nullable="true" xsi:type="text" comment="Description"/>
<column name="sort_order" nullable="false" xsi:type="int" default="0" comment="Sort Order Position" identity="false"/>
<column name="status" nullable="true" xsi:type="int" comment="Category Display Status" identity="false"/>
<column name="created_at" nullable="true" xsi:type="datetime" comment="Date of creation of this category" default="CURRENT_TIMESTAMP"/>
<column name="updated_at" nullable="true" xsi:type="datetime" comment="Last update date of this category" default="CURRENT_TIMESTAMP" on_update="true"/>
<index referenceId="MAGELEARN_CATEGORYFAQ_NAME" indexType="fulltext">
<column name="name"/>
</index>
<index referenceId="MAGELEARN_CATEGORYFAQ_DESCRIPTION" indexType="fulltext">
<column name="description"/>
</index>
</table>
<table name="magelearn_categoryfaq_question" resource="default" engine="innodb" comment="magelearn_categoryfaq_question Table">
<column name="id" nullable="true" xsi:type="int" comment="ID" identity="true"/>
<constraint xsi:type="primary" referenceId="PRIMARY">
<column name="id"/>
</constraint>
<column name="title" nullable="false" xsi:type="text" comment="Question Title"/>
<column name="answer" nullable="true" xsi:type="text" comment="Answer for the question"/>
<column name="status" nullable="true" xsi:type="int" comment="Question Display Status" identity="false"/>
<column name="sort_order" nullable="false" default="0" xsi:type="int" comment="Question Display Position" identity="false"/>
<column name="category_id" nullable="true" xsi:type="int" comment="Category associated with this question" identity="false"/>
<column name="created_at" nullable="true" xsi:type="datetime" comment="Date of creation of this category" default="CURRENT_TIMESTAMP"/>
<column name="updated_at" nullable="true" xsi:type="datetime" comment="Last update date of this category" default="CURRENT_TIMESTAMP" on_update="true"/>
<index referenceId="MAGELEARN_CATEGORYFAQ_QUESTION_TITLE" indexType="fulltext">
<column name="title"/>
</index>
<index referenceId="MAGELEARN_CATEGORYFAQ_QUESTION_ANSWER" indexType="fulltext">
<column name="answer"/>
</index>
<constraint xsi:type="foreign" referenceId="MAGELEARN_CATEGORYFAQ_QUESTION_CATEGORY_ID_MAGELEARN_CATEGORYFAQ_ID" table="magelearn_categoryfaq_question"
column="category_id" referenceTable="magelearn_categoryfaq_category" referenceColumn="id" onDelete="SET NULL"/>
</table>
</schema>Now, we will add our Data patch file. This file will add some dummy data into database table.
Create CreateDefaultSamples.php file inside Setup/Patch/Data folder.
<?php
namespace Magelearn\Categoryfaq\Setup\Patch\Data;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchRevertableInterface;
class CreateDefaultSamples implements DataPatchInterface, PatchRevertableInterface
{
/**
* @var ModuleDataSetupInterface
*/
private $moduleDataSetup;
public function __construct(
ModuleDataSetupInterface $moduleDataSetup
) {
$this->moduleDataSetup = $moduleDataSetup;
}
public function apply()
{
$this->moduleDataSetup->startSetup();
$setup = $this->moduleDataSetup;
$category_data = [
[
'name' => __('Default'),
'description' => __('When there is only 1 active category, only its questions will be displayed.'),
'sort_order' => 0,
'status' => 1
]
];
$this->moduleDataSetup->getConnection()->insertArray(
$this->moduleDataSetup->getTable('magelearn_categoryfaq_category'),
['name', 'description','sort_order','status'],
$category_data
);
$question_data = [
[
'title' => __('What should I use this module for?'),
'answer' => __('Category FAQ module was designed as a simple solution to provide information for your customers'),
'status' => 1,
'category_id' => 1,
'sort_order' => 0
]
];
$this->moduleDataSetup->getConnection()->insertArray(
$this->moduleDataSetup->getTable('magelearn_categoryfaq_question'),
['title', 'answer','status','category_id','sort_order'],
$question_data
);
$this->moduleDataSetup->endSetup();
}
/**
* @inheritdoc
*/
public function revert()
{
$this->moduleDataSetup->getConnection()->startSetup();
$this->moduleDataSetup->getConnection()->endSetup();
}
public function getAliases()
{
return [];
}
public static function getDependencies()
{
return [];
}
}To un-install our custom table we will create Uninstall script.
Create Uninstall.php file inside Setup folder.
<?php
namespace Magelearn\Categoryfaq\Setup;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\UninstallInterface;
use Magento\Config\Model\ResourceModel\Config\Data\CollectionFactory;
/**
* Class Uninstall
*
* @package Magelearn\Categoryfaq\Setup
*/
class Uninstall implements UninstallInterface
{
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param CollectionFactory $collectionFactory
*/
public function __construct(
CollectionFactory $collectionFactory
) {
$this->collectionFactory = $collectionFactory;
}
/**
* Drop sample table
*
* @param SchemaSetupInterface $setup
* @param ModuleContextInterface $context
*/
public function uninstall(SchemaSetupInterface $setup, ModuleContextInterface $context)
{
if ($setup->tableExists('magelearn_categoryfaq_category')) {
$setup->getConnection()->dropTable('magelearn_categoryfaq_category');
}
if ($setup->tableExists('magelearn_categoryfaq_question')) {
$setup->getConnection()->dropTable('magelearn_categoryfaq_question');
}
}
}Now, create di.xml file inside etc folder.
This file will define necessary nodes to display data in admin grid and save data.
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <preference for="Magelearn\Categoryfaq\Api\CategoryRepositoryInterface" type="Magelearn\Categoryfaq\Model\CategoryRepository"/> <preference for="Magelearn\Categoryfaq\Api\Data\CategoryInterface" type="Magelearn\Categoryfaq\Model\Category"/> <preference for="Magelearn\Categoryfaq\Api\Data\CategorySearchResultsInterface" type="Magento\Framework\Api\SearchResults"/> <virtualType name="Magelearn\Categoryfaq\Model\ResourceModel\Category\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">magelearn_categoryfaq_category</argument> <argument name="resourceModel" xsi:type="string">Magelearn\Categoryfaq\Model\ResourceModel\Category\Collection</argument> </arguments> </virtualType> <type name="Magento\Framework\View\Element\UiComponent\DataProvider\CollectionFactory"> <arguments> <argument name="collections" xsi:type="array"> <item name="magelearn_categoryfaq_category_listing_data_source" xsi:type="string">Magelearn\Categoryfaq\Model\ResourceModel\Category\Grid\Collection</item> <item name="magelearn_categoryfaq_question_listing_data_source" xsi:type="string">Magelearn\Categoryfaq\Model\ResourceModel\Question\Grid\Collection</item> </argument> </arguments> </type> <preference for="Magelearn\Categoryfaq\Api\QuestionRepositoryInterface" type="Magelearn\Categoryfaq\Model\QuestionRepository"/> <preference for="Magelearn\Categoryfaq\Api\Data\QuestionInterface" type="Magelearn\Categoryfaq\Model\Question"/> <preference for="Magelearn\Categoryfaq\Api\Data\QuestionSearchResultsInterface" type="Magento\Framework\Api\SearchResults"/> <virtualType name="Magelearn\Categoryfaq\Model\ResourceModel\Question\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">magelearn_categoryfaq_question</argument> <argument name="resourceModel" xsi:type="string">Magelearn\Categoryfaq\Model\ResourceModel\Question\Collection</argument> </arguments> </virtualType> </config>
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
interface CategoryRepositoryInterface
{
/**
* Save category
* @param \Magelearn\Categoryfaq\Api\Data\CategoryInterface $category
* @return \Magelearn\Categoryfaq\Api\Data\CategoryInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function save(
\Magelearn\Categoryfaq\Api\Data\CategoryInterface $category
);
/**
* Retrieve category
* @param string $categoryId
* @return \Magelearn\Categoryfaq\Api\Data\CategoryInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function get($categoryId);
/**
* Retrieve category matching the specified criteria.
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \Magelearn\Categoryfaq\Api\Data\CategorySearchResultsInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getList(
\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
);
/**
* Delete category
* @param \Magelearn\Categoryfaq\Api\Data\CategoryInterface $category
* @return bool true on success
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function delete(
\Magelearn\Categoryfaq\Api\Data\CategoryInterface $category
);
/**
* Delete category by ID
* @param string $categoryId
* @return bool true on success
* @throws \Magento\Framework\Exception\NoSuchEntityException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function deleteById($categoryId);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api;
use Magento\Framework\Api\SearchCriteriaInterface;
interface QuestionRepositoryInterface
{
/**
* Save question
* @param \Magelearn\Categoryfaq\Api\Data\QuestionInterface $question
* @return \Magelearn\Categoryfaq\Api\Data\QuestionInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function save(
\Magelearn\Categoryfaq\Api\Data\QuestionInterface $question
);
/**
* Retrieve question
* @param string $questionId
* @return \Magelearn\Categoryfaq\Api\Data\QuestionInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function get($questionId);
/**
* Retrieve question matching the specified criteria.
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \Magelearn\Categoryfaq\Api\Data\QuestionSearchResultsInterface
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function getList(
\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
);
/**
* Delete question
* @param \Magelearn\Categoryfaq\Api\Data\QuestionInterface $question
* @return bool true on success
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function delete(
\Magelearn\Categoryfaq\Api\Data\QuestionInterface $question
);
/**
* Delete question by ID
* @param string $questionId
* @return bool true on success
* @throws \Magento\Framework\Exception\NoSuchEntityException
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function deleteById($questionId);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api\Data;
interface CategorySearchResultsInterface extends \Magento\Framework\Api\SearchResultsInterface
{
/**
* Get category list.
* @return \Magelearn\Categoryfaq\Api\Data\CategoryInterface[]
*/
public function getItems();
/**
* Set id list.
* @param \Magelearn\Categoryfaq\Api\Data\CategoryInterface[] $items
* @return $this
*/
public function setItems(array $items);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api\Data;
interface QuestionSearchResultsInterface extends \Magento\Framework\Api\SearchResultsInterface
{
/**
* Get question list.
* @return \Magelearn\Categoryfaq\Api\Data\QuestionInterface[]
*/
public function getItems();
/**
* Set id list.
* @param \Magelearn\Categoryfaq\Api\Data\QuestionInterface[] $items
* @return $this
*/
public function setItems(array $items);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api\Data;
interface CategoryInterface
{
const STATUS = 'status';
const NAME = 'name';
const UPDATED_AT = 'updated_at';
const DESCRIPTION = 'description';
const ID = 'id';
const CREATED_AT = 'created_at';
const SORT_ORDER = 'sort_order';
/**
* Get id
* @return string|null
*/
public function getId();
/**
* Set id
* @param string $id
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setId($id);
/**
* Get name
* @return string|null
*/
public function getName();
/**
* Set name
* @param string $name
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setName($name);
/**
* Get description
* @return string|null
*/
public function getDescription();
/**
* Set description
* @param string $description
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setDescription($description);
/**
* Get sort_order
* @return string|null
*/
public function getSortOrder();
/**
* Set sort_order
* @param string $sortOrder
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setSortOrder($sortOrder);
/**
* Get status
* @return string|null
*/
public function getStatus();
/**
* Set status
* @param string $status
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setStatus($status);
/**
* Get created_at
* @return string|null
*/
public function getCreatedAt();
/**
* Set created_at
* @param string $createdAt
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setCreatedAt($createdAt);
/**
* Get updated_at
* @return string|null
*/
public function getUpdatedAt();
/**
* Set updated_at
* @param string $updatedAt
* @return \Magelearn\Categoryfaq\Category\Api\Data\CategoryInterface
*/
public function setUpdatedAt($updatedAt);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Api\Data;
interface QuestionInterface
{
const CATEGORY_ID = 'category_id';
const STATUS = 'status';
const UPDATED_AT = 'updated_at';
const ID = 'id';
const TITLE = 'title';
const ANSWER = 'answer';
const CREATED_AT = 'created_at';
const SORT_ORDER = 'sort_order';
/**
* Get id
* @return string|null
*/
public function getId();
/**
* Set id
* @param string $id
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setId($id);
/**
* Get title
* @return string|null
*/
public function getTitle();
/**
* Set title
* @param string $title
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setTitle($title);
/**
* Get answer
* @return string|null
*/
public function getAnswer();
/**
* Set answer
* @param string $answer
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setAnswer($answer);
/**
* Get status
* @return string|null
*/
public function getStatus();
/**
* Set status
* @param string $status
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setStatus($status);
/**
* Get sort_order
* @return string|null
*/
public function getSortOrder();
/**
* Set sort_order
* @param string $sortOrder
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setSortOrder($sortOrder);
/**
* Get category_id
* @return string|null
*/
public function getCategoryId();
/**
* Set category_id
* @param string $categoryId
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setCategoryId($categoryId);
/**
* Get created_at
* @return string|null
*/
public function getCreatedAt();
/**
* Set created_at
* @param string $createdAt
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setCreatedAt($createdAt);
/**
* Get updated_at
* @return string|null
*/
public function getUpdatedAt();
/**
* Set updated_at
* @param string $updatedAt
* @return \Magelearn\Categoryfaq\Question\Api\Data\QuestionInterface
*/
public function setUpdatedAt($updatedAt);
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model;
use Magelearn\Categoryfaq\Api\CategoryRepositoryInterface;
use Magelearn\Categoryfaq\Api\Data\CategoryInterface;
use Magelearn\Categoryfaq\Api\Data\CategoryInterfaceFactory;
use Magelearn\Categoryfaq\Api\Data\CategorySearchResultsInterfaceFactory;
use Magelearn\Categoryfaq\Model\ResourceModel\Category as ResourceCategory;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory as CategoryCollectionFactory;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
class CategoryRepository implements CategoryRepositoryInterface
{
/**
* @var CategoryInterfaceFactory
*/
protected $categoryFactory;
/**
* @var Category
*/
protected $searchResultsFactory;
/**
* @var ResourceCategory
*/
protected $resource;
/**
* @var CollectionProcessorInterface
*/
protected $collectionProcessor;
/**
* @var CategoryCollectionFactory
*/
protected $categoryCollectionFactory;
/**
* @param ResourceCategory $resource
* @param CategoryInterfaceFactory $categoryFactory
* @param CategoryCollectionFactory $categoryCollectionFactory
* @param CategorySearchResultsInterfaceFactory $searchResultsFactory
* @param CollectionProcessorInterface $collectionProcessor
*/
public function __construct(
ResourceCategory $resource,
CategoryInterfaceFactory $categoryFactory,
CategoryCollectionFactory $categoryCollectionFactory,
CategorySearchResultsInterfaceFactory $searchResultsFactory,
CollectionProcessorInterface $collectionProcessor
) {
$this->resource = $resource;
$this->categoryFactory = $categoryFactory;
$this->categoryCollectionFactory = $categoryCollectionFactory;
$this->searchResultsFactory = $searchResultsFactory;
$this->collectionProcessor = $collectionProcessor;
}
/**
* @inheritDoc
*/
public function save(CategoryInterface $category)
{
try {
$this->resource->save($category);
} catch (\Exception $exception) {
throw new CouldNotSaveException(__(
'Could not save the category: %1',
$exception->getMessage()
));
}
return $category;
}
/**
* @inheritDoc
*/
public function get($categoryId)
{
$category = $this->categoryFactory->create();
$this->resource->load($category, $categoryId);
if (!$category->getId()) {
throw new NoSuchEntityException(__('category with id "%1" does not exist.', $categoryId));
}
return $category;
}
/**
* @inheritDoc
*/
public function getList(
\Magento\Framework\Api\SearchCriteriaInterface $criteria
) {
$collection = $this->categoryCollectionFactory->create();
$this->collectionProcessor->process($criteria, $collection);
$searchResults = $this->searchResultsFactory->create();
$searchResults->setSearchCriteria($criteria);
$items = [];
foreach ($collection as $model) {
$items[] = $model;
}
$searchResults->setItems($items);
$searchResults->setTotalCount($collection->getSize());
return $searchResults;
}
/**
* @inheritDoc
*/
public function delete(CategoryInterface $category)
{
try {
$categoryModel = $this->categoryFactory->create();
$this->resource->load($categoryModel, $category->getCategoryId());
$this->resource->delete($categoryModel);
} catch (\Exception $exception) {
throw new CouldNotDeleteException(__(
'Could not delete the category: %1',
$exception->getMessage()
));
}
return true;
}
/**
* @inheritDoc
*/
public function deleteById($categoryId)
{
return $this->delete($this->get($categoryId));
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model;
use Magelearn\Categoryfaq\Api\Data\QuestionInterface;
use Magelearn\Categoryfaq\Api\Data\QuestionInterfaceFactory;
use Magelearn\Categoryfaq\Api\Data\QuestionSearchResultsInterfaceFactory;
use Magelearn\Categoryfaq\Api\QuestionRepositoryInterface;
use Magelearn\Categoryfaq\Model\ResourceModel\Question as ResourceQuestion;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory as QuestionCollectionFactory;
use Magento\Framework\Api\SearchCriteria\CollectionProcessorInterface;
use Magento\Framework\Exception\CouldNotDeleteException;
use Magento\Framework\Exception\CouldNotSaveException;
use Magento\Framework\Exception\NoSuchEntityException;
class QuestionRepository implements QuestionRepositoryInterface
{
/**
* @var ResourceQuestion
*/
protected $resource;
/**
* @var Question
*/
protected $searchResultsFactory;
/**
* @var CollectionProcessorInterface
*/
protected $collectionProcessor;
/**
* @var QuestionInterfaceFactory
*/
protected $questionFactory;
/**
* @var QuestionCollectionFactory
*/
protected $questionCollectionFactory;
/**
* @param ResourceQuestion $resource
* @param QuestionInterfaceFactory $questionFactory
* @param QuestionCollectionFactory $questionCollectionFactory
* @param QuestionSearchResultsInterfaceFactory $searchResultsFactory
* @param CollectionProcessorInterface $collectionProcessor
*/
public function __construct(
ResourceQuestion $resource,
QuestionInterfaceFactory $questionFactory,
QuestionCollectionFactory $questionCollectionFactory,
QuestionSearchResultsInterfaceFactory $searchResultsFactory,
CollectionProcessorInterface $collectionProcessor
) {
$this->resource = $resource;
$this->questionFactory = $questionFactory;
$this->questionCollectionFactory = $questionCollectionFactory;
$this->searchResultsFactory = $searchResultsFactory;
$this->collectionProcessor = $collectionProcessor;
}
/**
* @inheritDoc
*/
public function save(QuestionInterface $question)
{
try {
$this->resource->save($question);
} catch (\Exception $exception) {
throw new CouldNotSaveException(__(
'Could not save the question: %1',
$exception->getMessage()
));
}
return $question;
}
/**
* @inheritDoc
*/
public function get($questionId)
{
$question = $this->questionFactory->create();
$this->resource->load($question, $questionId);
if (!$question->getId()) {
throw new NoSuchEntityException(__('question with id "%1" does not exist.', $questionId));
}
return $question;
}
/**
* @inheritDoc
*/
public function getList(
\Magento\Framework\Api\SearchCriteriaInterface $criteria
) {
$collection = $this->questionCollectionFactory->create();
$this->collectionProcessor->process($criteria, $collection);
$searchResults = $this->searchResultsFactory->create();
$searchResults->setSearchCriteria($criteria);
$items = [];
foreach ($collection as $model) {
$items[] = $model;
}
$searchResults->setItems($items);
$searchResults->setTotalCount($collection->getSize());
return $searchResults;
}
/**
* @inheritDoc
*/
public function delete(QuestionInterface $question)
{
try {
$questionModel = $this->questionFactory->create();
$this->resource->load($questionModel, $question->getQuestionId());
$this->resource->delete($questionModel);
} catch (\Exception $exception) {
throw new CouldNotDeleteException(__(
'Could not delete the question: %1',
$exception->getMessage()
));
}
return true;
}
/**
* @inheritDoc
*/
public function deleteById($questionId)
{
return $this->delete($this->get($questionId));
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model;
use Magelearn\Categoryfaq\Api\Data\CategoryInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;
use Magento\Framework\Stdlib\DateTime\DateTimeFactory;
class Category extends AbstractModel implements CategoryInterface
{
/**
* @var DateTimeFactory
*/
private $dateTimeFactory;
public function __construct(
Context $context,
Registry $registry,
DateTimeFactory $dateTimeFactory,
AbstractResource $resource = null,
AbstractDb $resourceCollection = null,
array $data = []
) {
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->dateTimeFactory = $dateTimeFactory;
}
/**
* @inheritDoc
*/
public function _construct()
{
$this->_init(\Magelearn\Categoryfaq\Model\ResourceModel\Category::class);
}
/**
* @inheritDoc
*/
public function getId()
{
return $this->getData(self::ID);
}
/**
* @inheritDoc
*/
public function setId($id)
{
return $this->setData(self::ID, $id);
}
/**
* @inheritDoc
*/
public function getName()
{
return $this->getData(self::NAME);
}
/**
* @inheritDoc
*/
public function setName($name)
{
return $this->setData(self::NAME, $name);
}
/**
* @inheritDoc
*/
public function getDescription()
{
return $this->getData(self::DESCRIPTION);
}
/**
* @inheritDoc
*/
public function setDescription($description)
{
return $this->setData(self::DESCRIPTION, $description);
}
/**
* @inheritDoc
*/
public function getSortOrder()
{
return $this->getData(self::SORT_ORDER);
}
/**
* @inheritDoc
*/
public function setSortOrder($sortOrder)
{
return $this->setData(self::SORT_ORDER, $sortOrder);
}
/**
* @inheritDoc
*/
public function getStatus()
{
return $this->getData(self::STATUS);
}
/**
* @inheritDoc
*/
public function setStatus($status)
{
return $this->setData(self::STATUS, $status);
}
/**
* @inheritDoc
*/
public function getCreatedAt()
{
return $this->getData(self::CREATED_AT);
}
/**
* @inheritDoc
*/
public function setCreatedAt($createdAt)
{
return $this->setData(self::CREATED_AT, $createdAt);
}
/**
* @inheritDoc
*/
public function getUpdatedAt()
{
return $this->getData(self::UPDATED_AT);
}
/**
* @inheritDoc
*/
public function setUpdatedAt($updatedAt)
{
return $this->setData(self::UPDATED_AT, $updatedAt);
}
/**
* Set updated_at before saving
*
* @return AbstractModel
*/
public function beforeSave()
{
if ($this->getId()) {
$this->setUpdatedAt($this->dateTimeFactory->create()->gmtDate());
}
return parent::beforeSave();
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model;
use Magelearn\Categoryfaq\Api\Data\QuestionInterface;
use Magento\Framework\Data\Collection\AbstractDb;
use Magento\Framework\Model\AbstractModel;
use Magento\Framework\Model\Context;
use Magento\Framework\Model\ResourceModel\AbstractResource;
use Magento\Framework\Registry;
use Magento\Framework\Stdlib\DateTime\DateTimeFactory;
class Question extends AbstractModel implements QuestionInterface {
/**
* @var DateTimeFactory
*/
private $dateTimeFactory;
public function __construct(
Context $context,
Registry $registry,
DateTimeFactory $dateTimeFactory,
AbstractResource $resource = null,
AbstractDb $resourceCollection = null,
array $data = []
) {
parent::__construct($context, $registry, $resource, $resourceCollection, $data);
$this->dateTimeFactory = $dateTimeFactory;
}
/**
* @inheritDoc
*/
public function _construct()
{
$this->_init(\Magelearn\Categoryfaq\Model\ResourceModel\Question::class);
}
/**
* @inheritDoc
*/
public function getId()
{
return $this->getData(self::ID);
}
/**
* @inheritDoc
*/
public function setId($id)
{
return $this->setData(self::ID, $id);
}
/**
* @inheritDoc
*/
public function getTitle()
{
return $this->getData(self::TITLE);
}
/**
* @inheritDoc
*/
public function setTitle($title)
{
return $this->setData(self::TITLE, $title);
}
/**
* @inheritDoc
*/
public function getAnswer()
{
return $this->getData(self::ANSWER);
}
/**
* @inheritDoc
*/
public function setAnswer($answer)
{
return $this->setData(self::ANSWER, $answer);
}
/**
* @inheritDoc
*/
public function getStatus()
{
return $this->getData(self::STATUS);
}
/**
* @inheritDoc
*/
public function setStatus($status)
{
return $this->setData(self::STATUS, $status);
}
/**
* @inheritDoc
*/
public function getSortOrder()
{
return $this->getData(self::SORT_ORDER);
}
/**
* @inheritDoc
*/
public function setSortOrder($sortOrder)
{
return $this->setData(self::SORT_ORDER, $sortOrder);
}
/**
* @inheritDoc
*/
public function getCategoryId()
{
return $this->getData(self::CATEGORY_ID);
}
/**
* @inheritDoc
*/
public function setCategoryId($categoryId)
{
return $this->setData(self::CATEGORY_ID, $categoryId);
}
/**
* @inheritDoc
*/
public function getCreatedAt()
{
return $this->getData(self::CREATED_AT);
}
/**
* @inheritDoc
*/
public function setCreatedAt($createdAt)
{
return $this->setData(self::CREATED_AT, $createdAt);
}
/**
* @inheritDoc
*/
public function getUpdatedAt()
{
return $this->getData(self::UPDATED_AT);
}
/**
* @inheritDoc
*/
public function setUpdatedAt($updatedAt)
{
return $this->setData(self::UPDATED_AT, $updatedAt);
}
/**
* Set updated_at before saving
*
* @return AbstractModel
*/
public function beforeSave()
{
if ($this->getId()) {
$this->setUpdatedAt($this->dateTimeFactory->create()->gmtDate());
}
return parent::beforeSave();
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Category extends AbstractDb
{
/**
* @inheritDoc
*/
protected function _construct()
{
$this->_init('magelearn_categoryfaq_category', 'id');
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\ResourceModel;
use Magento\Framework\Model\ResourceModel\Db\AbstractDb;
class Question extends AbstractDb
{
/**
* @inheritDoc
*/
protected function _construct()
{
$this->_init('magelearn_categoryfaq_question', 'id');
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\ResourceModel\Category;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
/**
* @inheritDoc
*/
protected $_idFieldName = 'id';
/**
* @inheritDoc
*/
protected function _construct()
{
$this->_init(
\Magelearn\Categoryfaq\Model\Category::class,
\Magelearn\Categoryfaq\Model\ResourceModel\Category::class
);
}
/**
* Initialize select object
*
* @return Collection
*/
protected function _initSelect()
{
parent::_initSelect();
return $this;
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\ResourceModel\Question;
use Magento\Framework\Model\ResourceModel\Db\Collection\AbstractCollection;
class Collection extends AbstractCollection
{
const TABLE_NAME = 'magelearn_categoryfaq_category';
/**
* @inheritDoc
*/
protected $_idFieldName = 'id';
/**
* @inheritDoc
*/
protected function _construct()
{
$this->_init(
\Magelearn\Categoryfaq\Model\Question::class,
\Magelearn\Categoryfaq\Model\ResourceModel\Question::class
);
}
/**
* Initialize select object
*
* @return Collection
*/
protected function _initSelect()
{
parent::_initSelect();
$this->addFilterToMap('id', 'main_table.id');
$this->getSelect()->joinLeft(
['categories' => $this->getTable(self::TABLE_NAME)],
'main_table.category_id = categories.id',
['name']
)->columns(
[
'categoryName' => 'categories.name',
'id' => 'main_table.id',
'created_at' => 'main_table.created_at',
'updated_at' => 'main_table.updated_at',
'status' => 'main_table.status'
]
);
return $this;
}
}<?php
namespace Magelearn\Categoryfaq\Model\ResourceModel\Question\Grid;
use Magento\Framework\Data\Collection\Db\FetchStrategyInterface as FetchStrategy;
use Magento\Framework\Data\Collection\EntityFactoryInterface as EntityFactory;
use Magento\Framework\Event\ManagerInterface as EventManager;
use Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult;
use Magelearn\Categoryfaq\Model\ResourceModel\Question as ResourceModel;
use Magelearn\Categoryfaq\Model\ResourceModel\Category as CategoryResourceModel;
use Psr\Log\LoggerInterface as Logger;
class Collection extends SearchResult
{
const TABLE_NAME = 'magelearn_categoryfaq_category';
/**
* Collection constructor.
* @param EntityFactory $entityFactory
* @param Logger $logger
* @param FetchStrategy $fetchStrategy
* @param EventManager $eventManager
* @param string $mainTable
* @param string $resourceModel
* @param null $identifierName
* @param null $connectionName
* @throws \Magento\Framework\Exception\LocalizedException
*/
public function __construct(
EntityFactory $entityFactory,
Logger $logger,
FetchStrategy $fetchStrategy,
EventManager $eventManager,
$mainTable = self::TABLE_NAME,
$resourceModel = ResourceModel::class,
$identifierName = null,
$connectionName = null
) {
parent::__construct(
$entityFactory,
$logger,
$fetchStrategy,
$eventManager,
$mainTable,
$resourceModel,
$identifierName,
$connectionName
);
}
/**
* Initialize select object
*
* @return Collection
*/
protected function _initSelect()
{
parent::_initSelect();
$this->addFilterToMap('id', 'main_table.id');
$this->getSelect()->joinLeft(
['categories' => $this->getTable(self::TABLE_NAME)],
'main_table.category_id = categories.id',
['name']
)->columns(
[
'categoryName' => 'categories.name',
'id' => 'main_table.id',
'created_at' => 'main_table.created_at',
'updated_at' => 'main_table.updated_at',
'status' => 'main_table.status']
);
return $this;
}
}Now we will check files to create Admin menu and ui_component files used in Admin data listing.
Create etc/adminhtml/menu.xml file.
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Backend:etc/menu.xsd"> <menu> <add id="Magelearn_Categoryfaq::management" title="FAQ" module="Magelearn_Categoryfaq" parent="Magento_Backend::content" sortOrder="30" resource="Magelearn_Categoryfaq::management"/> <add id="Magelearn_Categoryfaq::magelearn_categoryfaq_category" title="Categories" module="Magelearn_Categoryfaq" sortOrder="9999" resource="Magelearn_Categoryfaq::magelearn_categoryfaq_category" parent="Magelearn_Categoryfaq::management" action="magelearn_categoryfaq/category/index"/> <add id="Magelearn_Categoryfaq::magelearn_categoryfaq_question" title="Questions" module="Magelearn_Categoryfaq" sortOrder="9999" resource="Magelearn_Categoryfaq::magelearn_categoryfaq_question" parent="Magelearn_Categoryfaq::management" action="magelearn_categoryfaq/question/index"/> </menu> </config>
Add etc/acl.xml
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd"> <acl> <resources> <resource id="Magento_Backend::admin"> <resource id="Magelearn_Categoryfaq::category" title="category" sortOrder="10"> <resource id="Magelearn_Categoryfaq::category_save" title="Save category" sortOrder="10"/> <resource id="Magelearn_Categoryfaq::category_delete" title="Delete category" sortOrder="20"/> <resource id="Magelearn_Categoryfaq::category_update" title="Update category" sortOrder="30"/> <resource id="Magelearn_Categoryfaq::category_view" title="View category" sortOrder="40"/> </resource> <resource id="Magelearn_Categoryfaq::question" title="question" sortOrder="10"> <resource id="Magelearn_Categoryfaq::question_save" title="Save question" sortOrder="10"/> <resource id="Magelearn_Categoryfaq::question_delete" title="Delete question" sortOrder="20"/> <resource id="Magelearn_Categoryfaq::question_update" title="Update question" sortOrder="30"/> <resource id="Magelearn_Categoryfaq::question_view" title="View question" sortOrder="40"/> </resource> </resource> </resources> </acl> </config>
Now Create routes file for admin at etc/adminhtml/routes.xml
<?xml version="1.0" ?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd"> <router id="admin"> <route frontName="magelearn_categoryfaq" id="magelearn_categoryfaq"> <module before="Magento_Backend" name="Magelearn_Categoryfaq"/> </route> </router> </config>
Now as per highlighted in menu.xml file create controller and layout files.
Create Controller/Adminhtml/Category/Index.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
class Index extends \Magento\Backend\App\Action
{
protected $resultPageFactory;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* Index action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$resultPage = $this->resultPageFactory->create();
$resultPage->setActiveMenu('Magelearn_Categoryfaq::magelearn_categoryfaq_category');
$resultPage->getConfig()->getTitle()->prepend(__("FAQ Categories"));
return $resultPage;
}
}Create Controller/Adminhtml/Question/Index.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
class Index extends \Magento\Backend\App\Action
{
protected $resultPageFactory;
/**
* Constructor
*
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
/**
* Index action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
$resultPage = $this->resultPageFactory->create();
$resultPage->setActiveMenu('Magelearn_Categoryfaq::magelearn_categoryfaq_question');
$resultPage->getConfig()->getTitle()->prepend(__("Frequently Asked Questions"));
return $resultPage;
}
}Now add layout file at view/adminhtml/layout/magelearn_categoryfaq_category_index.xml file.
<?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="styles"/>
<head>
<title>FAQ Categories</title>
</head>
<body>
<referenceContainer name="content">
<uiComponent name="magelearn_categoryfaq_category_listing"/>
</referenceContainer>
</body>
</page>Now add layout file at view/adminhtml/layout/magelearn_categoryfaq_question_index.xml file.
<?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="styles"/> <body> <referenceContainer name="content"> <uiComponent name="magelearn_categoryfaq_question_listing"/> </referenceContainer> </body> </page>
Now add category_listing ui_component file at view/adminhtml/ui_component/magelearn_categoryfaq_category_listing.xml
<?xml version="1.0" ?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_listing_data_source</item>
</item>
</argument>
<settings>
<spinner>magelearn_categoryfaq_category_columns</spinner>
<deps>
<dep>magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_listing_data_source</dep>
</deps>
<buttons>
<button name="add">
<url path="*/*/new"/>
<class>primary</class>
<label translate="true">Add new category</label>
</button>
</buttons>
</settings>
<dataSource name="magelearn_categoryfaq_category_listing_data_source" component="Magento_Ui/js/grid/provider">
<settings>
<storageConfig>
<param name="indexField" xsi:type="string">id</param>
</storageConfig>
<updateUrl path="mui/index/render"/>
</settings>
<aclResource>Magelearn_Categoryfaq::category</aclResource>
<dataProvider name="magelearn_categoryfaq_category_listing_data_source" class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<listingToolbar name="listing_top">
<settings>
<sticky>true</sticky>
</settings>
<bookmark name="bookmarks"/>
<columnsControls name="columns_controls"/>
<filterSearch name="fulltext"/>
<filters name="listing_filters"/>
<paging name="listing_paging"/>
<exportButton name="export_button"/>
<massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions">
<action name="delete">
<settings>
<confirm>
<message translate="true">Are you sure to delete selected FAQ categories?</message>
<title translate="true">Delete items</title>
</confirm>
<url path="magelearn_categoryfaq/category/massDelete"/>
<type>delete</type>
<label translate="true">Delete</label>
</settings>
</action>
<action name="massEnable">
<settings>
<url path="magelearn_categoryfaq/category/massEnable"/>
<type>enable</type>
<label translate="true">Enable</label>
</settings>
</action>
<action name="massDisable">
<settings>
<url path="magelearn_categoryfaq/category/massDisable"/>
<type>disable</type>
<label translate="true">Disable</label>
</settings>
</action>
</massaction>
</listingToolbar>
<columns name="magelearn_categoryfaq_category_columns">
<settings>
<editorConfig>
<param name="selectProvider" xsi:type="string">magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_columns.ids</param>
<param name="enabled" xsi:type="boolean">true</param>
<param name="indexField" xsi:type="string">id</param>
<param name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="magelearn_categoryfaq/category/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</param>
</editorConfig>
<childDefaults>
<param name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_listing.magelearn_categoryfaq_category_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</param>
</childDefaults>
</settings>
<selectionsColumn name="ids">
<settings>
<indexField>id</indexField>
</settings>
</selectionsColumn>
<column name="id">
<settings>
<filter>text</filter>
<label translate="true">id</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<actionsColumn name="actions" class="Magelearn\Categoryfaq\Ui\Component\Listing\Column\CategoryActions">
<settings>
<indexField>id</indexField>
<resizeEnabled>false</resizeEnabled>
<resizeDefaultWidth>107</resizeDefaultWidth>
</settings>
</actionsColumn>
<column name="name">
<settings>
<filter>text</filter>
<label translate="true">Name</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="description">
<settings>
<filter>text</filter>
<label translate="true">Description</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="status">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Magelearn\Categoryfaq\Model\Category\Status\Options</item>
<item name="config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="dataType" xsi:type="string">select</item>
<item name="editor" xsi:type="string">select</item>
<item name="sorting" xsi:type="string">asc</item>
</item>
</argument>
<settings>
<filter>select</filter>
<label translate="true">Status</label>
<editor>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="sort_order">
<settings>
<filter>text</filter>
<label translate="true">Sort Order</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</editor>
</settings>
</column>
<column name="created_at" class="Magento\Ui\Component\Listing\Columns\Date">
<settings>
<filter>text</filter>
<label translate="true">Created</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="updated_at" class="Magento\Ui\Component\Listing\Columns\Date">
<settings>
<filter>text</filter>
<label translate="true">Modified</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
</columns>
</listing>Now as per highlighted code we will create file to fetch the status options. Create file at Model/Category/Status/Options.php
<?php
namespace Magelearn\Categoryfaq\Model\Category\Status;
class Options implements \Magento\Framework\Data\OptionSourceInterface
{
/**
* Grid display options.
*
* @return array
*/
public function toOptionArray()
{
return [
['value' => '0', 'label' => __('Disabled')],
['value' => '1', 'label' => __('Enabled')]
];
}
}For Massactions,
Create file at Controller/Adminhtml/Category/MassDelete.php file.
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassDelete
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Category
*/
class MassDelete extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute Mass Delete Action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->delete();
}
$this->messageManager->addSuccessMessage(__('A total of %1 categories have been deleted.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}Create file at Controller/Adminhtml/Category/MassDisable.php file.
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassDisable
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Category
*/
class MassDisable extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute Mass Disable Action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->setStatus(0);
$item->save();
}
$this->messageManager->addSuccessMessage(__('A total of %1 categories have been disabled.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}Create file at Controller/Adminhtml/Category/MassEnable.php file.
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassEnable
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Category
*/
class MassEnable extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->setStatus(1);
$item->save();
}
$this->messageManager->addSuccessMessage(__('A total of %1 categories have been enabled.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
class InlineEdit extends \Magento\Backend\App\Action
{
protected $jsonFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Controller\Result\JsonFactory $jsonFactory
) {
parent::__construct($context);
$this->jsonFactory = $jsonFactory;
}
/**
* Inline edit action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->jsonFactory->create();
$error = false;
$messages = [];
if ($this->getRequest()->getParam('isAjax')) {
$postItems = $this->getRequest()->getParam('items', []);
if (!count($postItems)) {
$messages[] = __('Please correct the data sent.');
$error = true;
} else {
foreach (array_keys($postItems) as $modelid) {
/** @var \Magelearn\Categoryfaq\Model\Category $model */
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Category::class)->load($modelid);
try {
$model->setData(array_merge($model->getData(), $postItems[$modelid]));
$model->save();
} catch (\Exception $e) {
$messages[] = "[Category ID: {$modelid}] {$e->getMessage()}";
$error = true;
}
}
}
}
return $resultJson->setData([
'messages' => $messages,
'error' => $error
]);
}
}We will also provide our action column links at Ui/Component/Listing/Column/CategoryActions.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Ui\Component\Listing\Column;
class CategoryActions extends \Magento\Ui\Component\Listing\Columns\Column
{
const URL_PATH_DETAILS = 'magelearn_categoryfaq/category/details';
protected $urlBuilder;
const URL_PATH_EDIT = 'magelearn_categoryfaq/category/edit';
const URL_PATH_DELETE = 'magelearn_categoryfaq/category/delete';
/**
* @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context
* @param \Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory
* @param \Magento\Framework\UrlInterface $urlBuilder
* @param array $components
* @param array $data
*/
public function __construct(
\Magento\Framework\View\Element\UiComponent\ContextInterface $context,
\Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory,
\Magento\Framework\UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item['id'])) {
$item[$this->getData('name')] = [
'edit' => [
'href' => $this->urlBuilder->getUrl(
static::URL_PATH_EDIT,
[
'id' => $item['id']
]
),
'label' => __('Edit')
]
];
}
}
}
return $dataSource;
}
}Now as per highlighted code in magelearn_categoryfaq_category_listing.xml file, we will add our new action controller and layout files.
First Create controller file at Controller/Adminhtml/Category/NewAction.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
class NewAction extends \Magelearn\Categoryfaq\Controller\Adminhtml\Category
{
protected $resultForwardFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
) {
$this->resultForwardFactory = $resultForwardFactory;
parent::__construct($context, $coreRegistry);
}
/**
* New action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Forward $resultForward */
$resultForward = $this->resultForwardFactory->create();
return $resultForward->forward('edit');
}
}This file extends Magelearn/Categoryfaq/Controller/Adminhtml/Category class so we will create our Controller file at Magelearn/Categoryfaq/Controller/Adminhtml/Category.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml;
abstract class Category extends \Magento\Backend\App\Action
{
const ADMIN_RESOURCE = 'Magelearn_Categoryfaq::management';
protected $_coreRegistry;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry
) {
$this->_coreRegistry = $coreRegistry;
parent::__construct($context);
}
/**
* Init page
*
* @param \Magento\Backend\Model\View\Result\Page $resultPage
* @return \Magento\Backend\Model\View\Result\Page
*/
public function initPage($resultPage)
{
$resultPage->setActiveMenu(self::ADMIN_RESOURCE)
->addBreadcrumb(__('Magelearn'), __('Magelearn'))
->addBreadcrumb(__('Category'), __('Category'));
return $resultPage;
}
}Now add layout file at view/adminhtml/layout/magelearn_categoryfaq_category_new.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="magelearn_categoryfaq_category_edit"/> </page>
Note that Controller/Adminhtml/Category/NewAction.php redirects to edit action, so we will create necessary files for Edit action.
Create file at Controller/Adminhtml/Category/Edit.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
class Edit extends \Magelearn\Categoryfaq\Controller\Adminhtml\Category
{
protected $resultPageFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context, $coreRegistry);
}
/**
* Edit action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
// 1. Get ID and create model
$id = $this->getRequest()->getParam('category_id');
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Category::class);
// 2. Initial checking
if ($id) {
$model->load($id);
if (!$model->getId()) {
$this->messageManager->addErrorMessage(__('This Category no longer exists.'));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
$this->_coreRegistry->register('magelearn_categoryfaq_category', $model);
// 3. Build edit form
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$this->initPage($resultPage)->addBreadcrumb(
$id ? __('Edit Category') : __('New Category'),
$id ? __('Edit Category') : __('New Category')
);
$resultPage->getConfig()->getTitle()->prepend(__('Categorys'));
$resultPage->getConfig()->getTitle()->prepend($model->getId() ? __('Edit Category %1', $model->getId()) : __('New Category'));
return $resultPage;
}
}Now layout file magelearn_categoryfaq_category_new.xml file targeting edit xml file (<update handle="magelearn_categoryfaq_category_edit"/>).
Create file at view/adminhtml/layout/magelearn_categoryfaq_category_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="styles"/> <body> <referenceContainer name="content"> <uiComponent name="magelearn_categoryfaq_category_form"/> </referenceContainer> </body> </page>
Now Add xml file for category edit form. create file at view/adminhtml/ui_component/magelearn_categoryfaq_category_form.xml
<?xml version="1.0" ?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_category_form.category_form_data_source</item>
</item>
<item name="label" xsi:type="string" translate="true">General Information</item>
<item name="template" xsi:type="string">templates/form/collapsible</item>
</argument>
<settings>
<buttons>
<button name="back" class="Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit\BackButton"/>
<button name="delete" class="Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit\DeleteButton"/>
<button name="save" class="Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit\SaveButton"/>
<button name="save_and_continue" class="Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit\SaveAndContinueButton"/>
</buttons>
<namespace>magelearn_categoryfaq_category_form</namespace>
<dataScope>data</dataScope>
<deps>
<dep>magelearn_categoryfaq_category_form.category_form_data_source</dep>
</deps>
</settings>
<dataSource name="category_form_data_source">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
</item>
</argument>
<settings>
<submitUrl path="*/*/save"/>
</settings>
<dataProvider name="category_form_data_source" class="Magelearn\Categoryfaq\Model\Category\DataProvider">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<fieldset name="general">
<settings>
<label>General</label>
</settings>
<field name="status" formElement="checkbox" sortOrder="50">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">category</item>
<item name="prefer" xsi:type="string">toggle</item>
<item name="valueMap" xsi:type="array">
<item name="true" xsi:type="string">1</item>
<item name="false" xsi:type="string">0</item>
</item>
<item name="default" xsi:type="string">1</item>
</item>
</argument>
<settings>
<dataType>boolean</dataType>
<label translate="true">Status</label>
<dataScope>status</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</settings>
</field>
<field name="name" formElement="textarea" sortOrder="20">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">category</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Name</label>
<dataScope>name</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</settings>
</field>
<field name="description" formElement="textarea" sortOrder="30">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">category</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Description</label>
<dataScope>description</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</settings>
</field>
<field name="sort_order" formElement="input" sortOrder="40">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">category</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Sort Order</label>
<dataScope>sort_order</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
<rule name="integer" xsi:type="boolean">true</rule>
</validation>
</settings>
</field>
</fieldset>
</form>As per highlighted code above we will add our DataProvider file at Model/Category/DataProvider.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\Category;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Ui\DataProvider\AbstractDataProvider;
class DataProvider extends AbstractDataProvider
{
/**
* @var DataPersistorInterface
*/
protected $dataPersistor;
/**
* @var array
*/
protected $loadedData;
/**
* @inheritDoc
*/
protected $collection;
/**
* @param string $name
* @param string $primaryFieldName
* @param string $requestFieldName
* @param CollectionFactory $collectionFactory
* @param DataPersistorInterface $dataPersistor
* @param array $meta
* @param array $data
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
CollectionFactory $collectionFactory,
DataPersistorInterface $dataPersistor,
array $meta = [],
array $data = []
) {
$this->collection = $collectionFactory->create();
$this->dataPersistor = $dataPersistor;
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
}
/**
* @inheritDoc
*/
public function getData()
{
if (isset($this->loadedData)) {
return $this->loadedData;
}
$items = $this->collection->getItems();
foreach ($items as $model) {
$this->loadedData[$model->getId()] = $model->getData();
}
$data = $this->dataPersistor->get('magelearn_categoryfaq_category');
if (!empty($data)) {
$model = $this->collection->getNewEmptyItem();
$model->setData($data);
$this->loadedData[$model->getId()] = $model->getData();
$this->dataPersistor->clear('magelearn_categoryfaq_category');
}
return $this->loadedData;
}
}Add Save action file at Controller/Adminhtml/Category/Save.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Category;
use Magento\Framework\Exception\LocalizedException;
class Save extends \Magento\Backend\App\Action
{
protected $dataPersistor;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
) {
$this->dataPersistor = $dataPersistor;
parent::__construct($context);
}
/**
* Save action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
$data = $this->getRequest()->getPostValue();
if ($data) {
$id = $this->getRequest()->getParam('category_id');
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Category::class)->load($id);
if (!$model->getId() && $id) {
$this->messageManager->addErrorMessage(__('This Category no longer exists.'));
return $resultRedirect->setPath('*/*/');
}
$model->setData($data);
try {
$model->save();
$this->messageManager->addSuccessMessage(__('You saved the Category.'));
$this->dataPersistor->clear('magelearn_categoryfaq_category');
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath('*/*/edit', ['category_id' => $model->getId()]);
}
return $resultRedirect->setPath('*/*/');
} catch (LocalizedException $e) {
$this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the Category.'));
}
$this->dataPersistor->set('magelearn_categoryfaq_category', $data);
return $resultRedirect->setPath('*/*/edit', ['category_id' => $this->getRequest()->getParam('category_id')]);
}
return $resultRedirect->setPath('*/*/');
}
}Now add different button action files.
Add file at Block/Adminhtml/Category/Edit/BackButton.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class BackButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Back'),
'on_click' => sprintf("location.href = '%s';", $this->getBackUrl()),
'class' => 'back',
'sort_order' => 10
];
}
/**
* Get URL for back (reset) button
*
* @return string
*/
public function getBackUrl()
{
return $this->getUrl('*/*/');
}
}Add file at Block/Adminhtml/Category/Edit/GenericButton.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit;
use Magento\Backend\Block\Widget\Context;
abstract class GenericButton
{
protected $context;
/**
* @param \Magento\Backend\Block\Widget\Context $context
*/
public function __construct(Context $context)
{
$this->context = $context;
}
/**
* Return model ID
*
* @return int|null
*/
public function getModelId()
{
return $this->context->getRequest()->getParam('category_id');
}
/**
* Generate url by route and parameters
*
* @param string $route
* @param array $params
* @return string
*/
public function getUrl($route = '', $params = [])
{
return $this->context->getUrlBuilder()->getUrl($route, $params);
}
}Add file at Block/Adminhtml/Category/Edit/DeleteButton.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class DeleteButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
$data = [];
if ($this->getModelId()) {
$data = [
'label' => __('Delete Category'),
'class' => 'delete',
'on_click' => 'deleteConfirm(\'' . __(
'Are you sure you want to do this?'
) . '\', \'' . $this->getDeleteUrl() . '\')',
'sort_order' => 20,
];
}
return $data;
}
/**
* Get URL for delete button
*
* @return string
*/
public function getDeleteUrl()
{
return $this->getUrl('*/*/delete', ['category_id' => $this->getModelId()]);
}
}Add file at Block/Adminhtml/Category/Edit/SaveAndContinueButton.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveAndContinueButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Save and Continue Edit'),
'class' => 'save',
'data_attribute' => [
'mage-init' => [
'button' => ['event' => 'saveAndContinueEdit'],
],
],
'sort_order' => 80,
];
}
}Add file at Block/Adminhtml/Category/Edit/SaveButton.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Category\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Save Category'),
'class' => 'save primary',
'data_attribute' => [
'mage-init' => ['button' => ['event' => 'save']],
'form-role' => 'save',
],
'sort_order' => 90,
];
}
}Now as per defined in magelearn_categoryfaq_question_index.xml file we will add our ui_component file at view/adminhtml/ui_component/magelearn_categoryfaq_question_listing.xml
<?xml version="1.0" ?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_listing_data_source</item>
</item>
</argument>
<settings>
<spinner>magelearn_categoryfaq_question_columns</spinner>
<deps>
<dep>magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_listing_data_source</dep>
</deps>
<buttons>
<button name="add">
<url path="*/*/new"/>
<class>primary</class>
<label translate="true">Add new question</label>
</button>
<button name="add_category">
<url path="*/category/new"/>
<class>primary</class>
<label translate="true">Add Category</label>
</button>
</buttons>
</settings>
<dataSource name="magelearn_categoryfaq_question_listing_data_source" component="Magento_Ui/js/grid/provider">
<settings>
<storageConfig>
<param name="indexField" xsi:type="string">id</param>
</storageConfig>
<updateUrl path="mui/index/render"/>
</settings>
<aclResource>Magelearn_Categoryfaq::question</aclResource>
<dataProvider name="magelearn_categoryfaq_question_listing_data_source" class="Magento\Framework\View\Element\UiComponent\DataProvider\DataProvider">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<listingToolbar name="listing_top">
<settings>
<sticky>true</sticky>
</settings>
<bookmark name="bookmarks"/>
<columnsControls name="columns_controls"/>
<exportButton name="export_button"/>
<filterSearch name="fulltext"/>
<filters name="listing_filters"/>
<massaction name="listing_massaction" component="Magento_Ui/js/grid/tree-massactions">
<action name="delete">
<settings>
<confirm>
<message translate="true">Are you sure to delete selected FAQ questions?</message>
<title translate="true">Delete items</title>
</confirm>
<url path="magelearn_categoryfaq/question/massDelete"/>
<type>delete</type>
<label translate="true">Delete</label>
</settings>
</action>
<action name="massEnable">
<settings>
<url path="magelearn_categoryfaq/question/massEnable"/>
<type>enable</type>
<label translate="true">Enable</label>
</settings>
</action>
<action name="massDisable">
<settings>
<url path="magelearn_categoryfaq/question/massDisable"/>
<type>disable</type>
<label translate="true">Disable</label>
</settings>
</action>
</massaction>
<paging name="listing_paging"/>
</listingToolbar>
<columns name="magelearn_categoryfaq_question_columns">
<settings>
<editorConfig>
<param name="selectProvider" xsi:type="string">magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_columns.ids</param>
<param name="enabled" xsi:type="boolean">true</param>
<param name="indexField" xsi:type="string">id</param>
<param name="clientConfig" xsi:type="array">
<item name="saveUrl" xsi:type="url" path="magelearn_categoryfaq/question/inlineEdit"/>
<item name="validateBeforeSave" xsi:type="boolean">false</item>
</param>
</editorConfig>
<childDefaults>
<param name="fieldAction" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_listing.magelearn_categoryfaq_question_columns_editor</item>
<item name="target" xsi:type="string">startEdit</item>
<item name="params" xsi:type="array">
<item name="0" xsi:type="string">${ $.$data.rowIndex }</item>
<item name="1" xsi:type="boolean">true</item>
</item>
</param>
</childDefaults>
</settings>
<selectionsColumn name="ids">
<settings>
<indexField>id</indexField>
</settings>
</selectionsColumn>
<column name="id">
<settings>
<filter>text</filter>
<label translate="true">ID</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="title">
<settings>
<filter>text</filter>
<label translate="true">Title</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</editor>
</settings>
</column>
<column name="answer" class="Magelearn\Categoryfaq\Ui\Component\Listing\Column\Answer">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="bodyTmpl" xsi:type="string">ui/grid/cells/html</item>
<item name="filter" xsi:type="string">text</item>
<item name="sortOrder" xsi:type="number">100</item>
<item name="label" xsi:type="string" translate="true">Answer</item>
<item name="editor" xsi:type="array">
<item name="editorType" xsi:type="string">text</item>
<item name="validation" xsi:type="array">
<item name="required-entry" xsi:type="boolean">false</item>
</item>
</item>
</item>
</argument>
</column>
<column name="status">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Magelearn\Categoryfaq\Model\Question\Status\Options</item>
<item name="config" xsi:type="array">
<item name="filter" xsi:type="string">select</item>
<item name="component" xsi:type="string">Magento_Ui/js/grid/columns/select</item>
<item name="dataType" xsi:type="string">select</item>
<item name="editor" xsi:type="string">select</item>
<item name="sorting" xsi:type="string">asc</item>
<item name="label" xsi:type="string" translate="true">Status</item>
</item>
</argument>
</column>
<column name="sort_order">
<settings>
<filter>text</filter>
<label translate="true">Sort Order</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
<rule name="integer" xsi:type="boolean">true</rule>
</validation>
</editor>
</settings>
</column>
<column name="categoryName">
<settings>
<filter>text</filter>
<label translate="true">Category</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</editor>
</settings>
</column>
<column name="created_at">
<settings>
<filter>text</filter>
<label translate="true">created_at</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<column name="updated_at">
<settings>
<filter>text</filter>
<label translate="true">updated_at</label>
<editor>
<editorType>text</editorType>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</editor>
</settings>
</column>
<actionsColumn name="actions" class="Magelearn\Categoryfaq\Ui\Component\Listing\Column\QuestionActions">
<settings>
<indexField>id</indexField>
<resizeEnabled>false</resizeEnabled>
<resizeDefaultWidth>107</resizeDefaultWidth>
</settings>
</actionsColumn>
</columns>
</listing>Now as per highlighted code above we will add Ui/Component/Listing/Column/Answer.php file.
<?php
namespace Magelearn\Categoryfaq\Ui\Component\Listing\Column;
use \Magento\Framework\View\Element\UiComponent\ContextInterface;
use \Magento\Framework\View\Element\UiComponentFactory;
use \Magento\Ui\Component\Listing\Columns\Column;
class Answer extends Column
{
/**
* Answer constructor.
* @param ContextInterface $context
* @param UiComponentFactory $uiComponentFactory
* @param array $components
* @param array $data
*/
public function __construct(
ContextInterface $context,
UiComponentFactory $uiComponentFactory,
array $components = [],
array $data = []
) {
parent::__construct($context, $uiComponentFactory, $components, $data);
}
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
$item[$this->getData('answer')] = html_entity_decode(nl2br($item['answer']));
}
}
return $dataSource;
}
}Also add Model/Question/Status/Options.php file.
<?php
namespace Magelearn\Categoryfaq\Model\Question\Status;
class Options implements \Magento\Framework\Data\OptionSourceInterface
{
/**
* Grid display options.
*
* @return array
*/
public function toOptionArray()
{
return [
['value' => '0', 'label' => __('Disabled')],
['value' => '1', 'label' => __('Enabled')]
];
}
}
Now we will add different massaction files for Questions.
Add file at Controller/Adminhtml/Question/MassDelete.php
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassDelete
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Question
*/
class MassDelete extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute Mass Delete Action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->delete();
}
$this->messageManager->addSuccessMessage(__('A total of %1 questions have been deleted.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}Add file at Controller/Adminhtml/Question/MassDisable.php
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassDisable
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Question
*/
class MassDisable extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute Mass Disable Action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->setStatus(0);
$item->save();
}
$this->messageManager->addSuccessMessage(__('A total of %1 questions have been disabled.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}Add file at Controller/Adminhtml/Question/MassEnable.php
<?php
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\ResultFactory;
use Magento\Ui\Component\MassAction\Filter;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
use Magento\Framework\App\Action\HttpPostActionInterface;
/**
* Class MassEnable
* @package Magelearn\Categoryfaq\Controller\Adminhtml\Question
*/
class MassEnable extends Action implements HttpPostActionInterface
{
/**
* @var Filter
*/
protected $filter;
/**
* @var CollectionFactory
*/
protected $collectionFactory;
/**
* @param Context $context
* @param Filter $filter
* @param CollectionFactory $collectionFactory
*/
public function __construct(Context $context, Filter $filter, CollectionFactory $collectionFactory)
{
parent::__construct($context);
$this->filter = $filter;
$this->collectionFactory = $collectionFactory;
}
/**
* Execute action
*
* @return \Magento\Backend\Model\View\Result\Redirect
* @throws \Magento\Framework\Exception\LocalizedException|\Exception
*/
public function execute()
{
$collection = $this->filter->getCollection($this->collectionFactory->create());
$collectionSize = $collection->getSize();
foreach ($collection as $item) {
$item->setStatus(1);
$item->save();
}
$this->messageManager->addSuccessMessage(__('A total of %1 questions have been enabled.', $collectionSize));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultFactory->create(ResultFactory::TYPE_REDIRECT);
return $resultRedirect->setPath('*/*/');
}
}Add Ui/Component/Listing/Column/QuestionActions.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Ui\Component\Listing\Column;
class QuestionActions extends \Magento\Ui\Component\Listing\Columns\Column
{
const URL_PATH_DELETE = 'magelearn_categoryfaq/question/delete';
protected $urlBuilder;
const URL_PATH_EDIT = 'magelearn_categoryfaq/question/edit';
const URL_PATH_DETAILS = 'magelearn_categoryfaq/question/details';
/**
* @param \Magento\Framework\View\Element\UiComponent\ContextInterface $context
* @param \Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory
* @param \Magento\Framework\UrlInterface $urlBuilder
* @param array $components
* @param array $data
*/
public function __construct(
\Magento\Framework\View\Element\UiComponent\ContextInterface $context,
\Magento\Framework\View\Element\UiComponentFactory $uiComponentFactory,
\Magento\Framework\UrlInterface $urlBuilder,
array $components = [],
array $data = []
) {
$this->urlBuilder = $urlBuilder;
parent::__construct($context, $uiComponentFactory, $components, $data);
}
/**
* Prepare Data Source
*
* @param array $dataSource
* @return array
*/
public function prepareDataSource(array $dataSource)
{
if (isset($dataSource['data']['items'])) {
foreach ($dataSource['data']['items'] as & $item) {
if (isset($item['id'])) {
$item[$this->getData('name')] = [
'edit' => [
'href' => $this->urlBuilder->getUrl(
static::URL_PATH_EDIT,
[
'id' => $item['id']
]
),
'label' => __('Edit')
],
'delete' => [
'href' => $this->urlBuilder->getUrl(
static::URL_PATH_DELETE,
[
'id' => $item['id']
]
),
'label' => __('Delete'),
'confirm' => [
'title' => __('Delete "${ $.$data.title }"'),
'message' => __('Are you sure you wan\'t to delete a "${ $.$data.title }" record?')
]
]
];
}
}
}
return $dataSource;
}
}Now To provide Delete action properly for Questions, we will add file at Controller/Adminhtml/Question/Delete.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
class Delete extends \Magelearn\Categoryfaq\Controller\Adminhtml\Question
{
/**
* Delete action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
// check if we know what should be deleted
$id = $this->getRequest()->getParam('question_id');
if ($id) {
try {
// init model and delete
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Question::class);
$model->load($id);
$model->delete();
// display success message
$this->messageManager->addSuccessMessage(__('You deleted the Question.'));
// go to grid
return $resultRedirect->setPath('*/*/');
} catch (\Exception $e) {
// display error message
$this->messageManager->addErrorMessage($e->getMessage());
// go back to edit form
return $resultRedirect->setPath('*/*/edit', ['question_id' => $id]);
}
}
// display error message
$this->messageManager->addErrorMessage(__('We can\'t find a Question to delete.'));
// go to grid
return $resultRedirect->setPath('*/*/');
}
}To Provide proper extend class for this Delete.php file, we will add Controller/Adminhtml/Question.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml;
abstract class Question extends \Magento\Backend\App\Action
{
protected $_coreRegistry;
const ADMIN_RESOURCE = 'Magelearn_Categoryfaq::management';
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry
) {
$this->_coreRegistry = $coreRegistry;
parent::__construct($context);
}
/**
* Init page
*
* @param \Magento\Backend\Model\View\Result\Page $resultPage
* @return \Magento\Backend\Model\View\Result\Page
*/
public function initPage($resultPage)
{
$resultPage->setActiveMenu(self::ADMIN_RESOURCE)
->addBreadcrumb(__('Magelearn'), __('Magelearn'))
->addBreadcrumb(__('Question'), __('Question'));
return $resultPage;
}
}Add Controller/Adminhtml/Question/InlineEdit.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
class InlineEdit extends \Magento\Backend\App\Action
{
protected $jsonFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Controller\Result\JsonFactory $jsonFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Controller\Result\JsonFactory $jsonFactory
) {
parent::__construct($context);
$this->jsonFactory = $jsonFactory;
}
/**
* Inline edit action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Json $resultJson */
$resultJson = $this->jsonFactory->create();
$error = false;
$messages = [];
if ($this->getRequest()->getParam('isAjax')) {
$postItems = $this->getRequest()->getParam('items', []);
if (!count($postItems)) {
$messages[] = __('Please correct the data sent.');
$error = true;
} else {
foreach (array_keys($postItems) as $modelid) {
/** @var \Magelearn\Categoryfaq\Model\Question $model */
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Question::class)->load($modelid);
try {
$model->setData(array_merge($model->getData(), $postItems[$modelid]));
$model->save();
} catch (\Exception $e) {
$messages[] = "[Question ID: {$modelid}] {$e->getMessage()}";
$error = true;
}
}
}
}
return $resultJson->setData([
'messages' => $messages,
'error' => $error
]);
}
}<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
class NewAction extends \Magelearn\Categoryfaq\Controller\Adminhtml\Question
{
protected $resultForwardFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Backend\Model\View\Result\ForwardFactory $resultForwardFactory
) {
$this->resultForwardFactory = $resultForwardFactory;
parent::__construct($context, $coreRegistry);
}
/**
* New action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Framework\Controller\Result\Forward $resultForward */
$resultForward = $this->resultForwardFactory->create();
return $resultForward->forward('edit');
}
}This NewAction.php file is redirect to Edit action.
For that first we will add Controller/Adminhtml/Question/Edit.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
class Edit extends \Magelearn\Categoryfaq\Controller\Adminhtml\Question
{
protected $resultPageFactory;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\Registry $coreRegistry
* @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\Registry $coreRegistry,
\Magento\Framework\View\Result\PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context, $coreRegistry);
}
/**
* Edit action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
// 1. Get ID and create model
$id = $this->getRequest()->getParam('question_id');
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Question::class);
// 2. Initial checking
if ($id) {
$model->load($id);
if (!$model->getId()) {
$this->messageManager->addErrorMessage(__('This Question no longer exists.'));
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
return $resultRedirect->setPath('*/*/');
}
}
$this->_coreRegistry->register('magelearn_categoryfaq_question', $model);
// 3. Build edit form
/** @var \Magento\Backend\Model\View\Result\Page $resultPage */
$resultPage = $this->resultPageFactory->create();
$this->initPage($resultPage)->addBreadcrumb(
$id ? __('Edit Question') : __('New Question'),
$id ? __('Edit Question') : __('New Question')
);
$resultPage->getConfig()->getTitle()->prepend(__('Questions'));
$resultPage->getConfig()->getTitle()->prepend($model->getId() ? __('Edit Question %1', $model->getId()) : __('New Question'));
return $resultPage;
}
}And layout for new question at view/adminhtml/layout/magelearn_categoryfaq_question_new.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="magelearn_categoryfaq_question_edit"/> </page>
Its Redirects to edit xml file. So we will add view/adminhtml/layout/magelearn_categoryfaq_question_edit.xml file.
<?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="styles"/> <body> <referenceContainer name="content"> <uiComponent name="magelearn_categoryfaq_question_form"/> </referenceContainer> </body> </page>
Now we will add our ui_component file at view/adminhtml/ui_component/magelearn_categoryfaq_question_form.xml
<?xml version="1.0" ?>
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="provider" xsi:type="string">magelearn_categoryfaq_question_form.question_form_data_source</item>
</item>
<item name="label" xsi:type="string" translate="true">General Information</item>
<item name="template" xsi:type="string">templates/form/collapsible</item>
</argument>
<settings>
<buttons>
<button name="back" class="Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit\BackButton"/>
<button name="delete" class="Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit\DeleteButton"/>
<button name="save" class="Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit\SaveButton"/>
<button name="save_and_continue" class="Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit\SaveAndContinueButton"/>
</buttons>
<namespace>magelearn_categoryfaq_question_form</namespace>
<dataScope>data</dataScope>
<deps>
<dep>magelearn_categoryfaq_question_form.question_form_data_source</dep>
</deps>
</settings>
<dataSource name="question_form_data_source">
<argument name="data" xsi:type="array">
<item name="js_config" xsi:type="array">
<item name="component" xsi:type="string">Magento_Ui/js/form/provider</item>
</item>
</argument>
<settings>
<submitUrl path="*/*/save"/>
</settings>
<dataProvider name="question_form_data_source" class="Magelearn\Categoryfaq\Model\Question\DataProvider">
<settings>
<requestFieldName>id</requestFieldName>
<primaryFieldName>id</primaryFieldName>
</settings>
</dataProvider>
</dataSource>
<fieldset name="general">
<settings>
<label>General</label>
</settings>
<field name="status" formElement="checkbox" sortOrder="80">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">question</item>
<item name="prefer" xsi:type="string">toggle</item>
<item name="valueMap" xsi:type="array">
<item name="true" xsi:type="string">1</item>
<item name="false" xsi:type="string">0</item>
</item>
<item name="default" xsi:type="string">1</item>
</item>
</argument>
<settings>
<dataType>boolean</dataType>
<label translate="true">Status</label>
<dataScope>status</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">false</rule>
</validation>
</settings>
</field>
<field name="title" formElement="textarea" sortOrder="90">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">question</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Title</label>
<dataScope>title</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</settings>
</field>
<field name="answer" formElement="wysiwyg" sortOrder="100">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">question</item>
<item name="wysiwygConfigData" xsi:type="array">
<item name="height" xsi:type="string">100px</item>
<item name="add_variables" xsi:type="boolean">true</item>
<item name="add_widgets" xsi:type="boolean">true</item>
<item name="add_images" xsi:type="boolean">true</item>
<item name="add_directives" xsi:type="boolean">true</item>
</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Answer</label>
<dataScope>answer</dataScope>
</settings>
<formElements>
<wysiwyg>
<settings>
<rows>8</rows>
<wysiwyg>true</wysiwyg>
</settings>
</wysiwyg>
</formElements>
</field>
<field name="category_id" formElement="input" sortOrder="120">
<argument name="data" xsi:type="array">
<item name="options" xsi:type="object">Magelearn\Categoryfaq\Model\Category\Source\Categories</item>
<item name="config" xsi:type="array">
<item name="formElement" xsi:type="string">select</item>
<item name="source" xsi:type="string">question</item>
<item name="default" xsi:type="string">0</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Category</label>
<dataScope>category_id</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
</settings>
</field>
<field name="sort_order" formElement="input" sortOrder="130">
<argument name="data" xsi:type="array">
<item name="config" xsi:type="array">
<item name="source" xsi:type="string">question</item>
</item>
</argument>
<settings>
<dataType>text</dataType>
<label translate="true">Sort Order</label>
<dataScope>sort_order</dataScope>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
<rule name="integer" xsi:type="boolean">true</rule>
</validation>
</settings>
</field>
</fieldset>
</form>As per highlighted code above we will add different action buttons files.
Add Block/Adminhtml/Question/Edit/BackButton.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class BackButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Back'),
'on_click' => sprintf("location.href = '%s';", $this->getBackUrl()),
'class' => 'back',
'sort_order' => 10
];
}
/**
* Get URL for back (reset) button
*
* @return string
*/
public function getBackUrl()
{
return $this->getUrl('*/*/');
}
}Add Block/Adminhtml/Question/Edit/GenericButton.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit;
use Magento\Backend\Block\Widget\Context;
abstract class GenericButton
{
protected $context;
/**
* @param \Magento\Backend\Block\Widget\Context $context
*/
public function __construct(Context $context)
{
$this->context = $context;
}
/**
* Return model ID
*
* @return int|null
*/
public function getModelId()
{
return $this->context->getRequest()->getParam('question_id');
}
/**
* Generate url by route and parameters
*
* @param string $route
* @param array $params
* @return string
*/
public function getUrl($route = '', $params = [])
{
return $this->context->getUrlBuilder()->getUrl($route, $params);
}
}Add Block/Adminhtml/Question/Edit/DeleteButton.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class DeleteButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
$data = [];
if ($this->getModelId()) {
$data = [
'label' => __('Delete Question'),
'class' => 'delete',
'on_click' => 'deleteConfirm(\'' . __(
'Are you sure you want to do this?'
) . '\', \'' . $this->getDeleteUrl() . '\')',
'sort_order' => 20,
];
}
return $data;
}
/**
* Get URL for delete button
*
* @return string
*/
public function getDeleteUrl()
{
return $this->getUrl('*/*/delete', ['question_id' => $this->getModelId()]);
}
}Add Block/Adminhtml/Question/Edit/SaveAndContinueButton.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveAndContinueButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Save and Continue Edit'),
'class' => 'save',
'data_attribute' => [
'mage-init' => [
'button' => ['event' => 'saveAndContinueEdit'],
],
],
'sort_order' => 80,
];
}
}Add Block/Adminhtml/Question/Edit/SaveButton.php file.
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Block\Adminhtml\Question\Edit;
use Magento\Framework\View\Element\UiComponent\Control\ButtonProviderInterface;
class SaveButton extends GenericButton implements ButtonProviderInterface
{
/**
* @return array
*/
public function getButtonData()
{
return [
'label' => __('Save Question'),
'class' => 'save primary',
'data_attribute' => [
'mage-init' => ['button' => ['event' => 'save']],
'form-role' => 'save',
],
'sort_order' => 90,
];
}
}To Save the question we will add our file at Controller/Adminhtml/Question/Save.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Controller\Adminhtml\Question;
use Magento\Framework\Exception\LocalizedException;
class Save extends \Magento\Backend\App\Action
{
protected $dataPersistor;
/**
* @param \Magento\Backend\App\Action\Context $context
* @param \Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
*/
public function __construct(
\Magento\Backend\App\Action\Context $context,
\Magento\Framework\App\Request\DataPersistorInterface $dataPersistor
) {
$this->dataPersistor = $dataPersistor;
parent::__construct($context);
}
/**
* Save action
*
* @return \Magento\Framework\Controller\ResultInterface
*/
public function execute()
{
/** @var \Magento\Backend\Model\View\Result\Redirect $resultRedirect */
$resultRedirect = $this->resultRedirectFactory->create();
$data = $this->getRequest()->getPostValue();
if ($data) {
$id = $this->getRequest()->getParam('question_id');
$model = $this->_objectManager->create(\Magelearn\Categoryfaq\Model\Question::class)->load($id);
if (!$model->getId() && $id) {
$this->messageManager->addErrorMessage(__('This Question no longer exists.'));
return $resultRedirect->setPath('*/*/');
}
$model->setData($data);
try {
$model->save();
$this->messageManager->addSuccessMessage(__('You saved the Question.'));
$this->dataPersistor->clear('magelearn_categoryfaq_question');
if ($this->getRequest()->getParam('back')) {
return $resultRedirect->setPath('*/*/edit', ['question_id' => $model->getId()]);
}
return $resultRedirect->setPath('*/*/');
} catch (LocalizedException $e) {
$this->messageManager->addErrorMessage($e->getMessage());
} catch (\Exception $e) {
$this->messageManager->addExceptionMessage($e, __('Something went wrong while saving the Question.'));
}
$this->dataPersistor->set('magelearn_categoryfaq_question', $data);
return $resultRedirect->setPath('*/*/edit', ['question_id' => $this->getRequest()->getParam('question_id')]);
}
return $resultRedirect->setPath('*/*/');
}
}We will add our DataProvider for Question model at Model/Question/DataProvider.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\Question;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
use Magento\Framework\App\Request\DataPersistorInterface;
use Magento\Ui\DataProvider\AbstractDataProvider;
class DataProvider extends AbstractDataProvider
{
/**
* @var DataPersistorInterface
*/
protected $dataPersistor;
/**
* @var array
*/
protected $loadedData;
/**
* @inheritDoc
*/
protected $collection;
/**
* @param string $name
* @param string $primaryFieldName
* @param string $requestFieldName
* @param CollectionFactory $collectionFactory
* @param DataPersistorInterface $dataPersistor
* @param array $meta
* @param array $data
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
CollectionFactory $collectionFactory,
DataPersistorInterface $dataPersistor,
array $meta = [],
array $data = []
) {
$this->collection = $collectionFactory->create();
$this->dataPersistor = $dataPersistor;
parent::__construct($name, $primaryFieldName, $requestFieldName, $meta, $data);
}
/**
* @inheritDoc
*/
public function getData()
{
if (isset($this->loadedData)) {
return $this->loadedData;
}
$items = $this->collection->getItems();
foreach ($items as $model) {
$this->loadedData[$model->getId()] = $model->getData();
}
$data = $this->dataPersistor->get('magelearn_categoryfaq_question');
if (!empty($data)) {
$model = $this->collection->getNewEmptyItem();
$model->setData($data);
$this->loadedData[$model->getId()] = $model->getData();
$this->dataPersistor->clear('magelearn_categoryfaq_question');
}
return $this->loadedData;
}
}Add Model/Category/Source/Categories.php file.
<?php
namespace Magelearn\Categoryfaq\Model\Category\Source;
use Magelearn\Categoryfaq\Model\ResourceModel\Category\CollectionFactory;
class Categories implements \Magento\Framework\Data\OptionSourceInterface
{
/**
* @var null|array
*/
protected $options;
/**
* @var CollectionFactory
*/
private $collectionFactory;
/**
* @param CollectionFactory $collectionFactory
*/
public function __construct(
CollectionFactory $collectionFactory
) {
$this->collectionFactory = $collectionFactory;
}
/**
* Return associative array with Category IDs and Names
*
* @return array|null
*/
public function toOptionArray()
{
if (null == $this->options) {
$this->options = $this->collectionFactory->create()
->addFieldToFilter('status', true)
->setOrder('sort_order','ASC')
->toOptionArray();
}
return $this->options;
}
}Now we will check our front end files to add new menu called `faq`.
Add etc/frontend/routes.xml file.
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
<router id="standard">
<route id="faq" frontName="faq">
<module name="Magelearn_Categoryfaq"/>
</route>
</router>
</config>Add etc/frontend/di.xml file to modify code for top menu items.
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<type name="Magento\Theme\Block\Html\Topmenu">
<plugin name="faq-topmenu" type="Magelearn\Categoryfaq\Plugin\Block\Topmenu"/>
</type>
</config>Add controller file at Controller/Index/Index.php
<?php
namespace Magelearn\Categoryfaq\Controller\Index;
use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\HttpGetActionInterface;
/**
* Class Index
*
* @package Magelearn\Categoryfaq\Controller\Index
*/
class Index extends Action implements HttpGetActionInterface
{
/**
* @var PageFactory
*/
protected $resultPageFactory;
/**
* Index constructor.
*
* @param Context $context
* @param PageFactory $resultPageFactory
*/
public function __construct(
Context $context,
PageFactory $resultPageFactory
) {
$this->resultPageFactory = $resultPageFactory;
parent::__construct($context);
}
public function execute()
{
return $this->resultPageFactory->create();
}
}As per highlighted code above we will add Plugin/Block/Topmenu.php file.
<?php
namespace Magelearn\Categoryfaq\Plugin\Block;
use Magento\Framework\Data\Tree\NodeFactory;
use Magento\Framework\UrlInterface;
class Topmenu
{
/**
* @var NodeFactory
*/
protected $nodeFactory;
/**
* @var UrlInterface
*/
protected $urlBuilder;
/**
* Topmenu constructor.
*
* @param NodeFactory $nodeFactory
* @param UrlInterface $urlBuilder
*/
public function __construct(
NodeFactory $nodeFactory,
UrlInterface $urlBuilder
) {
$this->nodeFactory = $nodeFactory;
$this->urlBuilder = $urlBuilder;
}
/**
* Add a new node to the end of the Top Menu
*
* @param \Magento\Theme\Block\Html\Topmenu $subject
* @param string $outermostClass
* @param string $childrenWrapClass
* @param int $limit
*/
public function beforeGetHtml(
\Magento\Theme\Block\Html\Topmenu $subject,
$outermostClass = '',
$childrenWrapClass = '',
$limit = 0
) {
$node = $this->nodeFactory->create(
[
'data' => $this->getNodeAsArray(),
'idField' => 'id',
'tree' => $subject->getMenu()->getTree()
]
);
$subject->getMenu()->addChild($node);
}
/**
* Create a new node to be added to the Top Menu
*
* @return array
*/
protected function getNodeAsArray()
{
return [
'name' => __('FAQ'),
'id' => 'magelearn-faq',
'url' => $this->urlBuilder->getUrl('faq'),
'has_active' => false
];
}
}Add layout file for front end at view/frontend/layout/faq_index_index.xml
<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="2columns-right"
xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
<update handle="styles"/>
<head>
<title>FAQ — Frequently Asked Questions</title>
<css src="Magelearn_Categoryfaq::css/accordion.css"/>
</head>
<body>
<referenceContainer name="content">
<block name="faq_block_questions" template="Magelearn_Categoryfaq::questions.phtml">
<arguments>
<argument name="viewModel" xsi:type="object">Magelearn\Categoryfaq\ViewModel\Questions</argument>
</arguments>
</block>
</referenceContainer>
</body>
</page>Add ViewModel file at ViewModel/Questions.php
<?php
declare(strict_types=1);
namespace Magelearn\Categoryfaq\ViewModel;
use Magento\Framework\DataObject;
use Magento\Framework\View\Element\Block\ArgumentInterface;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
class Questions extends DataObject implements ArgumentInterface
{
/*
* This label won't be displayed in the frontend block
*/
public const MAIN_LABEL = 'Default';
/**
* @var CollectionFactory
*/
protected $collectionFactory;
public function __construct(
CollectionFactory $collectionFactory
) {
$this->collectionFactory = $collectionFactory;
parent::__construct();
}
/**
* Get All Questions
*
* @return \Magento\Framework\DataObject[]
*/
public function getItems()
{
$questionCollection = $this->collectionFactory->create();
$questionCollection->addFieldToFilter('main_table.status', 1);
$questionCollection->setOrder('categoryName', 'DESC');
return $questionCollection->getItems();
}
}Add template file at view/frontend/templates/questions.phtml
<?php
use Magelearn\Categoryfaq\ViewModel\Questions;
use Magento\Framework\View\Element\Template;
/** @var Template $block */
$categoryName = false;
$viewModel = $block->getData('viewModel');
$items = $viewModel->getItems();
?>
<div class="container">
<?php foreach ($items as $question): ?>
<?php if ($question->getData('categoryName') !== $categoryName) : ?>
<?php if ($categoryName != false): ?>
<?php echo '</div>'; ?>
<?php endif; ?>
<div class="accordion">
<?php $categoryName = $question->getData('categoryName'); ?>
<?php if ($viewModel::MAIN_LABEL != $categoryName) : ?>
<h2><?php echo $categoryName; ?></h2>
<?php endif; ?>
<?php endif; ?>
<div class="accordion-item">
<a><?php echo $question->getTitle(); ?></a>
<div class="content">
<?php echo $question->getAnswer(); ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<script>
require(['jquery', 'jquery/ui'], function($) {
items = document.querySelectorAll(".accordion a");
function toggleAccordion(){
this.classList.toggle('active');
this.nextElementSibling.classList.toggle('active');
}
items.forEach(item => item.addEventListener('click', toggleAccordion));
});
</script>As per highlighted in faq_index_index.xml file, we will add our css file at view/frontend/web/css/accordion.css
At last we will also check about GraphQL Implementation for this listing the FAQ data.
For that first create etc/schema.graphqls file.
type Query {
faq : Faq @resolver( class: "Magelearn\\Categoryfaq\\Model\\Resolver\\Faq") @doc(description: "Query by Get FAQ List By Category.")
}
type Faq @doc(description: "Faq defines the FAQ information") {
allFaqs : [FaqRecord] @doc(description: "FAQ records with info")
}
type FaqRecord {
id: Int @doc(description: "Get FAQ ID")
title: String @doc(description: "Get FAQ title")
answer: String @doc(description: "Get FAQ answer")
status: Int @doc(description: "Get FAQ status")
categoryName: String @doc(description: "Get FAQ Category")
}As per highlighted above code, we will add resolver file at Model/Resolver/Faq.php
<?php
/**
* Copyright © All rights reserved.
* See COPYING.txt for license details.
*/
declare(strict_types=1);
namespace Magelearn\Categoryfaq\Model\Resolver;
use Magento\Framework\Exception\NoSuchEntityException;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
use Magento\Framework\GraphQl\Exception\GraphQlNoSuchEntityException;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magelearn\Categoryfaq\Model\ResourceModel\Question\CollectionFactory;
class Faq implements ResolverInterface
{
public function __construct(
CollectionFactory $collectionFactory
)
{
$this->collectionFactory = $collectionFactory;
}
/**
* @inheritdoc
*/
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$faqData = $this->getFaqData();
return $faqData;
}
/**
* @return array
* @throws GraphQlNoSuchEntityException
*/
private function getFaqData(): array
{
try {
/* filter for all the pages */
$questionCollection = $this->collectionFactory->create();
$questionCollection->addFieldToFilter('main_table.status', 1);
$questionCollection->setOrder('categoryName', 'DESC');
$faqItems = $questionCollection->getItems();
$faqRecord['allFaqs'] = [];
foreach($faqItems as $item) {
$faqId = $item->getId();
$faqRecord['allFaqs'][$faqId]['title'] = $item->getTitle();
$faqRecord['allFaqs'][$faqId]['answer'] = html_entity_decode(nl2br($item->getAnswer()));
$faqRecord['allFaqs'][$faqId]['status'] = $item->getStatus();
$faqRecord['allFaqs'][$faqId]['categoryName'] = $item['categoryName'];
}
} catch (NoSuchEntityException $e) {
throw new GraphQlNoSuchEntityException(__($e->getMessage()), $e);
}
return $faqRecord;
}
}Now to check the GraphQL Query on chrome browser intall GraphQL Extension for Chrome.
Open the Extension and set Endpoint URL as http://baseurl/graphql
And In Query Variable add below Json Data:
{
faq {
allFaqs {
id
title
answer
status
categoryName
}
}
}It will display all the FAQs data in the result box.





0 Comments On "FAQ (frequently asked questions) By Category with jQuery Accordion Magento2 Module Including GraphQL"