iperson1337 / opendxp-tag-management-bundle
Tag analytics for the OpenDXP admin: usage statistics per tag, find DataObjects by any/all of the selected tags, CSV/XLSX export
Package info
github.com/iperson1337/opendxp-tag-management-bundle
Type:opendxp-bundle
pkg:composer/iperson1337/opendxp-tag-management-bundle
Requires
- php: ^8.3
- doctrine/dbal: ^3.6 || ^4.0
- open-dxp/admin-bundle: ^1.0
- open-dxp/opendxp: ^1.0
- phpoffice/phpspreadsheet: ^1.29 || ^2.0 || ^3.0 || ^4.0 || ^5.0
- symfony/config: ^7.4
- symfony/dependency-injection: ^7.4
- symfony/event-dispatcher: ^7.4
- symfony/framework-bundle: ^7.4
- symfony/http-foundation: ^7.4
- symfony/routing: ^7.4
Requires (Dev)
- phpunit/phpunit: ^10.0 || ^11.0
README
Tag analytics for the OpenDXP classic admin: which tags exist, how many DataObjects carry each of them (with a per-class breakdown), which objects carry any / all of the selected tags — and a CSV / XLSX export of that list.
What it does:
- "Tags" entry in the main navigation (left bar) for users holding the configured permission
(
tags_search— a core permission, nothing to install). Opens a Tags tab. - Tag grid (left): hierarchical name path (
Parent / Child), number of objects, compact breakdown by class (Product 15 · Brand 7, full list in the tooltip). Search by name (case-insensitive), filter by object class (only tags used by that class, counts restricted to it), server-side sort by name or usage, paging. - Object grid (right): follows the tick marks in the tag grid automatically. Any tag / All tags switch, the selected tags as removable chips, total count, server-side sort on every column, paging, type / published icons, double-click (or the row button) opens the object. Selection survives paging, searching and re-filtering of the tag grid.
- Export (button menu): CSV (UTF-8 BOM,
;delimiter — opens cleanly in Excel with Cyrillic locales) or XLSX (bold frozen header, auto-filter). Streams in batches up toexport.max_rows; cells starting with=,+,-,@, TAB or CR are apostrophe-prefixed against spreadsheet formula injection.
Scope: DataObject assignments only (tags_assignment.ctype = 'object'); assets and documents
are not counted. A tag's count is its own assignments — child tags are not rolled up.
Requirements
- OpenDXP
^1.0with the classic admin bundle (open-dxp/admin-bundle), PHP 8.3. phpoffice/phpspreadsheetfor the XLSX export (declared as a dependency).
Installation
composer require iperson1337/opendxp-tag-management-bundle
config/bundles.php:
Iperson1337\OpenDxpTagManagementBundle\OpenDxpTagManagementBundle::class => ['all' => true],
Then:
bin/console cache:clear bin/console assets:install # publishes public/ to /bundles/opendxptagmanagement/ bin/console fos:js-routing:dump # routes are exposed for Routing.generate() in the admin JS
Routes (config/opendxp/routing.yaml) are picked up automatically by OpenDXP's bundle routing
loader; no entry in the application's config/routes.yaml is needed. No installer, no database
changes: the bundle only reads the core tags / tags_assignment / objects tables.
Grant the tags_search permission (Settings → Users) to every non-admin user who should see the
tab — or point permission at any other permission key you already use.
Configuration
Every key has a default, so the bundle runs without any config/packages/*.yaml. Reference:
bin/console config:dump-reference iperson1337_opendxp_tag_management bin/console debug:config iperson1337_opendxp_tag_management
iperson1337_opendxp_tag_management: permission: tags_search # admin permission for the menu entry and every endpoint page_size: 50 # rows per page in both grids max_page_size: 500 # hard upper bound of the "limit" request parameter export: max_rows: 10000 # objects written into one CSV/XLSX batch_size: 500 # objects fetched per query while streaming an export
Admin API
All endpoints are GET, sit behind the admin firewall and the configured permission
(403 otherwise), and answer {"success": true, ...}.
| Route | Purpose | Parameters |
|---|---|---|
/admin/tag-management/tags |
tags with usage statistics | search, className, orderBy (name|usageCount), orderDirection, limit, offset |
/admin/tag-management/objects |
objects carrying the tags | tagIds (comma-separated, required), matchMode (all|any), className, orderBy (id|key|path|className|published|updatedAt), orderDirection, limit, offset |
/admin/tag-management/export |
same filter, as a file | as /objects plus format (csv|xlsx) |
Unknown values fall back to the defaults instead of failing; limit is clamped to
[1, max_page_size].
Adding an export format
Implement Service\Export\ExporterInterface (format name, content type, extension, write() into
a stream). Services implementing the interface are tagged automatically
(iperson1337.opendxp_tag_management.exporter) and picked up by ExporterRegistry; the new format
appears in the admin export menu on the next page load (exportFormats in
opendxp.settings.tagManagement).
Layout
config/
opendxp/routing.yaml attribute routes of src/Controller, exposed to FOSJsRouting
services.yaml
public/
css/tag-management.css navigation / tab icon, chips, grid cell styles
img/tag.svg
js/opendxp/tagPanel.js the "Tags" tab (both grids, export)
js/opendxp/startup.js settings accessor + main-navigation entry (permission-gated)
src/
Controller/TagManagementController.php
DependencyInjection/ Configuration (iperson1337_opendxp_tag_management) + Extension
EventSubscriber/AdminSettingsSubscriber.php → opendxp.settings.tagManagement
Http/QueryParser.php raw query string → bounded TagQuery / ObjectQuery
Model/ TagManagementConfig, TagQuery, ObjectQuery, MatchMode, ObjectOrder, TagOrder, SortDirection
Repository/ TagStatsRepository, TaggedObjectRepository (+ TaggedObjectFinderInterface) — DBAL, read-only
Service/Export/ ExporterInterface, CsvExporter, XlsxExporter, ExporterRegistry, ExportRow, TaggedObjectExportSource
translations/ admin.ru.yml, admin.en.yml (prefix plugin_pimcore_tag_management_)
tests/ PHPUnit unit tests (mirrors src/; excluded from the dist archive)
Testing
Pure unit tests, no OpenDXP kernel or database needed:
composer install vendor/bin/phpunit
License
MIT.