Skip to content

Extension points

phil/listings

The library has three ports, all interfaces of Phil\Listings\Contract, which the Responder reads. Their adapters belong to the application.

BlockList

Whether one of two members blocked the other.

interface BlockList
{
    public function isBlocked(MemberId $one, MemberId $other): bool;
}

The answer is symmetric: true when either member blocked the other, so that a member who blocks someone neither receives their responses nor can respond to their listings. The responder asks it before a response and again before an acceptance, so that a block made in between wins.

MemberAges

The age of a member, in whole years at an instant.

interface MemberAges
{
    public function ageOf(MemberId $member, DateTimeImmutable $at): ?int;
}

Null when the application does not know it; a listing with a minimum age then refuses the member with age_unknown. The responder asks it only of a listing with a minimum age, before a response and again before an acceptance. An adapter computes the age from a birth date it keeps, or answers from a verification it trusts, and never needs to give the date itself to the library.

use DateTimeImmutable;
use Phil\Listings\Contract\MemberAges;
use Phil\Listings\MemberId;

final readonly class ProfileAges implements MemberAges
{
    public function __construct(
        private Profiles $profiles,
    ) {
    }

    public function ageOf(MemberId $member, DateTimeImmutable $at): ?int
    {
        $birthDate = $this->profiles->birthDateOf($member->value);

        return null === $birthDate ? null : $birthDate->diff($at)->y;
    }
}

ResponseStore

Where the responses live.

interface ResponseStore
{
    public function countOpen(ListingId $listing): int;

    public function hasOpen(ListingId $listing, MemberId $respondent): bool;

    public function save(Response $response): void;
}
  • countOpen() counts the pending and accepted responses to a listing — Response::isOpen() —, for the kind's bound of open responses; an index on the listing and the status keeps it cheap.
  • hasOpen() tells whether a member has an open response to a listing, so that each member has at most one.
  • save() writes a response, new or changed; Response::restore() rebuilds it from its fields.

Whether a count and the write that follows it are made atomic is the adapter's decision. Without a lock or a unique constraint on the listing, the respondent and the open statuses, two responses sent at the same instant may both pass the bounds; a unique index makes the second fail in the store instead.