{"id":12581,"date":"2016-07-14T08:43:07","date_gmt":"2016-07-14T15:43:07","guid":{"rendered":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-in-orocommerce"},"modified":"2018-01-26T03:42:52","modified_gmt":"2018-01-26T11:42:52","slug":"customizing-data-grids-orocommerce","status":"publish","type":"post","link":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/","title":{"rendered":"Customizing Data Grids in OroCommerce"},"content":{"rendered":"<p>Most business application users have to deal with significant amounts of data on a daily basis. Thus, efficiently navigating through large data sets becomes a must have requirement and OroCommerce is not an exception. The application users must be able to easily filter, sort, and search through thousands (or millions) of records, usually represented in the form of a data grid on a page.<\/p>\n<p>Today,\u00a0we will focus on customizing the existing data grids provided by the core product. If you are\u00a0not familiar with OroPlatform data grids, you may find it helpful to check the Oro Cookbook articles on <a href=\"https:\/\/www.orocrm.com\/doc\/current\/cookbook\/entities\/datagrid\" target=\"_blank\">how to create a simple data grid<\/a>, and <a href=\"https:\/\/www.orocrm.com\/doc\/current\/cookbook\/how-to-pass-request-parameter-to-grid\" target=\"_blank\">how to pass request parameters to your data grid<\/a>. The <a href=\"https:\/\/www.orocrm.com\/doc\/current\/reference\/format\/datagrid\" target=\"_blank\">datagrid.yml configuration reference<\/a> and the <a href=\"https:\/\/github.com\/orocrm\/platform\/tree\/master\/src\/Oro\/Bundle\/DataGridBundle\/Resources\/doc\" target=\"_blank\">OroDataGridBundle documentation<\/a> contain additional useful information.<\/p>\n<p><a href=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1.png\" rel=\"attachment wp-att-1652\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1654 size-medium\" src=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1.png\" alt=\"Customizing Data Grids in OroCommerce\" width=\"720\" height=\"346\" srcset=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1.png 952w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1-720x346.png 720w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1-768x369.png 768w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1-760x365.png 760w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/grid-example-1-360x173.png 360w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><\/a><\/p>\n<h3>Data Sources<\/h3>\n<p>A data grid is usually used to visualize some data coming from a data source. OroDataGridBundle allows for use of various data sources for data grids, and includes the ORM data source adapter out of the box. It is possible to <a href=\"https:\/\/github.com\/orocrm\/platform\/blob\/master\/src\/Oro\/Bundle\/DataGridBundle\/Resources\/doc\/backend\/datasources.md\" target=\"_blank\">implement your own data source adapters<\/a> as well.<\/p>\n<p>The ORM data source types allow for database query specification, sorters and filters definitions to be specified in the data grid configuration. Data grid configuration can be supplied by a developer in YAML format. By convention, the datagrid.yml files placed in Resources\/config folders of any application bundle are processed automatically. All supported data source configuration options that can be used in data source configuration section are described in the <a href=\"https:\/\/github.com\/orocrm\/platform\/blob\/master\/src\/Oro\/Bundle\/DataGridBundle\/Resources\/doc\/backend\/datasources.md\" target=\"_blank\">documentation<\/a>.<\/p>\n<h3>Inner Workings of Data Grids<\/h3>\n<p>Data grids in Oro applications are highly customizable. It is possible to modify an existing grid in order to fetch more data than was originally defined in the grid configuration. In this article, we will retrieve and present to a user some additional data in the existing <a href=\"https:\/\/github.com\/laboro\/dev\/blob\/56a4044c13cde93a31dfaac373de428962a13f56\/package\/commerce\/src\/OroB2B\/Bundle\/ProductBundle\/Resources\/config\/datagrid.yml#L101\" target=\"_blank\">products-grid<\/a>.<\/p>\n<p>And before we start customizing it, let&#8217;s take a deeper look into two aspects of how the data grids actually work:<\/p>\n<ul>\n<li>building and configuring a new DataGrid instance<\/li>\n<li>fetching data<\/li>\n<\/ul>\n<h4>Building Grids<\/h4>\n<p><a href=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml.jpg\" rel=\"attachment wp-att-1630\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1630 size-medium\" src=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml.jpg\" alt=\"OroDataGridBundle base class diagram\" width=\"720\" height=\"438\" srcset=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml.jpg 1581w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml-720x438.jpg 720w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml-768x467.jpg 768w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml-723x440.jpg 723w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/datagrid_base_uml-345x210.jpg 345w\" sizes=\"auto, (max-width: 720px) 100vw, 720px\" \/><\/a><\/p>\n<p>DatagridBuilder class is responsible for creating and configuring a data grid object and its data source. This is how the build method is processing the grid configuration internally:<\/p>\n<p><a href=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow.png\" rel=\"attachment wp-att-1631\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-1631 size-medium\" src=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow.png\" alt=\"Data grid build process\" width=\"422\" height=\"460\" srcset=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow.png 670w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow-422x460.png 422w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow-404x440.png 404w, https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2016\/07\/build-flow-193x210.png 193w\" sizes=\"auto, (max-width: 422px) 100vw, 422px\" \/><\/a><\/p>\n<p>Imagine that you need to show a list of related price lists for every product record in the product grid. You want it to be displayed in a separate column with a multi-select filter. Also you want to add one more column to display owner of each of the product records.<\/p>\n<p>One of the possible ways to customize this grid would be through events triggered by the system during the grid build process and when the data is fetched from the data source.<\/p>\n<p>There are several events triggered before processing the data grid configuration files. In this case, a\u00a0good choice is the onBuildBefore event. By listening to this event you can add new elements to the grid configuration or modify already existing configuration in your event listener.<\/p>\n<blockquote><p>More information about grid column definition configuration options is available in the <a href=\"https:\/\/github.com\/orocrm\/platform\/blob\/master\/src\/Oro\/Bundle\/DataGridBundle\/Resources\/doc\/backend\/datagrid.md#columns-and-properties\" target=\"_blank\">documentation<\/a>.<\/p><\/blockquote>\n<p>The Product entity has a many-to-one relation with the Business Unit entity, so in order to add the owner column to the grid and load the owner data from the data source, you should modify its query configuration by adding additional join and select parts.<\/p>\n<h4>Fetching Data<\/h4>\n<p>However, the retrieving data for the price lists column is a little bit more complicated, because the Product entity has many-to-many relation with price lists, and the join result will contain duplicate rows. In such situations or when some other dynamic data should be included into the query results, a possible solution would be data modification after the rows were fetched from the data source.<\/p>\n<p>This is how the data retrieval works in general:<\/p>\n<p><a href=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/orm-result-1.png\" rel=\"attachment wp-att-1632\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-1632\" src=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/orm-result-1.png\" alt=\"Data grid records retrieval\" width=\"550\" height=\"455\" \/><\/a><\/p>\n<p>So in our customization, we will fetch the price list data in a separate query, and then we will attach this data to each of the product records in the grid.<\/p>\n<h3>Product Grid Customization<\/h3>\n<p>The resulting implementation of the ProductsGridListener may look similar to this example:<\/p>\n<pre class=\"lang:php decode:true \">class ProductsGridListener\r\n{\r\n   ... \r\n\r\n   \/**\r\n    * @param BuildBefore $event\r\n    *\/\r\n   public function onBuildBefore(BuildBefore $event)\r\n   {\r\n       $datagridConfiguration = $event-&gt;getConfig();\r\n       $this-&gt;addBusinessUnitColumn($datagridConfiguration);\r\n       $this-&gt;addPriceListsColumn($datagridConfiguration);\r\n   }\r\n\r\n   \/**\r\n    * @param OrmResultAfter $event\r\n    *\/\r\n   public function onResultAfter(OrmResultAfter $event)\r\n   {\r\n       $records = $event-&gt;getRecords();\r\n       $this-&gt;addPriceListsToRecords($records);\r\n   }\r\n\r\n   \/**\r\n    * @param DatagridConfiguration $datagridConfiguration\r\n    *\/\r\n   protected function addPriceListsColumn(DatagridConfiguration $datagridConfiguration)\r\n   {\r\n       $column = [\r\n           'label' =&gt; 'Price Lists',\r\n           'type' =&gt; 'twig',\r\n           'template' =&gt; 'OroB2BPricingBundle:Datagrid:Column\/price_lists.html.twig',\r\n           'frontend_type' =&gt; 'html',\r\n           'renderable' =&gt; true,\r\n       ];\r\n       $datagridConfiguration-&gt;addColumn('price_lists', $column);\r\n   }\r\n\r\n   \/**\r\n    * @param DatagridConfiguration $datagridConfiguration\r\n    *\/\r\n   protected function addBusinessUnitColumn(DatagridConfiguration $datagridConfiguration)\r\n   {\r\n       $datagridConfiguration-&gt;joinTable(\r\n           'left',\r\n           [\r\n               'join' =&gt; BusinessUnit::class,\r\n               'alias' =&gt; 'business_unit',\r\n               'conditionType' =&gt; 'WITH',\r\n               'condition' =&gt; 'product.owner = business_unit',\r\n           ]\r\n       );\r\n\r\n       $column = [\r\n           'label' =&gt; 'Owner'\r\n       ];\r\n\r\n       \/\/ column name should be ther same as the field alias in the select query\r\n       $datagridConfiguration-&gt;addColumn('owner', $column, 'business_unit.name as owner');\r\n   }\r\n\r\n   \/**\r\n    * @param ResultRecord[] $records\r\n    * @throws DoctrineORMORMException\r\n    *\/\r\n   protected function addPriceListsToRecords(array $records)\r\n   {\r\n       $repository = $this-&gt;registry-&gt;getRepository(PriceListToProduct::class);\r\n       \/** @var EntityManager $objectManager *\/\r\n       $objectManager = $this-&gt;registry-&gt;getManager();\r\n\r\n       $products = [];\r\n       foreach ($records as $record) {\r\n           $products[] = $objectManager-&gt;getReference(Product::class, $record-&gt;getValue('id'));\r\n       }\r\n\r\n       $priceLists = [];\r\n       foreach ($repository-&gt;findBy(['product' =&gt; $products]) as $item) {\r\n           $priceLists[$item-&gt;getProduct()-&gt;getId()][] = $item-&gt;getPriceList();\r\n       }\r\n\r\n       \/** @var ResultRecord $record *\/\r\n       foreach ($records as $record) {\r\n           $id = $record-&gt;getValue('id');\r\n           $products[] = $objectManager-&gt;getReference(Product::class, $id);\r\n\r\n           $record-&gt;addData(['price_lists' =&gt; $priceLists[$id]]);\r\n       }\r\n   }\r\n}\r\n<\/pre>\n<p>We will need to register this event listener in the service container:<\/p>\n<pre class=\"lang:yaml decode:true \">grid_event_listener.product:\r\n    class: 'OroBundleCustomGridBundleDatagridProductsGridListener'\r\n    arguments:\r\n        - @doctrine\r\n    tags:\r\n        - { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before.products-grid, method: onBuildBefore }\r\n        - { name: kernel.event_listener, event: oro_datagrid.orm_datasource.result.after.products-grid, method: onResultAfter }\r\n<\/pre>\n<p>After the application cache is refreshed (or immediately in the dev mode) two new columns will appear in the product grid.<\/p>\n<h3>Custom Filters<\/h3>\n<p>Our second customization task will be to add filters for the newly introduced column.<\/p>\n<p>In most cases, the <a href=\"https:\/\/github.com\/orocrm\/platform\/blob\/master\/src\/Oro\/Bundle\/FilterBundle\/Resources\/doc\/reference\/filter_form_types.md\" target=\"_blank\">built-in filters<\/a> would work just perfectly. But in the case of the price lists column, a custom filter is required. The purpose of this filter will be to modify the data retrieval query depending on the filter values entered by a user.<\/p>\n<pre class=\"lang:php decode:true \">class ProductPriceListsFilter extends EntityFilter\r\n{\r\n    \/**\r\n     * @var RegistryInterface\r\n     *\/\r\n    protected $registry;\r\n\r\n    \/**\r\n     * @inheritdoc\r\n     *\/\r\n    public function apply(FilterDatasourceAdapterInterface $ds, $data)\r\n    {\r\n        \/** @var array $data *\/\r\n        $data = $this-&gt;parseData($data);\r\n        if (!$data) {\r\n            return false;\r\n        }\r\n\r\n        $this-&gt;restrictByPriceList($ds, $data['value']);\r\n\r\n        return true;\r\n    }\r\n\r\n    \/**\r\n     * @param RegistryInterface $registry\r\n     *\/\r\n    public function setRegistry(RegistryInterface $registry)\r\n    {\r\n        $this-&gt;registry = $registry;\r\n    }\r\n\r\n    \/**\r\n     * @param OrmFilterDatasourceAdapter|FilterDatasourceAdapterInterface $ds\r\n     * @param array $priceLists\r\n     *\/\r\n    public function restrictByPriceList($ds, array $priceLists)\r\n    {\r\n        $queryBuilder = $ds-&gt;getQueryBuilder();\r\n        $parentAlias = $queryBuilder-&gt;getRootAliases()[0];\r\n        $parameterName = $ds-&gt;generateParameterName('price_lists');\r\n\r\n        $repository = $this-&gt;registry-&gt;getRepository(PriceListToProduct::class);\r\n        $subQueryBuilder = $repository-&gt;createQueryBuilder('relation');\r\n        $subQueryBuilder-&gt;where(\r\n            $subQueryBuilder-&gt;expr()-&gt;andX(\r\n                $subQueryBuilder-&gt;expr()-&gt;eq('relation.product', $parentAlias),\r\n                $subQueryBuilder-&gt;expr()-&gt;in('relation.priceList', \":$parameterName\")\r\n            )\r\n        );\r\n\r\n        $queryBuilder-&gt;andWhere($subQueryBuilder-&gt;expr()-&gt;exists($subQueryBuilder-&gt;getQuery()-&gt;getDQL()));\r\n        $queryBuilder-&gt;setParameter($parameterName, $priceLists);\r\n    }\r\n}\r\n<\/pre>\n<p>Our new filter should be registered in the service container with the oro_filter.extension.orm_filter.filter tag:<\/p>\n<pre class=\"lang:yaml decode:true \">grid_filter.price_lists:\r\n    class: 'OroBundleCustomGridBundleFilterProductPriceListsFilter'\r\n    public: false\r\n    arguments:\r\n        - '@form.factory'\r\n        - '@oro_filter.filter_utility'\r\n    calls:\r\n        - [setRegistry, ['@doctrine']]\r\n    tags:\r\n        - { name: oro_filter.extension.orm_filter.filter, type: product-price-lists }\r\n<\/pre>\n<p>This filter can be added to the grid configuration similarly to how we added new columns &#8211; in an event listener. Thus the final implementation of the ProductsGridListener would look like this:<\/p>\n<pre class=\"lang:php decode:true \">class ProductsGridListener\r\n{\r\n    \/**\r\n     * @var RegistryInterface\r\n     *\/\r\n    protected $registry;\r\n\r\n    \/**\r\n     * @param RegistryInterface $registry\r\n     *\/\r\n    public function __construct(RegistryInterface $registry)\r\n    {\r\n        $this-&gt;registry = $registry;\r\n    }\r\n\r\n    \/**\r\n     * @param BuildBefore $event\r\n     *\/\r\n    public function onBuildBefore(BuildBefore $event)\r\n    {\r\n        $datagridConfiguration = $event-&gt;getConfig();\r\n        $this-&gt;addBusinessUnitColumn($datagridConfiguration);\r\n        $this-&gt;addPriceListsColumn($datagridConfiguration);\r\n        $this-&gt;addPriceListsFilter($datagridConfiguration);\r\n    }\r\n\r\n    \/**\r\n     * @param OrmResultAfter $event\r\n     *\/\r\n    public function onResultAfter(OrmResultAfter $event)\r\n    {\r\n        $records = $event-&gt;getRecords();\r\n        $this-&gt;addPriceListsToRecords($records);\r\n    }\r\n\r\n    \/**\r\n     * @param DatagridConfiguration $datagridConfiguration\r\n     *\/\r\n    protected function addPriceListsColumn(DatagridConfiguration $datagridConfiguration)\r\n    {\r\n        $column = [\r\n            'label' =&gt; 'Price Lists',\r\n            'type' =&gt; 'twig',\r\n            'template' =&gt; 'OroCustomGridBundle:Datagrid:Column\/price_lists.html.twig',\r\n            'frontend_type' =&gt; 'html',\r\n            'renderable' =&gt; true,\r\n        ];\r\n        $datagridConfiguration-&gt;addColumn('price_lists', $column);\r\n    }\r\n\r\n    \/**\r\n     * @param DatagridConfiguration $datagridConfiguration\r\n     *\/\r\n    protected function addBusinessUnitColumn(DatagridConfiguration $datagridConfiguration)\r\n    {\r\n        $datagridConfiguration-&gt;joinTable(\r\n            'left',\r\n            [\r\n                'join' =&gt; BusinessUnit::class,\r\n                'alias' =&gt; 'business_unit',\r\n                'conditionType' =&gt; 'WITH',\r\n                'condition' =&gt; 'product.owner = business_unit',\r\n            ]\r\n        );\r\n\r\n        $column = [\r\n            'label' =&gt; 'Owner'\r\n        ];\r\n\r\n        \/\/ column name should be ther same as the field alias in the select query\r\n        $datagridConfiguration-&gt;addColumn('owner', $column, 'business_unit.name as owner');\r\n    }\r\n\r\n    \/**\r\n     * @param DatagridConfiguration $datagridConfiguration\r\n     *\/\r\n    protected function addPriceListsFilter(DatagridConfiguration $datagridConfiguration)\r\n    {\r\n        $filter = [\r\n            'type' =&gt; 'product-price-lists',\r\n            'data_name' =&gt; 'price_lists',\r\n            'options' =&gt; [\r\n                'field_type' =&gt; 'entity',\r\n                'field_options' =&gt; [\r\n                    'class' =&gt; PriceList::class,\r\n                    'property' =&gt; 'name',\r\n                    'multiple' =&gt; true\r\n                ]\r\n            ]\r\n        ];\r\n\r\n        $datagridConfiguration-&gt;addFilter('price_lists', $filter);\r\n    }\r\n\r\n    \/**\r\n     * @param ResultRecord[] $records\r\n     * @throws DoctrineORMORMException\r\n     *\/\r\n    protected function addPriceListsToRecords(array $records)\r\n    {\r\n        $repository = $this-&gt;registry-&gt;getRepository(PriceListToProduct::class);\r\n        \/** @var EntityManager $objectManager *\/\r\n        $objectManager = $this-&gt;registry-&gt;getManager();\r\n\r\n        $products = [];\r\n        foreach ($records as $record) {\r\n            $products[] = $objectManager-&gt;getReference(Product::class, $record-&gt;getValue('id'));\r\n        }\r\n\r\n        $priceLists = [];\r\n        foreach ($repository-&gt;findBy(['product' =&gt; $products]) as $item) {\r\n            $priceLists[$item-&gt;getProduct()-&gt;getId()][] = $item-&gt;getPriceList();\r\n        }\r\n\r\n        \/** @var ResultRecord $record *\/\r\n        foreach ($records as $record) {\r\n            $id = $record-&gt;getValue('id');\r\n            $products[] = $objectManager-&gt;getReference(Product::class, $id);\r\n\r\n            $record-&gt;addData(['price_lists' =&gt; $priceLists[$id]]);\r\n        }\r\n    }\r\n}\r\n<\/pre>\n<p>A fully working example, organized into a custom bundle is attached. In order to add this bundle to your application please extract the content of the zip archive into a source code directory that is recognized by your composer autoload settings.<\/p>\n    <a class=\"orodmDownload-dialog-link zip -Download\" href=\"#download\"\n       title=\"Download  Download\"\n       data-id=\"2508\"\n    ><span>Download<\/span><\/a>\n\n<p>Please let us know if you find this article useful. We hope it can help the developers in the OroCommerce community to further improve user experience and make dealing with data even more enjoyable and fun.<\/p>\n<p>We will appreciate your feedback in our <a href=\"https:\/\/forum.oroinc.com\/orocommerce\/\" target=\"_blank\">forums<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Most business application users have to deal with significant amounts of data on a daily basis. Thus, efficiently navigating through large data sets becomes a must have requirement and OroCommerce is not an exception. The application users must be able to easily filter, sort, and search through thousands (or millions) of records, usually represented in [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":12541,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"ep_exclude_from_search":false,"footnotes":""},"categories":[132],"tags":[103],"class_list":{"0":"post-12581","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-developers-digest","8":"tag-orocommerce"},"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.8 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Customizing Data Grids in OroCommerce | OroCommerce<\/title>\n<meta name=\"description\" content=\"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Customizing Data Grids in OroCommerce | OroCommerce\" \/>\n<meta property=\"og:description\" content=\"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\" \/>\n<meta property=\"og:site_name\" content=\"OroCommerce\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/OroCommerce-333319140210515\/\" \/>\n<meta property=\"article:published_time\" content=\"2016-07-14T15:43:07+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-01-26T11:42:52+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"760\" \/>\n\t<meta property=\"og:image:height\" content=\"440\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Oro Team\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@orocommerce\" \/>\n<meta name=\"twitter:site\" content=\"@orocommerce\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Oro Team\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"8 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\"},\"author\":{\"name\":\"Oro Team\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/person\/420bb6bc049bf6da18b714558557558c\"},\"headline\":\"Customizing Data Grids in OroCommerce\",\"datePublished\":\"2016-07-14T15:43:07+00:00\",\"dateModified\":\"2018-01-26T11:42:52+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\"},\"wordCount\":914,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#organization\"},\"image\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png\",\"keywords\":[\"OroCommerce\"],\"articleSection\":[\"Developers' Digest\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\",\"url\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\",\"name\":\"Customizing Data Grids in OroCommerce | OroCommerce\",\"isPartOf\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png\",\"datePublished\":\"2016-07-14T15:43:07+00:00\",\"dateModified\":\"2018-01-26T11:42:52+00:00\",\"description\":\"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.\",\"breadcrumb\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage\",\"url\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png\",\"contentUrl\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png\",\"width\":760,\"height\":440},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/oroinc.com\/b2b-ecommerce\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Developers' Digest\",\"item\":\"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/category\/developers-digest\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"Customizing Data Grids in OroCommerce\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#website\",\"url\":\"https:\/\/oroinc.com\/b2b-ecommerce\/\",\"name\":\"OroCommerce\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/oroinc.com\/b2b-ecommerce\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#organization\",\"name\":\"Oro Inc.\",\"url\":\"https:\/\/oroinc.com\/b2b-ecommerce\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2019\/11\/Oro-OLogo.png\",\"contentUrl\":\"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2019\/11\/Oro-OLogo.png\",\"width\":40,\"height\":40,\"caption\":\"Oro Inc.\"},\"image\":{\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/OroCommerce-333319140210515\/\",\"https:\/\/x.com\/orocommerce\",\"https:\/\/www.youtube.com\/channel\/UClxsA8HS9KGEEsvFRn7JkvQ\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/person\/420bb6bc049bf6da18b714558557558c\",\"name\":\"Oro Team\",\"sameAs\":[\"http:\/\/www.orocrm.com\/\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Customizing Data Grids in OroCommerce | OroCommerce","description":"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/","og_locale":"en_US","og_type":"article","og_title":"Customizing Data Grids in OroCommerce | OroCommerce","og_description":"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.","og_url":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/","og_site_name":"OroCommerce","article_publisher":"https:\/\/www.facebook.com\/OroCommerce-333319140210515\/","article_published_time":"2016-07-14T15:43:07+00:00","article_modified_time":"2018-01-26T11:42:52+00:00","og_image":[{"width":760,"height":440,"url":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png","type":"image\/png"}],"author":"Oro Team","twitter_card":"summary_large_image","twitter_creator":"@orocommerce","twitter_site":"@orocommerce","twitter_misc":{"Written by":"Oro Team","Est. reading time":"8 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#article","isPartOf":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/"},"author":{"name":"Oro Team","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/person\/420bb6bc049bf6da18b714558557558c"},"headline":"Customizing Data Grids in OroCommerce","datePublished":"2016-07-14T15:43:07+00:00","dateModified":"2018-01-26T11:42:52+00:00","mainEntityOfPage":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/"},"wordCount":914,"commentCount":0,"publisher":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#organization"},"image":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage"},"thumbnailUrl":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png","keywords":["OroCommerce"],"articleSection":["Developers' Digest"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/","url":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/","name":"Customizing Data Grids in OroCommerce | OroCommerce","isPartOf":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#website"},"primaryImageOfPage":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage"},"image":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage"},"thumbnailUrl":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png","datePublished":"2016-07-14T15:43:07+00:00","dateModified":"2018-01-26T11:42:52+00:00","description":"OroCommerce enables users to easily filter, sort, and search through thousands of records, usually represented in the form of a data grid.","breadcrumb":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#primaryimage","url":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png","contentUrl":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2018\/01\/18-1.png","width":760,"height":440},{"@type":"BreadcrumbList","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/customizing-data-grids-orocommerce\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/oroinc.com\/b2b-ecommerce\/"},{"@type":"ListItem","position":2,"name":"Developers' Digest","item":"https:\/\/oroinc.com\/b2b-ecommerce\/blog\/category\/developers-digest\/"},{"@type":"ListItem","position":3,"name":"Customizing Data Grids in OroCommerce"}]},{"@type":"WebSite","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#website","url":"https:\/\/oroinc.com\/b2b-ecommerce\/","name":"OroCommerce","description":"","publisher":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/oroinc.com\/b2b-ecommerce\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#organization","name":"Oro Inc.","url":"https:\/\/oroinc.com\/b2b-ecommerce\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/logo\/image\/","url":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2019\/11\/Oro-OLogo.png","contentUrl":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-content\/uploads\/sites\/3\/2019\/11\/Oro-OLogo.png","width":40,"height":40,"caption":"Oro Inc."},"image":{"@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/OroCommerce-333319140210515\/","https:\/\/x.com\/orocommerce","https:\/\/www.youtube.com\/channel\/UClxsA8HS9KGEEsvFRn7JkvQ"]},{"@type":"Person","@id":"https:\/\/oroinc.com\/b2b-ecommerce\/#\/schema\/person\/420bb6bc049bf6da18b714558557558c","name":"Oro Team","sameAs":["http:\/\/www.orocrm.com\/"]}]}},"_links":{"self":[{"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/posts\/12581","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/comments?post=12581"}],"version-history":[{"count":0,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/posts\/12581\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/media\/12541"}],"wp:attachment":[{"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/media?parent=12581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/categories?post=12581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/oroinc.com\/b2b-ecommerce\/wp-json\/wp\/v2\/tags?post=12581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}