How to Spilt Address in Customer grid CSV Export
-
28-02-2021 - |
Question
While exporting customers by going to Customer->All Customers-> Export CSV from grid, billing & shipping addresses are shown as full address. Instead of full address, I need to have separate columns like billing_address_street,billing_address_city,shipping_address_street,shipping_address_city
etc..,
I understandard these are coming from listing file module-customer\view\adminhtml\ui_component\customer_listing.xml
and mapped to a table.
Is there a way to split the full billing & shipping address during the export operation instead of adding new columns to the listing or
how to add address columns to customer_grid_flat
table and populate the data in csv file
Your ideas will be appreciated.
Solution
Part one: Understanding Magento
Let me first explain what's going on, and why you're getting the result you're getting:
If you look at the columns defined in module-customer\view\adminhtml\ui_component\customer_listing.xml
you'll see that stuff like billing_postcode
and billing_city
are already available / exported, only they're labeled als ZIP
and City
:
<column name="billing_postcode" sortOrder="70">
<settings>
<filter>text</filter>
<editor>
<editorType>text</editorType>
</editor>
<label translate="true">ZIP</label>
</settings>
</column>
<column name="billing_city" sortOrder="210">
<settings>
<filter>text</filter>
<editor>
<editorType>text</editorType>
</editor>
<label translate="true">City</label>
<visible>false</visible>
</settings>
</column>
The addresses that are exported in as Billing Address
and Shipping Address
are billing_full
and shipping_full
:
<column name="billing_full" sortOrder="150">
<settings>
<label translate="true">Billing Address</label>
<visible>false</visible>
</settings>
</column>
<column name="shipping_full" sortOrder="160">
<settings>
<label translate="true">Shipping Address</label>
<visible>false</visible>
</settings>
</column>
These values are not as-is stored in the default database, but are generated for convenience purposes (like listing in a grid, or exporting in your case) in the flat table. See indexer.xml
:
<fieldset name="billing" source="Magento\Customer\Model\ResourceModel\Address\Collection"
provider="Magento\Customer\Model\Indexer\Address\AttributeProvider">
<reference fieldset="customer" from="entity_id" to="default_billing"/>
<field name="full" xsi:type="searchable" dataType="text" handler="BillingAddressHandler"/>
...
And BillingAddressHandler
is a virtual type:
<virtualType name="BillingAddressHandler" type="Magento\Framework\Indexer\Handler\ConcatHandler">
<arguments>
<argument name="concatExpression" xsi:type="object">BillingAddressExpression</argument>
</arguments>
</virtualType>
Which has an expression that builds the space concatenated data you have in your column:
<virtualType name="BillingAddressExpression" type="Magento\Framework\DB\Sql\ConcatExpression">
<arguments>
<argument name="columns" xsi:type="array">
<item name="prefix" xsi:type="array">
<item name="tableAlias" xsi:type="string">billing</item>
<item name="columnName" xsi:type="string">street</item>
</item>
<item name="firstname" xsi:type="array">
<item name="tableAlias" xsi:type="string">billing</item>
<item name="columnName" xsi:type="string">city</item>
</item>
<item name="middlename" xsi:type="array">
<item name="tableAlias" xsi:type="string">billing</item>
<item name="columnName" xsi:type="string">region</item>
</item>
<item name="lastname" xsi:type="array">
<item name="tableAlias" xsi:type="string">billing</item>
<item name="columnName" xsi:type="string">postcode</item>
</item>
</argument>
</arguments>
</virtualType>
Part two: Implementing
Now, as you can see, the export is done from the flat grid (=indexed) table, so if you want to export additional columns, you have to make sure that these are available.
As you can see in module-customer/etc/indexer.xml
, there are actually already a lot of fields for the billing information, like postcode, city, street, etc. However, for shipping there is only the full
-field (which maps to shipping_full
):
<fieldset name="shipping" source="Magento\Customer\Model\ResourceModel\Address\Collection">
<reference fieldset="customer" from="entity_id" to="default_shipping"/>
<field name="full" xsi:type="searchable" dataType="text" handler="ShippingAddressHandler"/>
</fieldset>
<fieldset name="billing" source="Magento\Customer\Model\ResourceModel\Address\Collection"
provider="Magento\Customer\Model\Indexer\Address\AttributeProvider">
<reference fieldset="customer" from="entity_id" to="default_billing"/>
<field name="full" xsi:type="searchable" dataType="text" handler="BillingAddressHandler"/>
<field name="firstname" xsi:type="searchable" dataType="varchar"/>
<field name="lastname" xsi:type="searchable" dataType="varchar"/>
<field name="telephone" xsi:type="searchable" dataType="varchar"/>
<field name="postcode" xsi:type="searchable" dataType="varchar"/>
<field name="country_id" xsi:type="filterable" dataType="varchar"/>
<field name="region" xsi:type="searchable" dataType="varchar"/>
<field name="street" xsi:type="searchable" dataType="varchar"/>
<field name="city" xsi:type="searchable" dataType="varchar"/>
<field name="fax" xsi:type="searchable" dataType="varchar"/>
<field name="vat_id" xsi:type="searchable" dataType="varchar"/>
<field name="company" xsi:type="searchable" dataType="varchar"/>
</fieldset>
Now if you also want to have more shipping details indexed, just create your own module that hooks with it's own indexer.xml
into this one (XML files are merged together after all), and add you desired fields:
<fieldset name="shipping" source="Magento\Customer\Model\ResourceModel\Address\Collection">
<field name="postcode" xsi:type="filterable" dataType="varchar"/>
<field name="street" xsi:type="filterable" dataType="varchar"/>
<field name="city" xsi:type="filterable" dataType="varchar"/>
</fieldset>
Please note that at this point if you mark too much fields as xsi:type="searchable"
you might get a SQL error: Syntax error or access violation: 1070 Too many key parts specified; max 16 parts allowed
. This is because if a field is searchable it gets added to the index, and (depending on the configuration of your database) MySQL might not allow too big indexes.
The xsi:type="filterable"
is required for \Magento\Framework\Indexer\SaveHandler\Grid
to populate the tables during indexing.
Flush the cache and run the reindex command to populate the table now:
php bin/magento cache:flush
php bin/magento indexer:reindex customer_grid
Now that the indexer is setup and the table is indexed, you can see in the database the extra columns present in customer_grid_flat
with the proper data.
Part 3: Presenting
Now the last thing to do is add the columns to our module-customer/view/adminhtml/ui_component/customer_listing.xml
. For example:
<column name="shipping_postcode" sortOrder="1000">
<settings>
<filter>text</filter>
<editor>
<editorType>text</editorType>
</editor>
<label translate="true">Shipping ZIP</label>
</settings>
</column>
<column name="shipping_street" sortOrder="1001">
<settings>
<label translate="true">Shipping Street Address</label>
<visible>false</visible>
</settings>
</column>
<column name="shipping_city" sortOrder="1002">
<settings>
<filter>text</filter>
<editor>
<editorType>text</editorType>
</editor>
<label translate="true">Shipping City</label>
<visible>false</visible>
</settings>
</column>
And the result:
So as you can see, it's not a matter of manipulating the existing columns, but rather understanding where the data comes from and to add your own columns to it.
Hopefully this answer helps you.