UserRepository
extends OrmRepository
in package
uses
SaveTrait
Abstract ORM repository with dynamic-includes support.
Every concrete repository extends this class.
Table of Contents
Constants
- CREDIT_REWARDS_TABLE : string = 'cms_system_user_credit_rewards'
- DISCOUNT_LEVELS_TABLE : string = 'cms_system_user_discount_levels'
- EMAIL_CHUNK : int = 1000
- Maximum number of ids sent in one `IN (...)` query; caps a batch digest.
- PAYMENTS_TABLE : string = 'cms_system_payments'
- PROFILE_COMPLETE_CONDITION : string = "TRIM(`firstname`) <> '' AND TRIM(`lastname`) <...
- SQL condition of a complete profile: name, address, phone, "about me" and the profile photo; a company account also the company name and registration number.
- REFERRAL_REWARDS_TABLE : string = 'cms_system_user_referral_rewards'
- USERS_TABLE : string = 'cms_system_users'
Properties
- $db : EntityManagerInterface
- $entityManager : EntityManagerInterface
- $entityUpdater : EntityUpdater
- $managerProvider : ManagerProvider
- $transactionManager : TransactionManager
Methods
- __construct() : mixed
- anonymizeClaimedAccount() : bool
- Final overwrite of the account row, in one statement guarded by the claim: it happens only while the run still owns the account (`deletion_nonce` = its claim, not deleted). `$values` = property => value of {@see User}, mapped to columns here.
- assignReferralCode() : void
- Gives the user a referral code when they have none: base36 of the id plus the given random suffix.
- attachReferrer() : void
- Stores who referred the user and the code used. Only once: an already linked account keeps its referrer.
- beginTransaction() : void
- cancelAccountDeletion() : bool
- Cancels the pending request only when it is still exactly the one the caller has read and no anonymization holds a fresh claim. 0 rows = refused: the account was anonymized, claimed, cancelled or requested again in the meantime — the caller must not report a success.
- claimAccountDeletion() : bool
- Atomically claims a pending account deletion before the anonymization takes any irreversible step. Compare-and-set on the exact request the caller has read (`deletion_requested_at` + `deletion_nonce`, null-safe for requests made before the nonce existed), not deleted, and no fresh claim of another run. The claim replaces the nonce with `$claimNonce` — from now on the run checks it owns the account by that value.
- commit() : void
- computeTotalCount() : int
- Counts the total number of records with applied filters.
- countCreditedContentRewardsSince() : int
- Count of credited content rewards of the user for the action created at or after `$since`.
- countCreditedReferralRewardsSince() : int
- Count of credited referral rewards of the referrer created at or after `$since`.
- creditPoints() : void
- Credits loyalty points and recomputes the discount band.
- debitCreditsForPayment() : CreditDebitOutcome
- Atomically debits the price of a credit payment, at most once per payment.
- delete() : Result
- deleteRelations() : Result
- Deletes rows from a relation table by parent FK column.
- findContact() : array{email: ?string, phone: ?string}|null
- Stored e-mail and phone of the account (scalar read for the save hook, an ORM read would clear the unit of work there); `null` when the row does not exist.
- findEmailsByIds() : array<int, string>
- Returns e-mails of users by id in a single query (chunked by {@see self::EMAIL_CHUNK}).
- findRatingThreadIds() : array<int, int>
- Returns the profile rating thread of existing users (`user_id => rating thread id`); a user without a thread or a missing user is left out. The delete reads it before and after, so it deletes the threads of exactly the users that are gone ({@see \App\UI\Api\System\Models\Managers\UserManager::delete()}).
- findReferralState() : array{ip: ?string, referrerUserId: ?int, referrerIp: ?string, referrerActive: bool, referrerAffiliateDisabled: bool}|null
- Data needed to reward an activated account: its IP and the state of its referrer.
- findReferralStats() : array{registered: int, activated: int, credits: int}
- Referral statistics of the user for the "My account" page.
- findReferrerByCode() : array{userId: int, ip: ?string, affiliateDisabled: bool}|null
- Active account that owns the referral code (activated, not banned, not deleted), or null.
- findUserTypeRequirements() : array{companyData: bool, vatId: bool}|null
- What the user type requires (`cms_system_user_types`): company data (reg. id and company name) and the VAT id.
- 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
- grantContentReward() : bool
- Records the reward and adds the credits in one transaction, at most once per content record (unique key `action` + `subject_id`). A skipped reward (`$skipReason`) is recorded with zero credits, so it is never re-evaluated later.
- grantReferralReward() : bool
- Records the reward and adds the credits in one transaction, at most once per referred account and type (unique key `referred_user_id` + `type`). A skipped reward (`$skipReason`) is recorded with zero credits, so it is never re-evaluated later.
- hasContentReward() : bool
- Whether the content record already has a reward decision (credited or skipped).
- holdsAccountDeletionClaim() : bool
- Tells whether the run still holds its claim; asked before every module step.
- insertRelations() : Result
- Inserts rows into a relation table.
- isAnonymized() : bool
- Account deletion — is the stored account anonymized (`deleted_at` set)? Scalar DBAL read on purpose: the save hook asks it mid-save, and an ORM read would clear the unit of work there.
- isInTransaction() : bool
- isSignInAllowedNow() : bool
- Tells whether the account may sign in right now. Scalar read of the stored row (bypasses the query cache and the identity map): active, not banned, no pending deletion, not anonymized. Unknown account = false.
- markProfileCompleted() : bool
- Sets the one-way flag `profile_completed_at` when the profile is complete for the first time and, in the same transaction, pays `$credits` (0 = only the flag). The flag is never cleared, so a profile completed once is never paid again, even after it is emptied and filled in anew; the ledger row (unique `action` + `subject_id`) is a second guard.
- 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
- updateColumns() : Result
- Updates a table using pre-prepared data from a mapper.
- updateRelations() : Result
- Updates rows in a relation table.
- 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).
Constants
CREDIT_REWARDS_TABLE
private
string
CREDIT_REWARDS_TABLE
= 'cms_system_user_credit_rewards'
DISCOUNT_LEVELS_TABLE
private
string
DISCOUNT_LEVELS_TABLE
= 'cms_system_user_discount_levels'
EMAIL_CHUNK
Maximum number of ids sent in one `IN (...)` query; caps a batch digest.
private
int
EMAIL_CHUNK
= 1000
PAYMENTS_TABLE
private
string
PAYMENTS_TABLE
= 'cms_system_payments'
PROFILE_COMPLETE_CONDITION
SQL condition of a complete profile: name, address, phone, "about me" and the profile photo; a company account also the company name and registration number.
private
string
PROFILE_COMPLETE_CONDITION
= "TRIM(`firstname`) <> '' AND TRIM(`lastname`) <> ''\n AND TRIM(COALESCE(`street`, '')) <> '' AND TRIM(COALESCE(`city`, '')) <> '' AND TRIM(COALESCE(`zip`, '')) <> ''\n AND TRIM(COALESCE(`phone`, '')) <> '' AND TRIM(COALESCE(`about_me`, '')) <> '' AND TRIM(COALESCE(`asset_path`, '')) <> ''\n AND (`is_company` = 0 OR (TRIM(COALESCE(`company_name`, '')) <> '' AND TRIM(COALESCE(`reg_id`, '')) <> ''))"
REFERRAL_REWARDS_TABLE
private
string
REFERRAL_REWARDS_TABLE
= 'cms_system_user_referral_rewards'
USERS_TABLE
private
string
USERS_TABLE
= 'cms_system_users'
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
anonymizeClaimedAccount()
Final overwrite of the account row, in one statement guarded by the claim: it happens only while the run still owns the account (`deletion_nonce` = its claim, not deleted). `$values` = property => value of {@see User}, mapped to columns here.
public
anonymizeClaimedAccount(int $userId, string $claimNonce, array<string, scalar|DateTimeInterface|null> $values) : bool
Parameters
- $userId : int
- $claimNonce : string
- $values : array<string, scalar|DateTimeInterface|null>
Return values
bool —true = overwritten; false = the claim was lost, nothing written
assignReferralCode()
Gives the user a referral code when they have none: base36 of the id plus the given random suffix.
public
assignReferralCode(int $userId, string $suffix) : void
The id part makes the code unique without a collision check (the suffix length is fixed).
Parameters
- $userId : int
- $suffix : string
attachReferrer()
Stores who referred the user and the code used. Only once: an already linked account keeps its referrer.
public
attachReferrer(int $userId, int $referrerUserId, string $code) : void
Parameters
- $userId : int
- $referrerUserId : int
- $code : string
beginTransaction()
public
beginTransaction() : void
cancelAccountDeletion()
Cancels the pending request only when it is still exactly the one the caller has read and no anonymization holds a fresh claim. 0 rows = refused: the account was anonymized, claimed, cancelled or requested again in the meantime — the caller must not report a success.
public
cancelAccountDeletion(int $userId, DateTimeInterface $requestedAt, string|null $nonce, DateTimeInterface $staleBefore) : bool
Parameters
- $userId : int
- $requestedAt : DateTimeInterface
- $nonce : string|null
- $staleBefore : DateTimeInterface
Return values
boolclaimAccountDeletion()
Atomically claims a pending account deletion before the anonymization takes any irreversible step. Compare-and-set on the exact request the caller has read (`deletion_requested_at` + `deletion_nonce`, null-safe for requests made before the nonce existed), not deleted, and no fresh claim of another run. The claim replaces the nonce with `$claimNonce` — from now on the run checks it owns the account by that value.
public
claimAccountDeletion(int $userId, DateTimeInterface $requestedAt, string|null $nonce, string $claimNonce, DateTimeInterface $now, DateTimeInterface $staleBefore) : bool
Parameters
- $userId : int
- $requestedAt : DateTimeInterface
- $nonce : string|null
- $claimNonce : string
- $now : DateTimeInterface
- $staleBefore : DateTimeInterface
Return values
bool —true = this run holds the claim
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
intcountCreditedContentRewardsSince()
Count of credited content rewards of the user for the action created at or after `$since`.
public
countCreditedContentRewardsSince(int $userId, string $action, DateTimeInterface $since) : int
Parameters
- $userId : int
- $action : string
- $since : DateTimeInterface
Return values
intcountCreditedReferralRewardsSince()
Count of credited referral rewards of the referrer created at or after `$since`.
public
countCreditedReferralRewardsSince(int $referrerUserId, DateTimeInterface $since) : int
Parameters
- $referrerUserId : int
- $since : DateTimeInterface
Return values
intcreditPoints()
Credits loyalty points and recomputes the discount band.
public
creditPoints(Connection $conn, int $userId, int $points) : void
IMPORTANT: $conn is the caller's connection (see
EmailQueueManager::enqueue()) and the write must join its
already open transaction: atomicity with the one-way gate orders.user_is_updated is the only
thing preventing a double credit. This method never opens its own transaction.
$points <= 0 is not a no-op: the discount band is recomputed anyway, because points may have
changed elsewhere. When no band matches the current total, the level is left unchanged, never reset.
Parameters
- $conn : Connection
- $userId : int
- $points : int
debitCreditsForPayment()
Atomically debits the price of a credit payment, at most once per payment.
public
debitCreditsForPayment(int $userId, int $paymentId, int $amount) : CreditDebitOutcome
IMPORTANT: a read-compare-write in PHP would let two concurrent credit payments of one user read
the same balance, and the second write would overwrite the first (only one payment is charged).
A single UPDATE with the condition credits >= amount leaves the decision to the database.
credits is int unsigned, so a negative balance cannot occur even by mistake.
IMPORTANT: the same statement marks the payment as paid (payment_datetime) and requires it was
not paid yet (payment_datetime IS NULL, not completed/refunded). The debit and its mark are one
write, so the price is never debited twice for the same payment: not by a repeated execute URL
of a paid payment the administrator cancelled afterwards (cancelled + payment_datetime kept),
not by a retry after the status write failed, and not by two concurrent requests.
The payment must belong to the user. When nothing was written, the payment row tells why.
Parameters
- $userId : int
- $paymentId : int
- $amount : int
Return values
CreditDebitOutcomedelete()
public
delete(BaseEntity $entity) : Result
Parameters
- $entity : BaseEntity
Return values
ResultdeleteRelations()
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
ResultfindContact()
Stored e-mail and phone of the account (scalar read for the save hook, an ORM read would clear the unit of work there); `null` when the row does not exist.
public
findContact(int $userId) : array{email: ?string, phone: ?string}|null
Parameters
- $userId : int
Return values
array{email: ?string, phone: ?string}|nullfindEmailsByIds()
Returns e-mails of users by id in a single query (chunked by {@see self::EMAIL_CHUNK}).
public
findEmailsByIds(array<string|int, int> $userIds) : array<int, string>
Users without an e-mail are omitted from the result, so a caller recognizes an undeliverable user
the same way as with IS NOT NULL. Used by the Bazaar/Comcat watchdog selects as the fallback
for COALESCE(watchdog.email, user.email).
Parameters
- $userIds : array<string|int, int>
Return values
array<int, string> —user_id => email
findRatingThreadIds()
Returns the profile rating thread of existing users (`user_id => rating thread id`); a user without a thread or a missing user is left out. The delete reads it before and after, so it deletes the threads of exactly the users that are gone ({@see \App\UI\Api\System\Models\Managers\UserManager::delete()}).
public
findRatingThreadIds(array<string|int, int> $userIds) : array<int, int>
Parameters
- $userIds : array<string|int, int>
Return values
array<int, int>findReferralState()
Data needed to reward an activated account: its IP and the state of its referrer.
public
findReferralState(int $userId) : array{ip: ?string, referrerUserId: ?int, referrerIp: ?string, referrerActive: bool, referrerAffiliateDisabled: bool}|null
Parameters
- $userId : int
Return values
array{ip: ?string, referrerUserId: ?int, referrerIp: ?string, referrerActive: bool, referrerAffiliateDisabled: bool}|nullfindReferralStats()
Referral statistics of the user for the "My account" page.
public
findReferralStats(int $userId) : array{registered: int, activated: int, credits: int}
Parameters
- $userId : int
Return values
array{registered: int, activated: int, credits: int}findReferrerByCode()
Active account that owns the referral code (activated, not banned, not deleted), or null.
public
findReferrerByCode(string $code) : array{userId: int, ip: ?string, affiliateDisabled: bool}|null
Parameters
- $code : string
Return values
array{userId: int, ip: ?string, affiliateDisabled: bool}|nullfindUserTypeRequirements()
What the user type requires (`cms_system_user_types`): company data (reg. id and company name) and the VAT id.
public
findUserTypeRequirements(int $userTypeId) : array{companyData: bool, vatId: bool}|null
null when the type does not exist. A scalar read for the save hook.
Parameters
- $userTypeId : int
Return values
array{companyData: bool, vatId: bool}|nullget()
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
intgrantContentReward()
Records the reward and adds the credits in one transaction, at most once per content record (unique key `action` + `subject_id`). A skipped reward (`$skipReason`) is recorded with zero credits, so it is never re-evaluated later.
public
grantContentReward(int $userId, string $action, int $subjectId, int $credits, string|null $skipReason) : bool
Parameters
- $userId : int
- $action : string
- $subjectId : int
- $credits : int
- $skipReason : string|null
Return values
bool —true when the row was written now (false = already recorded earlier)
grantReferralReward()
Records the reward and adds the credits in one transaction, at most once per referred account and type (unique key `referred_user_id` + `type`). A skipped reward (`$skipReason`) is recorded with zero credits, so it is never re-evaluated later.
public
grantReferralReward(int $recipientUserId, int $referredUserId, string $type, int $credits, string|null $skipReason) : bool
Parameters
- $recipientUserId : int
- $referredUserId : int
- $type : string
- $credits : int
- $skipReason : string|null
Return values
bool —true when the row was written now (false = already recorded earlier)
hasContentReward()
Whether the content record already has a reward decision (credited or skipped).
public
hasContentReward(string $action, int $subjectId) : bool
Parameters
- $action : string
- $subjectId : int
Return values
boolholdsAccountDeletionClaim()
Tells whether the run still holds its claim; asked before every module step.
public
holdsAccountDeletionClaim(int $userId, string $claimNonce) : bool
Scalar read straight from the DB (no ORM read — it would clear the unit of work).
Parameters
- $userId : int
- $claimNonce : string
Return values
boolinsertRelations()
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
ResultisAnonymized()
Account deletion — is the stored account anonymized (`deleted_at` set)? Scalar DBAL read on purpose: the save hook asks it mid-save, and an ORM read would clear the unit of work there.
public
isAnonymized(int $userId) : bool
Parameters
- $userId : int
Return values
boolisInTransaction()
public
isInTransaction() : bool
Return values
boolisSignInAllowedNow()
Tells whether the account may sign in right now. Scalar read of the stored row (bypasses the query cache and the identity map): active, not banned, no pending deletion, not anonymized. Unknown account = false.
public
isSignInAllowedNow(int $userId) : bool
Parameters
- $userId : int
Return values
boolmarkProfileCompleted()
Sets the one-way flag `profile_completed_at` when the profile is complete for the first time and, in the same transaction, pays `$credits` (0 = only the flag). The flag is never cleared, so a profile completed once is never paid again, even after it is emptied and filled in anew; the ledger row (unique `action` + `subject_id`) is a second guard.
public
markProfileCompleted(int $userId, int $credits, string $action) : bool
Parameters
- $userId : int
- $credits : int
- $action : string
Return values
bool —true when the flag was set now (false = incomplete or flagged earlier)
rollback()
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
updateColumns()
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
ResultupdateRelations()
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
ResultapplyFilters()
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