Create your own Hyvä theme in Magento 2

What is Hyvä?

Hyva is Magento 2 theme developed using alpine js and tailwind css . That is totally built from scratch using a completely blank theme. All layout and template files and all JavaScript have been removed in this theme; it’s running faster compared to the default Magento 2 theme.

Create Hyvä child theme step be step

We need to do similar steps to create Magento 2 child theme, But in Hyva theme we need to copy some extra files from the parent theme.

If we need to create our custom hyva theme than we need NPM for compile tailwind css.

lets create hyva child theme.

Register new theme in Magento 2

Using registration.php file we can register module, theme or any library. let’s register our custom theme in our application.

<?php
// FILE: app/design/frontend/RS/hyva/registration.php

use \Magento\Framework\Component\ComponentRegistrar;

ComponentRegistrar::register(
    ComponentRegistrar::THEME,
    'frontend/RS/hyva',
    __DIR__
);

Config your theme

For theme configuration we are use theme.xml file. This file configure the parent theme and theme title and theme preview image.

let’s create theme.xml file

<!--
// FILE: app/design/frontend/RS/hyva/theme.xml
-->
<theme xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Config/etc/theme.xsd">
    <title>CoolCodders Hyvä</title>
    <parent>Hyva/default</parent>
    <media>
        <preview_image>media/preview.png</preview_image>
    </media>
</theme>

Create composer.json (optional file)

This file is optional to create custom theme in your application. let’s create composer.json file

/* FILE: app/design/frontend/RS/hyva/composer.json */
{
    "name": "rs/magento2-hyva-theme",
    "description": "Hyvä child theme",
    "minimum-stability": "dev",
    "require": {
        "magento/framework": ">=102.0.0",
        "hyva-themes/magento2-default-theme": "^1.3"
    },
    "type": "magento2-theme",
    "authors": [{
        "name": "Raju Sadadiya",
        "email": "rsadadiya@gmail.com"
    }],
    "license": "",
    "autoload": {
        "files": [
            "registration.php"
        ]
    }
}

Copy require files from parent theme

We need to copy etc and i18n directory from parent theme. If we do not have preview.png file then we need to copy media directory from parent theme.

We need to copy parent theme web/css and web/tailwind directory to our custom own theme.

Configure tailwind.config.js file

This file you can find from your_theme/web/tailwind/tailwind.config.js. My theme is RS/hyva so my tailwind.config.js path look like app/design/frontend/RS/hyva/web/tailwind/tailwind.config.js

This file provide configuration for template file path and layout file path. We are going to create child theme so we need to configure our theme file path as well as parent theme file path.

What you need to modify in this file.

  plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
  // Examples for excluding patterns from purge
  content: [
    // this theme's phtml and layout XML files
    '../../**/*.phtml',
    '../../*/layout/*.xml',
    '../../*/page_layout/override/base/*.xml',
    // parent theme in Vendor (if this is a child-theme)
    //'../../../../../../../vendor/hyva-themes/magento2-default-theme/**/*.phtml',
    //'../../../../../../../vendor/hyva-themes/magento2-default-theme/*/layout/*.xml',
    //'../../../../../../../vendor/hyva-themes/magento2-default-theme/*/page_layout/override/base/*.xml',
    // app/code phtml files (if need tailwind classes from app/code modules)
    //'../../../../../../../app/code/**/*.phtml',
  ]

Above code you can find this from line number 120 to 140 in tailwind.config.js file

We just need to remove comment line in parent theme path. Just like this

  plugins: [require('@tailwindcss/forms'), require('@tailwindcss/typography')],
  // Examples for excluding patterns from purge
  content: [
    // this theme's phtml and layout XML files
    '../../**/*.phtml',
    '../../*/layout/*.xml',
    '../../*/page_layout/override/base/*.xml',
    // parent theme in Vendor (if this is a child-theme)
    '../../../../../../../vendor/hyva-themes/magento2-default-theme/**/*.phtml',
    '../../../../../../../vendor/hyva-themes/magento2-default-theme/*/layout/*.xml',
    '../../../../../../../vendor/hyva-themes/magento2-default-theme/*/page_layout/override/base/*.xml',
    // app/code phtml files (if need tailwind classes from app/code modules)
    //'../../../../../../../app/code/**/*.phtml',
  ]

After this step. Goto your terminal and run the bellow steps.

cd app/design/frontend/RS/hyva/web/tailwind/

We require minimum npm 12.13.0 for compiling tailwind css.

Install require packages

For installing require packages we need to run this command.

npm install

/*For development you can use below command*/
npm run watch

/*For production you can use below command */
npm run build-prod

That’s all for create new you own hyva child theme in Magento 2.

Contact me(rsadadiya@gmail.com) know anyone need help in hyva theme or related to configure UI component.

Creating custom console command in Magento 2

In Magento 2, Many application we need to do perform many things after did any deployment. Or other similar thing that we need to do every time or do perform that some specific of time. In that case we can use Magento cron or also we can create one console command for the same.

Like if we need to perform anything after the deployment then the console command is good thing to do. We need to implement all the thing in one console command.

When we run deployment commnad and once it’s completed so we just need to run our custom console command, it’s run everything that we need to perform everytime when we do deployment.

Let’s create small module for console command.

Register our custom module to Magento application

We need to create registration.php file for register our custom module in Magento 2.

<?php
//file path: MAGENTO_ROOT/app/code/RS/SimpleCmd/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'RS_SimpleCmd',
    __DIR__
);

Configure module version and module dependency

We need to create module.xml file for configure module version and module dependency.

<?xml version="1.0" ?>
<!-- file path: MAGENTO_ROOT/app/code/RS/SimpleCmd/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
	<module name="RS_SimpleCmd" setup_version="1.0.0">
	</module>
</config>

Register your custom console command in Magento application

Using di.xml we can register our custome commnad in Magento 2 Application. We need to add arguments on Magento\Framework\Console\CommandList this class adds the new console command in Magento 2. In that argument we need to give command class and command name.

<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/SimpleCmd/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\Console\CommandList">
        <arguments>
            <argument name="commands" xsi:type="array">
                <item name="simplecmd" xsi:type="object">RS\SimpleCmd\Console\Simple</item>
            </argument>
        </arguments>
    </type>
</config>

Creating the console command class

Magento 2 create console command using symfony library. We create console command extending of the symfony library class.

<?php
//file path: MAGENTO_ROOT/app/code/RS/SimpleCmd/Console/Simple.php 
namespace RS\SimpleCmd\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class Simple extends Command
{
    protected function configure()
    {
        $this->setName('rs:simple:command');
        $this->setDescription('This is simple command for demo');
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $output->writeln('Hi, Your custom command is called and executed successfully!');
        return \Magento\Framework\Console\Cli::RETURN_SUCCESS;
    }
}

Let’s check the output.

Output

Custom console command in Magento command list.

Custom command output

That’s all for create custom console command in Magento 2 application.

Load customer by email and customer Id in Magento 2

In Magento 2, Developer needs to load customer by email or sometime load customer by customer Id. Today we are going to load customer by email and customer id with different way.

Load customer by email or customer Id using CustomerRepositoryInterface

I am not writing a full code of block or controller. I just add the require resources for load customer by email or customer Id. You need to add that require resources on your block, controller or helper where you need.

<?php

use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;

/* Your namespace and class declaration */

/**
 * @var CustomerRepositoryInterface
 */
protected $customerRepository;

/**
 * @var StoreManagerInterface
 */
protected $storeManager;

/**
 * Add this perameter to your class constructor
 *
 * @param CustomerRepositoryInterface $customerRepository
 * @param StoreManagerInterface $storeManager
 */
public function __construct(
    CustomerRepositoryInterface $customerRepository,
    StoreManagerInterface $storeManager
) {
    $this->customerRepository = $customerRepository;
    $this->storeManager = $storeManager;
}

/* function for get customer by id*/
public function getCustomerById($customerId)
{
    return $this->customerRepository->getById($customerId);
}

/* function for get customer by email */
public function getCustomerByEmail($email)
{
    $websiteId = $this->getStore()->getWebsiteId();
    return $this->customerRepository->get($email, $websiteId);
}

/* function for get store */
protected function getStore()
{
    return $this->storeManager->getStore();
}

Load customer using Object Manager

As per the Magento standard we not need to use ObjectManager anyware in our custom module. We can use object manager for some short of time in phtml file when we developing anything. Once it’s done we need to move that logic in Block or ViewModel.

Also we cna use ObjectManager in our custom script.

<?php
use Magento\Framework\App\Bootstrap;
use Magento\Framework\App\State;
use Magento\Customer\Api\CustomerRepositoryInterface;
use Magento\Store\Model\StoreManagerInterface;

require 'app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get(State::class);
$state->setAreaCode('frontend');

$customerRepository = $objectManager->create(CustomerRepositoryInterface::class);
$storeManager = $objectManager->create(StoreManagerInterface::class);

$id = 1;
$email = 'rsadadiya@gmail.com';
$websiteId = $storeManager->getStore()->getWebsiteId();

/* Retrive customer by customer Id */
$customer = $customerRepository->getById($id);

/* Retrive customer by email */
$customer1 = $customerRepository->get($email, $websiteId);

Load customer using Factory class

I am not recommend to use this factory class to load any data in Magento 2. Factory class always create the new object while creating the instance of the class. When we need every time new object at that time we can use the factory class.

<?php

use Magento\Customer\Model\CustomerFactory;
use Magento\Store\Model\StoreManagerInterface;

/* Your namespace and class declaration */

/**
 * @var CustomerFactory
 */
protected $customerFactory;

/**
 * @var StoreManagerInterface
 */
protected $storeManager;

/**
 * Add this perameter to your class constructor
 *
 * @param CustomerFactory $customerFactory
 * @param StoreManagerInterface $storeManager
 */
public function __construct(
    CustomerFactory $customerFactory,
    StoreManagerInterface $storeManager
) {
    $this->customerFactory = $customerFactory;
    $this->storeManager = $storeManager;
}

/* function for get customer by id*/
public function getCustomerById($customerId)
{
    return $this->customerFactory->create()->load($customerId);
}

/* function for get customer by email */
public function getCustomerByEmail($email)
{
    $websiteId = $this->getStore()->getWebsiteId();
    return $this->customerFactory->create()->setWebsiteId($websiteId)->loadByEmail($email);
}

/* function for get store */
protected function getStore()
{
    return $this->storeManager->getStore();
}

Load customer in custom script by using Factory class

As per the Magento standard we not need to use ObjectManager anyware in our custom module. We can use object manager for some short of time in phtml file when we developing anything. Once it’s done we need to move that logic in Block or ViewModel.

Also we cna use ObjectManager in our custom script.

<?php
use Magento\Framework\App\Bootstrap;
use Magento\Framework\App\State;
use Magento\Customer\Model\CustomerFactory;
use Magento\Store\Model\StoreManagerInterface;

require 'app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get(State::class);
$state->setAreaCode('frontend');

$customerFactory = $objectManager->create(CustomerFactory::class);
$storeManager = $objectManager->create(StoreManagerInterface::class);

$id = 1;
$email = 'rsadadiya@gmail.com';
$websiteId = $storeManager->getStore()->getWebsiteId();

/* Retrive customer by customer Id */
$customer = $customerFactory->create()->load($id);

/* Retrive customer by email */
$customer1 = $customerFactory->create()->setWebsiteId($websiteId)->loadByEmail($email);

These are the way to load customer using email and customer id.

Creating extension attributes in Magento 2

About Extension Attribute

Using Extension attribute we can extends the data interface functionality without modify the actual file. We can add additional fields to Data interface as per our needs.

We can create extension attributes using extension_attributes.xml file. In that file need to pass the data interface and your attribute name that you need to create.

It’s not require to create that field in database. We can create extension attribute without the database field or attribute.

Let’s create one small module for extension attributes.

Register extension attribute module in Magento 2 Application.

Using registration.php file we can register our module component to Magento 2 application.

<?php
//file path: MAGENTO_ROOT/app/code/RS/ExtAttributes/registration.php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'RS_ExtAttributes',
    __DIR__
);

After register the our module to Magento, Need to config module version and dependency.

Config module dependency and configure module version.

Using module.xml we can configure module version and module dependency.

<?xml version="1.0" ?>
<!-- file path: MAGENTO_ROOT/app/code/RS/ExtAttributes/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
	<module name="RS_ExtAttributes" setup_version="1.0.0">
	</module>
</config>

Create extension attribute

Using extension_attributes.xml file we can create extension attributes.

<?xml version="1.0" ?>
<!-- file path: MAGENTO_ROOT/app/code/RS/ExtAttributes/etc/extension_attributes.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
    <extension_attributes for="Magento\Catalog\Api\Data\ProductInterface">
        <attribute code="custom_note" type="string" />
    </extension_attributes>
</config>

In above example I did not create any attribute or database field for that extension attribute. We can create extension attribute without any attribute or any database field.

Validate extension attribute is created or not

For validating extension attribute, We need to install this module.

php bin/magento module:enable RS_ExtAttributes
php bin/magento setup:upgrade
php bin/magento setup:di:compile

After that just open that Product extension class from the generated derectory.

just check that two

<magento_root>/generated/code/Magento/Catalog/Api/Data/ProductExtension.php

<magento_root>/generated/code/Magento/Catalog/Api/Data/ProductExtensionInterface.php

It’s auto generated extension attribute methods to that both class.

Let me add the screen sorts.

Get extension attribute data

At the moment creating the custom script to get extension attribute and it’s data

<?php

use Magento\Framework\App\Bootstrap;
use Magento\Framework\App\State;
use Magento\Catalog\Api\ProductRepositoryInterface;

require 'app/bootstrap.php';

$bootstrap = Bootstrap::create(BP, $_SERVER);
$objectManager = $bootstrap->getObjectManager();
$state = $objectManager->get(State::class);
$state->setAreaCode('frontend');

$productRepository = $objectManager->create(ProductRepositoryInterface::class);
$product = $productRepository->getById(1);
$extentionAttr = $product->getExtensionAttributes();

print_r(get_class_methods($extentionAttr));
print_r($extentionAttr->getCustomNote()); /* return null due to that we need to set menually when product data is load */

That’s all for creating extensions attribute in Magento 2.

Creating new repository in Bitbucket and setup in local machine.

About Bitbucket

Bitbucket is a Git-based source code repository hosting service owned by Atlassian. Bitbucket offers both commercial plans and free accounts with an unlimited number of private repositories.

Today we are going to learn about create new repository in Bitbucket and setup that repository in your local machine.

Let’s start to creating new repository in Bitbucket.

Login to your Bitbucket account or create new account in Bitbucket.

Login to your bitbucket account using your bitbucket login access(email/password). If you don’t’ have an account, then you can create an account in bitbucket with a few simple steps. 

After logging into your bitbucket account the bitbucket redirects to your workspace overview page. It shows recent activity in your workspace.

Creating a new Repository

Click on Repositories menu in header after login. When you click on the Repositories menu it’s redirected to your repositories list page, in that page have Create Repository button. 

Click on the Create Repository button for new project repository. In that form have few fields. 

Enter the project name or select project if you already created it.

Then enter the repository name. I suggest setting the same name for your project. If your project name is Abcshop then just type abcshop in repository name field.

Those two fields are required other field is optional like Access level, include a README, Default branch name, Include .gitignore file. etc

If you checked Private repository(Access level) then it’s create private repository when you click create repository button, Else it creates the public repository.

Include a README?: If you select yes then it’s auto created README file and added to your repository.

Default branch name is main if you want to change then you can type your custom branch name.

Include .gitignore?: If you select yes then it’s auto created .gitignore file and added to your repository.

Click on Create repository button to create repository. It’s create README and .gitignore file if you have select yes in that two fields else it’s create blank repository.

That’s all for creating repository in bitbucket now we are going to setup that repository in your local machine.

Setup repository in your local machine

My local system is ubuntu 22.04 LTS, I already install git in my local machine. If your system does not have git then install git first.

Install git in ubuntu

Just type this commands in your terminal

sudo apt update
sudo apt install git -y

Install git in window

Download & install Git for Windows software in your local window machine.

Create repository access token for accessing and commit new files to your repository.

Login to your bitbucket account. Click on setting icon in top right corner.

Click on Personal Bitbucket settings. It’s redirect to personal setting. In left sidebar have App passwords link. It’s open the App passwords page.

Click on Create app password button.

For new token you need to provide token name and token permissions like read, write and delete or admin.

We must select Repositories Read, Write permission for read and write repository from local. I recommend to you give full permission if you accessing your personal repository. After that click on Create button.

Copy that text show in your popup box. It’s not visible again. Please keep save that in your notepad. we need this token when we need to pull, clone and push data in the repository.

Get bitbucket clone URL

Login to your bitbucket account and go to your repository. it’s look like below image.

Click on Clone button that is in top right corner.

When you click on Clone button it’s open this above popup. In this popup have dropdown in top right corner. Just select HTTPS in this dropdown. After that copy the full clone URL that is in text box(It’s showing like git clone https://xxxxxx@bitbucket.org/xxxxx/yourproject.git).

Using that command we are able to clone project repository in you local machine.

Setup repository in Ubuntu

Keep your copy git clone url in notepad or any editor.

Setup your bitbucket repository

Open terminal and go to your location where you want to setup this repository.

In my case I want to setup in /var/www/abcshop. So I am move to this location using cd /var/www/abcshop

After that you just need to paste the git clone URL and enter.

After press enter asking password, you need to copy your access token and paste here and please press enter key.

it’s shows like this.

After that you just need to enter more command that is move git data to you current directory. If you current directory is have data or your current directory don’t have data that is work on both case. So, you can setup this in both project new and existing project or you can manually cut and paste to the current directory(git clone create project name directory to your current location you just need to cut all files from that folder and paste to your current directory).

mv abcshop/* abcshop/.* ./

Now you can run all the git command in your project like git add, git commit and git push.

Your git repository is setup in your ubuntu machine. Now you are able to pull, add, commit and push your changes to your repository.

Setup repository in Windows

Please keep git clone URL and access token in your notepad.

If you need to create new project then create project folder.

Open Git Bash and move to your project folder. in my case I have create project in D:/abcshop

After that just paste the git clone URL and press enter.

After that it’s asking password. Just copy access token and paste in the password field.

After enter your password and click Sign In button and then your repository is download in your current folder.

Now you need to move your git repository to your current folder. Please type this command to move all the git data to your current folder.

mv abcshop/* abcshop/.* ./

Now you can run all the git command in your project like git add, git commit and git push.

Your git repository is setup in your windows machine. Now you are able to pull, add, commit and push your changes to your repository.

Create custom rest API in Magento 2

Magento 2 has a lot of pre-defined apis, That provide the access of all the default magento entities. If we implement a new thing in our Magento application and that data we need to pass in to a third-party service provider then we need to do the custom rest api for accessing the custom data into the third-party service provider.

In Magento 1 it is difficult to create a custom rest api. But Magento 2 provides a good structure to create a Rest API.

Magento 2 has webapi.xml for registering our new rest api in Magento 2 application. And in that file have details about which interface and which method is responsible for processing our request and give response to the consumer.

Also need to create an Interface for defining the type of argument and response type. We need to pass the request as per type mentioned on the interface.

Let’s create a custom API step by step.

Register new Module for custom rest API.

Let’s create a registration.php file to register our custom module component.

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/registration.php

\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'RS_RestApi',
    __DIR__
);

Define the module version and module dependency.

Creating the module.xml file for config module version and other module dependency.

<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/RestApi/etc/module.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="RS_RestApi" setup_version="1.0.0" />
</config>

Register custom rest API.

Creating the webapi.xml for register new custom api in Magento 2 application. In this file have interface to represent the api request and response.

Also need to set the API type like GET and POST. In this file also we need to define the api endpoint.

Below webapi.xml, I registered a custom rest api that returns the user details. getAll is a method that represents the request and response.

Let’s create the webapi.xml file.

<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/RestApi/etc/webapi.xml -->
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../app/code/Magento/Webapi/etc/webapi.xsd">
    <route method="GET" url="/V1/rs/restapi/getDetails">
        <service class="RS\RestApi\Api\UserRepositoryInterface" method="getAll"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>
</routes>

Create the interfaces for this api.

We need to create a few interfaces for this api. And one interface for response type. Also we need to create one more interface for the result interface.

UserRepositoryInterface.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Api/UserRepositoryInterface.php 

namespace RS\RestApi\Api;

interface UserRepositoryInterface
{
    /**
     * Get all users
     * 
     * @api
     * 
     * @return \RS\RestApi\Api\Data\UserInfoSearchResultsInterface
     */
    public function getAll();
}

UserInfoInterface.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Api/Data/UserInfoInterface.php 

namespace RS\RestApi\Api\Data;
 
interface UserInfoInterface
{
    const NAME = "name";
    const POST = "post";
    const EXP = "exeperience";

    /**
     * Get user name
     * 
     * @return string
     */
    public function getName();

    /**
     * Set user name
     * 
     * @param string $name
     * @return $this
     */
    public function setName($name);

    /**
     * Get user post
     * 
     * @return string
     */
    public function getPost();

    /**
     * Set user post
     * 
     * @param string $post
     * @return $this
     */
    public function setPost($post);

    /**
     * Get user exeperience
     * 
     * @return string
     */
    public function getExeperience();

    /**
     * Set user exeperience
     * 
     * @param string $exp
     * @return $this
     */
    public function setExeperience($exp);
}

UserInfoSearchResultsInterface.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Api/Data/UserInfoSearchResultsInterface.php

namespace RS\RestApi\Api\Data;

use Magento\Framework\Api\SearchResultsInterface;

interface UserInfoSearchResultsInterface extends SearchResultsInterface
{
    /**
     * Get data list.
     *
     * @return \RS\RestApi\Api\Data\UserInfoInterface[]
     */
    public function getItems();

    /**
     * Set data list.
     *
     * @param \RS\RestApi\Api\Data\UserInfoInterface[] $items
     *
     * @return $this
     */
    public function setItems(array $items);
}

Let’s implement that all the interface.

We need to create a few models that implement this interface. Need to create a di.xml file for configuring all interface overriding classes.

di.xml

<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/RestApi/etc/di.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="RS\RestApi\Api\Data\UserInfoInterface" type="RS\RestApi\Model\Api\Data\UserInfo"/>
    <preference for="RS\RestApi\Api\Data\UserInfoSearchResultsInterface" type="RS\RestApi\Model\Api\Data\UserInfoSearchResult"/>
    <preference for="RS\RestApi\Api\UserRepositoryInterface" type="RS\RestApi\Model\UserRepository"/>
</config>

UserRepository.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Model/UserRepository.php 

namespace RS\RestApi\Model;

use Magento\Framework\Data\Collection;
use RS\RestApi\Api\Data\UserInfoInterfaceFactory;
use RS\RestApi\Api\Data\UserInfoSearchResultsInterfaceFactory;
use RS\RestApi\Api\UserRepositoryInterface;
 
class UserRepository implements UserRepositoryInterface
{
    /**
     * Creating static data
     */
    private $users = [
        [
            "name" => "Raju",
            "post" => "Magento",
            "exeperience" => "8 Years",
        ],
        [
            "name" => "Ketan",
            "post" => "QA",
            "exeperience" => "6 Years",
        ],
        [
            "name" => "Mahendra",
            "post" => "Java",
            "exeperience" => "11 Years",
        ]
    ];

    /**
     * @var Collection
     */
    private $collection;

    /**
     * @var UserInfoInterfaceFactory
     */
    private $userInfoFactory;

    /**
     * @var UserInfoSearchResultsInterfaceFactory
     */
    private $userInfoSearchResultsFactory;

    /**
     * 
     * @param UserInfoSearchResultsInterfaceFactory $userInfoSearchResultsFactory,
     * @param Collection $collection
     * @param UserInfoInterface $userInfoFactory
     */
    public function __construct(
        UserInfoSearchResultsInterfaceFactory $userInfoSearchResultsFactory,
        Collection $collection,
        UserInfoInterfaceFactory $userInfoFactory
    ) {
        $this->userInfoSearchResultsFactory = $userInfoSearchResultsFactory;
        $this->collection = $collection;
        $this->userInfoFactory = $userInfoFactory;
    }

    /**
     * Get all users
     * 
     * @api
     * 
     * @return UserInfoSearchResultsInterface
     */
    public function getAll()
    {
        foreach ($this->users as $user) {
            $item = $this->userInfoFactory->create()->setData($user);
            $this->collection->addItem($item);
        }
        $searchResults = $this->userInfoSearchResultsFactory
            ->create();
        $searchResults->setItems($this->collection->getItems());
        $searchResults->setTotalCount($this->collection->getSize());

        return $searchResults;
    }
}

UserInfo.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Model/Api/Data/UserInfoInterface.php 
namespace RS\RestApi\Model\Api\Data;

use Magento\Framework\DataObject;
use RS\RestApi\Api\Data\UserInfoInterface;
 
class UserInfo extends DataObject implements UserInfoInterface
{
    /**
     * Get user name
     * 
     * @return string
     */
    public function getName()
    {
        return $this->getData(self::NAME);
    }

    /**
     * Set user name
     * 
     * @param string $name
     * @return $this
     */
    public function setName($name)
    {
        return $this->setData(self::NAME, $name);
    }

    /**
     * Get user post
     * 
     * @return string
     */
    public function getPost()
    {
        return $this->getData(self::POST);
    }

    /**
     * Set user post
     * 
     * @param string $post
     * @return $this
     */
    public function setPost($post)
    {
        return $this->setData(self::POST, $post);
    }

    /**
     * Get user exeperience
     * 
     * @return string
     */
    public function getExeperience()
    {
        return $this->getData(self::EXP);
    }

    /**
     * Set user exeperience
     * 
     * @param string $exp
     * @return $this
     */
    public function setExeperience($exp)
    {
        return $this->setData(self::EXP, $exp);
    }
}

UserInfoSearchResult.php

<?php
//file path: MAGENTO_ROOT/app/code/RS/RestApi/Model/Api/Data/UserInfoSearchResult.php 

namespace RS\RestApi\Model\Api\Data;

use Magento\Framework\Api\SearchResults;
use RS\RestApi\Api\Data\UserInfoSearchResultsInterface;

class UserInfoSearchResult extends SearchResults implements UserInfoSearchResultsInterface
{

}

I have created all the interfaces and models that require creating a custom simple api. I am attaching the output.

Output.

That’s all for create custom rest api in Magento 2.

We can also set specific role in our custom api. Please reach out me if you need more help contact us : rsadadiya@gmail.com Or if you need hire me for create or customised your application or your APIs. Please click here to view more information about hire.

How to load product by SKU or product Id

In Magento 2, Most of the case we need to retrive product using sku or product id. We use ProductRepositoryInterface to retrive product.

Load product by sku and id using ProductRepositoryinterface

Just only write code for load product using id and sku. In your case you need to add that constructor argument to your class and add that functions to retrive product by using sku and product id.

<?php

/* Your namespace and class declaration */

protected $productRepository;

/* add Magento\Catalog\Api\ProductRepositoryInterface to you constructor */
public function __construct(
    \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
) {
    $this->productRepository = $productRepository;
}

/* function for get product by id*/
public function getProductById($productId)
{
    return $this->productRepository->getById($productId);
}

/* function for get product by sku */
public function getProductBySku($sku)
{
    return $this->productRepository->get($sku);
}

Load product by sku and id using ObjectManager

As per the Magento standard we not need to use ObjectManager anyware in our custom module. We can use object manager for some short of time in phtml file when we developing anything. Once it’s done we need to move that logic in Block or ViewModel.

Also we cna use ObjectManager in our custom script.

<?php
$id = 5;
$sku = 'PRD001';
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$productRepository = $objectManager->get(\Magento\Catalog\Api\ProductRepositoryInterface::class);
$product = $productRepository->get($sku); // get product by sku
$product2 = $productRepository->getById($id); // get product by product Id

Load product using Factory class

I am not recommend to use this factory class to load any data in Magento 2. Factory class always create the new object while creating the instance of the class. When we need every time new object at that time we can use the factory class.

    use Magento\Catalog\Model\ProductFactory;

    /** @var ProductFactory*/
    protected $productFactory;
    
    /**
     * @param ProductFactory$productFactory 
     */
    public function __construct(
        ProductFactory $productFactory 
    ) {
        $this->productFactory = $productFactory 
    }

    public function getProductById($productId)
    {
        return $this->productFactory->create()->load($productId);
    }

    public function getProductBySku($sku)
    {
        return $this->productFactory->create()->loadByAttribute('sku', $sku);
    }

Load product by sku and id using ObjectManager

As per the Magento standard we not need to use ObjectManager anyware in our custom module. We can use object manager for some short of time in phtml file when we developing anything. Once it’s done we need to move that logic in Block or ViewModel.

Also we cna use ObjectManager in our custom script.

<?php
$id = 5;
$sku = 'PRD001';
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();
$productFactory = $objectManager->create(\Magento\Catalog\Model\ProductFactory::class);
$product = $productFactory->create()->loadByAttribute('sku', $sku); // get product by sku
$product2 = $productRepository->load($id); // get product by product Id

Create product attribute in Magento 2

In Magento 2 we can create product attribute using two way.

  1. From Admin controll panel
  2. Using programmatically

1. Create product attribute using admin panel.

Magento 2 admin controll panel provide feature to add, update and remove product attribute from admin controll panel.

For creating product attribute we need follow the bellow simple step.

Login to Admin controll panel.

Login to admin panel using your username and password.

Goto product attribute.

After login click on Store > Attribute > Product. It’s redirect to product attribute grid.

Adding new Attribute.

Click on “Add New Attribute” button, It’s redirect to new attribute form.

Fill the attribute details as per your needs and click on “Save Attribute” button.

Assign attribute to attribute set.

Click on Store > Attribute > Attribute set, it’s redirect to attribute set grid.

Click on “Default”, It’s redirect to attribute set. In that page you can drag your attribute and move to any product section like general, content…etc which section you want that attribute on edit product form.

After that just click on save button. That attribute is assign to that attribute set with that specific section.

After saving attribute set you can get that attribute in edit product page in admin. Also you can save your custom data to that atttribute.

2. Creating product attribute using programattically.

In Magento 2 we can create product attribute easily by adding some line of code in our custom module.

Before Magento 2.3.x we use InstallData to perform any data level operation in Magento. Using InstallData we can add new data to table or we can modify or remove table data.

Magento 2.3 introduce the data patch and schema patch and the InstallData, UpgradeData, InstallSchema and UpgradeSchema was deprecated in Magento version 2.3.

We create product attribte using both ways.

Create product attribute using InstallData

We need to use UpgradeData when our module is already installed. For me I just creating fresh module so using InstallData we create product attribute.

<?php
//file path: MAGENTO_ROOT/app/code/RS/ProductAttribute/Setup/InstallData.php

namespace RS\ProductAttribute\Setup;

use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;

class InstallData implements InstallDataInterface
{
    /**
     * EAV setup factory
     *
     * @var EavSetupFactory
     */
    private $_eavSetupFactory;

    /**
     * Init
     *
     * @param EavSetupFactory $eavSetupFactory EnvSetupfactory
     */
    public function __construct(EavSetupFactory $eavSetupFactory)
    {
        $this->_eavSetupFactory = $eavSetupFactory;
    }

    /**
     * Install Data script
     *
     * @param ModuleDataSetupInterface $setup   Setup Object
     * @param ModuleContextInterface   $context Context Object
     *
     * @return void
     */
    public function install(
        ModuleDataSetupInterface $setup,
        ModuleContextInterface $context
    ) {

        $eavSetup = $this->_eavSetupFactory->create(['setup' => $setup]);
        $eavSetup->addAttribute(
            \Magento\Catalog\Model\Product::ENTITY,
            'custom_attr',
            [
            'group' => 'General',
            'type' => 'varchar',
            'sort_order' => 100,
            'label' => 'Custom Attribute 01',
            'input' => 'text',
            'global' => Attribute::SCOPE_GLOBAL,
            'visible' => true,
            'required' => false,
            'user_defined' => true,
            'default' => '',
            'searchable' => false,
            'filterable' => false,
            'comparable' => false,
            'visible_on_front' => false,
            'used_in_product_listing' => true,
            'unique' => false,
            'apply_to' => 'simple,configurable,virtual,bundle,downloadable'
            ]
        );
    }
}

After that we just need to run the setup commands (setup:upgrade and setup:static-contnet:deploy -f).

Create product attribute using data patch

We can use data patch to create attribute and manipulate and table data. We need create product attribute that is manipulate EAV data. So, we use data patch to create product attribute.

This patch is execute only one time. It’s automatically execute when you run setup:upgrade command.

After successfully execute patch it’s register in patch_list table. So, after that patch is no logger execute when we run the setup:upgrade.

<?php
//file path: MAGENTO_ROOT/app/code/RS/ProductAttribute/Setup/Patch/Data/CustomAttrPatch.php

namespace RS\ProductAttribute\Setup\Patch\Data;

use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Catalog\Model\Product;
use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
use Magento\Eav\Setup\EavSetupFactory;

class CustomAttrPatch implements DataPatchInterface
{
    /**
     * @var ModuleDataSetupInterface
     */
    protected $moduleDataSetup;

    /**
     * @var EavSetupFactory
     */
    protected $eavSetupFactory;

    /**
     * @param ModuleDataSetupInterface $moduleDataSetup
     * @param EavSetupFactory $eavSetupFactory
     */
    public function __construct(
        ModuleDataSetupInterface $moduleDataSetup,
        EavSetupFactory $eavSetupFactory
    ) {
        $this->moduleDataSetup = $moduleDataSetup;
        $this->eavSetupFactory = $eavSetupFactory;
    }

    /**
     * Default patch method that cover the patch logic
     */
    public function apply()
    {
        $eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);

        /**
        * Add attributes to the eav/attribute
        */
        $eavSetup->addAttribute(
            Product::ENTITY,
            'custom_attr2',
            [
                'group' => 'General',
                'type' => 'int',
                'backend' => '',
                'frontend' => '',
                'label' => 'Custom Attribute 02',
                'input' => 'text',
                'class' => '',
                'source' => '',
                'global' => Attribute::SCOPE_GLOBAL,
                'visible' => true,
                'required' => false,
                'user_defined' => true,
                'default' => '',
                'searchable' => true,
                'filterable' => true,
                'comparable' => false,
                'visible_on_front' => true,
                'used_in_product_listing' => true,
                'unique' => false,
                'apply_to' => 'simple,configurable,virtual,bundle,downloadable'
            ]
        );
    }

    /**
     * Return the dependency array
     */
    public static function getDependencies()
    {
        return [];
    }

    /**
     * Return the alias array
     */
    public function getAliases()
    {
        return [];
    }
}

That’s all for creating product attribute using admin panel and programattically.

Create custom knockout js component in Magento 2.

What is knockout Js ?

Knockout is a JavaScript library that helps you to create rich, responsive display and editor user interfaces with a clean underlying data model. Using knockout js we can update UI based on the data model. It’s auto update the UI when the data getting update.

Magento use knockout js in many place. Like minicart, checkout, recently view section…etc.

Today we create our custom simple component in knockout js. Using that component we display the simple message.

Let’s use FirstModule. We add new Knockout js component and using that component we will display the simple message.

Creating Js Component

I am creating js component that component have a message property. That message property we update after 5 second.

//file path: MAGENTO_ROOT/app/code/RS/FirstModule/view/frontend/web/js/hello.js

define(['uiComponent', 'ko'], function(Component, ko) {
    return Component.extend({
        defaults: {
            template: 'RS_FirstModule/hello',
        },

        message: ko.observable("This message comming from knockout js component"),
        initialize: function () {
            let self = this;
            this._super();
            
            setTimeout(function(){
                self.message("Message is updated after 5 second");
            }, 5000)
        }
    });
});

Creating component template

This template is represent the UI. We write the frontend UI code in template file. Also we can use the component peroperties in this template.

We use message property in this template file. Using that message property we display message using Js component.

When we update data dynamically then in magento mostly use knockout js. In our example I updated message property value after 5 second.

<!-- file path: MAGENTO_ROOT/app/code/RS/FirstModule/view/frontend/web/template/hello.html -->

<p class="message" data-bind="text: message()"></p>

How to access the Js component in PHTML template

We access our custom js component in message.phtml template which we created in our FirstModule.

<?php 
// file path: MAGENTO_ROOT/app/code/RS/FirstModule/view/frontend/templates/message.phtml 

/**
 * Product list template
 *
 * @var $block \Magento\Framework\View\Element\Template
 * @var \Magento\Framework\Escaper $escaper
 */
?>
<?= $escaper->escapeHtml(__("This is my first Magento 2.x Module")); ?>
<h2><?= $escaper->escapeHtml(__("Knockout Section")); ?></h2>
<div class="knockout-section" data-bind="scope: 'message_ex'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

<script type="text/x-magento-init">
        {
            "*": {
                "Magento_Ui/js/core/app": {
                    "components": {
                        "message_ex": {
                            "component": "RS_FirstModule/js/hello"
                        }
                    }
                }
            }
        }
</script>

In above example we access the custom component in PHTML template file. In template file created new section for js component. All the js component data is showing on “knockout-section” div.

Output:

That’s all, For creating a custom js component in Magento 2.

What is ViewModel In Magento 2

What is ViewModel ?

ViewModel is a PHP class. That fulfills a single or multiple object needs without extending the original block.

It avoids unnecessary overriding of block and block constructor arguments. ViewModel has required resources only. So, we can get data faster compared to block and helper classes.

How to implement ViewModel ?

In layout we can define ViewModel in block arguments with xsi:type as object. We can pass multiple ViewModel in a single block.

Let’s use this FirstModule and add new ViewModel in layout. Adding this RS\FirstModule\ViewModel\Calculate to message block.

<?xml version="1.0"?>
<!-- file path: MAGENTO_ROOT/app/code/RS/FirstModule/view/frontend/layout/first_module_first_index.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="page.main.title">
            <action method="setPageTitle">
                <argument translate="true" name="title" xsi:type="string">First Module</argument>
                </action>
        </referenceBlock>
        <referenceContainer name="content">
            <block class="Magento\Framework\View\Element\Template" name="message" template="RS_FirstModule::message.phtml">
                <arguments>
                    <argument name="view_model" xsi:type="object">\RS\FirstModule\ViewModel\Calculate</argument>
                </arguments>
            </block>
        </referenceContainer>
    </body>
</page>

Create ViewModel class

As per the Magento 2 standards, We create ViewModel class under ViewModel directory in your custom module. Basically we create this ViewModel for simple calculation.

For creating ViewModel we need implement the ArgumentInterface (\Magento\Framework\View\Element\Block\ArgumentInterface).

<?php
//file path: MAGENTO_ROOT/app/code/RS/FirstModule/ViewModel/Calculate.php
namespace RS\FirstModule\ViewModel;

use Magento\Framework\View\Element\Block\ArgumentInterface;

class Calculate implements ArgumentInterface
{
    
    /**
     * Return the sum of array arguments
     * 
     * @param mixed $args
     * @return int
     */
    public function sum($args)
    {
        $tot = 0;
        foreach ($args as $argsItem) {
            $tot += $argsItem;
        }
        return $tot;
    }
}

How to access ViewModel in template file ?

After declare and create ViewModel. Now we use that ViewModel to our template file. When we add any ViewModel to any block using layout, It’s add new argument value in block. So we can use that argument value using setter getter methods.

Let’s access ViewModel.

<?php 
// file path: MAGENTO_ROOT/app/code/RS/FirstModule/view/frontend/templates/message.phtml 

/**
 * Product list template
 *
 * @var $block \Magento\Framework\View\Element\Template
 * @var \Magento\Framework\Escaper $escaper
 */
?>
<?php 
$viewModel = $block->getViewModel(); // Or you can use $block->getData('view_model')
$subjectMarks = [
    "php" => 75,
    "java" => 80,
    ".net" => 85
];
?>
<?= $escaper->escapeHtml(__("This is my first Magento 2.x Module")); ?>
<br />
<?= $escaper->escapeHtml(__("We can calculate sum using ViewModel.")) ?>
<br />
<?= $escaper->escapeHtml(__("Sum is: %1", $viewModel->sum($subjectMarks))) ?>

Using getViewModel() or getData(‘view_model’) we can get the ViewModel object. We can set any of the name instead of the view_model. it’s not necessary to set name as view_model.

Let’s check the output: