se7enxweb / exp-platform-ng-subtree-copy-with-layouts
Copies an Ibexa DXP content subtree and carries over Netgen Layouts resolver rules, section assignments and object state metadata to the newly created locations.
Package info
github.com/se7enxweb/SevenxExpPlatformNGSubtreeCopyWithLayouts
Type:symfony-bundle
pkg:composer/se7enxweb/exp-platform-ng-subtree-copy-with-layouts
Fund package maintenance!
Requires
- php: >=8.2
- doctrine/dbal: ^3.0 || ^4.0
- ibexa/core: ^5.0
- netgen/layouts-core: ^2.0
- ramsey/uuid: ^4.0
- symfony/console: ^7.1
- symfony/framework-bundle: ^7.1
Requires (Dev)
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
- symfony/phpunit-bridge: ^7.1
This package is auto-updated.
Last update: 2026-03-12 19:07:38 UTC
README
Copies an Ibexa content subtree and automatically:
- Duplicates all matching Netgen Layouts resolver rules for the newly created location IDs.
- Re-applies the section assignment of each source content item to its corresponding copy.
- Re-applies all object state values of each source content item to its corresponding copy.
Ibexa's built-in LocationService::copySubtree() creates new locations with
new IDs but loses three pieces of metadata that are not part of content
versions: the Netgen Layouts resolver rules, the section assignment, and the
object state assignments. This bundle restores all three.
Files
| File | Purpose |
|---|---|
SevenxExpPlatformNGSubtreeCopyWithLayoutsBundle.php |
Bundle entry point — loads Resources/config/services.yaml |
Resources/config/services.yaml |
Autowire / autoconfigure for the bundle namespace |
Service/SubtreeLayoutRuleCopier.php |
Core service: parallel tree traversal + Netgen Layouts rule duplication |
Service/SubtreeSectionCopier.php |
Service: re-applies section assignments to each copied content item |
Service/SubtreeObjectStateCopier.php |
Service: re-applies all object state values to each copied content item |
Command/CopySubtreeWithLayoutsCommand.php |
Console command se7enx:nglayouts:copy-subtree-with-layouts |
Modified by bundle registration: config/bundles.php
Namespace
App\Bundle\SevenxExpPlatformNGSubtreeCopyWithLayouts\
The project's existing composer.json autoload entry "App\\": "src/" covers
this namespace automatically — no additional PSR-4 entry is needed and none was
added (adding a second overlapping entry triggers Symfony's DebugClassLoader
to double-include the bundle class file, causing a fatal "Cannot redeclare
class" error).
Installation
See doc/INSTALL.md for full installation instructions.
The bundle is registered in config/bundles.php:
App\Bundle\SevenxExpPlatformNGSubtreeCopyWithLayouts\SevenxExpPlatformNGSubtreeCopyWithLayoutsBundle::class => ['all' => true],
No other configuration is required. All services are autowired.
Usage
# Full copy — subtree + sections + object states + Netgen Layouts rules php bin/console se7enx:nglayouts:copy-subtree-with-layouts <source-location-id> <target-parent-location-id> # Preview what would happen — no content or rules are written php bin/console se7enx:nglayouts:copy-subtree-with-layouts 385 42 --dry-run # Copy content + sections + object states only; skip layout rules php bin/console se7enx:nglayouts:copy-subtree-with-layouts 385 42 --skip-layout-rules # Copy content + layout rules only; skip section and object state re-assignment php bin/console se7enx:nglayouts:copy-subtree-with-layouts 385 42 --skip-sections --skip-object-states
Arguments
| Argument | Description |
|---|---|
source-location-id |
Location ID of the root of the subtree to copy |
target-parent-location-id |
Location ID of the parent that will receive the copied subtree |
Options
| Option | Description |
|---|---|
--dry-run |
Print what would be done without writing anything to the repository |
--skip-layout-rules |
Do not duplicate Netgen Layouts resolver rules |
--skip-sections |
Do not re-apply section assignments |
--skip-object-states |
Do not re-apply object state values |
How It Works
-
Copy the Ibexa content subtree.
LocationService::copySubtree(source, targetParent)is called with the admin user (ID 14) as the current user reference, so the copy has full repository access. The new rootLocationis returned. -
Build an old → new location ID map.
Both the original and the copied tree are traversed in parallel (child by child, in order) viaLocationService::loadLocationChildren(). The result is an array of the form[oldLocationId => newLocationId]covering every node in the subtree. -
Re-apply section assignments.
For each[oldLocationId => newLocationId]pair the section of the source content item (SectionService::getSectionOfContent()) is read and then assigned to the new content item (SectionService::assignSection()).
Ibexa's built-in copy inherits the section of the target parent; this step restores the original per-item section granularity. -
Re-apply object state assignments.
For everyObjectStateGroupreturned byObjectStateService::loadObjectStateGroups(), the source content item's state is read viaObjectStateService::getContentState()and written to the new content item viaObjectStateService::setContentState(). -
Duplicate
ibexa_locationrules for every location.
For eacholdLocationIdin the map,nglayouts_rule_targetis queried directly via Doctrine DBAL (theLayoutResolverServiceAPI exposes no "find rules by target value" lookup). Every published, enabled rule whose target type isibexa_locationand whose value equals the old location ID is duplicated:- A new rule draft is created via
LayoutResolverService::createRule(). - A target is added via
LayoutResolverService::addTarget(). - The rule is published via
LayoutResolverService::publishRule().
- A new rule draft is created via
-
Duplicate
ibexa_subtreerules for the copy root.
ibexa_subtreerules are subtree-wide, so they are only meaningful for the topmost copied location. The service copies these for the source root into a matching rule targeting the new root. -
Priority offset.
New rules receiveoriginalPriority − 1. This ensures the original rules always take precedence over the copies, preserving existing page behaviour.
Supported Target Types
| Target type | Copied for |
|---|---|
ibexa_location |
Every location in the subtree |
ibexa_subtree |
The copy root only |
path_info_prefix |
Not copied — path-based rules depend on URL aliases that Ibexa manages separately |
Dependencies
All injected via autowiring:
| Service | Used for |
|---|---|
Ibexa\Contracts\Core\Repository\LocationService |
copySubtree(), loadLocation(), loadLocationChildren() |
Ibexa\Contracts\Core\Repository\PermissionResolver |
Setting admin user as current reference |
Ibexa\Contracts\Core\Repository\UserService |
Loading admin user by ID |
Ibexa\Contracts\Core\Repository\SectionService |
getSectionOfContent(), assignSection() on each copied item |
Ibexa\Contracts\Core\Repository\ObjectStateService |
loadObjectStateGroups(), getContentState(), setContentState() on each copied item |
Netgen\Layouts\API\Service\LayoutResolverService |
newRuleCreateStruct(), createRule(), newTargetCreateStruct(), addTarget(), publishRule(), loadRuleGroup() |
Doctrine\DBAL\Connection |
Direct query of nglayouts_rule_target by target value |
Database Schema Reference
The following nglayouts tables are read (never written to directly):
-- status = 1 means PUBLISHED in all nglayouts tables
nglayouts_rule (id, status, uuid, rule_group_id, layout_uuid, description)
nglayouts_rule_data (rule_id, enabled, priority)
nglayouts_rule_target (id, status, uuid, rule_id, type, value)
The root rule group UUID is fixed in the nglayouts schema:
00000000-0000-0000-0000-000000000000
All new rules are created under this group, matching the behaviour of rules created via the Netgen Layouts admin UI.
License & Copyright
Copyright © 1998 - 2026, 7x. All rights reserved.
SevenxExpPlatformNGSubtreeCopyWithLayouts is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
SevenxExpPlatformNGSubtreeCopyWithLayouts is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with SevenxExpPlatformNGSubtreeCopyWithLayouts in LICENSE. If not, see http://www.gnu.org/licenses/.
See COPYRIGHT.md for full copyright and license assignment details.