CoolCMS API

AdvertFollowerRepository extends ApiRepository
in package
uses SaveTrait, DeleteTrait, AdvertScopeHintTrait

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

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

Methods

__construct()  : mixed
delete()  : Result
deleteAll()  : Result
Stops following through the dedicated internal action `...:myUnfollow` ({@see \App\UI\Api\Bazaar\Presenters\AdvertFollowerPresenter::internalActionMyUnfollow()}), not `:Api:Bazaar:AdvertFollower:deleteAll` (a collection action, admin-only; `ALLOW_IF_OWNER` is always a deny there).
find()  : array<string|int, mixed>
findAll()  : array<string|int, mixed>
findBy()  : array<string|int, mixed>
Reads the state of the follow button through the dedicated internal action `:Api:Bazaar:AdvertFollower:myOne` ({@see \App\Core\Traits\Api\Presenters\OwnedOneTrait}), not `:Api:Bazaar:AdvertFollower:getBy`.
save()  : Result
Starts following through the dedicated internal action `...:myFollow` ({@see \App\UI\Api\Bazaar\Presenters\AdvertFollowerPresenter::internalActionMyFollow()}), not `:Api:Bazaar:AdvertFollower:save`.
updateColumns()  : Result
advertScopeHint()  : int|null
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`.
referenceId()  : int|null
Returns the id from a ToOne reference in the `extract()` payload.

Properties

$apiEndpoint

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

protected string $apiEndpoint = 'api/bazaar/advert-follower'

Methods

delete()

public delete(int|array<string|int, mixed> $id) : Result
Parameters
$id : int|array<string|int, mixed>
Return values
Result

deleteAll()

Stops following through the dedicated internal action `...:myUnfollow` ({@see \App\UI\Api\Bazaar\Presenters\AdvertFollowerPresenter::internalActionMyUnfollow()}), not `:Api:Bazaar:AdvertFollower:deleteAll` (a collection action, admin-only; `ALLOW_IF_OWNER` is always a deny there).

public deleteAll([array<int, mixed> $filters = [] ]) : Result

The caller userId filter is dropped; the server sets it from the JWT. The advert filter is only a hint which advert (AdvertScopeHintTrait); a forged value can at most delete the own follow of another advert.

Parameters
$filters : array<int, mixed> = []
Return values
Result

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()

Reads the state of the follow button through the dedicated internal action `:Api:Bazaar:AdvertFollower:myOne` ({@see \App\Core\Traits\Api\Presenters\OwnedOneTrait}), not `:Api:Bazaar:AdvertFollower:getBy`.

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

getBy has actionGetBy() (externally reachable) and is a collection query with caller-supplied filters: ALLOW_IF_OWNER is always a deny there, and a registrated grant would open the query "whom does user N follow". myOne has no action*() and the server sets userId from the JWT.

IMPORTANT: a guest gets [], the same result getBy returns with userId = null, so the button stays in the "not following" state.

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

save()

Starts following through the dedicated internal action `...:myFollow` ({@see \App\UI\Api\Bazaar\Presenters\AdvertFollowerPresenter::internalActionMyFollow()}), not `:Api:Bazaar:AdvertFollower:save`.

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

save without data.id is an INSERT, the $owned closure returns null and isOwner(null, ...) is a fail-closed false, so the conditional grant owner = 1 ends in a deny. The server-sets-owner pattern exists only in the external verifyOwnership().

Overriding the generic method (instead of a new method across four layers) makes every Front caller, including future ones, go through this path.

IMPORTANT: advertId is taken from the association in the payload, because it is the only thing the server needs; the owner is set from the JWT. When it is missing the result is a fail-closed INVALID_VALUE, never a silent write under a foreign identity.

Parameters
$data : array<string, mixed>
$meta : array<string, mixed> = []
$skipFields : array<int, 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

advertScopeHint()

protected static advertScopeHint(array<int, mixed>|null $filters[, string $field = 'advertId' ]) : int|null
Parameters
$filters : array<int, mixed>|null
$field : string = 'advertId'

field name in the caller filter; advertId for collections, advert for the follow button (FollowTrait filters by the association, not a scalar)

Return values
int|null

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

referenceId()

Returns the id from a ToOne reference in the `extract()` payload.

private static referenceId(mixed $reference) : int|null

IMPORTANT: casting an array with (int) returns 1, not an error, which would create the follow for advert 1. EntityHydrator::extract() emits a ToOne relation as a thin reference ['id' => ['id' => 67]]. The reference is therefore unwrapped while the value is an array with an id key, and the final conversion is done by OwnershipPayload::normalizeId(), the same implementation that forbids a blind (int) cast across the Api ('abc', null and 0 give null).

Parameters
$reference : mixed

67 | '67' | ['id' => 67] | ['id' => ['id' => 67]]

Return values
int|null
On this page

Search results