Today I will be focusing to extend the export product functionality by adding a website filter.
This original functionality can be seen by visiting the administrator’s:
System -> Data Transfer -> Export
Assuming you have a basic module already set (out of scope of this article),
adding a website filter can be achieved, in theory, by going through these two steps:
- Override the form to have a select option of website ids
- Override the model that actually prepares the collection whose data will be send to a .csv file
Eventually, we will have this look available
First we need to add new field to the field set.
The original field set is created in
https://gist.github.com/marjan7790/06a22fe29355acd192c5907eddb99e30
For those of you who wonder why this is the class that we need, consider these following hints:
- the admin url is /admin/export/ which to file system will translate into Controller/Adminhtml/Export/Index.php
- given the controller path, the layout handle on filesystem will translate into view/adminhtml/layout/adminhtml_export_index.xml
- there should not be too many files named adminhtml_export_index.xml in a default Magento module that may have “import” or “export” as part of its name
Anyway, the _prepareForm() should be overridden by adding
https://gist.github.com/marjan7790/52c963d0c7e9e24531d119164aeea1ef
We will start by telling Magento that we have preference over the original form. We will do this in etc/ di.xml by adding
https://gist.github.com/marjan7790/c3e64a2e86b3429391bf7253ae3700c7
Now lets create our file Form.php and add the _prepareForm() method as
https://gist.github.com/marjan7790/b7d120725382dbc31a5afdc9232f1490
However, we still need a protected property $websiteFactory
and we also need to override the __construct method so we can get an instance from the Website Factory object.
So, add this code in your class
https://gist.github.com/marjan7790/fc0e849f832357945615cb82868f9599
You might need to re-compile after this. After that, lets reopen our page and you should see the website selector.
Now, for “Entity Type” select “Products”, for “Website” the website of your choice and lets inspect the “Continue” button. You will notice that it leads to a link like admin/export/export.
This given, lets try to capture the website id selection in Magento\ImportExport\Controller\Adminhtml\Export\Export. Directly in this file (and you will revert code to original later ) add these lines inside the execute() method like
https://gist.github.com/marjan7790/3f828641a48bee2bdeddb42ad680733f
Then, set a break point and execute. You will notice:
$post becomes an array that has these keys: “form_key”, “frontend_label”, “attribute_code” and “export_filter” which is array on its own, 82 members long.
$params becomes an array that has the same keys $post has plus: “entity”, “file_format”, “fields_enclosure”.
Hey, this is good. We see in the $params that collected value from the FORM elements. But, sadly, not our “website_id”.
What gives?
Open, the layout handle adminhtml_export_index.xml in view/adminhtml/layout and look for this line
https://gist.github.com/marjan7790/1d659a55fab191466a51272d030264ff
Ok, that given, lets open file before.phtml, located in view/adminhtml/templates/export/form. This file extends the VarienExport.prototype by adding at least a couple of methods, out of which, interesting for us is
https://gist.github.com/marjan7790/4351fd28608371703201b5b8c6ae7889
right after which there is something like varienExport = new VarienExport();
This varienExport JS object is actually called on change on the “Entity Type” selection. Go back to the Form.php and notice the onchange value.
But, what about the larger picture? Magento uses RequireJs to load a component and execute it immediatelly. And all of this is done inside a .phtml file?
Magento certainly can find a way to surprise me. One should always try to be both aware of such subtleties and to simultaneously stay sane 🙂
Anyways, knowing this, we need to override the before.phtml now. Create new file adminhtml_export_index.xml inside view/adminhtml/layout and add this content
https://gist.github.com/marjan7790/2b6aabba3c7281a4da52321fa1003b5c
Later, create before.phtml inside view/adminhtml/templates and add this content
https://gist.github.com/marjan7790/4ad063bc41e1b59668565ddfd1a255de
I have the full code practically copied over just to add one small change inside getFile()
https://gist.github.com/marjan7790/fd8f6b41df9941f525737639c29bd86f
I don’t like this. Dear reader, please suggest an improvement?
Anyway, lets continue and again, for “Entity Type” select “Products”, for “Website” the website of your choice and lets click the “Continue” button. Sure enough, now we have a website_id inside the $params array. And, you can revert to the original code now and, for clarity, clear any break points.
Lets now continue to override the model that fetches the data.
With all said earlier, open Magento\ImportExport\Controller\Adminhtml\Export\Export and focus on this code
https://gist.github.com/marjan7790/ee04a8b841897c319ccd5ce63c6c2ba3
$model is of type \Magento\ImportExport\Model\Export and after this line
https://gist.github.com/marjan7790/f90678d8c8578462bc8034a288dd8f87
the protected property _data will have encapsulated whatever came from the getParams() from the Request object.
For those of you who wonder what happens here, the chain of inheritance is
\Magento\ImportExport\Model\Export extends \Magento\ImportExport\Model\AbstractModel extends \Magento\Framework\DataObject, and the last one, DataObject, produces the setData() method.
Back to what the controller does
https://gist.github.com/marjan7790/0a6008c7987908795c1a0a3312dc2aa3
So, we turn our attention to $model->export(). Open class Magento\ImportExport\Model\Export and you will see within the export()
https://gist.github.com/marjan7790/a418a0ef04146a53fa2c716abb4a1f5f
This line actually calls the export() method from Magento\CatalogImportExport\Model\Export\Product and within there is something like
https://gist.github.com/marjan7790/3488f1615889c437314b19b0685e7770
Now, we could override at two places
a. accept website_id
https://gist.github.com/marjan7790/37a77f22ce00e173ed83c572922e7996
b. add website filter
https://gist.github.com/marjan7790/1b47ec68a4fab007e258cbe028fa3347
Now, that should do it. So lets go ahead and edit etc/di.xml and add
https://gist.github.com/marjan7790/15b079ad2947dd47567593a839a9d538
And create the following files with the content from below
https://gist.github.com/marjan7790/ea6c314fba3d8ad8a554e62bfc72f988
and
https://gist.github.com/marjan7790/a93b070ae92166953807e13441b93c9f
Recompile if you must, but I guess this should do it.
Eventually, you can pick up the source code from
https://github.com/marjan7790/datatransfer
Finally, you are encouraged on your own to continue add an option to download data from all websites at once, as the original function was. Notice, we don’t have such option now.
Conclusion
Adding new behavior to some admin functionality in this case meant
- overriding the Form class
- overriding .phtml file that executes RequireJS module
for this step we needed to add our own layout handle, i.e. orverride how the handle works, and, we needed to override the RequireJS module
- overriding two Models from two different modules that would do the actual data export
Usually, one might expect to even override a Controller class, but there it goes, we went into overriding RequireJS module.
Regardless, keep in mind that it will be unlikely to achieve any similar requirements without overriding at least one Block and one Model class, whichever they are.
Thank you for reading.