ApiClientRepository
extends OrmRepository
in package
uses
SaveTrait
External Api clients (`cms_system_api_clients`), their keys (`cms_system_api_client_keys`: several keys per application, optional expiry) and allowed roles (`cms_system_api_client_roles`).
Pure DBAL: key verification is one indexed query per external request (outside the cache) and management writes small rows, never whole entities through the hydrator, so a key digest cannot be smuggled in through a generic save.
Role codes are read by a JOIN on cms_system_user_roles; a role is just another row of that
table (flags implicit / conditional / privileged), no role code is hardcoded here.
Time: datetime columns are written and compared in the application time zone
(date.timezone), so conditions with "now" receive the time as a PHP parameter, never the
database NOW().
Table of Contents
Constants
- CLIENT_COLUMNS : string = 'c.api_client_id, c.name, c.enabled, c.created_...
- DATETIME : string = 'Y-m-d H:i:s'
- VALID_KEY_CONDITION : string = 'k.revoked_at IS NULL AND (k.expires_at IS NULL...
- Condition "the key is valid at :now" over the alias `k`.
Properties
- $db : EntityManagerInterface
- $entityManager : EntityManagerInterface
- $entityUpdater : EntityUpdater
- $managerProvider : ManagerProvider
- $transactionManager : TransactionManager
Methods
- __construct() : mixed
- beginTransaction() : void
- commit() : void
- computeTotalCount() : int
- Counts the total number of records with applied filters.
- countValidKeys() : int
- Counts the client's valid keys at `$now` (ceiling {@see \App\UI\Api\System\Models\Managers\ApiClientManager::MAX_VALID_KEYS}).
- delete() : Result
- deleteKey() : int
- Deletes a key, only an expired or revoked one (the condition is in the statement itself).
- deleteRelations() : Result
- Deletes rows from a relation table by parent FK column.
-
fetchByKeyHash()
: array{id: int, name: string, enabled: bool, revoked: bool, roleCodes: list
, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null, lastUsedAt: int|null, keyId: int, keyExpiresAt: int|null, keyRevoked: bool, keyLastUsedAt: int|null}|null - Returns the client record by key digest, with the allowed role codes and the key state; the only key verification query. `lastUsedAt`, `keyLastUsedAt` and `keyExpiresAt` are unix time.
- fetchKeyHashes() : array<int, string>
- Returns the digests of all client keys (only to purge the cache); they never leave the Api.
- fetchKeys() : array<int, array<int, array{id: int, keyPrefix: string, label: string|null, createdAt: string|null, expiresAt: string|null, revokedAt: string|null, lastUsedAt: string|null}>>
- Returns the client keys for the admin, without the digest. Newest first.
- fetchRoleIds() : array<int, array<int, int>>
- fetchRoleOptions() : array<int, array{id: int, code: string, name: string, implicit: string, conditional: bool, privileged: bool}>
- Returns all user roles with their flags (for choosing the client's allowed roles).
- fetchRows() : array<int, array{id: int, name: string, enabled: bool, revoked: bool, createdAt: string|null, updatedAt: string|null, revokedAt: string|null, lastUsedAt: string|null, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null}>
- Returns the client rows for the admin (without keys), ordered: active first, then revoked; by name.
- get() : BaseEntity|null
- Finds a single entity by ID with dynamic includes.
-
getAll()
: array{0: array
, 1: int} - getBy() : BaseEntity|null
- Finds a single entity by filters with dynamic includes.
- getEntityClass() : class-string
- Returns the entity class name.
- getTransactionLevel() : int
- insertClient() : int
- insertKey() : int
- Issues a key, only for a non-revoked client and only below the ceiling of valid keys. Both conditions are in the statement itself, so concurrent issuing cannot slip past the ceiling.
- insertRelations() : Result
- Inserts rows into a relation table.
- isInTransaction() : bool
- replaceRoles() : void
- Replaces the client's allowed roles (set-based: deletes the missing ones, adds the new ones).
- revokeKey() : int
- rollback() : void
- save() : Result
- selectRelationIds() : array<int, int>|null
- Returns ids of target records linked to the parent in a relation table (for example gallery asset ids).
- setTransactionManager() : void
- touchLastUsed() : int
- Updates `last_used_at` of the key and the client at most once per `$intervalSeconds`. The condition is in every `UPDATE`, so concurrent requests (several processes) write at most one row. Returns the number of changed rows.
- updateClient() : int
- updateColumns() : Result
- Updates a table using pre-prepared data from a mapper.
- updateEnabled() : int
- A revoked client cannot be enabled again; the condition is in the `UPDATE` itself.
- updateKey() : int
- Sets the key label and expiry; a revoked key (or a key of another client) is not changed.
- updateRelations() : Result
- Updates rows in a relation table.
- updateRevoked() : int
- Revokes the client and, at the same time, all its not yet revoked keys. Returns the number of revoked clients (0/1).
- applyFilters() : void
- applyIdentifierFilter() : void
- applyIncludes() : array<int, string>
- Applies includes as LEFT JOINs for hydration.
- applyOrdering() : void
- dumpDbError() : void
- entityHasMethod() : bool
- Checks whether the entity class has the given method (association).
- fetchCompleteEntities() : array<string|int, mixed>
- Second phase: pure hydration.
- fetchIdentifiers() : array<int, array<string|int, mixed>>
- fetchSimple() : array<string|int, mixed>
- getAssociationFromMethod() : string
- Derives the association name from a getter method.
- getClassMetadata() : ClassMetadata<string|int, object>
- Returns the Doctrine metadata for the entity.
- getDefaultIncludes() : array<string|int, string>
- Default includes for this entity.
- getEntityManager() : EntityManagerInterface
- Returns the EntityManager.
- getJoinedAliases() : array<string, true>
- Returns all aliases already joined in the QueryBuilder.
- getPrimaryKey() : string
- Returns the entity primary key.
- getPrimaryKeyField() : string
- Returns the primary key field name.
- getRelationField() : string|null
- Returns the field name of the relation to the target entity.
- getTableAlias() : string|null
- Reads the table alias from the `TableAlias` attribute, escaped against reserved keywords.
- hydrateByIdentifiers() : array<int, mixed>
- Loads entities by PK and hydrates includes while avoiding the cartesian explosion caused by parallel ToMany joins.
- isJoined() : bool
- Checks whether the alias is already joined in the QueryBuilder.
- mergeWithDefaults() : array<string|int, string>
- Merges explicit includes with the defaults.
- sortResultsByIdentifiers() : array<int, mixed>
- buildDistinctIdentityExpression() : string
- Returns the expression whose distinct values are the distinct rows of the root entity.
- extractIdsFromScalarResult() : array<int, int|string|array<string, mixed>>
- Converts the scalar result of `fetchIdentifiers()` (`{alias}_{field}` keys) to the format the hydrator expects (scalar for a single PK, `[field => value]` for a composite one).
- identityProjectionAlias() : string
- Alias of an `IDENTITY()` projection of an association identifier. Deliberately outside the `{alias}_{relation}` namespace JoinHelper uses for JOIN aliases.
- rawSqlParam() : mixed
- Returns the value for a bare SQL parameter (no DBAL type).
- toInt() : int
- toNullableInt() : int|null
- toNullableString() : string|null
- toTimestamp() : int|null
Constants
CLIENT_COLUMNS
private
string
CLIENT_COLUMNS
= 'c.api_client_id, c.name, c.enabled, c.created_at, c.revoked_at,' . ' c.last_used_at, c.updated_at, c.notes, c.limit_per_second, c.limit_per_minute, c.user_limit_per_minute,' . ' c.ip_limit_per_minute'
DATETIME
private
string
DATETIME
= 'Y-m-d H:i:s'
VALID_KEY_CONDITION
Condition "the key is valid at :now" over the alias `k`.
private
string
VALID_KEY_CONDITION
= 'k.revoked_at IS NULL AND (k.expires_at IS NULL OR k.expires_at > :now)'
Properties
$db
protected
EntityManagerInterface
$db
$entityManager
protected
EntityManagerInterface
$entityManager
$entityUpdater
protected
EntityUpdater
$entityUpdater
$managerProvider
protected
ManagerProvider
$managerProvider
$transactionManager
protected
TransactionManager
$transactionManager
Methods
__construct()
public
__construct(ManagerProvider $managerProvider, EntityUpdater $entityUpdater, EntityManagerInterface $entityManager, TransactionManager $transactionManager) : mixed
Parameters
- $managerProvider : ManagerProvider
- $entityUpdater : EntityUpdater
- $entityManager : EntityManagerInterface
- $transactionManager : TransactionManager
beginTransaction()
public
beginTransaction() : void
commit()
public
commit() : void
computeTotalCount()
Counts the total number of records with applied filters.
public
computeTotalCount(array<string|int, mixed> $filters) : int
Parameters
- $filters : array<string|int, mixed>
Return values
intcountValidKeys()
Counts the client's valid keys at `$now` (ceiling {@see \App\UI\Api\System\Models\Managers\ApiClientManager::MAX_VALID_KEYS}).
public
countValidKeys(int $clientId, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $now : DateTimeInterface
Return values
intdelete()
public
delete(BaseEntity $entity) : Result
Parameters
- $entity : BaseEntity
Return values
ResultdeleteKey()
Deletes a key, only an expired or revoked one (the condition is in the statement itself).
public
deleteKey(int $clientId, int $keyId, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $keyId : int
- $now : DateTimeInterface
Return values
intdeleteRelations()
Deletes rows from a relation table by parent FK column.
public
deleteRelations(string $table, string $column, array<string|int, mixed> $parentIds) : Result
Parameters
- $table : string
-
Table name
- $column : string
-
FK column name
- $parentIds : array<string|int, mixed>
-
Parent IDs to delete
Return values
ResultfetchByKeyHash()
Returns the client record by key digest, with the allowed role codes and the key state; the only key verification query. `lastUsedAt`, `keyLastUsedAt` and `keyExpiresAt` are unix time.
public
fetchByKeyHash(string $keyHash) : array{id: int, name: string, enabled: bool, revoked: bool, roleCodes: list, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null, lastUsedAt: int|null, keyId: int, keyExpiresAt: int|null, keyRevoked: bool, keyLastUsedAt: int|null}|null
Whether the key has expired is decided by the caller from the request time (the record may come from the cache).
Parameters
- $keyHash : string
Return values
array{id: int, name: string, enabled: bool, revoked: bool, roleCodes: listfetchKeyHashes()
Returns the digests of all client keys (only to purge the cache); they never leave the Api.
public
fetchKeyHashes(int $clientId) : array<int, string>
Parameters
- $clientId : int
Return values
array<int, string>fetchKeys()
Returns the client keys for the admin, without the digest. Newest first.
public
fetchKeys(array<int, int> $clientIds) : array<int, array<int, array{id: int, keyPrefix: string, label: string|null, createdAt: string|null, expiresAt: string|null, revokedAt: string|null, lastUsedAt: string|null}>>
Parameters
- $clientIds : array<int, int>
Return values
array<int, array<int, array{id: int, keyPrefix: string, label: string|null, createdAt: string|null, expiresAt: string|null, revokedAt: string|null, lastUsedAt: string|null}>> —client id => keys
fetchRoleIds()
public
fetchRoleIds(array<int, int> $clientIds) : array<int, array<int, int>>
Parameters
- $clientIds : array<int, int>
Return values
array<int, array<int, int>> —client id => ids of the allowed roles
fetchRoleOptions()
Returns all user roles with their flags (for choosing the client's allowed roles).
public
fetchRoleOptions() : array<int, array{id: int, code: string, name: string, implicit: string, conditional: bool, privileged: bool}>
Return values
array<int, array{id: int, code: string, name: string, implicit: string, conditional: bool, privileged: bool}>fetchRows()
Returns the client rows for the admin (without keys), ordered: active first, then revoked; by name.
public
fetchRows([int|null $clientId = null ]) : array<int, array{id: int, name: string, enabled: bool, revoked: bool, createdAt: string|null, updatedAt: string|null, revokedAt: string|null, lastUsedAt: string|null, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null}>
Parameters
- $clientId : int|null = null
Return values
array<int, array{id: int, name: string, enabled: bool, revoked: bool, createdAt: string|null, updatedAt: string|null, revokedAt: string|null, lastUsedAt: string|null, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null}>get()
Finds a single entity by ID with dynamic includes.
public
get(int $id[, string|array<string|int, mixed> $includes = [] ]) : BaseEntity|null
Delegates to getAll() with a PK filter and limit 1 and shares its two-phase fetch,
which avoids the LIMIT+JOIN problem (a join multiplies rows, so LIMIT 1 would cut
collection members).
Parameters
- $id : int
- $includes : string|array<string|int, mixed> = []
-
e.g. ['translations', 'routes.translations'] or '*' for all
Return values
BaseEntity|nullgetAll()
public
getAll([array<string|int, mixed>|null $filters = [] ][, array<string|int, mixed>|null $orderBys = null ][, int|null $offset = null ][, int|null $limit = null ][, array<string|int, mixed>|null $columns = null ][, bool $computeTotalCount = false ][, string|array<string|int, mixed> $includes = [] ]) : array{0: array, 1: int}
Parameters
- $filters : array<string|int, mixed>|null = []
- $orderBys : array<string|int, mixed>|null = null
- $offset : int|null = null
- $limit : int|null = null
- $columns : array<string|int, mixed>|null = null
- $computeTotalCount : bool = false
- $includes : string|array<string|int, mixed> = []
Return values
array{0: array[entities (or $columns rows), total count]
getBy()
Finds a single entity by filters with dynamic includes.
public
getBy([array<string|int, mixed>|null $filters = null ][, string|array<string|int, mixed> $includes = [] ]) : BaseEntity|null
Delegates to getAll() with limit 1 and shares its two-phase fetch, which avoids the
LIMIT+JOIN problem (a join multiplies rows, so LIMIT 1 would cut collection members).
Parameters
- $filters : array<string|int, mixed>|null = null
- $includes : string|array<string|int, mixed> = []
-
e.g. ['translations', 'routes.translations'] or '*' for all
Return values
BaseEntity|nullgetEntityClass()
Returns the entity class name.
public
getEntityClass() : class-string
Return values
class-stringgetTransactionLevel()
public
getTransactionLevel() : int
Return values
intinsertClient()
public
insertClient(array{name: string, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null} $values, DateTimeInterface $now) : int
Parameters
- $values : array{name: string, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null}
- $now : DateTimeInterface
Return values
intinsertKey()
Issues a key, only for a non-revoked client and only below the ceiling of valid keys. Both conditions are in the statement itself, so concurrent issuing cannot slip past the ceiling.
public
insertKey(int $clientId, string $keyHash, string $keyPrefix, string|null $label, DateTimeInterface|null $expiresAt, DateTimeInterface $now, int $maxValidKeys) : int
Returns the key id, or 0 when a condition is not met.
Parameters
- $clientId : int
- $keyHash : string
- $keyPrefix : string
- $label : string|null
- $expiresAt : DateTimeInterface|null
- $now : DateTimeInterface
- $maxValidKeys : int
Return values
intinsertRelations()
Inserts rows into a relation table.
public
insertRelations(string $table, array<int, array<string, mixed>> $rows) : Result
Parameters
- $table : string
-
Table name
- $rows : array<int, array<string, mixed>>
-
Array of ['column_name' => value] rows
Return values
ResultisInTransaction()
public
isInTransaction() : bool
Return values
boolreplaceRoles()
Replaces the client's allowed roles (set-based: deletes the missing ones, adds the new ones).
public
replaceRoles(int $clientId, array<int, int> $roleIds) : void
Parameters
- $clientId : int
- $roleIds : array<int, int>
revokeKey()
public
revokeKey(int $clientId, int $keyId, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $keyId : int
- $now : DateTimeInterface
Return values
introllback()
public
rollback() : void
save()
public
save(T $entity[, array<string|int, string> $skipFields = [] ]) : Result
Parameters
- $entity : T
- $skipFields : array<string|int, string> = []
-
Field names to skip during UPDATE (e.g. ['userRoles'])
Return values
ResultselectRelationIds()
Returns ids of target records linked to the parent in a relation table (for example gallery asset ids).
public
selectRelationIds(string $table, string $parentColumn, int $parentId, string $targetColumn) : array<int, int>|null
Soft-removed rows are read too, as they still belong to the same parent. On a database
error it returns null; the caller decides, and a foreign id must then not be accepted.
Parameters
- $table : string
- $parentColumn : string
- $parentId : int
- $targetColumn : string
Return values
array<int, int>|nullsetTransactionManager()
public
setTransactionManager(TransactionManager $transactionManager) : void
Parameters
- $transactionManager : TransactionManager
touchLastUsed()
Updates `last_used_at` of the key and the client at most once per `$intervalSeconds`. The condition is in every `UPDATE`, so concurrent requests (several processes) write at most one row. Returns the number of changed rows.
public
touchLastUsed(int $clientId, int $keyId, DateTimeInterface $now, int $intervalSeconds) : int
Parameters
- $clientId : int
- $keyId : int
- $now : DateTimeInterface
- $intervalSeconds : int
Return values
intupdateClient()
public
updateClient(int $clientId, array{name: string, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null} $values, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $values : array{name: string, notes: string|null, limitPerSecond: int|null, limitPerMinute: int|null, userLimitPerMinute: int|null, ipLimitPerMinute: int|null}
- $now : DateTimeInterface
Return values
intupdateColumns()
Updates a table using pre-prepared data from a mapper.
public
updateColumns(array<string, mixed> $set, array<int, array<string, array{column: string, value: mixed}>> $where, string $table) : Result
Parameters
- $set : array<string, mixed>
-
['column_name' => value|SqlValue]
- $where : array<int, array<string, array{column: string, value: mixed}>>
-
[[ 'paramName' => ['column'=>col, 'value'=>val], ... ], ... ]
- $table : string
-
table name
Return values
ResultupdateEnabled()
A revoked client cannot be enabled again; the condition is in the `UPDATE` itself.
public
updateEnabled(int $clientId, bool $enabled, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $enabled : bool
- $now : DateTimeInterface
Return values
intupdateKey()
Sets the key label and expiry; a revoked key (or a key of another client) is not changed.
public
updateKey(int $clientId, int $keyId, string|null $label, DateTimeInterface|null $expiresAt) : int
Parameters
- $clientId : int
- $keyId : int
- $label : string|null
- $expiresAt : DateTimeInterface|null
Return values
intupdateRelations()
Updates rows in a relation table.
public
updateRelations(string $table, array<int, array{set: array, where: array}> $updates) : Result
Parameters
- $table : string
-
Table name
-
$updates
: array<int, array{set: array
, where: array }> -
Array of ['set' => [col => val], 'where' => [col => val]]
Return values
ResultupdateRevoked()
Revokes the client and, at the same time, all its not yet revoked keys. Returns the number of revoked clients (0/1).
public
updateRevoked(int $clientId, DateTimeInterface $now) : int
Parameters
- $clientId : int
- $now : DateTimeInterface
Return values
intapplyFilters()
protected
applyFilters(QueryBuilder $qb, array<string|int, mixed>|null $filters, class-string $entityClass, string $fromTableAlias) : void
Parameters
- $qb : QueryBuilder
- $filters : array<string|int, mixed>|null
- $entityClass : class-string
- $fromTableAlias : string
applyIdentifierFilter()
protected
applyIdentifierFilter(QueryBuilder $qb, array<int, array<string|int, mixed>> $identifiers, array<int, string> $identifierFields, string $fromTableAlias) : void
Parameters
- $qb : QueryBuilder
- $identifiers : array<int, array<string|int, mixed>>
- $identifierFields : array<int, string>
- $fromTableAlias : string
applyIncludes()
Applies includes as LEFT JOINs for hydration.
protected
applyIncludes(QueryBuilder $qb, array<string|int, mixed> $includes, string $alias[, array<string|int, mixed> &$joinedAliases = [] ][, class-string|null $entityClass = null ]) : array<int, string>
Hydration only: no WHERE and no WITH conditions. Filters are applied in the first phase
(fetchIdentifiers()) over ids; the second phase only loads the full collections for the
selected entities.
Parameters
- $qb : QueryBuilder
-
Query builder
- $includes : array<string|int, mixed>
-
Includes, e.g. ['translations', 'routes.translations']
- $alias : string
-
Alias of the root entity
- $joinedAliases : array<string|int, mixed> = []
-
Reference tracking already-joined aliases
- $entityClass : class-string|null = null
-
Root entity for resolving includes (default: root repo entity)
Return values
array<int, string> —Aliases for the SELECT
applyOrdering()
protected
applyOrdering(QueryBuilder $qb, array<string|int, mixed>|null $orderBys, string $fromTableAlias) : void
Parameters
- $qb : QueryBuilder
- $orderBys : array<string|int, mixed>|null
- $fromTableAlias : string
dumpDbError()
protected
dumpDbError(Throwable $e) : void
Parameters
- $e : Throwable
entityHasMethod()
Checks whether the entity class has the given method (association).
protected
entityHasMethod(string $entityClass, string $method) : bool
Parameters
- $entityClass : string
- $method : string
-
Method name, e.g. 'getTranslations'
Return values
boolfetchCompleteEntities()
Second phase: pure hydration.
protected
fetchCompleteEntities(class-string $entityClass, string $fromTableAlias, array<int, array<string|int, mixed>> $identifiers, array<string|int, mixed>|null $columns, array<string|int, mixed> $includes[, array<string|int, mixed>|null $filters = null ]) : array<string|int, mixed>
Delegates to the shared hydrateByIdentifiers() of OrmRepository, which loads
to-many branches separately via WHERE parent_id IN (:ids) (no cartesian explosion
from parallel to-many joins).
Parameters
- $entityClass : class-string
- $fromTableAlias : string
- $identifiers : array<int, array<string|int, mixed>>
- $columns : array<string|int, mixed>|null
- $includes : array<string|int, mixed>
- $filters : array<string|int, mixed>|null = null
Return values
array<string|int, mixed>fetchIdentifiers()
protected
fetchIdentifiers(class-string $entityClass, string $fromTableAlias, array<string|int, mixed>|null $filters, array<string|int, mixed>|null $orderBys, int|null $offset, int|null $limit) : array<int, array<string|int, mixed>>
Parameters
- $entityClass : class-string
- $fromTableAlias : string
- $filters : array<string|int, mixed>|null
- $orderBys : array<string|int, mixed>|null
- $offset : int|null
- $limit : int|null
Return values
array<int, array<string|int, mixed>> —scalar rows keyed {alias}_{field}
fetchSimple()
protected
fetchSimple(class-string $entityClass, string $fromTableAlias, array<string|int, mixed>|null $filters, array<string|int, mixed>|null $orderBys, int|null $offset, int|null $limit, array<string|int, mixed>|null $columns) : array<string|int, mixed>
Parameters
- $entityClass : class-string
- $fromTableAlias : string
- $filters : array<string|int, mixed>|null
- $orderBys : array<string|int, mixed>|null
- $offset : int|null
- $limit : int|null
- $columns : array<string|int, mixed>|null
Return values
array<string|int, mixed>getAssociationFromMethod()
Derives the association name from a getter method.
protected
getAssociationFromMethod(string $method) : string
E.g. 'getTranslations' → 'translations'.
Parameters
- $method : string
Return values
stringgetClassMetadata()
Returns the Doctrine metadata for the entity.
protected
getClassMetadata() : ClassMetadata<string|int, object>
Return values
ClassMetadata<string|int, object>getDefaultIncludes()
Default includes for this entity.
protected
getDefaultIncludes() : array<string|int, string>
Override in a child, or place the DefaultIncludes attribute on the entity.
Return values
array<string|int, string>getEntityManager()
Returns the EntityManager.
protected
getEntityManager() : EntityManagerInterface
Return values
EntityManagerInterfacegetJoinedAliases()
Returns all aliases already joined in the QueryBuilder.
protected
getJoinedAliases(QueryBuilder $qb) : array<string, true>
Parameters
- $qb : QueryBuilder
Return values
array<string, true>getPrimaryKey()
Returns the entity primary key.
protected
getPrimaryKey() : string
Return values
stringgetPrimaryKeyField()
Returns the primary key field name.
protected
getPrimaryKeyField() : string
Return values
stringgetRelationField()
Returns the field name of the relation to the target entity.
protected
getRelationField(class-string $entityClass, string $targetEntity) : string|null
Parameters
- $entityClass : class-string
- $targetEntity : string
Return values
string|nullgetTableAlias()
Reads the table alias from the `TableAlias` attribute, escaped against reserved keywords.
protected
getTableAlias(class-string $entityClass) : string|null
The attribute value is used verbatim as the QueryBuilder identification variable and FROM
alias (e.g. ->createQueryBuilder('order')), and the query lexer treats a bare reserved word
as a keyword token instead of an identifier, which breaks the parser. Escaping here, the
single choke point that find() and getAll() funnel through, protects every present and
future entity without hand-picking alias strings per #[TableAlias] declaration.
Parameters
- $entityClass : class-string
Return values
string|nullhydrateByIdentifiers()
Loads entities by PK and hydrates includes while avoiding the cartesian explosion caused by parallel ToMany joins.
protected
hydrateByIdentifiers(class-string $entityClass, string $fromTableAlias, array<string|int, int|string|array<string, mixed>> $ids, array<string|int, mixed> $includes[, array<string|int, mixed>|null $columns = null ][, array<string|int, mixed>|null $filters = null ]) : array<int, mixed>
The root query holds only ToOne chains (no DISTINCT / temp table). Every
ToMany branch is loaded separately via WHERE parent_id IN (:ids) and the
collections are then stitched back onto the parent entities in PHP.
Filters (if passed) propagate into the ToMany branches only when they opt in with
BaseFilter::setApplyToCollections(true): such a filter with the dot-path A.B.C
is applied to every branch whose path is a prefix of it (with matching trimming).
Other filters, and those with isPhaseOneOnly() === true, stay purely in phase 1,
so included collections are loaded complete. See filtersForBranch().
Parameters
- $entityClass : class-string
-
Root entity
- $fromTableAlias : string
-
alias of the root entity in the query
- $ids : array<string|int, int|string|array<string, mixed>>
-
PK list: either scalar (single-column PK), or [field => value] (composite)
- $includes : array<string|int, mixed>
-
Dot-notation includes, e.g. ['routes.translations', 'parameterRels.parameter']
- $columns : array<string|int, mixed>|null = null
-
Column restriction on the root entity
- $filters : array<string|int, mixed>|null = null
-
Phase 1 filters (for propagation into ToMany branches)
Return values
array<int, mixed> —entities (or column rows when $columns is given)
isJoined()
Checks whether the alias is already joined in the QueryBuilder.
protected
isJoined(QueryBuilder $qb, string $alias) : bool
Parameters
- $qb : QueryBuilder
- $alias : string
Return values
boolmergeWithDefaults()
Merges explicit includes with the defaults.
protected
mergeWithDefaults(string|array<string|int, string> $includes) : array<string|int, string>
Passing '*' loads every association of the entity.
Parameters
- $includes : string|array<string|int, string>
-
Explicit includes, or '*' for all
Return values
array<string|int, string> —Merged includes
sortResultsByIdentifiers()
protected
sortResultsByIdentifiers(array<string|int, mixed> $results, array<int, array<string|int, mixed>> $identifiers, class-string $entityClass, string $fromTableAlias) : array<int, mixed>
Parameters
- $results : array<string|int, mixed>
- $identifiers : array<int, array<string|int, mixed>>
- $entityClass : class-string
- $fromTableAlias : string
Return values
array<int, mixed>buildDistinctIdentityExpression()
Returns the expression whose distinct values are the distinct rows of the root entity.
private
buildDistinctIdentityExpression(ClassMetadata<string|int, object> $metadata, string $alias) : string
IMPORTANT: every identifier component must be counted. Counting only the first one makes a
compound identity (CategoryText = languageId + parent) return the number of distinct
languages instead of rows. DQL has no multi-column COUNT(DISTINCT), so the components are
joined with a separator; an association component goes through IDENTITY() (its FK), a
scalar one directly. CONCAT is portable (the SQLite platform renders ||), and the
separator cannot occur inside integer keys.
Parameters
- $metadata : ClassMetadata<string|int, object>
- $alias : string
Return values
stringextractIdsFromScalarResult()
Converts the scalar result of `fetchIdentifiers()` (`{alias}_{field}` keys) to the format the hydrator expects (scalar for a single PK, `[field => value]` for a composite one).
private
extractIdsFromScalarResult(array<int, array<string|int, mixed>> $identifiers, array<int, string> $identifierFields, string $fromTableAlias) : array<int, int|string|array<string, mixed>>
Parameters
- $identifiers : array<int, array<string|int, mixed>>
- $identifierFields : array<int, string>
- $fromTableAlias : string
Return values
array<int, int|string|array<string, mixed>>identityProjectionAlias()
Alias of an `IDENTITY()` projection of an association identifier. Deliberately outside the `{alias}_{relation}` namespace JoinHelper uses for JOIN aliases.
private
static identityProjectionAlias(string $fromTableAlias, string $idField) : string
Parameters
- $fromTableAlias : string
- $idField : string
Return values
stringrawSqlParam()
Returns the value for a bare SQL parameter (no DBAL type).
private
rawSqlParam(mixed $value) : mixed
The mysqli driver would send false as '' and cannot bind a DateTime at all; a database
in strict mode then rejects the write ("Incorrect integer value"), while a non-strict one
silently converts it to 0.
Parameters
- $value : mixed
toInt()
private
static toInt(mixed $value) : int
Parameters
- $value : mixed
Return values
inttoNullableInt()
private
static toNullableInt(mixed $value) : int|null
Parameters
- $value : mixed
Return values
int|nulltoNullableString()
private
static toNullableString(mixed $value) : string|null
Parameters
- $value : mixed
Return values
string|nulltoTimestamp()
private
static toTimestamp(mixed $value) : int|null
Parameters
- $value : mixed