Building Magento 2 Extension – CustomerGrid

When you are building new M2 extension, you most likely going to add additional field for some existing Magento entity. In this post I want to show simple example for adding new EAV attribute to Customer entity and displaying it in Customer grid.
If you want to see code only, without reading my typos and Ukrainian English  – go to GitHub.

CustomerGrid module

This updated post explains how to create new module. After adding registration.php, etc/module.xml and Setup namespace (will be explained later), CustomerGrid skeleton looks like this in my PHPStorm project:

customergrid-1

If you will use composer package, you need also create composer.json file in root folder.

Of course module.xml declares dependency on Magento_Customer module, because it is going to add new attribute for Customer entity, and Magento_Customer must be loaded before Maxyek_CustomerGrid.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Maxyek_CustomerGrid" setup_version="0.0.2">
        <sequence>
            <module name="Magento_Customer">
        </sequence>
    </module>
</config>

Setup

There are different interfaces for Schema (read DB) install/update, and Data install and update scripts. CustomerGrid module will not alter any DB tables nor add new tables or fields, it will add new EAV attribute, so it must implement InstallDataInterface interface. All setup and upgrade scripts must be located in Setup namespace, for CustomerGrid it will be Maxyek\CustomerGrid\Setup namespace. Below full code for install script, which will be executed during setup or upgrade:

<?php

namespace Maxyek\CustomerGrid\Setup;

use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetup;
use Magento\Framework\Setup\InstallDataInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

class InstallData implements InstallDataInterface
{

    /**
     * Customer setup factory
     *
     * @var \Magento\Customer\Setup\CustomerSetupFactory
     */
    private $customerSetupFactory;

    /**
     * Init
     *
     * @param \Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory
     */
    public function __construct(\Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory)
    {
        $this->customerSetupFactory = $customerSetupFactory;
    }

    /**
     * Installs DB schema for a module
     *
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     * @return void
     */
    public function install(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();

        /** @var CustomerSetup $customerSetup */
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);

        $customerSetup->addAttribute(
            Customer::ENTITY,
            'client_idn',
            [

                'label' => 'Client IDN',
                'input' => 'text',
                'required' => false,
                'sort_order' => 40,
                'visible' => true,
                'system' => false,
                'is_used_in_grid' => true,
                'is_visible_in_grid' => true,
                'is_filterable_in_grid' => true,
                'is_searchable_in_grid' => true
            ]
        );

        // add attribute to form
        /** @var  $attribute */
        $attribute = $customerSetup->getEavConfig()->getAttribute('customer', 'client_idn');
        $attribute->setData('used_in_forms', ['adminhtml_customer']);
        $attribute->save();

        $setup->endSetup();
    }
}

The InstallData class requires CustomerSetupFactory to initialize
\Magento\Customer\Setup\CustomerSetup. You will not find CustomerSetupFactory class in core modules neither in framework. It will be autogenerated by Magento 2 Dependency Injection during module setup, and you will find it after (or during) installation in var/generation. In order to give DI instruction to generate it, it must be passed as parameter in constructor (and yes Magento 2 uses constructor DI).

Why does module need \Magento\Customer\Setup\CustomerSetup? Basically it is extended Magento\Eav\Setup\EavSetup with Customer entity related logic. Setup script will use it to create new attribute.

addAttribute takes params with entity type, attribute code and additional attribute configuration, the configuration are self-explaining, so no need to go throw each.

 $customerSetup->addAttribute(
            Customer::ENTITY,
            'client_idn',
            [

                'label' => 'Client IDN',
                'input' => 'text',
                'required' => false,
                'sort_order' => 40,
                'visible' => true,
                'system' => false,
                'is_used_in_grid' => true,
                'is_visible_in_grid' => true,
                'is_filterable_in_grid' => true,
                'is_searchable_in_grid' => true
            ]
        );

After module development completed, don’t forget to run:

>php bin/magento setup:upgrade

Forms

customeredit

In order to edit/save newly created customer attribute, it must be associated with the form, which will use it. There is “used_in_form” param for Customer EAV attribute in Magento 2, basically it links attribute to forms. The example code do link newly created attribute with “adminhtml_customer” form (Customer Edit form in admin).

        // add attribute to form        
        $attribute = $customerSetup->getEavConfig()->getAttribute('customer', 'client_idn');
        $attribute->setData('used_in_forms', ['adminhtml_customer']);
        $attribute->save();

In case, if you will not link attribute to form, it is not possible to edit it, however it may still be rendered. In order to find existing forms for customer entity, i recommend to open customer_form_attribute table and check if your attribute is linked with any form. For example the client_idn has attribute_id = 134 , and after setup it linked to the “adminhtml_customer” form.

customer_form_attribue eav_attribues

Grid

Most likely you want to see the attribute in Customer grid. To do it, the attribute requires to specify *_in_grid params during attribute creation (or after). You need to let Magento 2 know, whether the attribute will be used in grid, visible, filterable or searchable, and Magento 2 Grid component will take care of rendering it.

                'is_used_in_grid' =&gt; true,
                'is_visible_in_grid' =&gt; true,
                'is_filterable_in_grid' =&gt; true,
                'is_searchable_in_grid' =&gt; true

customergrid

Summary

As I promised, it is very short blog post to show how to add EAV attribute. There are a lot of possible attribute params you will find, like frontend and backend renderers, data source model e.t.c.. Magento 2 provides powerfull tool for extension developers to customize and extend it.  Go for it!

Advertisements

9 thoughts on “Building Magento 2 Extension – CustomerGrid

  1. Hi Max,

    The ability to dynamically add to the backend in this manner will be a godsend given the amount of extensions for M1 that rewrite the blocks to achieve this. How easy will it be to get a field shown on the frontend in the register form / account area though? I’ve had several ideas for modules I’d like to create for M1, however the requirement for such commonly customised templates to be modified or overloaded makes it not exactly accessible to the non-developer and has always put me off.

    Liked by 1 person

  2. Hi,
    Thanks for the information
    I am facing a small issue, i am not able to save customer form.

    exception ‘PDOException’ with message ‘SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘date_founded’ in ‘field list” in /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Statement/Pdo.php:228 Stack trace: #0 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Statement/Pdo.php(228): PDOStatement->execute(Array) #1 /var/www/demos/magento2ce/vendor/magento/framework/DB/Statement/Pdo/Mysql.php(95): Zend_Db_Statement_Pdo->_execute(Array) #2 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Statement.php(303): Magento\Framework\DB\Statement\Pdo\Mysql->_execute(Array) #3 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array) #4 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query(‘INSERT INTO `cu…’, Array) #5 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(444): Zend_Db_Adapter_Pdo_Abstract->query(‘INSERT INTO `cu…’, Array) #6 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(499): Magento\Framework\DB\Adapter\Pdo\Mysql->_query(‘INSERT INTO `cu…’, Array) #7 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(1842): Magento\Framework\DB\Adapter\Pdo\Mysql->query(‘INSERT INTO `cu…’, Array) #8 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/SaveHandler/Grid.php(45): Magento\Framework\DB\Adapter\Pdo\Mysql->insertOnDuplicate(‘customer_grid_f…’, Array, Array) #9 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/SaveHandler/Grid.php(23): Magento\Framework\Indexer\SaveHandler\Grid->insertDocumentsForFilterable(Array, Array) #10 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/Action/Base.php(170): Magento\Framework\Indexer\SaveHandler\Grid->saveIndex(Array, Object(Magento\Customer\Model\ResourceModel\Customer\Collection)) #11 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/Action/Base.php(202): Magento\Framework\Indexer\Action\Base->execute(Array) #12 /var/www/demos/magento2ce/vendor/magento/module-indexer/Model/Indexer.php(433): Magento\Framework\Indexer\Action\Base->executeRow(’10’) #13 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/Customer.php(1116): Magento\Indexer\Model\Indexer->reindexRow(’10’) #14 [internal function]: Magento\Customer\Model\Customer->reindex() #15 /var/www/demos/magento2ce/vendor/magento/framework/Model/ResourceModel/AbstractResource.php(94): call_user_func(Array) #16 /var/www/demos/magento2ce/vendor/magento/module-eav/Model/Entity/VersionControl/AbstractEntity.php(98): Magento\Framework\Model\ResourceModel\AbstractResource->commit() #17 /var/www/demos/magento2ce/vendor/magento/framework/Model/AbstractModel.php(615): Magento\Eav\Model\Entity\VersionControl\AbstractEntity->save(Object(Magento\Customer\Model\Backend\Customer)) #18 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/ResourceModel/CustomerRepository.php(195): Magento\Framework\Model\AbstractModel->save() #19 [internal function]: Magento\Customer\Model\ResourceModel\CustomerRepository->save(Object(Magento\Customer\Model\Data\Customer), NULL) #20 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(144): call_user_func_array(Array, Array) #21 /var/www/demos/magento2ce/var/generation/Magento/Customer/Model/ResourceModel/CustomerRepository/Interceptor.php(27): Magento\Customer\Model\ResourceModel\CustomerRepository\Interceptor->___callPlugins(‘save’, Array, Array) #22 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/AccountManagement.php(527): Magento\Customer\Model\ResourceModel\CustomerRepository\Interceptor->save(Object(Magento\Customer\Model\Data\Customer), NULL) #23 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/AccountManagement.php(484): Magento\Customer\Model\AccountManagement->createAccountWithPasswordHash(Object(Magento\Customer\Model\Data\Customer), NULL, ”) #24 /var/www/demos/magento2ce/vendor/magento/module-customer/Controller/Adminhtml/Index/Save.php(236): Magento\Customer\Model\AccountManagement->createAccount(Object(Magento\Customer\Model\Data\Customer)) #25 /var/www/demos/magento2ce/var/generation/Magento/Customer/Controller/Adminhtml/Index/Save/Interceptor.php(24): Magento\Customer\Controller\Adminhtml\Index\Save->execute() #26 /var/www/demos/magento2ce/vendor/magento/framework/App/Action/Action.php(102): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->execute() #27 /var/www/demos/magento2ce/vendor/magento/module-backend/App/AbstractAction.php(226): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http)) #28 [internal function]: Magento\Backend\App\AbstractAction->dispatch(Object(Magento\Framework\App\Request\Http)) #29 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array) #30 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->___callParent(‘dispatch’, Array) #31 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘adminAuthentica…’) #32 /var/www/demos/magento2ce/vendor/magento/module-backend/App/Action/Plugin/Authentication.php(143): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #33 [internal function]: Magento\Backend\App\Action\Plugin\Authentication->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #34 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #35 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘designLoader’) #36 /var/www/demos/magento2ce/vendor/magento/framework/App/Action/Plugin/Design.php(39): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #37 [internal function]: Magento\Framework\App\Action\Plugin\Design->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #38 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #39 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘adminMassaction…’) #40 /var/www/demos/magento2ce/vendor/magento/module-backend/App/Action/Plugin/MassactionKey.php(33): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http)) #41 [internal function]: Magento\Backend\App\Action\Plugin\MassactionKey->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #42 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array) #43 /var/www/demos/magento2ce/var/generation/Magento/Customer/Controller/Adminhtml/Index/Save/Interceptor.php(39): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->___callPlugins(‘dispatch’, Array, Array) #44 /var/www/demos/magento2ce/vendor/magento/framework/App/FrontController.php(55): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http)) #45 [internal function]: Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http)) #46 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array) #47 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Framework\App\FrontController\Interceptor->___callParent(‘dispatch’, Array) #48 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘install’) #49 /var/www/demos/magento2ce/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #50 [internal function]: Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #51 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #52 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘storeCookieVali…’) #53 /var/www/demos/magento2ce/vendor/magento/module-store/Model/Plugin/StoreCookie.php(78): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http)) #54 [internal function]: Magento\Store\Model\Plugin\StoreCookie->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #55 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array) #56 /var/www/demos/magento2ce/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins(‘dispatch’, Array, Array) #57 /var/www/demos/magento2ce/vendor/magento/framework/App/Http.php(115): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http)) #58 /var/www/demos/magento2ce/vendor/magento/framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch() #59 /var/www/demos/magento2ce/index.php(38): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http)) #60 {main} Next exception ‘Zend_Db_Statement_Exception’ with message ‘SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘date_founded’ in ‘field list’, query was: INSERT INTO `customer_grid_flat` (`entity_id`,`name`,`email`,`group_id`,`created_at`,`website_id`,`confirmation`,`created_in`,`dob`,`gender`,`taxvat`,`last_visit_at`,`date_founded`,`website_url`,`billing_full`,`billing_firstname`,`billing_lastname`,`billing_telephone`,`billing_postcode`,`billing_country_id`,`billing_region`,`billing_street`,`billing_city`,`billing_fax`,`billing_vat_id`,`billing_company`,`shipping_full`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `email` = VALUES(`email`), `group_id` = VALUES(`group_id`), `created_at` = VALUES(`created_at`), `website_id` = VALUES(`website_id`), `confirmation` = VALUES(`confirmation`), `created_in` = VALUES(`created_in`), `dob` = VALUES(`dob`), `gender` = VALUES(`gender`), `taxvat` = VALUES(`taxvat`), `last_visit_at` = VALUES(`last_visit_at`), `date_founded` = VALUES(`date_founded`), `website_url` = VALUES(`website_url`), `billing_full` = VALUES(`billing_full`), `billing_firstname` = VALUES(`billing_firstname`), `billing_lastname` = VALUES(`billing_lastname`), `billing_telephone` = VALUES(`billing_telephone`), `billing_postcode` = VALUES(`billing_postcode`), `billing_country_id` = VALUES(`billing_country_id`), `billing_region` = VALUES(`billing_region`), `billing_street` = VALUES(`billing_street`), `billing_city` = VALUES(`billing_city`), `billing_fax` = VALUES(`billing_fax`), `billing_vat_id` = VALUES(`billing_vat_id`), `billing_company` = VALUES(`billing_company`), `shipping_full` = VALUES(`shipping_full`)’ in /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Statement/Pdo.php:235 Stack trace: #0 /var/www/demos/magento2ce/vendor/magento/framework/DB/Statement/Pdo/Mysql.php(95): Zend_Db_Statement_Pdo->_execute(Array) #1 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Statement.php(303): Magento\Framework\DB\Statement\Pdo\Mysql->_execute(Array) #2 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Adapter/Abstract.php(480): Zend_Db_Statement->execute(Array) #3 /var/www/demos/magento2ce/vendor/magento/zendframework1/library/Zend/Db/Adapter/Pdo/Abstract.php(238): Zend_Db_Adapter_Abstract->query(‘INSERT INTO `cu…’, Array) #4 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(444): Zend_Db_Adapter_Pdo_Abstract->query(‘INSERT INTO `cu…’, Array) #5 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(499): Magento\Framework\DB\Adapter\Pdo\Mysql->_query(‘INSERT INTO `cu…’, Array) #6 /var/www/demos/magento2ce/vendor/magento/framework/DB/Adapter/Pdo/Mysql.php(1842): Magento\Framework\DB\Adapter\Pdo\Mysql->query(‘INSERT INTO `cu…’, Array) #7 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/SaveHandler/Grid.php(45): Magento\Framework\DB\Adapter\Pdo\Mysql->insertOnDuplicate(‘customer_grid_f…’, Array, Array) #8 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/SaveHandler/Grid.php(23): Magento\Framework\Indexer\SaveHandler\Grid->insertDocumentsForFilterable(Array, Array) #9 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/Action/Base.php(170): Magento\Framework\Indexer\SaveHandler\Grid->saveIndex(Array, Object(Magento\Customer\Model\ResourceModel\Customer\Collection)) #10 /var/www/demos/magento2ce/vendor/magento/framework/Indexer/Action/Base.php(202): Magento\Framework\Indexer\Action\Base->execute(Array) #11 /var/www/demos/magento2ce/vendor/magento/module-indexer/Model/Indexer.php(433): Magento\Framework\Indexer\Action\Base->executeRow(’10’) #12 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/Customer.php(1116): Magento\Indexer\Model\Indexer->reindexRow(’10’) #13 [internal function]: Magento\Customer\Model\Customer->reindex() #14 /var/www/demos/magento2ce/vendor/magento/framework/Model/ResourceModel/AbstractResource.php(94): call_user_func(Array) #15 /var/www/demos/magento2ce/vendor/magento/module-eav/Model/Entity/VersionControl/AbstractEntity.php(98): Magento\Framework\Model\ResourceModel\AbstractResource->commit() #16 /var/www/demos/magento2ce/vendor/magento/framework/Model/AbstractModel.php(615): Magento\Eav\Model\Entity\VersionControl\AbstractEntity->save(Object(Magento\Customer\Model\Backend\Customer)) #17 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/ResourceModel/CustomerRepository.php(195): Magento\Framework\Model\AbstractModel->save() #18 [internal function]: Magento\Customer\Model\ResourceModel\CustomerRepository->save(Object(Magento\Customer\Model\Data\Customer), NULL) #19 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(144): call_user_func_array(Array, Array) #20 /var/www/demos/magento2ce/var/generation/Magento/Customer/Model/ResourceModel/CustomerRepository/Interceptor.php(27): Magento\Customer\Model\ResourceModel\CustomerRepository\Interceptor->___callPlugins(‘save’, Array, Array) #21 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/AccountManagement.php(527): Magento\Customer\Model\ResourceModel\CustomerRepository\Interceptor->save(Object(Magento\Customer\Model\Data\Customer), NULL) #22 /var/www/demos/magento2ce/vendor/magento/module-customer/Model/AccountManagement.php(484): Magento\Customer\Model\AccountManagement->createAccountWithPasswordHash(Object(Magento\Customer\Model\Data\Customer), NULL, ”) #23 /var/www/demos/magento2ce/vendor/magento/module-customer/Controller/Adminhtml/Index/Save.php(236): Magento\Customer\Model\AccountManagement->createAccount(Object(Magento\Customer\Model\Data\Customer)) #24 /var/www/demos/magento2ce/var/generation/Magento/Customer/Controller/Adminhtml/Index/Save/Interceptor.php(24): Magento\Customer\Controller\Adminhtml\Index\Save->execute() #25 /var/www/demos/magento2ce/vendor/magento/framework/App/Action/Action.php(102): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->execute() #26 /var/www/demos/magento2ce/vendor/magento/module-backend/App/AbstractAction.php(226): Magento\Framework\App\Action\Action->dispatch(Object(Magento\Framework\App\Request\Http)) #27 [internal function]: Magento\Backend\App\AbstractAction->dispatch(Object(Magento\Framework\App\Request\Http)) #28 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array) #29 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->___callParent(‘dispatch’, Array) #30 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘adminAuthentica…’) #31 /var/www/demos/magento2ce/vendor/magento/module-backend/App/Action/Plugin/Authentication.php(143): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #32 [internal function]: Magento\Backend\App\Action\Plugin\Authentication->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #33 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #34 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘designLoader’) #35 /var/www/demos/magento2ce/vendor/magento/framework/App/Action/Plugin/Design.php(39): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #36 [internal function]: Magento\Framework\App\Action\Plugin\Design->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #37 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #38 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Custome…’, ‘dispatch’, Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Array, ‘adminMassaction…’) #39 /var/www/demos/magento2ce/vendor/magento/module-backend/App/Action/Plugin/MassactionKey.php(33): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http)) #40 [internal function]: Magento\Backend\App\Action\Plugin\MassactionKey->aroundDispatch(Object(Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #41 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array) #42 /var/www/demos/magento2ce/var/generation/Magento/Customer/Controller/Adminhtml/Index/Save/Interceptor.php(39): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->___callPlugins(‘dispatch’, Array, Array) #43 /var/www/demos/magento2ce/vendor/magento/framework/App/FrontController.php(55): Magento\Customer\Controller\Adminhtml\Index\Save\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http)) #44 [internal function]: Magento\Framework\App\FrontController->dispatch(Object(Magento\Framework\App\Request\Http)) #45 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(74): call_user_func_array(Array, Array) #46 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(70): Magento\Framework\App\FrontController\Interceptor->___callParent(‘dispatch’, Array) #47 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(63): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘install’) #48 /var/www/demos/magento2ce/vendor/magento/framework/Module/Plugin/DbStatusValidator.php(69): Magento\Framework\Interception\Chain\Chain->Magento\Framework\Interception\Chain\{closure}(Object(Magento\Framework\App\Request\Http)) #49 [internal function]: Magento\Framework\Module\Plugin\DbStatusValidator->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #50 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Chain/Chain.php(68): call_user_func_array(Array, Array) #51 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(136): Magento\Framework\Interception\Chain\Chain->invokeNext(‘Magento\Framewo…’, ‘dispatch’, Object(Magento\Framework\App\FrontController\Interceptor), Array, ‘storeCookieVali…’) #52 /var/www/demos/magento2ce/vendor/magento/module-store/Model/Plugin/StoreCookie.php(78): Magento\Framework\App\FrontController\Interceptor->Magento\Framework\Interception\{closure}(Object(Magento\Framework\App\Request\Http)) #53 [internal function]: Magento\Store\Model\Plugin\StoreCookie->aroundDispatch(Object(Magento\Framework\App\FrontController\Interceptor), Object(Closure), Object(Magento\Framework\App\Request\Http)) #54 /var/www/demos/magento2ce/vendor/magento/framework/Interception/Interceptor.php(141): call_user_func_array(Array, Array) #55 /var/www/demos/magento2ce/var/generation/Magento/Framework/App/FrontController/Interceptor.php(26): Magento\Framework\App\FrontController\Interceptor->___callPlugins(‘dispatch’, Array, Array) #56 /var/www/demos/magento2ce/vendor/magento/framework/App/Http.php(115): Magento\Framework\App\FrontController\Interceptor->dispatch(Object(Magento\Framework\App\Request\Http)) #57 /var/www/demos/magento2ce/vendor/magento/framework/App/Bootstrap.php(258): Magento\Framework\App\Http->launch() #58 /var/www/demos/magento2ce/index.php(38): Magento\Framework\App\Bootstrap->run(Object(Magento\Framework\App\Http)) #59 {main}

    Liked by 1 person

  3. Hi Max,
    Following your instructions I encountered some issues. Maybe you can help.

    First, unless I add a column for the new customer attribute to the customer_grid_flat table an exception is thrown when the adminhtml customer form is saved.

    After I add the field, no exception is thrown any more, but the value isn’t saved either (neither in the EAV nor in the grid table).
    Agter sme debugging, I found the problem to be that DataObjectHelper::_setDataValues() does not set the posted attribute value on the data object.
    It seems the new attribute has to be marked as a CustomAttribute or ExtensionAttribute.
    Since I like to use auto completion, I added the extension_attributes.xml file specifying that the new attribute is an extension attribute of type string for the CustomerInterface. The CustomerExtensionInterface can be correctly generated with the new setter and getter.
    Unfortunately now I think I have to add plugins to populate the ExtensionAttributes instance. But how should I do that for example in a CustomerRepositoryPlugin::afterGetId() plugin? Do I really have to load the attribute manually??
    It would be much more efficient to have it loaded together with the other EAV attributes.
    For complex attributes like the product gallery I see this makes sense, but I am currently missing a good way to handle the simpler use cases.

    It would be very valuable if you could shed some insight into this. Thanks!

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s