CoolCMS API

UserRepository extends ApiRepository
in package
uses SaveTrait

FinalYes

Front repository that reads through the Api bridge and uses the query cache when the service has it enabled (`cache.config.enabledServices`).

This is the deliberate opposite of ApiRepository: the front end only displays data, so a 5 minute old number is at most cosmetic, whereas the admin reads it into a form that is written back to the DB.

->useCache(false) on the query object overrides the policy for one query; it travels to the payload as ApiBridge::PARAM_USE_CACHE.

IMPORTANT: null is passed on as null (no opinion), not as true. If this layer substituted true, it would override QueryCacheContext, and reads through a front manager inside an admin request (AssetManager, ThemeManager, UserManager) would still be served from the cache. The true default therefore lives in the bridge.

Table of Contents

Properties

$apiAuthEndpoint  : string
$apiEndpoint  : string
Api presenter path (e.g. `api/system/page`); set by each concrete repository.
$bridge  : ApiBridge

Methods

__construct()  : mixed
activateUser()  : Result
Account activation from the e-mailed link. The front end only passes the pair from the URL; the Api manager verifies the key and consumes it.
cancelDeletion()  : Result
Account deletion — `:Api:System:User:cancelDeletion` (e-mailed key or sign-in ticket).
confirmDeletion()  : Result
Account deletion — `:Api:System:User:confirmDeletion` (one-time key from the e-mail).
confirmExternalLink()  : Result
`:Api:System:Auth:confirmExternalLink` (one-time key from the e-mail).
deauthorizeExternal()  : Result
emailExists()  : bool|null
E-mail counterpart of {@see self::usernameExists()}; same reasons and the same return contract.
find()  : array<string|int, mixed>
findAll()  : array<string|int, mixed>
findBy()  : array<string|int, mixed>
findPublicProfile()  : array<string, mixed>
The only front end path to another user's profile.
findPublicProfiles()  : array<string, mixed>
Bulk path to other users' profiles from the front end.
login()  : Result
`$deviceToken` = raw cookie of a known device ({@see \App\Core\Security\LoginDeviceCookie}); only the Api verifies it, by signature. Without the cookie the key is not sent.
loginExternal()  : Result
referralOverview()  : Result
Affiliate program — `:Api:System:User:referralOverview`; `$userId` from the session, the Api checks the owner.
requestDeletion()  : Result
Account deletion — `:Api:System:User:requestDeletion`; `$userId` from the front end session, the Api compares it with the JWT identity (owner grant).
resetPassword()  : Result
`:Api:System:User:resetPassword` (public, one-time key from the e-mail).
revealPhone()  : array<string, mixed>
The only path to the raw phone of another user's profile.
save()  : Result
sendActivationEmail()  : Result
sendNewPassword()  : Result
Replaced `generateNewPassword(int $userId)`. The front end no longer sends `userId` (or looks it up) and sends no e-mail; the whole "lost password" flow including sending `SY_LOST_PASSWORD` is done by the Api manager. The response contains no password and is constant for an existing and a nonexistent account.
updateColumns()  : Result
updatePassword()  : Result
Api verifies the current password and answers with a fresh token pair.
usernameExists()  : bool|null
Duplicate check on self-registration.
failSoftRead()  : array<string|int, mixed>
Normalizes a bridge response to plain data; a failed envelope or a non-array gives `[]`.
getApiCallSource()  : string
Returns the caller branch ({@see \App\Core\Bridge\ApiBridge::PARAM_SOURCE}); implemented by the Front and Admin base `ApiRepository`.
callUserAction()  : Result
readExists()  : bool|null

Properties

$apiAuthEndpoint

protected string $apiAuthEndpoint = 'api/system/auth'

$apiEndpoint

Api presenter path (e.g. `api/system/page`); set by each concrete repository.

protected string $apiEndpoint = 'api/system/user'

Methods

activateUser()

Account activation from the e-mailed link. The front end only passes the pair from the URL; the Api manager verifies the key and consumes it.

public activateUser(string $email, string $activationKey) : Result
Parameters
$email : string
$activationKey : string
Return values
Result

cancelDeletion()

Account deletion — `:Api:System:User:cancelDeletion` (e-mailed key or sign-in ticket).

public cancelDeletion(string $key) : Result
Parameters
$key : string
Return values
Result

confirmDeletion()

Account deletion — `:Api:System:User:confirmDeletion` (one-time key from the e-mail).

public confirmDeletion(string $key) : Result
Parameters
$key : string
Return values
Result

`:Api:System:Auth:confirmExternalLink` (one-time key from the e-mail).

public confirmExternalLink(string $key) : Result
Parameters
$key : string
Return values
Result

deauthorizeExternal()

public deauthorizeExternal(string $provider, string $externalId) : Result
Parameters
$provider : string
$externalId : string
Return values
Result

emailExists()

E-mail counterpart of {@see self::usernameExists()}; same reasons and the same return contract.

public emailExists(string $email[, int|null $excludeId = null ]) : bool|null
Parameters
$email : string
$excludeId : int|null = null
Return values
bool|null

find()

public find(int $id[, string|array<string|int, mixed> $includes = [] ][, bool|null $useCache = null ]) : array<string|int, mixed>
Parameters
$id : int
$includes : string|array<string|int, mixed> = []
$useCache : bool|null = null
Return values
array<string|int, mixed>

findAll()

public findAll([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 = [] ][, bool|null $useCache = null ]) : array<string|int, mixed>
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> = []
$useCache : bool|null = null
Return values
array<string|int, mixed>

findBy()

public findBy([array<string|int, mixed>|null $filters = [] ][, string|array<string|int, mixed> $includes = [] ][, bool|null $useCache = null ]) : array<string|int, mixed>
Parameters
$filters : array<string|int, mixed>|null = []
$includes : string|array<string|int, mixed> = []
$useCache : bool|null = null
Return values
array<string|int, mixed>

findPublicProfile()

The only front end path to another user's profile.

public findPublicProfile(int $id) : array<string, mixed>

Targets :Api:System:User:publicProfile (guest = 1), not get (ApiRepository::find()), which is admin-only and returns the whole entity including password/salt/email/ip. An anonymous profile page failed on get (HTTP 500 inside a 200) under internalMode: enforce.

IMPORTANT: simplifying this method back to $this->find($id) brings back both the blocking failure of the anonymous page and the leak of personal data.

Parameters
$id : int
Return values
array<string, mixed> —

allowlist from UserPresenter::PUBLIC_PROFILE_FIELDS; empty array = the profile does not exist or the guard refused

findPublicProfiles()

Bulk path to other users' profiles from the front end.

public findPublicProfiles(array<int, int> $ids) : array<string, mixed>

Targets the same public-profile endpoint (so the same key :Api:System:User:publicProfile), only with the ids parameter; see UserPresenter::internalActionPublicProfile() for why the bulk mode is not an action of its own.

It replaces findAll(['id' => InFilter]) on :Api:System:User:getAll, which is admin-only and under internalMode: enforce returned Result::failure, so the template dereferenced null and a public page returned 500 inside a 200.

IMPORTANT: id is deliberately sent as 0: if it were missing, the Api would get a default in singular mode and the bulk branch would not run. A non-empty ids decides.

Parameters
$ids : array<int, int>
Return values
array<string, mixed> —

shape {items: {id: profile}}; empty items = nothing found or the guard refused

login()

`$deviceToken` = raw cookie of a known device ({@see \App\Core\Security\LoginDeviceCookie}); only the Api verifies it, by signature. Without the cookie the key is not sent.

public login(string $username, string $password[, string|null $deviceToken = null ]) : Result
Parameters
$username : string
$password : string
$deviceToken : string|null = null
Return values
Result

loginExternal()

public loginExternal(string $provider, string $id, string $email, string $firstname, string $lastname[, bool $emailVerified = false ][, string $referralCode = '' ]) : Result
Parameters
$provider : string
$id : string
$email : string
$firstname : string
$lastname : string
$emailVerified : bool = false
$referralCode : string = ''
Return values
Result

referralOverview()

Affiliate program — `:Api:System:User:referralOverview`; `$userId` from the session, the Api checks the owner.

public referralOverview(int $userId) : Result
Parameters
$userId : int
Return values
Result

requestDeletion()

Account deletion — `:Api:System:User:requestDeletion`; `$userId` from the front end session, the Api compares it with the JWT identity (owner grant).

public requestDeletion(int $userId, string $password) : Result
Parameters
$userId : int
$password : string
Return values
Result

resetPassword()

`:Api:System:User:resetPassword` (public, one-time key from the e-mail).

public resetPassword(string $email, string $key, string $password) : Result
Parameters
$email : string
$key : string
$password : string
Return values
Result

revealPhone()

The only path to the raw phone of another user's profile.

public revealPhone(int $id) : array<string, mixed>

Targets :Api:System:User:showPhone (a dedicated action, not an extension of publicProfile; see UserPresenter::internalActionShowPhone()). The Api layer does the guard and the rate limit; this method only passes the call on.

Parameters
$id : int
Return values
array<string, mixed> —

shape {phone: string}; an empty or missing phone = refused or the user/phone does not exist (the caller does not distinguish). The return type is deliberately wider than array{phone?: string} (the bridge returns raw Api data, not a verified shape, as in self::findPublicProfile()).

save()

public save(array<string, mixed> $data[, array<string, mixed> $meta = [] ][, array<int, string> $skipFields = [] ]) : Result
Parameters
$data : array<string, mixed>

extracted entity payload

$meta : array<string, mixed> = []
$skipFields : array<int, string> = []
Return values
Result

sendActivationEmail()

public sendActivationEmail(string $username) : Result
Parameters
$username : string
Return values
Result

sendNewPassword()

Replaced `generateNewPassword(int $userId)`. The front end no longer sends `userId` (or looks it up) and sends no e-mail; the whole "lost password" flow including sending `SY_LOST_PASSWORD` is done by the Api manager. The response contains no password and is constant for an existing and a nonexistent account.

public sendNewPassword(string $username) : Result
Parameters
$username : string
Return values
Result

updateColumns()

public updateColumns(array<string|int, mixed> $ids, array<string, mixed> $values) : Result
Parameters
$ids : array<string|int, mixed>

ids or composite-id arrays

$values : array<string, mixed>

property name => new value

Return values
Result

updatePassword()

Api verifies the current password and answers with a fresh token pair.

public updatePassword(int $userId, string $currentPassword, string $password) : Result
Parameters
$userId : int
$currentPassword : string
$password : string
Return values
Result

usernameExists()

Duplicate check on self-registration.

public usernameExists(string $username[, int|null $excludeId = null ]) : bool|null

Targets :Api:System:User:usernameExists, not getBy. getBy is admin-only and must stay so (it returns the whole User entity: password, salt, email, ip, address…). The registration form used to call isUnique() through it and under enforce got an empty result through FailSoftReadTrait, read as "name is free": the duplicate check was silently off and only the unique index in the database remained.

This action has no actionUsernameExists(), so external HTTP cannot reach it, and it returns a single bool: no user data leaves the Api layer.

Parameters
$username : string
$excludeId : int|null = null
Return values
bool|null —

null = the answer could not be determined (guard/outage), not "free"; the caller must tell them apart, otherwise fail-soft would turn into fail-open again

failSoftRead()

Normalizes a bridge response to plain data; a failed envelope or a non-array gives `[]`.

protected failSoftRead(mixed $response, string $endpoint) : array<string|int, mixed>

NOTE: it does not log on purpose. ApiBridge::call(), which sees both the endpoint and the caller, has already written the API_FAILSOFT source=bridge line; a second copy would turn one failure into three lines in auth.log.

Parameters
$response : mixed
$endpoint : string

only for call readability; the log takes the endpoint from the bridge

Return values
array<string|int, mixed>

getApiCallSource()

Returns the caller branch ({@see \App\Core\Bridge\ApiBridge::PARAM_SOURCE}); implemented by the Front and Admin base `ApiRepository`.

protected abstract getApiCallSource() : string
Return values
string

callUserAction()

private callUserAction(string $action, array<string, mixed> $params) : Result
Parameters
$action : string
$params : array<string, mixed>
Return values
Result

readExists()

private readExists(string $endpoint, array<string, mixed> $params) : bool|null
Parameters
$endpoint : string
$params : array<string, mixed>
Return values
bool|null —

null when the response has no exists key (refused by the guard or empty fail-soft); deliberately not turned into false

On this page

Search results