Reading list Switch to dark mode

    Magento 2 Service Contract

    Updated 6 March 2024

    Introduction to service contract:

    In general understanding service contract is an agreement between the two parties, where one is the service provider and other is the service consumer. A service contract defines all the services that are going to be provided with some pre and post conditions. And it works as the same way in programming too.

    What is the service contract in Magento 2 ?

    In Magento 2 service contract is a set of classes and interfaces that provide some services that hides the business logic and also provides data integrity, so you can create modules using magento 2 service contract layer without worrying about the magento 2 upgrades(until any major ones).

    Why Magento 2 Uses service contract ?

    If you have worked on magento 1 you should know that there was not a proper way of interacting with other modules, you have to use models, collection helper classes (business logic) directly and if you want to work on a eav entity you will have a lot of difficulty, but through service contracts you can easily set, get and manipulate data without worrying about the structure of the system, and you can also easily convert these services in to web API’s.

    Benefits of using service contract design pattern in magento 2:

    • services ensures data integrity .
    • very easy for the third party developers and modules to utilize services provided in service contract.
    • services can be easily converted to robust web api’s .
    • using service contract ensures module stability even in magento 2 upgrades.

    Lets understand how the structure works:

    You need to define some classes and interfaces in order to create service contract in your module:

    • Data Interfaces
    • Repositories Interface
    • Management Interfaces

    Data Interfaces:

    Data interfaces are used to maintain the integrity of the data, data interfaces defines all the setters and getters for the related entity. So even if there is any changes in the model or business logic you will always get consistent data. Data interfaces resides in VendorName\ModuleName\Api\Data, so in all the modules you can found data interfaces in Api/Data folders, related model need to implement these interfaces and provide the setters, getters definition. Lets see the example of magento 2 customer module Magento\Customer\Api\Data\CustomerInterface :

    Searching for an experienced
    Magento 2 Company ?
    Find out More
    <?php
    /**
     * Copyright © Magento, Inc. All rights reserved.
     * See COPYING.txt for license details.
     */
    namespace Magento\Customer\Api\Data;
    
    /**
     * Customer entity interface for API handling.
     *
     * @api
     * @since 100.0.2
     */
    interface CustomerInterface extends \Magento\Framework\Api\CustomAttributesDataInterface
    {
        /**#@+
         * Constants defined for keys of the data array. Identical to the name of the getter in snake case
         */
        const ID = 'id';
        const CONFIRMATION = 'confirmation';
        const CREATED_AT = 'created_at';
        const UPDATED_AT = 'updated_at';
        const CREATED_IN = 'created_in';
        const DOB = 'dob';
        const EMAIL = 'email';
        const FIRSTNAME = 'firstname';
        const GENDER = 'gender';
        const GROUP_ID = 'group_id';
        const LASTNAME = 'lastname';
        const MIDDLENAME = 'middlename';
        const PREFIX = 'prefix';
        const STORE_ID = 'store_id';
        const SUFFIX = 'suffix';
        const TAXVAT = 'taxvat';
        const WEBSITE_ID = 'website_id';
        const DEFAULT_BILLING = 'default_billing';
        const DEFAULT_SHIPPING = 'default_shipping';
        const KEY_ADDRESSES = 'addresses';
        const DISABLE_AUTO_GROUP_CHANGE = 'disable_auto_group_change';
        /**#@-*/
    
        /**
         * Get customer id
         *
         * @return int|null
         */
        public function getId();
    
        /**
         * Set customer id
         *
         * @param int $id
         * @return $this
         */
        public function setId($id);
    
        /**
         * Get group id
         *
         * @return int|null
         */
        public function getGroupId();
    
        /**
         * Set group id
         *
         * @param int $groupId
         * @return $this
         */
        public function setGroupId($groupId);
    
        /**
         * Get default billing address id
         *
         * @return string|null
         */
        public function getDefaultBilling();
    
        /**
         * Set default billing address id
         *
         * @param string $defaultBilling
         * @return $this
         */
        public function setDefaultBilling($defaultBilling);
    
        /**
         * Get default shipping address id
         *
         * @return string|null
         */
        public function getDefaultShipping();
    
        /**
         * Set default shipping address id
         *
         * @param string $defaultShipping
         * @return $this
         */
        public function setDefaultShipping($defaultShipping);
    
        /**
         * Get confirmation
         *
         * @return string|null
         */
        public function getConfirmation();
    
        /**
         * Set confirmation
         *
         * @param string $confirmation
         * @return $this
         */
        public function setConfirmation($confirmation);
    
        /**
         * Get created at time
         *
         * @return string|null
         */
        public function getCreatedAt();
    
        /**
         * Set created at time
         *
         * @param string $createdAt
         * @return $this
         */
        public function setCreatedAt($createdAt);
    
        /**
         * Get updated at time
         *
         * @return string|null
         */
        public function getUpdatedAt();
    
        /**
         * Set updated at time
         *
         * @param string $updatedAt
         * @return $this
         */
        public function setUpdatedAt($updatedAt);
    
        /**
         * Get created in area
         *
         * @return string|null
         */
        public function getCreatedIn();
    
        /**
         * Set created in area
         *
         * @param string $createdIn
         * @return $this
         */
        public function setCreatedIn($createdIn);
    
        /**
         * Get date of birth
         *
         * @return string|null In keeping with current security and privacy best practices, be sure you are aware of any
         * potential legal and security risks associated with the storage of customers’ full date of birth
         * (month, day, year) along with other personal identifiers (e.g., full name) before collecting or processing
         * such data.
         */
        public function getDob();
    
        /**
         * Set date of birth
         *
         * @param string $dob
         * @return $this
         */
        public function setDob($dob);
    
        /**
         * Get email address
         *
         * @return string
         */
        public function getEmail();
    
        /**
         * Set email address
         *
         * @param string $email
         * @return $this
         */
        public function setEmail($email);
    
        /**
         * Get first name
         *
         * @return string
         */
        public function getFirstname();
    
        /**
         * Set first name
         *
         * @param string $firstname
         * @return $this
         */
        public function setFirstname($firstname);
    
        /**
         * Get last name
         *
         * @return string
         */
        public function getLastname();
    
        /**
         * Set last name
         *
         * @param string $lastname
         * @return $this
         */
        public function setLastname($lastname);
    
        /**
         * Get middle name
         *
         * @return string|null
         */
        public function getMiddlename();
    
        /**
         * Set middle name
         *
         * @param string $middlename
         * @return $this
         */
        public function setMiddlename($middlename);
    
        /**
         * Get prefix
         *
         * @return string|null
         */
        public function getPrefix();
    
        /**
         * Set prefix
         *
         * @param string $prefix
         * @return $this
         */
        public function setPrefix($prefix);
    
        /**
         * Get suffix
         *
         * @return string|null
         */
        public function getSuffix();
    
        /**
         * Set suffix
         *
         * @param string $suffix
         * @return $this
         */
        public function setSuffix($suffix);
    
        /**
         * Get gender
         *
         * @return int|null
         */
        public function getGender();
    
        /**
         * Set gender
         *
         * @param int $gender
         * @return $this
         */
        public function setGender($gender);
    
        /**
         * Get store id
         *
         * @return int|null
         */
        public function getStoreId();
    
        /**
         * Set store id
         *
         * @param int $storeId
         * @return $this
         */
        public function setStoreId($storeId);
    
        /**
         * Get tax Vat
         *
         * @return string|null
         */
        public function getTaxvat();
    
        /**
         * Set tax Vat
         *
         * @param string $taxvat
         * @return $this
         */
        public function setTaxvat($taxvat);
    
        /**
         * Get website id
         *
         * @return int|null
         */
        public function getWebsiteId();
    
        /**
         * Set website id
         *
         * @param int $websiteId
         * @return $this
         */
        public function setWebsiteId($websiteId);
    
        /**
         * Get customer addresses.
         *
         * @return \Magento\Customer\Api\Data\AddressInterface[]|null
         */
        public function getAddresses();
    
        /**
         * Set customer addresses.
         *
         * @param \Magento\Customer\Api\Data\AddressInterface[] $addresses
         * @return $this
         */
        public function setAddresses(array $addresses = null);
    
        /**
         * Get disable auto group change flag.
         *
         * @return int|null
         */
        public function getDisableAutoGroupChange();
    
        /**
         * Set disable auto group change flag.
         *
         * @param int $disableAutoGroupChange
         * @return $this
         */
        public function setDisableAutoGroupChange($disableAutoGroupChange);
    
        /**
         * Retrieve existing extension attributes object or create a new one.
         *
         * @return \Magento\Customer\Api\Data\CustomerExtensionInterface|null
         */
        public function getExtensionAttributes();
    
        /**
         * Set an extension attributes object.
         *
         * @param \Magento\Customer\Api\Data\CustomerExtensionInterface $extensionAttributes
         * @return $this
         */
        public function setExtensionAttributes(\Magento\Customer\Api\Data\CustomerExtensionInterface $extensionAttributes);
    }

    now in the above interface you can see there are some member variables and some setters, getters, above data interface for customer declares all the required information that any developer or api can get or set . There are two methods in the last :

    public function getExtensionAttributes();

    public function setExtensionAttributes(\Magento\Customer\Api\Data\CustomerExtensionInterface $extensionAttributes);

    these method are to get and set extension attributes of the entity, extension attributes are different from custom attributes as they can store complex data types you can learn about extension attributes here:

    EAV and extension attributes

    using this way of defining your module resources gives you an advantage in creating the web API for the module, I have already explained creating rest based web API in my previous article:

    Now all the data interfaces are implemented and defined in the related models, since the above example is of customer model lets see its concrete version:

    <?php
    /**
     * Copyright © Magento, Inc. All rights reserved.
     * See COPYING.txt for license details.
     */
    
    namespace Magento\Customer\Model;
    
    use Magento\Customer\Api\CustomerMetadataInterface;
    use Magento\Customer\Api\Data\CustomerInterfaceFactory;
    use Magento\Customer\Api\GroupRepositoryInterface;
    use Magento\Customer\Model\Config\Share;
    use Magento\Customer\Model\ResourceModel\Address\CollectionFactory;
    use Magento\Customer\Model\ResourceModel\Customer as ResourceCustomer;
    use Magento\Framework\App\Config\ScopeConfigInterface;
    use Magento\Framework\Exception\AuthenticationException;
    use Magento\Framework\Exception\EmailNotConfirmedException;
    use Magento\Framework\Exception\InvalidEmailOrPasswordException;
    use Magento\Framework\Indexer\StateInterface;
    use Magento\Framework\Reflection\DataObjectProcessor;
    use Magento\Store\Model\ScopeInterface;
    use Magento\Framework\App\ObjectManager;
    use Magento\Framework\Math\Random;
    use Magento\Framework\Indexer\IndexerInterface;
    
    /**
     * Customer model
     *
     * @api
     * @method int getWebsiteId() getWebsiteId()
     * @method Customer setWebsiteId($value)
     * @method int getStoreId() getStoreId()
     * @method string getEmail() getEmail()
     * @method mixed getDisableAutoGroupChange()
     * @method Customer setDisableAutoGroupChange($value)
     * @method Customer setGroupId($value)
     * @method Customer setDefaultBilling($value)
     * @method Customer setDefaultShipping($value)
     * @method Customer setPasswordHash($string)
     * @method string getPasswordHash()
     * @method string getConfirmation()
     * @SuppressWarnings(PHPMD.ExcessivePublicCount)
     * @SuppressWarnings(PHPMD.TooManyFields)
     * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
     * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
     * @since 100.0.2
     */
    class Customer extends \Magento\Framework\Model\AbstractModel
    {
        /**
         * Configuration paths for email templates and identities
         */
        const XML_PATH_REGISTER_EMAIL_TEMPLATE = 'customer/create_account/email_template';
    
        const XML_PATH_REGISTER_EMAIL_IDENTITY = 'customer/create_account/email_identity';
    
        const XML_PATH_REMIND_EMAIL_TEMPLATE = 'customer/password/remind_email_template';
    
        const XML_PATH_FORGOT_EMAIL_TEMPLATE = 'customer/password/forgot_email_template';
    
        const XML_PATH_FORGOT_EMAIL_IDENTITY = 'customer/password/forgot_email_identity';
    
        const XML_PATH_RESET_PASSWORD_TEMPLATE = 'customer/password/reset_password_template';
    
        /**
         * @deprecated @see \Magento\Customer\Model\AccountConfirmation::XML_PATH_IS_CONFIRM
         */
        const XML_PATH_IS_CONFIRM = 'customer/create_account/confirm';
    
        const XML_PATH_CONFIRM_EMAIL_TEMPLATE = 'customer/create_account/email_confirmation_template';
    
        const XML_PATH_CONFIRMED_EMAIL_TEMPLATE = 'customer/create_account/email_confirmed_template';
    
        const XML_PATH_GENERATE_HUMAN_FRIENDLY_ID = 'customer/create_account/generate_human_friendly_id';
    
        const SUBSCRIBED_YES = 'yes';
    
        const SUBSCRIBED_NO = 'no';
    
        const ENTITY = 'customer';
    
        const CUSTOMER_GRID_INDEXER_ID = 'customer_grid';
    
        /**
         * Configuration path to expiration period of reset password link
         */
        const XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD = 'customer/password/reset_link_expiration_period';
    
        /**
         * Model event prefix
         *
         * @var string
         */
        protected $_eventPrefix = 'customer';
    
        /**
         * Name of the event object
         *
         * @var string
         */
        protected $_eventObject = 'customer';
    
        /**
         * List of errors
         *
         * @var array
         */
        protected $_errors = [];
    
        /**
         * Assoc array of customer attributes
         *
         * @var array
         */
        protected $_attributes;
    
        /**
         * Customer addresses collection
         *
         * @var \Magento\Customer\Model\ResourceModel\Address\Collection
         */
        protected $_addressesCollection;
    
        /**
         * Is model deletable
         *
         * @var boolean
         */
        protected $_isDeleteable = true;
    
        /**
         * Is model readonly
         *
         * @var boolean
         */
        protected $_isReadonly = false;
    
        /**
         * @var \Magento\Store\Model\StoreManagerInterface
         */
        protected $_storeManager;
    
        /**
         * @var \Magento\Eav\Model\Config
         */
        protected $_config;
    
        /**
         * @var \Magento\Framework\App\Config\ScopeConfigInterface
         */
        protected $_scopeConfig;
    
        /**
         * @var Share
         */
        protected $_configShare;
    
        /**
         * @var AddressFactory
         */
        protected $_addressFactory;
    
        /**
         * @var CollectionFactory
         */
        protected $_addressesFactory;
    
        /**
         * @var \Magento\Framework\Mail\Template\TransportBuilder
         */
        protected $_transportBuilder;
    
        /**
         * @var GroupRepositoryInterface
         */
        protected $_groupRepository;
    
        /**
         * @var \Magento\Framework\Encryption\EncryptorInterface
         */
        protected $_encryptor;
    
        /**
         * @var Random
         */
        protected $mathRandom;
    
        /**
         * @var \Magento\Framework\Stdlib\DateTime
         */
        protected $dateTime;
    
        /**
         * @var CustomerInterfaceFactory
         */
        protected $customerDataFactory;
    
        /**
         * @var DataObjectProcessor
         */
        protected $dataObjectProcessor;
    
        /**
         * @var \Magento\Framework\Api\DataObjectHelper
         */
        protected $dataObjectHelper;
    
        /**
         * @var \Magento\Customer\Api\CustomerMetadataInterface
         */
        protected $metadataService;
    
        /**
         * @var \Magento\Framework\Indexer\IndexerRegistry
         */
        protected $indexerRegistry;
    
        /**
         * @var AccountConfirmation
         */
        private $accountConfirmation;
    
        /**
         * Caching property to store customer address data models by the address ID.
         *
         * @var array
         */
        private $storedAddress;
    
        /**
         * @var IndexerInterface|null
         */
        private $indexer;
    
        /**
         * @param \Magento\Framework\Model\Context $context
         * @param \Magento\Framework\Registry $registry
         * @param \Magento\Store\Model\StoreManagerInterface $storeManager
         * @param \Magento\Eav\Model\Config $config
         * @param ScopeConfigInterface $scopeConfig
         * @param ResourceCustomer $resource
         * @param Share $configShare
         * @param AddressFactory $addressFactory
         * @param CollectionFactory $addressesFactory
         * @param \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder
         * @param GroupRepositoryInterface $groupRepository
         * @param \Magento\Framework\Encryption\EncryptorInterface $encryptor
         * @param \Magento\Framework\Stdlib\DateTime $dateTime
         * @param CustomerInterfaceFactory $customerDataFactory
         * @param DataObjectProcessor $dataObjectProcessor
         * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
         * @param CustomerMetadataInterface $metadataService
         * @param \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry
         * @param \Magento\Framework\Data\Collection\AbstractDb|null $resourceCollection
         * @param array $data
         * @param AccountConfirmation|null $accountConfirmation
         * @param Random|null $mathRandom
         *
         * @SuppressWarnings(PHPMD.ExcessiveParameterList)
         */
        public function __construct(
            \Magento\Framework\Model\Context $context,
            \Magento\Framework\Registry $registry,
            \Magento\Store\Model\StoreManagerInterface $storeManager,
            \Magento\Eav\Model\Config $config,
            \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
            \Magento\Customer\Model\ResourceModel\Customer $resource,
            \Magento\Customer\Model\Config\Share $configShare,
            \Magento\Customer\Model\AddressFactory $addressFactory,
            \Magento\Customer\Model\ResourceModel\Address\CollectionFactory $addressesFactory,
            \Magento\Framework\Mail\Template\TransportBuilder $transportBuilder,
            GroupRepositoryInterface $groupRepository,
            \Magento\Framework\Encryption\EncryptorInterface $encryptor,
            \Magento\Framework\Stdlib\DateTime $dateTime,
            CustomerInterfaceFactory $customerDataFactory,
            DataObjectProcessor $dataObjectProcessor,
            \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
            \Magento\Customer\Api\CustomerMetadataInterface $metadataService,
            \Magento\Framework\Indexer\IndexerRegistry $indexerRegistry,
            \Magento\Framework\Data\Collection\AbstractDb $resourceCollection = null,
            array $data = [],
            AccountConfirmation $accountConfirmation = null,
            Random $mathRandom = null
        ) {
            $this->metadataService = $metadataService;
            $this->_scopeConfig = $scopeConfig;
            $this->_storeManager = $storeManager;
            $this->_config = $config;
            $this->_configShare = $configShare;
            $this->_addressFactory = $addressFactory;
            $this->_addressesFactory = $addressesFactory;
            $this->_transportBuilder = $transportBuilder;
            $this->_groupRepository = $groupRepository;
            $this->_encryptor = $encryptor;
            $this->dateTime = $dateTime;
            $this->customerDataFactory = $customerDataFactory;
            $this->dataObjectProcessor = $dataObjectProcessor;
            $this->dataObjectHelper = $dataObjectHelper;
            $this->indexerRegistry = $indexerRegistry;
            $this->accountConfirmation = $accountConfirmation ?: ObjectManager::getInstance()
                ->get(AccountConfirmation::class);
            $this->mathRandom = $mathRandom ?: ObjectManager::getInstance()->get(Random::class);
            parent::__construct(
                $context,
                $registry,
                $resource,
                $resourceCollection,
                $data
            );
        }
    
        /**
         * Micro-caching optimization
         *
         * @return IndexerInterface
         */
        private function getIndexer() : IndexerInterface
        {
            if ($this->indexer === null) {
                $this->indexer = $this->indexerRegistry->get(self::CUSTOMER_GRID_INDEXER_ID);
            }
            return $this->indexer;
        }
    
        /**
         * Initialize customer model
         *
         * @return void
         */
        public function _construct()
        {
            $this->_init(\Magento\Customer\Model\ResourceModel\Customer::class);
        }
    
        /**
         * Retrieve customer model with customer data
         *
         * @return \Magento\Customer\Api\Data\CustomerInterface
         */
        public function getDataModel()
        {
            $customerData = $this->getData();
            $addressesData = [];
            /** @var \Magento\Customer\Model\Address $address */
            foreach ($this->getAddresses() as $address) {
                if (!isset($this->storedAddress[$address->getId()])) {
                    $this->storedAddress[$address->getId()] = $address->getDataModel();
                }
                $addressesData[] = $this->storedAddress[$address->getId()];
            }
            $customerDataObject = $this->customerDataFactory->create();
            $this->dataObjectHelper->populateWithArray(
                $customerDataObject,
                $customerData,
                \Magento\Customer\Api\Data\CustomerInterface::class
            );
            $customerDataObject->setAddresses($addressesData)
                ->setId($this->getId());
            return $customerDataObject;
        }
    
        /**
         * Update customer data
         *
         * @param \Magento\Customer\Api\Data\CustomerInterface $customer
         * @return $this
         */
        public function updateData($customer)
        {
            $customerDataAttributes = $this->dataObjectProcessor->buildOutputDataArray(
                $customer,
                \Magento\Customer\Api\Data\CustomerInterface::class
            );
    
            foreach ($customerDataAttributes as $attributeCode => $attributeData) {
                if ($attributeCode == 'password') {
                    continue;
                }
                $this->setDataUsingMethod($attributeCode, $attributeData);
            }
    
            $customAttributes = $customer->getCustomAttributes();
            if ($customAttributes !== null) {
                foreach ($customAttributes as $attribute) {
                    $this->setData($attribute->getAttributeCode(), $attribute->getValue());
                }
            }
    
            $customerId = $customer->getId();
            if ($customerId) {
                $this->setId($customerId);
            }
    
            return $this;
        }
    
        /**
         * Retrieve customer sharing configuration model
         *
         * @return Share
         */
        public function getSharingConfig()
        {
            return $this->_configShare;
        }
    
        /**
         * Authenticate customer
         *
         * @param  string $login
         * @param  string $password
         * @return bool
         * @throws \Magento\Framework\Exception\LocalizedException
         * Use \Magento\Customer\Api\AccountManagementInterface::authenticate
         */
        public function authenticate($login, $password)
        {
            $this->loadByEmail($login);
            if ($this->getConfirmation() &&
                $this->accountConfirmation->isConfirmationRequired($this->getWebsiteId(), $this->getId(), $this->getEmail())
            ) {
                throw new EmailNotConfirmedException(
                    __("This account isn't confirmed. Verify and try again.")
                );
            }
            if (!$this->validatePassword($password)) {
                throw new InvalidEmailOrPasswordException(
                    __('Invalid login or password.')
                );
            }
            $this->_eventManager->dispatch(
                'customer_customer_authenticated',
                ['model' => $this, 'password' => $password]
            );
    
            return true;
        }
    
        /**
         * Load customer by email
         *
         * @param string $customerEmail
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function loadByEmail($customerEmail)
        {
            $this->_getResource()->loadByEmail($this, $customerEmail);
            return $this;
        }
    
        /**
         * Change customer password
         *
         * @param string $newPassword
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function changePassword($newPassword)
        {
            $this->_getResource()->changePassword($this, $newPassword);
            return $this;
        }
    
        /**
         * Get full customer name
         *
         * @return string
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function getName()
        {
            $name = '';
    
            if ($this->_config->getAttribute('customer', 'prefix')->getIsVisible() && $this->getPrefix()) {
                $name .= $this->getPrefix() . ' ';
            }
            $name .= $this->getFirstname();
            if ($this->_config->getAttribute('customer', 'middlename')->getIsVisible() && $this->getMiddlename()) {
                $name .= ' ' . $this->getMiddlename();
            }
            $name .= ' ' . $this->getLastname();
            if ($this->_config->getAttribute('customer', 'suffix')->getIsVisible() && $this->getSuffix()) {
                $name .= ' ' . $this->getSuffix();
            }
            return $name;
        }
    
        /**
         * Add address to address collection
         *
         * @param Address $address
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function addAddress(Address $address)
        {
            $this->getAddressesCollection()->addItem($address);
            return $this;
        }
    
        /**
         * Retrieve customer address by address id
         *
         * @param   int $addressId
         * @return  Address
         */
        public function getAddressById($addressId)
        {
            return $this->_createAddressInstance()->load($addressId);
        }
    
        /**
         * Getting customer address object from collection by identifier
         *
         * @param int $addressId
         * @return Address
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function getAddressItemById($addressId)
        {
            return $this->getAddressesCollection()->getItemById($addressId);
        }
    
        /**
         * Retrieve not loaded address collection
         *
         * @return \Magento\Customer\Model\ResourceModel\Address\Collection
         */
        public function getAddressCollection()
        {
            return $this->_createAddressCollection();
        }
    
        /**
         * Customer addresses collection
         *
         * @return \Magento\Customer\Model\ResourceModel\Address\Collection
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function getAddressesCollection()
        {
            if ($this->_addressesCollection === null) {
                $this->_addressesCollection = $this->getAddressCollection()->setCustomerFilter(
                    $this
                )->addAttributeToSelect(
                    '*'
                );
                foreach ($this->_addressesCollection as $address) {
                    $address->setCustomer($this);
                }
            }
    
            return $this->_addressesCollection;
        }
    
        /**
         * Retrieve customer address array
         *
         * @return \Magento\Framework\DataObject[]
         */
        public function getAddresses()
        {
            return $this->getAddressesCollection()->getItems();
        }
    
        /**
         * Retrieve all customer attributes
         *
         * @return Attribute[]
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function getAttributes()
        {
            if ($this->_attributes === null) {
                $this->_attributes = $this->_getResource()->loadAllAttributes($this)->getSortedAttributes();
            }
            return $this->_attributes;
        }
    
        /**
         * Get customer attribute model object
         *
         * @param   string $attributeCode
         * @return  \Magento\Customer\Model\ResourceModel\Attribute | null
         */
        public function getAttribute($attributeCode)
        {
            $this->getAttributes();
            if (isset($this->_attributes[$attributeCode])) {
                return $this->_attributes[$attributeCode];
            }
            return null;
        }
    
        /**
         * Set plain and hashed password
         *
         * @param string $password
         * @return $this
         */
        public function setPassword($password)
        {
            $this->setData('password', $password);
            $this->setPasswordHash($this->hashPassword($password));
            return $this;
        }
    
        /**
         * Hash customer password
         *
         * @param string $password
         * @param bool|int|string $salt
         * @return string
         */
        public function hashPassword($password, $salt = true)
        {
            return $this->_encryptor->getHash($password, $salt);
        }
    
        /**
         * Validate password with salted hash
         *
         * @param string $password
         * @return boolean
         * @throws \Exception
         */
        public function validatePassword($password)
        {
            $hash = $this->getPasswordHash();
            if (!$hash) {
                return false;
            }
            return $this->_encryptor->validateHash($password, $hash);
        }
    
        /**
         * Encrypt password
         *
         * @param   string $password
         * @return  string
         */
        public function encryptPassword($password)
        {
            return $this->_encryptor->encrypt($password);
        }
    
        /**
         * Decrypt password
         *
         * @param   string $password
         * @return  string
         */
        public function decryptPassword($password)
        {
            return $this->_encryptor->decrypt($password);
        }
    
        /**
         * Retrieve default address by type(attribute)
         *
         * @param   string $attributeCode address type attribute code
         * @return  Address|false
         */
        public function getPrimaryAddress($attributeCode)
        {
            $primaryAddress = $this->getAddressesCollection()->getItemById($this->getData($attributeCode));
    
            return $primaryAddress ? $primaryAddress : false;
        }
    
        /**
         * Get customer default billing address
         *
         * @return Address
         */
        public function getPrimaryBillingAddress()
        {
            return $this->getPrimaryAddress('default_billing');
        }
    
        /**
         * Get customer default billing address
         *
         * @return Address
         */
        public function getDefaultBillingAddress()
        {
            return $this->getPrimaryBillingAddress();
        }
    
        /**
         * Get default customer shipping address
         *
         * @return Address
         */
        public function getPrimaryShippingAddress()
        {
            return $this->getPrimaryAddress('default_shipping');
        }
    
        /**
         * Get default customer shipping address
         *
         * @return Address
         */
        public function getDefaultShippingAddress()
        {
            return $this->getPrimaryShippingAddress();
        }
    
        /**
         * Retrieve ids of default addresses
         *
         * @return array
         */
        public function getPrimaryAddressIds()
        {
            $ids = [];
            if ($this->getDefaultBilling()) {
                $ids[] = $this->getDefaultBilling();
            }
            if ($this->getDefaultShipping()) {
                $ids[] = $this->getDefaultShipping();
            }
            return $ids;
        }
    
        /**
         * Retrieve all customer default addresses
         *
         * @return Address[]
         */
        public function getPrimaryAddresses()
        {
            $addresses = [];
            $primaryBilling = $this->getPrimaryBillingAddress();
            if ($primaryBilling) {
                $addresses[] = $primaryBilling;
                $primaryBilling->setIsPrimaryBilling(true);
            }
    
            $primaryShipping = $this->getPrimaryShippingAddress();
            if ($primaryShipping) {
                if ($primaryBilling && $primaryBilling->getId() == $primaryShipping->getId()) {
                    $primaryBilling->setIsPrimaryShipping(true);
                } else {
                    $primaryShipping->setIsPrimaryShipping(true);
                    $addresses[] = $primaryShipping;
                }
            }
            return $addresses;
        }
    
        /**
         * Retrieve not default addresses
         *
         * @return Address[]
         */
        public function getAdditionalAddresses()
        {
            $addresses = [];
            $primatyIds = $this->getPrimaryAddressIds();
            foreach ($this->getAddressesCollection() as $address) {
                if (!in_array($address->getId(), $primatyIds)) {
                    $addresses[] = $address;
                }
            }
            return $addresses;
        }
    
        /**
         * Check if address is primary
         *
         * @param Address $address
         * @return boolean
         */
        public function isAddressPrimary(Address $address)
        {
            if (!$address->getId()) {
                return false;
            }
            return $address->getId() == $this->getDefaultBilling() || $address->getId() == $this->getDefaultShipping();
        }
    
        /**
         * Send email with new account related information
         *
         * @param string $type
         * @param string $backUrl
         * @param string $storeId
         * @return $this
         * @throws \Magento\Framework\Exception\LocalizedException
         */
        public function sendNewAccountEmail($type = 'registered', $backUrl = '', $storeId = '0')
        {
            $types = $this->getTemplateTypes();
    
            if (!isset($types[$type])) {
                throw new \Magento\Framework\Exception\LocalizedException(
                    __('The transactional account email type is incorrect. Verify and try again.')
                );
            }
    
            if (!$storeId) {
                $storeId = $this->_getWebsiteStoreId($this->getSendemailStoreId());
            }
    
            $this->_sendEmailTemplate(
                $types[$type],
                self::XML_PATH_REGISTER_EMAIL_IDENTITY,
                ['customer' => $this, 'back_url' => $backUrl, 'store' => $this->getStore()],
                $storeId
            );
    
            return $this;
        }
    
        /**
         * Check if accounts confirmation is required in config
         *
         * @return bool
         * @deprecated 101.0.4
         * @see AccountConfirmation::isConfirmationRequired
         */
        public function isConfirmationRequired()
        {
            $websiteId = $this->getWebsiteId() ? $this->getWebsiteId() : null;
    
            return $this->accountConfirmation->isConfirmationRequired($websiteId, $this->getId(), $this->getEmail());
        }
    
        /**
         * Generate random confirmation key
         *
         * @return string
         */
        public function getRandomConfirmationKey()
        {
            return $this->mathRandom->getRandomString(32);
        }
    
        /**
         * Send email with new customer password
         *
         * @return $this
         */
        public function sendPasswordReminderEmail()
        {
            $this->_sendEmailTemplate(
                self::XML_PATH_REMIND_EMAIL_TEMPLATE,
                self::XML_PATH_FORGOT_EMAIL_IDENTITY,
                ['customer' => $this, 'store' => $this->getStore()],
                $this->getStoreId()
            );
    
            return $this;
        }
    
        /**
         * Send corresponding email template
         *
         * @param string $template configuration path of email template
         * @param string $sender configuration path of email identity
         * @param array $templateParams
         * @param int|null $storeId
         * @return $this
         */
        protected function _sendEmailTemplate($template, $sender, $templateParams = [], $storeId = null)
        {
            /** @var \Magento\Framework\Mail\TransportInterface $transport */
            $transport = $this->_transportBuilder->setTemplateIdentifier(
                $this->_scopeConfig->getValue($template, ScopeInterface::SCOPE_STORE, $storeId)
            )->setTemplateOptions(
                ['area' => \Magento\Framework\App\Area::AREA_FRONTEND, 'store' => $storeId]
            )->setTemplateVars(
                $templateParams
            )->setFrom(
                $this->_scopeConfig->getValue($sender, ScopeInterface::SCOPE_STORE, $storeId)
            )->addTo(
                $this->getEmail(),
                $this->getName()
            )->getTransport();
            $transport->sendMessage();
    
            return $this;
        }
    
        /**
         * Send email with reset password confirmation link
         *
         * @return $this
         */
        public function sendPasswordResetConfirmationEmail()
        {
            $storeId = $this->getStoreId();
            if (!$storeId) {
                $storeId = $this->_getWebsiteStoreId();
            }
    
            $this->_sendEmailTemplate(
                self::XML_PATH_FORGOT_EMAIL_TEMPLATE,
                self::XML_PATH_FORGOT_EMAIL_IDENTITY,
                ['customer' => $this, 'store' => $this->getStore()],
                $storeId
            );
    
            return $this;
        }
    
        /**
         * Retrieve customer group identifier
         *
         * @return int
         */
        public function getGroupId()
        {
            if (!$this->hasData('group_id')) {
                $storeId = $this->getStoreId() ? $this->getStoreId() : $this->_storeManager->getStore()->getId();
                $groupId = $this->_scopeConfig->getValue(
                    GroupManagement::XML_PATH_DEFAULT_ID,
                    ScopeInterface::SCOPE_STORE,
                    $storeId
                );
                $this->setData('group_id', $groupId);
            }
            return $this->getData('group_id');
        }
    
        /**
         * Retrieve customer tax class identifier
         *
         * @return int
         */
        public function getTaxClassId()
        {
            if (!$this->getData('tax_class_id')) {
                $groupTaxClassId = $this->_groupRepository->getById($this->getGroupId())->getTaxClassId();
                $this->setData('tax_class_id', $groupTaxClassId);
            }
            return $this->getData('tax_class_id');
        }
    
        /**
         * Retrieve store where customer was created
         *
         * @return \Magento\Store\Model\Store
         */
        public function getStore()
        {
            return $this->_storeManager->getStore($this->getStoreId());
        }
    
        /**
         * Retrieve shared store ids
         *
         * @return array
         */
        public function getSharedStoreIds()
        {
            $ids = $this->_getData('shared_store_ids');
            if ($ids === null) {
                $ids = [];
                if ((bool)$this->getSharingConfig()->isWebsiteScope()) {
                    $ids = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds();
                } else {
                    foreach ($this->_storeManager->getStores() as $store) {
                        $ids[] = $store->getId();
                    }
                }
                $this->setData('shared_store_ids', $ids);
            }
    
            return $ids;
        }
    
        /**
         * Retrieve shared website ids
         *
         * @return int[]
         */
        public function getSharedWebsiteIds()
        {
            $ids = $this->_getData('shared_website_ids');
            if ($ids === null) {
                $ids = [];
                if ((bool)$this->getSharingConfig()->isWebsiteScope()) {
                    $ids[] = $this->getWebsiteId();
                } else {
                    foreach ($this->_storeManager->getWebsites() as $website) {
                        $ids[] = $website->getId();
                    }
                }
                $this->setData('shared_website_ids', $ids);
            }
            return $ids;
        }
    
        /**
         * Retrieve attribute set id for customer.
         *
         * @return int
         * @since 102.0.1
         */
        public function getAttributeSetId()
        {
            // phpstan:ignore "Call to an undefined static method*"
            return parent::getAttributeSetId() ?: CustomerMetadataInterface::ATTRIBUTE_SET_ID_CUSTOMER;
        }
    
        /**
         * Set store to customer
         *
         * @param \Magento\Store\Model\Store $store
         * @return $this
         */
        public function setStore(\Magento\Store\Model\Store $store)
        {
            $this->setStoreId($store->getId());
            $this->setWebsiteId($store->getWebsite()->getId());
            return $this;
        }
    
        /**
         * Validate customer attribute values.
         *
         * @deprecated 100.1.0
         * @return bool
         */
        public function validate()
        {
            return true;
        }
    
        /**
         * Unset subscription
         *
         * @return $this
         */
        public function unsetSubscription()
        {
            if (isset($this->_isSubscribed)) {
                unset($this->_isSubscribed);
            }
            return $this;
        }
    
        /**
         * Clean all addresses
         *
         * @return void
         */
        public function cleanAllAddresses()
        {
            $this->_addressesCollection = null;
        }
    
        /**
         * Add error
         *
         * @param mixed $error
         * @return $this
         */
        public function addError($error)
        {
            $this->_errors[] = $error;
            return $this;
        }
    
        /**
         * Retrieve errors
         *
         * @return array
         */
        public function getErrors()
        {
            return $this->_errors;
        }
    
        /**
         * Reset errors array
         *
         * @return $this
         */
        public function resetErrors()
        {
            $this->_errors = [];
            return $this;
        }
    
        /**
         * Processing object after save data
         *
         * @return $this
         */
        public function afterSave()
        {
            if ($this->getIndexer()->getState()->getStatus() == StateInterface::STATUS_VALID) {
                $this->_getResource()->addCommitCallback([$this, 'reindex']);
            }
            return parent::afterSave();
        }
    
        /**
         * Init indexing process after customer delete
         *
         * @return \Magento\Framework\Model\AbstractModel
         */
        public function afterDeleteCommit()
        {
            $this->reindex();
            return parent::afterDeleteCommit();
        }
    
        /**
         * Init indexing process after customer save
         *
         * @return void
         */
        public function reindex()
        {
            $this->getIndexer()->reindexRow($this->getId());
        }
    
        /**
         * Get customer created at date timestamp
         *
         * @return int|null
         */
        public function getCreatedAtTimestamp()
        {
            $date = $this->getCreatedAt();
            if ($date) {
                return (new \DateTime($date))->getTimestamp();
            }
            return null;
        }
    
        /**
         * Reset all model data
         *
         * @return $this
         */
        public function reset()
        {
            $this->setData([]);
            $this->setOrigData();
            $this->_attributes = null;
    
            return $this;
        }
    
        /**
         * Checks model is deletable
         *
         * @return boolean
         */
        public function isDeleteable()
        {
            return $this->_isDeleteable;
        }
    
        /**
         * Set is deletable flag
         *
         * @param boolean $value
         * @return $this
         */
        public function setIsDeleteable($value)
        {
            $this->_isDeleteable = (bool)$value;
            return $this;
        }
    
        /**
         * Checks model is readonly
         *
         * @return boolean
         */
        public function isReadonly()
        {
            return $this->_isReadonly;
        }
    
        /**
         * Set is readonly flag
         *
         * @param boolean $value
         * @return $this
         */
        public function setIsReadonly($value)
        {
            $this->_isReadonly = (bool)$value;
            return $this;
        }
    
        /**
         * Check whether confirmation may be skipped when registering using certain email address
         *
         * @return bool
         * @deprecated 101.0.4
         * @see AccountConfirmation::isConfirmationRequired
         */
        protected function canSkipConfirmation()
        {
            if (!$this->getId()) {
                return false;
            }
    
            /* If an email was used to start the registration process and it is the same email as the one
               used to register, then this can skip confirmation.
               */
            $skipConfirmationIfEmail = $this->_registry->registry("skip_confirmation_if_email");
            if (!$skipConfirmationIfEmail) {
                return false;
            }
    
            return strtolower($skipConfirmationIfEmail) === strtolower($this->getEmail());
        }
    
        /**
         * Clone current object
         *
         * @return void
         */
        public function __clone()
        {
            $newAddressCollection = $this->getPrimaryAddresses();
            $newAddressCollection = array_merge($newAddressCollection, $this->getAdditionalAddresses());
            $this->setId(null);
            $this->cleanAllAddresses();
            foreach ($newAddressCollection as $address) {
                $this->addAddress(clone $address);
            }
        }
    
        /**
         * Return Entity Type instance
         *
         * @return \Magento\Eav\Model\Entity\Type
         */
        public function getEntityType()
        {
            return $this->_getResource()->getEntityType();
        }
    
        /**
         * Get either first store ID from a set website or the provided as default
         *
         * @param int|string|null $defaultStoreId
         *
         * @return int
         */
        protected function _getWebsiteStoreId($defaultStoreId = null)
        {
            if ($this->getWebsiteId() != 0 && empty($defaultStoreId)) {
                $storeIds = $this->_storeManager->getWebsite($this->getWebsiteId())->getStoreIds();
                reset($storeIds);
                $defaultStoreId = current($storeIds);
            }
            return $defaultStoreId;
        }
    
        /**
         * Change reset password link token
         *
         * Stores new reset password link token
         *
         * @param string $passwordLinkToken
         * @return $this
         * @throws \Magento\Framework\Exception\AuthenticationException
         */
        public function changeResetPasswordLinkToken($passwordLinkToken)
        {
            if (!is_string($passwordLinkToken) || empty($passwordLinkToken)) {
                throw new AuthenticationException(
                    __('A valid password reset token is missing. Enter and try again.')
                );
            }
            $this->_getResource()->changeResetPasswordLinkToken($this, $passwordLinkToken);
            return $this;
        }
    
        /**
         * Check if current reset password link token is expired
         *
         * @return boolean
         */
        public function isResetPasswordLinkTokenExpired()
        {
            $linkToken = $this->getRpToken();
            $linkTokenCreatedAt = $this->getRpTokenCreatedAt();
    
            if (empty($linkToken) || empty($linkTokenCreatedAt)) {
                return true;
            }
    
            $expirationPeriod = $this->getResetPasswordLinkExpirationPeriod();
    
            $currentTimestamp = (new \DateTime())->getTimestamp();
            $tokenTimestamp = (new \DateTime($linkTokenCreatedAt))->getTimestamp();
            if ($tokenTimestamp > $currentTimestamp) {
                return true;
            }
    
            $dayDifference = floor(($currentTimestamp - $tokenTimestamp) / (24 * 60 * 60));
            if ($dayDifference >= $expirationPeriod) {
                return true;
            }
    
            return false;
        }
    
        /**
         * Retrieve customer reset password link expiration period in days
         *
         * @return int
         */
        public function getResetPasswordLinkExpirationPeriod()
        {
            return (int)$this->_scopeConfig->getValue(
                self::XML_PATH_CUSTOMER_RESET_PASSWORD_LINK_EXPIRATION_PERIOD,
                ScopeConfigInterface::SCOPE_TYPE_DEFAULT
            );
        }
    
        /**
         * Create Address from Factory
         *
         * @return Address
         */
        protected function _createAddressInstance()
        {
            return $this->_addressFactory->create();
        }
    
        /**
         * Create Address Collection from Factory
         *
         * @return \Magento\Customer\Model\ResourceModel\Address\Collection
         */
        protected function _createAddressCollection()
        {
            return $this->_addressesFactory->create();
        }
    
        /**
         * Get Template Types
         *
         * @return array
         */
        protected function getTemplateTypes()
        {
            /**
             * 'registered'   welcome email, when confirmation is disabled
             * 'confirmed'    welcome email, when confirmation is enabled
             * 'confirmation' email with confirmation link
             */
            $types = [
                'registered' => self::XML_PATH_REGISTER_EMAIL_TEMPLATE,
                'confirmed' => self::XML_PATH_CONFIRMED_EMAIL_TEMPLATE,
                'confirmation' => self::XML_PATH_CONFIRM_EMAIL_TEMPLATE,
            ];
            return $types;
        }
    
        /**
         * Check if customer is locked
         *
         * @return boolean
         * @since 100.1.0
         */
        public function isCustomerLocked()
        {
            if ($this->getLockExpires()) {
                $lockExpires = new \DateTime($this->getLockExpires());
                if ($lockExpires > new \DateTime()) {
                    return true;
                }
            }
            return false;
        }
    
        /**
         * Return Password Confirmation
         *
         * @return string
         * @since 100.1.0
         */
        public function getPasswordConfirm()
        {
            return (string) $this->getData('password_confirm');
        }
    
        /**
         * Return Password
         *
         * @return string
         * @since 100.1.0
         */
        public function getPassword()
        {
            return (string) $this->getData('password');
        }
    }

    as you can see in the above class it has implemented the CustomerInterface and defined all the abstract methods .

    That’s all for this article, there is a lot more to explain about this topic but this will be too much for one article I will explain repositories and mananagement interfaces in my next article .
    If you have any questions in this article you can ask in the comments.
    Thanks 🙂 .

    . . .

    Leave a Comment

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


    2 comments

  • Sameer Bhayani
  • Magento 2 service contract | Web development
  • Back to Top

    Message Sent!

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

    Back to Home