Oihana PHP System

appointments

Table of Contents

Functions

hydrateAppointment()  : mixed
Hydrate an array definition with the Appointment class, or with a subclass of it.
hydrateAppointmentStatus()  : mixed
Hydrate an array definition with the member class of {@see AppointmentStatus} it names.
hydrateFollowUp()  : mixed
Hydrate an array definition with the FollowUp class.
hydrateMeetingReport()  : mixed
Hydrate an array definition with the MeetingReport class, or with a subclass of it.
hydrateVisitReport()  : mixed
Hydrate an array definition with the VisitReport class.

Functions

hydrateAppointment()

Hydrate an array definition with the Appointment class, or with a subclass of it.

hydrateAppointment([mixed $init = null ][, DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> $termClass = ThesaurusTerm::class ][, Appointment> $class = Appointment::class ]) : mixed

Handles both a single appointment array and an array of appointments.

What it resolves is everything a meeting may carry, and every polymorphic case is settled by the stored type of the value itself — never by a class this helper would impose :

  • eventStatus and appointmentStatus — the two axes of state, each read back as the member class holding its URI ;
  • appointmentType and tags — the vocabularies of the meeting itself ;
  • about — the counterpart : a value announcing a customer is read back as one, and anything else falls back on its @type, an organization or a person ;
  • attendee — entry by entry : an account, a customer contact, or a plain person or organization — one table may seat them together ;
  • makesOffer — what one means to put in front of the counterpart ;
  • report — by its stored type : a visit's write-up comes back with its richer class, any other with the common one, each resolved in depth by its own helper.

The diary (organizer) needs no resolver : its attribute says enough for reflection to answer it.

🔑 The target class is a parameter, so a caller with a subclass of its own reuses this whole body rather than copying it.

Parameters
$init : mixed = null

Single appointment data or array of appointment data.

$termClass : DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> = ThesaurusTerm::class

The class the term properties are hydrated into, or a map naming them one by one. See termClassOf().

$class : Appointment> = Appointment::class

The class to build — a subclass of Appointment, or itself.

Tags
throws
HydrationException
ReflectionException
example
$meeting = hydrateAppointment
([
    'startDate'         => '2035-09-01T10:00:00+02:00' ,
    'appointmentStatus' => 'https://schema.oihana.xyz/AppointmentPlanned' ,
    'appointmentType'   => [ 'id' => 'CALL' ] ,
]);

$meeting->appointmentType instanceof ThesaurusTerm ; // true

hydrateAppointmentStatus()

Hydrate an array definition with the member class of {@see AppointmentStatus} it names.

hydrateAppointmentStatus([mixed $init = null ]) : mixed

The twin of hydrateEventStatus(), on the other axis : that one says what became of the slot, this one what became of the meeting. Both are read back the same way, so a consumer holding a stored appointment does not have to treat them differently.

A status is written one of two ways : the bare constant, when there is nothing more to say, or the member class, when there is — new AppointmentNoShow([ 'description' => '…' ]) says what was found on the doorstep, and a string cannot.

🔑 The bare constant comes back untouched. It is not an array, so it falls through the first guard : whichever form was stored can be handed to this helper.

The member is resolved by findEnumerationMember(), from the payload's additionalType — the URI every member states of itself — then from its @type, which carries the short name.

⚠️ The two never coincide here, unlike on the Schema.org side : this vocabulary spells its members …/AppointmentStatus#NoShow while the class is named AppointmentNoShow, so neither key can be derived from the other — which is precisely why the URI is stated by the member rather than read off its class name.

Nothing recognized gives an AppointmentStatus : the status is unknown to this vocabulary, and answering null would throw away the reason it carried.

🔑 A bare reference survives inside a list, exactly as it does on its own : a list of unresolved handles comes back as it stands, and only an entry that was an array and resolved to nothing is dropped. The keys stay gap-free — a filtered list left with holes serializes as a JSON object, and a consumer walking the value gets something it cannot walk.

Parameters
$init : mixed = null

Single status data, a list of such data, or any other value.

Tags
throws
ReflectionException
example
hydrateAppointmentStatus( AppointmentStatus::DONE ) ;                        // the string, untouched
hydrateAppointmentStatus( [ '@type' => 'AppointmentNoShow' ] ) ;              // AppointmentNoShow
hydrateAppointmentStatus( [ 'additionalType' => AppointmentStatus::DONE ] ) ; // AppointmentDone

hydrateFollowUp()

Hydrate an array definition with the FollowUp class.

hydrateFollowUp([mixed $init = null ]) : mixed

Handles both a single follow-up array and an array of follow-ups — the second is the usual shape, a report commonly carrying « call them back » beside « send the quote ».

Each nested reference is hydrated only when the raw value is an array — when there is something to hydrate. The helper's answer is then written as is, null included : an array that resolves to nothing becomes null, never a leftover raw array. Anything that is not an array — an unresolved string reference, an already typed instance — is left untouched.

🚨 result is built flat, and deliberately so. It names the meeting booked to honour the promise ; that meeting has a report, that report has follow-ups, and each of those may name a meeting in turn. Going down through hydrateAppointment() would follow that chain for as long as the data holds, and only the data would stop it. What is named here is a reference — a meeting to open, not a document to unfold — so it is typed one level and no further : its own nested references stay raw, and a consumer that needs them asks for that meeting on its own.

🔑 An empty list is kept as an empty list, where the rest of the family answers null — the hydrateAdjustment() rule. « This report has no follow-up » is an answer, and it is not the answer « nothing here was readable » : a consumer mapping over the value deserves the empty list it can map over. A non-empty list that hydrates to nothing keeps the family's null.

🔑 A bare reference survives inside a list, exactly as it does on its own : a list of unresolved handles comes back as it stands, and only an entry that was an array and resolved to nothing is dropped. The keys stay gap-free — a filtered list left with holes serializes as a JSON object, and a consumer walking the value gets something it cannot walk.

Parameters
$init : mixed = null

Single follow-up data or array of follow-up data.

Tags
throws
ReflectionException
example
$followUps = hydrateFollowUp
([
    [
        'followUpType'  => [ 'id' => 'CALL_BACK' , 'name' => 'Call back' ] ,
        'scheduledTime' => '2026-09-15' ,
        'agent'         => [ '@type' => 'Person' , 'name' => 'Jane Doe' ] ,
    ] ,
]) ;

$followUps[ 0 ]->followUpType instanceof DefinedTerm ; // true

hydrateMeetingReport()

Hydrate an array definition with the MeetingReport class, or with a subclass of it.

hydrateMeetingReport([mixed $init = null ][, DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> $termClass = ThesaurusTerm::class ][, MeetingReport> $class = MeetingReport::class ]) : mixed

Handles both a single report array and an array of reports.

Each nested reference is hydrated only when the raw value is an array — when there is something to hydrate. The helper's answer is then written as is, null included : an array that resolves to nothing becomes null, never a leftover raw array, so the report answers the same thing as the nested helper called on its own. Anything that is not an array — an unresolved string reference, an already typed instance — is left untouched.

What it resolves :

  • followUp — what comes next, through hydrateFollowUp() ;
  • tags and topics — several terms each ;
  • author — whoever wrote it, an Organization|Person union that only the payload's @type can settle.

⚠️ attendee is deliberately left alone here. Who sits at a table depends on the kind of meeting — a customer's staff, a colleague — and this helper cannot know. Each family resolves it after this one has run, which is what hydrateVisitReport() does.

🔑 An empty followUp list stays an empty list. « This report has no follow-up » is an answer worth serving, and a reader walking the value deserves a list to walk — hydrateFollowUp() holds that rule, and this helper writes back whatever it answers.

🔑 The vocabularies are read as the class their family serves. Those families carry properties DefinedTerm does not declare — color first among them — so hydrating them as a plain DefinedTerm dropped those properties silently, and the same term changed shape depending on where it was read. The class is a parameter rather than a hard-wired name, in the two forms termClassOf() reads : one class for every property, or a map naming them one by one.

🔑 The target class is a parameter too, so a subclass reuses this whole body rather than copying it : hydrateVisitReport() asks for its own class here, then resolves what only a visit carries.

Parameters
$init : mixed = null

Single report data or array of report data.

$termClass : DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> = ThesaurusTerm::class

The class the term properties are hydrated into, or a map naming them one by one. A map may carry entries this helper does not read. See termClassOf().

$class : MeetingReport> = MeetingReport::class

The class to build — a subclass of MeetingReport, or itself.

Tags
throws
ReflectionException
example
$report = hydrateMeetingReport
([
    'topics'   => [ [ 'id' => 'ROADMAP' ] ] ,
    'followUp' => [ [ 'followUpType' => [ 'id' => 'CALL_BACK' ] ] ] ,
    'text'     => 'Everything the boxes cannot hold.' ,
]) ;

$report->followUp[ 0 ] instanceof FollowUp      ; // true
$report->topics[ 0 ]   instanceof ThesaurusTerm ; // true

hydrateVisitReport()

Hydrate an array definition with the VisitReport class.

hydrateVisitReport([mixed $init = null ][, DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> $termClass = ThesaurusTerm::class ]) : mixed

Handles both a single report array and an array of reports.

🔑 What every report carries is resolved by hydrateMeetingReport() — the promises, the qualifiers, what was covered, who wrote it — and this helper asks it for a VisitReport rather than copying its body. What is left here is what only a visit carries :

  • attendee — the people actually met, as CustomerEmployee, one or several. The parent leaves the union wide on purpose : who sits at a table depends on the kind of meeting, and a visit is the family that knows ;
  • mood and outcome — one term each, how it felt and what it produced.

🔑 The vocabularies are read as the class their family serves, in the two forms termClassOf() reads — one class for every property, or a map naming them one by one :

hydrateVisitReport( $raw ) ;                       // the house term, everywhere
hydrateVisitReport( $raw , DefinedTerm::class ) ;  // one named class, everywhere
hydrateVisitReport( $raw , [ Prop::DEFAULT => ThesaurusTerm::class , VisitReport::MOOD => MoodTerm::class ] ) ;

🔑 A bare reference survives inside a list, exactly as it does on its own : a list of unresolved handles comes back as it stands, and only an entry that was an array and resolved to nothing is dropped. The keys stay gap-free — a filtered list left with holes serializes as a JSON object, and a consumer walking the value gets something it cannot walk.

Parameters
$init : mixed = null

Single report data or array of report data.

$termClass : DefinedTerm>|array<string, DefinedTerm>|array<string, DefinedTerm>>> = ThesaurusTerm::class

The class the term properties are hydrated into, or a map naming them one by one. A map may carry entries this helper does not read — a report hydrated from a meeting receives the meeting's map as it stands, branches included. See termClassOf().

Tags
throws
ReflectionException
example
$report = hydrateVisitReport
([
    'mood'     => [ 'id' => 'SATISFIED' , 'name' => 'Satisfied' ] ,
    'attendee' => [ [ 'name' => 'Jane Doe' , 'jobTitle' => [ 'id' => 'BUYER' ] ] ] ,
    'followUp' => [ [ 'followUpType' => [ 'id' => 'CALL_BACK' ] ] ] ,
    'text'     => 'Everything the boxes cannot hold.' ,
]) ;

$report->attendee[ 0 ] instanceof CustomerEmployee ; // true
$report->followUp[ 0 ] instanceof FollowUp         ; // true
$report->mood          instanceof ThesaurusTerm    ; // true
On this page

Search results