9

I am using B2B extensions and I want to add custom attributes to company entity, As a text field and create 6-digit number randomly for that i'm using

$customcompanyattribute     = mt_rand(100000, 999999);

Attribute should be read-only and should generate value when a new company form is submitted.

I found that we don't have a mechanism like Magento has for adding attributes to customer CustomerSetupFactory, and using

Here is the work i did.

app/code/Custom/OverrideCompany/view/base/ui_component/company_form.xml

 <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <fieldset name="general">
        <field name="account_number" formElement="input">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                <item name="disabled" xsi:type="boolean">true</item>

                    <item name="source" xsi:type="string">Account Number</item>
                </item>
            </argument>
            <settings>
                <validation>
                    <rule name="required-entry" xsi:type="boolean">true</rule>
                </validation>
                <dataType>text</dataType>
                <label translate="true">Account Number</label>
                <dataScope>account_number</dataScope>
            </settings>
        </field>

    </fieldset>
</form>

app/code/Custom/OverrideCompany/Setup/InstallSchema.php

   <?php
namespace Custom\OverrideCompany\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\SchemaSetupInterface;
use Magento\Framework\Setup\ModuleContextInterface;

class InstallSchema implements InstallSchemaInterface {
    const COMPANY_TABLE_NAME = 'company';

    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context){
        $setup->startSetup();
        $table = $setup->getTable(self::COMPANY_TABLE_NAME);

        $columns = [
            'account_number',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 11,
                    'nullable' => true,
                    'comment' => 'Account Number'
            ],
        ];
        $connection = $setup->getConnection();
        foreach ($columns as $col_name => $col_array){
            $connection->addColumn($table, $col_name, $col_array);
        }
        $setup->endSetup();
    }
}

app/code/Custom/OverrideCompany/Setup/UpgradeSchema.php

<?php

namespace Custom\OverrideCompany\Setup;

use Magento\Framework\Setup\UpgradeSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;

class UpgradeSchema implements UpgradeSchemaInterface
{

    /**
     * {@inheritdoc}
     */
    public function upgrade(
        SchemaSetupInterface $setup,
        ModuleContextInterface $context
    ) {
        $installer = $setup;

        $installer->startSetup();
        if (version_compare($context->getVersion(), '1.0.0', '<')) {
                $installer->getConnection()->addColumn(
                $installer->getTable('company'),
                'account_number',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
                    'length' => 11,
                    'nullable' => true,
                    'comment' => 'Account Number'
                ]
            );
        }
        $installer->endSetup();
    }
}

enter image description here How to achieve it.

1
  • did you find a solution to this?
    – Asad Ullah
    Commented Jul 2, 2020 at 15:16

2 Answers 2

3

I am assuming that you have your custom column added in the database and custom field present on the admin company edit page in the General section.

  1. For reading the column value from the database into the field on the admin page:

Add in di.xml:

<!-- override core class in order to have the custom column show on the admin company edit page -->
<preference for="Magento\Company\Model\Company\DataProvider" type="MyVendor\MyModule\Model\Company\DataProvider" />

And in your DataProvider.php:

namespace MyVendor\MyModule\Model\Company;

class DataProvider extends \Magento\Company\Model\Company\DataProvider {
    
    public function getGeneralData(\Magento\Company\Api\Data\CompanyInterface $company) {
        
        $result = parent::getGeneralData($company);
        
        # add custom column value to the General data section so that the value populates the custom field on the admin company edit page
        $result['my_custom_column'] = $company->getMyCustomColumn();
        
        return $result;
    }
}

  1. For saving the field into the database custom column:

Add this code in di.xml:

<!-- override core class by plugin in order to save value from the admin company edit page into the custom column my_custom_column -->
<type name="Magento\Company\Model\CompanyRepository">
    <plugin name="myvendor_mymodule_after_save_company" type="MyVendor\MyModule\Plugin\Model\CompanyRepository" />
</type>

And in your CompanyRepository.php:

namespace MyVendor\MyModule\Plugin\Model;

class CompanyRepository {
    
    public function afterSave(\Magento\Company\Model\CompanyRepository $companyRepository, \Magento\Company\Api\Data\CompanyInterface $company) {
        
        $my_custom_column = 'some value here';
        
        $company->setMyCustomColumn($my_custom_column);
        
        $company->save();
        
        return $company;
    }
}
2
  • on your DataProvider.php, it should be $result['my_custom_column'] = $company->getMyCustomColumn();. That is getMyCustomColumn() instead of setMyCustomColumn(). I tried to make an edit, but it requires 6 changes, and the rest is good and worked great. Thanks for posting. Commented Jan 6, 2021 at 15:55
  • It is working fine. Commented Jan 9, 2022 at 11:49
2

This looks like an older thread, but the following worked for me.

You can also add your own extension attribute with its own data model/table, and use the db_schema.xml file instead of an install/upgrade script.

Something like the following:

  1. extension_attributes.xml
<?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd">
        <extension_attributes for="Magento\Company\Api\Data\CompanyInterface">
            <attribute code="your_attribute_code" type="string">
                <join reference_table="your_custom_table" reference_field="reference_in_new_table" join_on_field="entity_id">
                    <field>extension_attribute_field</field>
                </join>
            </attribute>
        </extension_attributes>
    </config>

Mind the join in the above code. It will make Magento add your extension attribute getters and setters to the generated extension classes upon code generation (also worth mentioning the vendor/magento/framework/Model/AbstractExtensibleModel.php class, it helps a lot with understanding how the extension attributes work). This is pretty neat.

  1. You can add your custom table to the db with a db_schema.xml (don't forget to generate a schema whitelist!):
    <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/etc/schema.xsd">
        <table name="custom_table_name" resource="default" engine="innodb" comment="Company Custom Table">
            <column xsi:type="int" name="id" padding="10" unsigned="true" nullable="false" identity="true" comment="Id"/>
            <column xsi:type="varchar" name="extension_attribute_field" length="255" nullable="true" comment="YOUR EXTENSION ATTRIBUTE"/>
            <column xsi:type="int" name="company_id" padding="10" unsigned="true" nullable="false"  comment="Company Id"/>
            <constraint xsi:type="primary" referenceId="PRIMARY">
                <column name="id"/>
            </constraint>
            <constraint xsi:type="foreign" referenceId="CUSTOM_TABLE_ID_COMPANY_ENTITY_ID"
                        table="custom_table_name" column="company_id" referenceTable="company"
                        referenceColumn="entity_id" onDelete="CASCADE"/>
        </table>
    </schema>
  1. Then you can add your interfaces and model+resource model to retrieve the data from this table. I won't provide that code here.

  2. Following the above link, you can add your plugins in your module's di.xml:

    <?xml version="1.0"?>
    <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
        <type name="Magento\Company\Api\CompanyRepositoryInterface">
            <plugin name="company_extension_attribute" type="MyCompany\MyModule\Plugin\CompanyRepository" />
        </type>
    
        <type name="Magento\Company\Model\SaveHandlerPool">
            <arguments>
                <argument name="handlers" xsi:type="array">
                    <item name="customHandler" xsi:type="object">MyCompany\MyModule\Model\SaveHandler\CustomHandler</item>
                </argument>
            </arguments>
        </type>
    
        <type name="Magento\Company\Model\Company\DataProvider">
            <plugin name="company_data_provider_information" type="MyCompany\MyModule\Plugin\CompanyDataProvider" />
        </type>
    </config>

For the CompanyRepository plugin, please find the code on that link above that I provided (except for the afterSave method). There are two things here that need some explanation. First, since the Company object uses a handler pool for saving different type of information, we are adding another saveHandler that will take care of our extra piece of info. If you take a look at the vendor/magento/module-company/etc/di.xml file, you should find this code:

<type name="Magento\Company\Model\SaveHandlerPool">
            <arguments>
                <argument name="handlers" xsi:type="array">
                    <item name="defaultRole" xsi:type="object">\Magento\Company\Model\SaveHandler\DefaultRole</item>
                    <item name="superUser" xsi:type="object">\Magento\Company\Model\SaveHandler\SuperUser</item>
                    <item name="salesRepresentative" xsi:type="object">\Magento\Company\Model\SaveHandler\SalesRepresentative</item>
                    <item name="companyStatus" xsi:type="object">\Magento\Company\Model\SaveHandler\CompanyStatus</item>
                    <item name="customerGroup" xsi:type="object">\Magento\Company\Model\SaveHandler\CustomerGroup</item>
                </argument>
            </arguments>
    </type>

So instead of creating a plugin method to do an afterSave, it makes more sense to create another handler that will save the new information (you will just need to implement the execute method there, take one of the already present handlers as an example). For me it looked like this:

MyCompany/MyModule/Model/SaveHandler/CustomHandler

    class CustomHandler implements \Magento\Company\Model\SaveHandlerInterface
    {

        private $customDataFactory;
        private $customDataRepository;
    
    
        public function __construct(
            MyCompany\MyModule\Model\CustomDataFactory $customDataFactory,
            MyCompany\MyModule\Model\CustomDataRepository $customDataRepository
        )
        {
            $this->customDataFactory = $customDataFactory;
            $this->customDataRepository = $customDataRepository;
        }
    
        public function execute(\Magento\Company\Api\Data\CompanyInterface $company, \Magento\Company\Api\Data\CompanyInterface $initialCompany)
        {
            if (!$initialCompany->getId()) {
                $customData = $this->customDataFactory->create();
                $customData->setCompanyId($company->getId());
            } else {
                $customData = $this->customDataRepository->getByCompanyId($initialCompany->getId());
            }
            $customData->setCustomAttribute($company->getExtensionAttributes()->getCustomAttribute());
            $this->customDataRepository->save($customData);
        }
    }
  1. b. The other thing that would need some explanation is the MyCompany\MyModule\Plugin\CompanyDataProvider. This is where you give the data to your ui component. You can override the default Magento data provider for the company (as stated in the previous answers) or hook into the save function with a plugin or find another method to add the relevant information to the data provider. I used an after plugin, there might be (probably is) a better way to do this. However, it would look something like this:

class CompanyDataProvider
{
    protected $customDataRepository;

    public function __construct(
        \MyCompany\MyModule\Api\CustomDataRepository $customDataRepository
    )
    {
        $this->customDataRepository = $customDataRepository;
    }

        public function afterGetInformationData(
    \Magento\Company\Model\Company\DataProvider $subject, $result, \Magento\Company\Api\Data\CompanyInterface $company){
                $data = $this->customDataRepository->getByCompanyId($company->getId());
                if ($company->getId() && $data->getId()){
                    $result['extension_attributes']['custom_attribute'] = $data->getCustomAttribute();
                    return $result;
                }
                $result['extension_attributes']['custom_attribute'] = '';
        
                return $result;
            }
  1. And finally you can add your field to the information fieldset on the company form in the MyCompany/MyModule/view/base/ui_component/company_form.xml file:
    <?xml version="1.0" encoding="UTF-8"?>
    <form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
        <fieldset name="information" sortOrder="20">
            <field name="extension_attributes.custom_attribute" formElement="input" sortOrder="10">
                <argument name="data" xsi:type="array">
                    <item name="config" xsi:type="array">
                        <item name="source" xsi:type="string">custom_attribute</item>
                    </item>
                </argument>
                <settings>
                    <dataType>text</dataType>
                    <label translate="true">Custom Attribute Value</label>
                </settings>
            </field>
        </fieldset>
    </form>

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.