Stratified relation graphs¶
A scope can contain a root manager and several dependency managers.
$configs = [
new StratifiedEntityConfig(CatalogManager::class, 'catalog', 'shopId', scopeRoot: true),
new StratifiedEntityConfig(CatalogSettingsManager::class, 'catalog', 'shopId'),
new StratifiedEntityConfig(ProductManager::class, 'catalog', 'shopId'),
new StratifiedEntityConfig(TagManager::class, 'catalog', 'shopId'),
new StratifiedEntityConfig(ProductTagManager::class, 'catalog', 'shopId'),
];
Include the strate in every relation key¶
#[ToOne(
CatalogSettingsManager::class,
[
'id' => 'catalogId',
'buildStrate' => 'buildStrate',
],
)]
private ?CatalogSettingsEntity $settings = null;
#[ToMany(
ProductManager::class,
[
'id' => 'catalogId',
'buildStrate' => 'buildStrate',
],
)]
private ?EntityCollection $products = null;
Without buildStrate in the mapping, a join can combine a current row with a relation from an older snapshot.
Many-to-many¶
Represent many-to-many relations with an explicit stratified join entity. The join entity owns two ToOne relations; each side exposes a ToMany relation to join rows. Include buildStrate in both mappings and enforce uniqueness with database constraints.
Database constraints and indexes¶
The database schema must enforce the same snapshot boundary as the ORM metadata. For each stratified table:
- index
(scope_id, build_strate)for released queries and garbage collection; - make relation targets unique on
(id, build_strate)when a composite foreign key references both columns; - include
build_stratein child foreign keys; - include
build_stratein join-entity uniqueness constraints.
Example:
CREATE INDEX idx_product_scope
ON product (shop_id, build_strate);
ALTER TABLE product
ADD CONSTRAINT uniq_product_id_build UNIQUE (id, build_strate);
ALTER TABLE product_tag
ADD CONSTRAINT fk_product_tag_product
FOREIGN KEY (product_id, build_strate)
REFERENCES product (id, build_strate)
ON DELETE CASCADE;
ALTER TABLE product_tag
ADD CONSTRAINT uniq_product_tag_link
UNIQUE (product_id, tag_id, build_strate);
The table primary-key strategy must permit every snapshot row to coexist. Generate a fresh row id for every snapshot, as in the supplied fixtures, or use a schema whose composite primary key includes build_strate.
Persistence order¶
Persist parents before foreign-key children, then release only after the complete graph has been written.
$strate = $stratifiedPersist->createNewVersion(
[
CatalogManager::class,
CatalogSettingsManager::class,
ProductManager::class,
TagManager::class,
ProductTagManager::class,
],
'catalog',
$shopId,
);
$stratifiedPersist->persist($catalog, $strate);
$stratifiedPersist->persist($settings, $strate);
$stratifiedPersist->persistMany($products, $strate);
$stratifiedPersist->persistMany($tags, $strate);
$stratifiedPersist->persistMany($productTags, $strate);
$stratifiedPersist->release('catalog', $shopId, $strate);
The package does not traverse or persist a relation graph automatically. Every entity or collection must be persisted explicitly.