$fields Predicate fields. * @param int $depth Loop-protection depth. * @param list $ancestor_ids Automation ids already in the chain. * @param bool $is_test Test run (no rate/pause side effects). */ public function __construct( private readonly string $trigger_event_id, private readonly string $event, private readonly SubjectType $subject_type, private readonly int $subject_id, private readonly ?int $customer_id, private readonly DateTimeImmutable $snapshot_at, private readonly array $fields, private readonly int $depth = 0, private readonly array $ancestor_ids = [], private readonly bool $is_test = false, ) {} /** * Trigger event id. * * @since 0.1.0 */ public function trigger_event_id(): string { return $this->trigger_event_id; } /** * Event name. * * @since 0.1.0 */ public function event(): string { return $this->event; } /** * Subject type. * * @since 0.1.0 */ public function subject_type(): SubjectType { return $this->subject_type; } /** * Subject id. * * @since 0.1.0 */ public function subject_id(): int { return $this->subject_id; } /** * Customer id. * * @since 0.1.0 */ public function customer_id(): ?int { return $this->customer_id; } /** * Snapshot instant. * * @since 0.1.0 */ public function snapshot_at(): DateTimeImmutable { return $this->snapshot_at; } /** * Raw fields. * * @return array * * @since 0.1.0 */ public function fields(): array { return $this->fields; } /** * String field. * * @param string $key Key. * @param string $fallback Fallback. * * @since 0.1.0 */ public function field_string( string $key, string $fallback = '' ): string { $value = $this->fields[ $key ] ?? $fallback; return is_scalar( $value ) ? (string) $value : $fallback; } /** * Integer field. * * @param string $key Key. * @param int $fallback Fallback. * * @since 0.1.0 */ public function field_int( string $key, int $fallback = 0 ): int { $value = $this->fields[ $key ] ?? $fallback; return is_numeric( $value ) ? (int) $value : $fallback; } /** * Boolean field. * * @param string $key Key. * @param bool $fallback Fallback. * * @since 0.1.0 */ public function field_bool( string $key, bool $fallback = false ): bool { if ( ! array_key_exists( $key, $this->fields ) ) { return $fallback; } return (bool) $this->fields[ $key ]; } /** * Loop depth. * * @since 0.1.0 */ public function depth(): int { return $this->depth; } /** * Ancestor automation ids. * * @return list * * @since 0.1.0 */ public function ancestor_ids(): array { return $this->ancestor_ids; } /** * Whether this is a test run. * * @since 0.1.0 */ public function is_test(): bool { return $this->is_test; } /** * Copy with incremented depth and this automation on the chain. * * @param int $automation_id Automation id. * * @since 0.1.0 */ public function descend( int $automation_id ): self { $ancestors = $this->ancestor_ids; $ancestors[] = $automation_id; return new self( $this->trigger_event_id, $this->event, $this->subject_type, $this->subject_id, $this->customer_id, $this->snapshot_at, $this->fields, $this->depth + 1, $ancestors, $this->is_test ); } /** * Copy marked as a test run. * * @since 0.1.0 */ public function as_test(): self { return new self( $this->trigger_event_id, $this->event, $this->subject_type, $this->subject_id, $this->customer_id, $this->snapshot_at, $this->fields, $this->depth, $this->ancestor_ids, true ); } /** * Replace fields (fresh resolve). * * @param array $fields New fields. * @param DateTimeImmutable $at When. * * @since 0.1.0 */ public function with_fields( array $fields, DateTimeImmutable $at ): self { return new self( $this->trigger_event_id, $this->event, $this->subject_type, $this->subject_id, $this->customer_id, $at, $fields, $this->depth, $this->ancestor_ids, $this->is_test ); } /** * Serialise. * * @return array * * @since 0.1.0 */ public function to_array(): array { return [ 'trigger_event_id' => $this->trigger_event_id, 'event' => $this->event, 'subject_type' => $this->subject_type->value, 'subject_id' => $this->subject_id, 'customer_id' => $this->customer_id, 'snapshot_at' => $this->snapshot_at->format( DATE_ATOM ), 'fields' => $this->fields, 'depth' => $this->depth, 'ancestor_ids' => $this->ancestor_ids, 'is_test' => $this->is_test, ]; } /** * Hydrate. * * @param array $data Data. * * @since 0.1.0 */ public static function from_array( array $data ): self { $at = DateTimeImmutable::createFromFormat( DATE_ATOM, (string) ( $data['snapshot_at'] ?? '' ) ); if ( false === $at ) { $at = new DateTimeImmutable( 'now', new \DateTimeZone( 'UTC' ) ); } $fields = isset( $data['fields'] ) && is_array( $data['fields'] ) ? $data['fields'] : []; $anc = []; if ( isset( $data['ancestor_ids'] ) && is_array( $data['ancestor_ids'] ) ) { foreach ( $data['ancestor_ids'] as $id ) { $anc[] = (int) $id; } } return new self( (string) ( $data['trigger_event_id'] ?? '' ), (string) ( $data['event'] ?? '' ), SubjectType::from( (string) ( $data['subject_type'] ?? SubjectType::Booking->value ) ), (int) ( $data['subject_id'] ?? 0 ), isset( $data['customer_id'] ) ? (int) $data['customer_id'] : null, $at, $fields, (int) ( $data['depth'] ?? 0 ), $anc, (bool) ( $data['is_test'] ?? false ) ); } }