AdminMenuItemService
extends Service
in package
implements
TreeServiceInterface
uses
TreeTrait
Base Api service over a mapper; every concrete service owns exactly one entity.
Tags
Table of Contents
Interfaces
- TreeServiceInterface
- Service of a nested-set tree entity.
Properties
Methods
- __construct() : mixed
- beginTransaction() : void
- commit() : void
- count() : int
- Count all entities matching filters.
- delete() : Result
- Delete entity by ID.
- deleteAll() : Result
- Delete multiple entities by IDs.
- deleteBy() : Result
- Delete entity by filters.
- deleteTreeNode() : Result
- Deletes a node together with its whole subtree:
- exists() : bool
- Check if entity exists by ID.
- existsBy() : bool
- Check if entity exists by filters.
- find() : T|null
- Find entity by ID.
-
findAll()
: T>, 1: int} : array{0: list
>, 1: int}) - Find all entities with optional filters and includes.
- findAllBy() : array<int, T>
- Find entities by filters.
- findBy() : T|null
- Find one entity by filters.
- findByOrFail() : T
- Find one entity by filters or throw exception.
- findOrFail() : T
- Find entity by ID or throw exception.
- findRelatedIds() : array<int, int>|null
- Returns ids of target entities linked to the parent through the relation collection (see the mapper).
- getTransactionLevel() : int
- getTreeSubtreeIds() : array<int, int>
- Returns ids of a node and all its descendants, for a caller that needs to know the subtree before deleting it (image cleanup, {@see \App\Core\Traits\Api\Models\Managers\TreeTrait::deleteNode()}).
- insertTreeNode() : Result
- Computes `lft`/`rgt`/`level` of a newly inserted node (after `save()`); delegates to the mapper.
- inTreeLock() : Result
- Runs the operation under the tree lock (table plus scope from the `TreeConfig`), so the manager holds the lock across the commit of its own transaction ({@see \App\Core\Traits\Api\Models\Managers\TreeTrait::treeTransaction()}).
- isInTransaction() : bool
- moveSubtree() : Result
- Moves a whole subtree under another parent; delegates to the mapper.
- moveTreeNode() : Result
- reorderTreeChildren() : Result
- Reorders the direct children of a parent into the given order; delegates to the mapper.
- resolveEntityClass() : TEntity>
- Exposes the root entity class of the service publicly.
- rollback() : void
- save() : Result
- Save entity.
- saveAll() : Result
- Save multiple entities at once.
- updateAllRelations() : Result
- Update all relations of parent entity with given values.
- updateColumns() : Result
- Update specific columns.
- updateRelations() : Result
- Update existing relation entities via Mapper.
- deleteWithoutCache() : Result
- Delete entity from database via Mapper.
-
findAllFromDb()
: T>, 1: int} : array{0: list
>, 1: int}) - Load all entities from database via Mapper → Repository.
- findFromDb() : T|null
- Load entity from database via Mapper.
- getEntityClass() : TEntity>
- Returns the service entity class; every concrete service returns `Entity::class` matching its `@extends Service<Entity>`.
- narrowEntities() : array<int, TEntity>
- narrowEntity() : TEntity|null
- Narrows the generic {@see BaseEntity} returned by the mapper to the service entity.
- narrowRows() : array<int, array<string, mixed>>
- Narrows query rows for `$columns` (no hydration); every item must be an array keyed by column names.
- saveWithoutCache() : Result
- Save entity to database via Mapper.
- updateColumnsWithoutCache() : Result
- Update columns in database via Mapper.
- deleteTreeNodeUnderLock() : Result
- isTechnicalDeleteFailure() : bool
- Technical failure = the persistence layer broke (exception attached or a technical code); the EntityManager may be closed and no further write may be attempted.
- requireTreeMapper() : TreeMapperInterface
- Mapper with tree operations; a service without a tree mapper has no such methods.
Properties
$mapper read-only
protected
MapperInterface
$mapper
Methods
__construct()
public
__construct(AdminMenuItemMapper $mapper) : mixed
Parameters
- $mapper : AdminMenuItemMapper
beginTransaction()
public
beginTransaction() : void
commit()
public
commit() : void
count()
Count all entities matching filters.
public
count([array<string|int, mixed> $filters = [] ][, bool $useCache = true ]) : int
Parameters
- $filters : array<string|int, mixed> = []
-
Filter conditions
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Return values
int —Total count
delete()
Delete entity by ID.
public
delete(int|array<string|int, mixed> $id) : Result
IMPORTANT: an array means a list of ids, not a composite key (Mapper::find() takes
only int). Without this redirect to deleteAll() every bulk delete from the DataGrid
ends with TypeError: Mapper::find(): Argument #1 ($id) must be of type int, array given
(the grid calls Manager::delete($ids) with the array of selected rows).
Parameters
- $id : int|array<string|int, mixed>
-
Entity ID to delete
Return values
Result —Success if deleted, Error if entity not found
deleteAll()
Delete multiple entities by IDs.
public
deleteAll(array<string|int, mixed> $ids) : Result
Partial success is the contract for domain refusals (missing id, guard): the loop
continues and the errors are listed next to deletedCount. A technical failure
(DB_ERROR and similar, see ResultCode::isTechnical()) aborts the loop instead: after a
failed ORM flush Doctrine closes the EntityManager, so the next remove() would throw
EntityManagerClosed out of the loop and the caller would get no summary of the rows
already deleted (durable in autocommit). The failed Result carries deletedIds,
failedId and remainingIds; the caller decides whether the accepted prefix stays
(autocommit) or is rolled back (the manager bulk paths run inside a transaction and roll
it back).
Parameters
- $ids : array<string|int, mixed>
-
entity ids (int or numeric string); for a composite key an array
[property => value]
Return values
Result —Success with delete count, or Error
deleteBy()
Delete entity by filters.
public
deleteBy(array<string|int, mixed> $filters) : Result
Parameters
- $filters : array<string|int, mixed>
-
Filter conditions
Return values
Result —Success if deleted, Error if not found
deleteTreeNode()
Deletes a node together with its whole subtree:
public
deleteTreeNode(int $id, TreeConfig $config) : Result
- collects the subtree ids (from leaves to root, safe for FKs),
- deletes them via DeleteTrait::deleteAll(): Doctrine remove (cascades, lifecycle) plus per-entity cache invalidation,
- closes the gap in
lft/rgtof the remaining nodes (set-based UPDATE).
A partial delete is an error: it returns a failure, and the manager holds the transaction (TreeTrait) and rolls it back.
IMPORTANT: the whole sequence runs under one tree lock: the bounds read in step 1 must stay
valid until the gap is closed in step 3 (the inner closeTreeGap lock is re-entrant).
Parameters
- $id : int
- $config : TreeConfig
Return values
Resultexists()
Check if entity exists by ID.
public
exists(int|array<string|int, mixed> $id[, bool $useCache = true ]) : bool
Parameters
- $id : int|array<string|int, mixed>
-
Entity ID
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Return values
bool —True if entity exists
existsBy()
Check if entity exists by filters.
public
existsBy(array<string|int, mixed> $filters[, bool $useCache = true ]) : bool
Parameters
- $filters : array<string|int, mixed>
-
Filter conditions
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Return values
bool —True if entity exists
find()
Find entity by ID.
public
find(int|array<string|int, mixed> $id[, array<string|int, mixed> $includes = [] ][, bool $useCache = true ]) : T|null
Parameters
- $id : int|array<string|int, mixed>
-
Entity ID (scalar for simple keys, array for composite keys)
- $includes : array<string|int, mixed> = []
-
Relations to load (e.g.,
['translations', 'user.roles']) - $useCache : bool = true
-
kept for backward compatibility (caching is done in the presenter); no effect here
Return values
T|null —Entity instance or null if not found
findAll()
Find all entities with optional filters and includes.
public
findAll([array<string|int, mixed> $includes = [] ][, array<string|int, mixed> $filters = [] ][, array<string|int, mixed>|null $orderBy = null ][, int|null $offset = null ][, int|null $limit = null ][, array<string|int, mixed>|null $columns = null ][, bool $computeTotalCount = false ][, bool $useCache = true ]) : T>, 1: int} : array{0: list>, 1: int})
Parameters
- $includes : array<string|int, mixed> = []
-
Relations to load (e.g., ['translations', 'user.roles'])
- $filters : array<string|int, mixed> = []
-
Filter conditions (e.g., ['activated' => 1])
- $orderBy : array<string|int, mixed>|null = null
-
Sorting (e.g.,
['createdAt' => 'DESC']) - $offset : int|null = null
-
Offset for pagination
- $limit : int|null = null
-
Limit number of results
- $columns : array<string|int, mixed>|null = null
-
Columns to select (null = all)
- $computeTotalCount : bool = false
-
Whether to compute total count for pagination
- $useCache : bool = true
-
kept for backward compatibility (caching is done in the presenter); no effect here
Return values
T>, 1: int} : array{0: listTuple: [entities (or $columns rows), totalCount]
findAllBy()
Find entities by filters.
public
findAllBy(array<string|int, mixed> $filters[, array<string|int, mixed> $includes = [] ][, array<string|int, mixed>|null $order = null ][, int|null $offset = null ][, int|null $limit = null ][, bool $useCache = true ]) : array<int, T>
Parameters
- $filters : array<string|int, mixed>
-
Filter conditions (e.g., ['activated' => 1, 'userId' => 5])
- $includes : array<string|int, mixed> = []
-
Relations to load
- $order : array<string|int, mixed>|null = null
-
Sorting
- $offset : int|null = null
-
Offset
- $limit : int|null = null
-
Limit
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Return values
array<int, T> —Array of entities
findBy()
Find one entity by filters.
public
findBy(array<string|int, mixed> $filters[, array<string|int, mixed> $includes = [] ][, bool $useCache = true ]) : T|null
Parameters
- $filters : array<string|int, mixed>
-
Filter conditions
- $includes : array<string|int, mixed> = []
-
Relations to load
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Return values
T|null —Entity or null if not found
findByOrFail()
Find one entity by filters or throw exception.
public
findByOrFail(array<string|int, mixed> $filters[, array<string|int, mixed> $includes = [] ][, bool $useCache = true ]) : T
Parameters
- $filters : array<string|int, mixed>
-
Filter conditions
- $includes : array<string|int, mixed> = []
-
Relations to load
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Tags
Return values
T —Entity instance
findOrFail()
Find entity by ID or throw exception.
public
findOrFail(int|array<string|int, mixed> $id[, array<string|int, mixed> $includes = [] ][, bool $useCache = true ]) : T
Parameters
- $id : int|array<string|int, mixed>
-
Entity ID
- $includes : array<string|int, mixed> = []
-
Relations to load
- $useCache : bool = true
-
kept for backward compatibility; no effect here
Tags
Return values
T —Entity instance
findRelatedIds()
Returns ids of target entities linked to the parent through the relation collection (see the mapper).
public
findRelatedIds(int $parentId, string $property, string $targetAssociation) : array<int, int>|null
Parameters
- $parentId : int
- $property : string
- $targetAssociation : string
Return values
array<int, int>|nullgetTransactionLevel()
public
getTransactionLevel() : int
Return values
intgetTreeSubtreeIds()
Returns ids of a node and all its descendants, for a caller that needs to know the subtree before deleting it (image cleanup, {@see \App\Core\Traits\Api\Models\Managers\TreeTrait::deleteNode()}).
public
getTreeSubtreeIds(int $id, TreeConfig $config) : array<int, int>
It locks and changes nothing; a missing node gives an empty array.
Parameters
- $id : int
- $config : TreeConfig
Return values
array<int, int>insertTreeNode()
Computes `lft`/`rgt`/`level` of a newly inserted node (after `save()`); delegates to the mapper.
public
insertTreeNode(int $id, TreeConfig $config) : Result
The manager bumps the cache tag after commit.
Parameters
- $id : int
- $config : TreeConfig
Return values
ResultinTreeLock()
Runs the operation under the tree lock (table plus scope from the `TreeConfig`), so the manager holds the lock across the commit of its own transaction ({@see \App\Core\Traits\Api\Models\Managers\TreeTrait::treeTransaction()}).
public
inTreeLock(TreeConfig $config, callable(): Result $operation) : Result
Parameters
- $config : TreeConfig
- $operation : callable(): Result
Return values
ResultisInTransaction()
public
isInTransaction() : bool
Return values
boolmoveSubtree()
Moves a whole subtree under another parent; delegates to the mapper.
public
moveSubtree(int $id, int|null $newParentId, TreeConfig $config[, int|null $afterSiblingId = null ][, TreeMoveExpectation|null $expectation = null ]) : Result
The manager bumps the cache tag after commit.
Parameters
- $id : int
- $newParentId : int|null
-
null moves to the root level
- $config : TreeConfig
- $afterSiblingId : int|null = null
-
null = last child, 0 = first child, >0 = after that sibling
- $expectation : TreeMoveExpectation|null = null
-
client contract verified under the lock, null skips the check
Return values
ResultmoveTreeNode()
public
moveTreeNode(int $id, int $direction, TreeConfig $config) : Result
Parameters
- $id : int
- $direction : int
- $config : TreeConfig
Return values
ResultreorderTreeChildren()
Reorders the direct children of a parent into the given order; delegates to the mapper.
public
reorderTreeChildren(int|null $parentId, array<int, int> $orderedIds, TreeConfig $config) : Result
The manager bumps the cache tag after commit.
Parameters
- $parentId : int|null
-
null is the root level
- $orderedIds : array<int, int>
-
child ids in the new order
- $config : TreeConfig
Return values
ResultresolveEntityClass()
Exposes the root entity class of the service publicly.
public
resolveEntityClass() : TEntity>
getEntityClass() is protected in the traits (implemented per service); this method
makes it available to the manager so that the write endpoint takes the entity from the
presenter, not from the request.
Return values
TEntity>rollback()
public
rollback() : void
save()
Save entity.
public
save(TEntity $entity[, array<int, string> $skipFields = [] ]) : Result
Parameters
- $entity : TEntity
-
Entity to save
- $skipFields : array<int, string> = []
-
Field names to skip during UPDATE
Return values
Result —Success/error result
saveAll()
Save multiple entities at once.
public
saveAll(array<string|int, TEntity> $entities) : Result
Parameters
- $entities : array<string|int, TEntity>
-
Array of entities to save
Return values
Result —Success with saved count, or Error
updateAllRelations()
Update all relations of parent entity with given values.
public
updateAllRelations(array<string|int, mixed> $entityIds, string $property, array<string, mixed> $values) : Result
Parameters
- $entityIds : array<string|int, mixed>
- $property : string
- $values : array<string, mixed>
Return values
ResultupdateColumns()
Update specific columns.
public
updateColumns(array<string|int, mixed> $entityIds, array<string, mixed> $values) : Result
Parameters
- $entityIds : array<string|int, mixed>
-
Entity IDs (scalar or composite-id arrays)
- $values : array<string, mixed>
-
Column values to update (property name => value)
Return values
Result —Success/error result
updateRelations()
Update existing relation entities via Mapper.
public
updateRelations(array<string|int, mixed> $entityIds, string $property, array<string|int, object> $relationEntities) : Result
Parameters
- $entityIds : array<string|int, mixed>
- $property : string
- $relationEntities : array<string|int, object>
Return values
ResultdeleteWithoutCache()
Delete entity from database via Mapper.
protected
deleteWithoutCache(BaseEntity $entity) : Result
Called internally by delete() and deleteAll().
Parameters
- $entity : BaseEntity
Return values
ResultfindAllFromDb()
Load all entities from database via Mapper → Repository.
protected
findAllFromDb([array<string|int, mixed> $includes = [] ][, array<string|int, mixed> $filters = [] ][, array<string|int, mixed>|null $orderBy = null ][, int|null $offset = null ][, int|null $limit = null ][, array<string|int, mixed>|null $columns = null ][, bool $computeTotalCount = false ]) : T>, 1: int} : array{0: list>, 1: int})
Parameters
- $includes : array<string|int, mixed> = []
-
Relations to load
- $filters : array<string|int, mixed> = []
-
Filter conditions
- $orderBy : array<string|int, mixed>|null = null
-
Sorting
- $offset : int|null = null
-
Offset
- $limit : int|null = null
-
Limit
- $columns : array<string|int, mixed>|null = null
-
Columns to select
- $computeTotalCount : bool = false
-
Whether to compute total count
Return values
T>, 1: int} : array{0: listTuple: [entities (or $columns rows), totalCount]
findFromDb()
Load entity from database via Mapper.
protected
findFromDb(int|array<string|int, mixed> $id, array<string|int, mixed> $includes) : T|null
Parameters
- $id : int|array<string|int, mixed>
-
Entity ID
- $includes : array<string|int, mixed>
-
Relations to load
Return values
T|null —Entity or null
getEntityClass()
Returns the service entity class; every concrete service returns `Entity::class` matching its `@extends Service<Entity>`.
protected
getEntityClass() : TEntity>
Return values
TEntity>narrowEntities()
protected
narrowEntities(array<string|int, mixed> $items) : array<int, TEntity>
Parameters
- $items : array<string|int, mixed>
Return values
array<int, TEntity>narrowEntity()
Narrows the generic {@see BaseEntity} returned by the mapper to the service entity.
protected
narrowEntity(BaseEntity|null $entity) : TEntity|null
The service owns a single entity (getEntityClass()), so a foreign class means a wiring
error (wrong mapper), not a normal state.
Parameters
- $entity : BaseEntity|null
Return values
TEntity|nullnarrowRows()
Narrows query rows for `$columns` (no hydration); every item must be an array keyed by column names.
protected
narrowRows(array<string|int, mixed> $items) : array<int, array<string, mixed>>
Parameters
- $items : array<string|int, mixed>
Return values
array<int, array<string, mixed>>saveWithoutCache()
Save entity to database via Mapper.
protected
saveWithoutCache(BaseEntity $entity[, array<int, string> $skipFields = [] ]) : Result
Parameters
- $entity : BaseEntity
- $skipFields : array<int, string> = []
Return values
ResultupdateColumnsWithoutCache()
Update columns in database via Mapper.
protected
updateColumnsWithoutCache(array<string|int, mixed> $entityIds, array<string, mixed> $values) : Result
Parameters
- $entityIds : array<string|int, mixed>
- $values : array<string, mixed>
Return values
ResultdeleteTreeNodeUnderLock()
private
deleteTreeNodeUnderLock(int $id, TreeConfig $config) : Result
Parameters
- $id : int
- $config : TreeConfig
Return values
ResultisTechnicalDeleteFailure()
Technical failure = the persistence layer broke (exception attached or a technical code); the EntityManager may be closed and no further write may be attempted.
private
isTechnicalDeleteFailure(Result $result) : bool
Parameters
- $result : Result
Return values
boolrequireTreeMapper()
Mapper with tree operations; a service without a tree mapper has no such methods.
private
requireTreeMapper() : TreeMapperInterface