
Oro Datagrid
- 4 installs
- 2 repo stars
- Updated July 22, 2026
- netresearch/orocommerce-skill
Helps with ai & agent building tasks.
About
oro-datagrid is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- oro-datagrid
- AI & Agent Building
- AI-coding skill
Oro Datagrid by the numbers
- 4 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #13,348 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/netresearch/orocommerce-skill --skill oro-datagridAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 4 |
|---|---|
| repo stars | ★ 2 |
| Last updated | July 22, 2026 |
| Repository | netresearch/orocommerce-skill ↗ |
What it does
Helps with ai & agent building tasks.
Files
OroCommerce v6.1 Datagrid Configuration & Customization
Core File Location
Datagrids live in Resources/config/oro/datagrids.yml within your bundle. Bundle auto-discovery loads these on kernel compilation.
Grid Structure (v6.1 Canonical Example)
datagrids:
my_grid:
source:
type: orm
query:
select: [d]
from: [{ table: Acme\Bundle\DemoBundle\Entity\Document, alias: d }]
columns:
id:
label: ID
subject:
label: Subject
filters:
columns:
subject:
type: string
data_name: d.subject
sorters:
columns:
id:
data_name: d.id
default:
id: DESC
actions:
view:
type: navigate
label: View
icon: eye
link: acme_demo_document_view
rowAction: trueEvery grid needs: source (ORM query), columns (display), filters (with data_name), sorters (with data_name), and actions.
Column Types
Most common: string, integer, boolean, datetime, currency (requires currency_code option). See references/column-types.md for the full list.
Extending Existing Grids
Never modify core grid YAMLs. Use a BuildBefore event listener to add columns, filters, or modify config programmatically. For related entity data, use onResultAfter to attach data post-fetch instead of adding JOINs.
See references/datagrid-patterns.md for full listener examples (onBuildBefore, onResultAfter), service registration, join patterns, mass actions, and inline editing configuration.
Key Pitfalls
1. Column `data_name` mismatch — If sorters/filters use data_name: d.subject but the query doesn't have alias d, sorting fails silently. Always verify alias consistency. 2. Filter without `data_name` — Renders in the UI but produces no WHERE clause. Users see the filter but it does nothing. 3. ACL on grid actions is separate from entity ACL — Both acl_resource on mass_actions AND entity-level ACL must pass. Define the resource in acl.yml.
Cache
Datagrids are compiled into the DIC. After YAML changes: php bin/console cache:clear.
See Also
references/column-types.md— Full column & filter type referencereferences/datagrid-patterns.md— Inline editing, mass actions, listener examples, join patterns, additional pitfallsreferences/v6.1.md— v6.1 specifics, backward compatibility, and troubleshootingreferences/v7.0.md— v7.0 changes (placeholder)
OroCommerce v6.1 Datagrid Column & Filter Types Reference
Column Types
string
Basic text rendering. No special formatting.
columns:
title:
label: Title
type: stringinteger
Numeric integers. Right-aligned by default.
columns:
count:
label: Count
type: integerboolean
True/false rendered as checkmark (✓) or X.
columns:
active:
label: Active
type: booleandate
Dates in YYYY-MM-DD format. Requires DateTime entity property.
columns:
createdAt:
label: Created
type: date
frontend_type: datedatetime
Full timestamp YYYY-MM-DD HH:MM:SS.
columns:
updatedAt:
label: Updated
type: datetime
frontend_type: datetimedecimal
Floating-point with configurable precision.
columns:
amount:
label: Amount
type: decimal
frontend_type: decimalpercent
Percentage display (value rendered as %).
columns:
completion:
label: % Complete
type: percentcurrency
Formatted with currency symbol (USD $, EUR €, etc.). Requires currency_code option.
columns:
price:
label: Price
type: currency
options:
currency_code: USDhtml
Raw HTML rendering. Use sparingly & never with user input.
columns:
description:
label: Description
type: html
safe: true # Sanitize HTML if sourced from untrusted datalink
Hyperlink. Requires route or url option.
columns:
document_link:
label: Document
type: link
route: acme_demo_document_view
routeParameters:
id: $.idtwig
Render column value via Twig template.
columns:
status_badge:
label: Status
type: twig
template: '@AcmeDemoBundle/datagrid/status_badge.html.twig'
context:
status: $.status
class: $.statusClassphone
Phone number formatting with country code detection.
columns:
phone:
label: Phone
type: phoneimage
Display image thumbnail.
columns:
thumbnail:
label: Image
type: image
width: 100
height: 100color
Display a color swatch.
columns:
color:
label: Color
type: color---
Filter Types
string
Text search. Case-insensitive substring match.
filters:
columns:
subject:
type: string
data_name: d.subject
label: Subjectinteger
Filter by exact integer or range.
filters:
columns:
count:
type: integer
data_name: d.countboolean
Filter by true/false.
filters:
columns:
active:
type: boolean
data_name: d.activedate
Date range filter (from–to).
filters:
columns:
created:
type: date
data_name: d.createdAtdatetime
Datetime range filter.
filters:
columns:
updated:
type: datetime
data_name: d.updatedAtdecimal
Decimal range filter.
filters:
columns:
amount:
type: decimal
data_name: d.amountentity
Filter by related entity. Requires field_options.
filters:
columns:
author:
type: entity
data_name: d.author
options:
field_options:
class: Acme\Bundle\DemoBundle\Entity\User
property: name
choice_label: namechoice
Dropdown filter with predefined options.
filters:
columns:
status:
type: choice
data_name: d.status
options:
field_options:
choices:
new: New
active: Active
closed: Closedmultiselect
Multi-select filter. Returns items matching ANY selected value.
filters:
columns:
categories:
type: multiselect
data_name: d.categories
options:
field_options:
choices:
1: Category A
2: Category B
3: Category Cexclusion
Special filter for excluding specific items. Rarely used.
filters:
columns:
exclude_ids:
type: exclusion
data_name: d.id---
Filter Options Reference
data_name (Required)
The entity property path for filtering. Must match query alias.
data_name: d.subjectlabel (Optional)
Filter display label. Defaults to property name.
label: Document Subjectfield_options (Optional)
Symfony form field options.
field_options:
class: Entity\Class
property: displayProperty
choice_label: displayProperty
multiple: trueoperator (Optional)
Comparison operator. Default: = for most, LIKE for strings.
operator: LIKEcase_insensitive (Optional)
For string filters. Default: true.
case_insensitive: truerenderable (Optional)
Show filter in UI. Default: true.
renderable: true---
Common Column Options
label (Required)
Column header text.
label: Document Subjectdata_name (Optional)
Property path. Auto-inferred from column key if omitted.
data_name: d.subjecttype (Optional)
Column type. Default: string.
type: decimalfrontend_type (Optional)
Explicit frontend type override.
frontend_type: dateeditable (Optional)
Enable inline editing on this column.
editable: truesortable (Optional)
Enable sorting. Requires entry in sorters section.
sortable: truerenderable (Optional)
Show/hide column. Default: true.
renderable: truewidth (Optional for image/color)
CSS width. Example: "100px".
width: 100pxalign (Optional)
Text alignment: left, center, right. Default: left.
align: right---
Example: Multi-Type Grid
datagrids:
document_list:
source:
type: orm
query:
select: [d]
from: [{ table: Acme\Bundle\DemoBundle\Entity\Document, alias: d }]
columns:
id:
label: ID
type: integer
align: right
subject:
label: Subject
type: string
status:
label: Status
type: choice
amount:
label: Amount
type: currency
options:
currency_code: USD
active:
label: Active
type: boolean
createdAt:
label: Created
type: datetime
actions:
label: Actions
type: action
filters:
columns:
subject:
type: string
data_name: d.subject
status:
type: choice
data_name: d.status
options:
field_options:
choices:
new: New
active: Active
closed: Closed
amount:
type: decimal
data_name: d.amount
active:
type: boolean
data_name: d.active
createdAt:
type: datetime
data_name: d.createdAt
sorters:
columns:
id:
data_name: d.id
subject:
data_name: d.subject
amount:
data_name: d.amount
createdAt:
data_name: d.createdAt
default:
createdAt: DESC
actions:
view:
type: navigate
label: View
icon: eye
link: acme_demo_document_view
rowAction: true
edit:
type: navigate
label: Edit
icon: pencil
link: acme_demo_document_update---
Performance Notes
- String filters: LIKE queries are slow on large datasets. Index
data_namecolumns in database. - Entity filters: Each filter triggers a query to load choices. Cache if choices are static.
- Datetime filters: Always use indexed columns for
createdAt,updatedAt. - Joined columns: Avoid filters/sorters on joined relationships. Use
onResultAfterto attach data instead.
Datagrid Patterns Reference
Filter Types & Configuration
Filters restrict grid data. Types: string, integer, boolean, date, datetime, decimal, entity, choice, multiselect.
filters:
columns:
subject:
type: string
data_name: d.subject
status:
type: choice
data_name: d.status
options:
field_options:
choices:
new: New
active: Active
closed: Closed
priority:
type: integer
createdAt:
type: datetimeFilter clarity: Filters generate server-side DQL WHERE clauses. The data_name property binds the filter to a query alias. If data_name is missing, the filter UI renders but produces no WHERE clause, so results are unaffected.
Sorter Configuration
Sorters tie columns to sortable attributes. Critical: data_name must match your query alias.
sorters:
columns:
id:
data_name: d.id
subject:
data_name: d.subject
priority:
data_name: d.priority
default:
id: DESCThe default key sets initial sort order (ASC or DESC).
Row Actions (Detailed)
Row actions appear as buttons per row (view, edit, delete):
actions:
view:
type: navigate
label: View
icon: eye
link: acme_demo_document_view
rowAction: true
edit:
type: navigate
label: Edit
icon: pencil
link: acme_demo_document_update
rowAction: true
delete:
type: delete
label: Delete
icon: trash
link: acme_demo_document_delete
rowAction: falseMass Actions
Mass actions apply to multiple selected rows:
mass_actions:
delete:
type: delete
label: Delete Selected
icon: trash
acl_resource: acme_demo_document_delete
approve:
type: frontend
label: Approve
icon: check
route: acme_demo_document_approve
query_param_name: idThe acl_resource gates the action via ACL (separate from entity ACL — critical distinction).
Inline Editing
Enable inline edit without navigating to a detail page:
options:
entity_pagination: true
export: true
inline_editing:
enable: true
entity_class: Acme\Bundle\DemoBundle\Entity\Document
behaviour: enable_all
column_options:
subject:
save_api_accessor:
route: acme_demo_document_patch
query_param: id
status:
data_type: string
default_value: newInline editing requires: 1. Entity class reference 2. API endpoint that accepts PATCH 3. Column-specific configuration for form types
Extending Existing Grids: onBuildBefore Listener
Don't modify core grid YAMLs. Instead, register an event listener for Oro\Bundle\DataGridBundle\Event\BuildBefore:
// src/Acme/Bundle/DemoBundle/EventListener/GridListener.php
namespace Acme\Bundle\DemoBundle\EventListener;
use Oro\Bundle\DataGridBundle\Event\BuildBefore;
class GridListener {
public function onBuildBefore(BuildBefore $event) {
$config = $event->getConfig();
if ($event->getDatagrid()->getName() === 'product-grid') {
// Add a new column
$config->offsetSetByPath('[columns][custom_field]', [
'label' => 'Custom Field',
'type' => 'string',
'data_name' => 'p.customField'
]);
// Add a filter
$config->offsetSetByPath('[filters][columns][custom_field]', [
'type' => 'string',
'data_name' => 'p.customField'
]);
}
}
}Register the listener:
# services.yml
services:
acme.event_listener.grid:
class: Acme\Bundle\DemoBundle\EventListener\GridListener
tags:
- { name: kernel.event_listener, event: oro_datagrid.datagrid.build.before, method: onBuildBefore }Adding Relationship Data: onResultAfter Pattern
Performance pitfall: Never add extra JOINs to fetch related entities in the grid query. Use onResultAfter to attach data post-fetch:
// GridListener.php
use Oro\Bundle\DataGridBundle\Event\OrmResultAfter;
public function onResultAfter(OrmResultAfter $event) {
if ($event->getDatagrid()->getName() !== 'document-grid') {
return;
}
$records = $event->getRecords();
$documentIds = array_map(fn($r) => $r->getValue('id'), $records);
// Fetch related data in a single query
$comments = $this->doctrineRepository->findCommentsByDocumentIds($documentIds);
// Attach to records
foreach ($records as $record) {
$record->setValue(
'comment_count',
count($comments[$record->getValue('id')] ?? [])
);
}
}Register with higher priority than data loading:
tags:
- { name: kernel.event_listener, event: oro_datagrid.orm_datasource.result.after, method: onResultAfter, priority: 10 }Grid with Joined Entities
When you need a join for filtering/sorting (unavoidable), add it directly to the query:
datagrids:
document_grid:
source:
type: orm
query:
select: [d, IDENTITY(d.author) as author_id]
from:
- { table: Acme\Bundle\DemoBundle\Entity\Document, alias: d }
join:
left:
- { join: d.author, alias: a }
where:
and:
- a.active = true
columns:
author_name:
label: Author
data_name: a.name
filters:
columns:
author_name:
type: string
data_name: a.name
sorters:
columns:
author_name:
data_name: a.nameWhy: If the join is essential for the grid's function (not bonus data), keep it in the query. But ask yourself first: can I fetch this in onResultAfter?
Additional Pitfalls
ACL on Grid Actions ≠ Entity ACL
Grid action ACL (acl_resource in mass_actions) is separate from entity ACL. Both must pass:
mass_actions:
delete:
acl_resource: acme_demo_document_delete # Checks user permission to DELETE this resourceYour ACL config must define this resource in acl.yml.
Inline Editing Without API Endpoint
Inline editing requires a PATCH API endpoint. If missing, edits silently fail. Provide the full route:
inline_editing:
column_options:
status:
save_api_accessor:
route: acme_demo_document_patch # Must exist and accept PATCH
query_param: idFilter Display Without Effect
A filter without data_name renders in the UI but doesn't filter. Users get confused. Always bind filters to sortable columns or explicitly set data_name.
Cache & Compilation
Datagrids are compiled into the DIC. After YAML changes:
php bin/console cache:clearIn production, rebuild the container cache.
Datagrid — v6.1 Notes
Changes from v6.0
- No breaking changes to YAML structure
onResultAfterpriority handling clarified (higher = runs first)- Inline editing column options structure finalized
Backward Compatibility
Supported
- All datagrid YAML configurations from v5.4 remain valid
- Event listener interfaces stable across v6.x
Deprecated (Still Works, Logs Warning)
- Direct modification of core grid YAMLs — use event listeners instead
- Inline ORM query SQL in datasource — use Doctrine query builder notation
Removed
- PHP-based grid registration (deprecated in v5.4)
Common Troubleshooting
Grid Shows No Data
- Check
data_namepaths match query aliases - Verify entity class path is correct
- Ensure query alias is used in columns, filters, sorters
Sorting/Filtering Not Working
- Confirm filter/sorter has
data_nameentry - Check
data_namematches the query's SELECT/FROM alias - Ensure column is
sortable: trueif required
Inline Editing Fails Silently
- Verify API endpoint route exists and accepts PATCH
- Confirm entity class is correct in inline_editing config
- Check column has
save_api_accessorwith valid route
Relationship Data Not Loading
- Use
onResultAfterlistener instead of JOIN - Register listener with
priority: 10to run after data load - Batch fetch related entities in single query
Action ACL Not Enforcing
- Verify
acl_resourceexists inacl.yml - Check user has permission in security context
- Grid action ACL is independent of entity ACL
Datagrid — v7.0 Notes
v7.0 is not yet released. This file will be updated when v7.0 stabilizes.
Expected Changes
- TBD