AdvertFollowerRepository
extends ApiRepository
in package
uses
SaveTrait, DeleteTrait, AdvertScopeHintTrait
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'
$bridge read-only
protected
ApiBridge
$bridge
Methods
__construct()
public
__construct(ApiBridge $bridge) : mixed
Parameters
- $bridge : ApiBridge
delete()
public
delete(int|array<string|int, mixed> $id) : Result
Parameters
- $id : int|array<string|int, mixed>
Return values
ResultdeleteAll()
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
Resultfind()
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
ResultupdateColumns()
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
ResultadvertScopeHint()
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;
advertIdfor collections,advertfor the follow button (FollowTraitfilters by the association, not a scalar)
Return values
int|nullfailSoftRead()
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
stringreferenceId()
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]]