*/ public const VALID_FIELD_TYPES = [ self::FIELD_TEXT, self::FIELD_TEXTAREA, self::FIELD_SELECT, self::FIELD_CHECKBOX, ]; /** * All valid scopes. * * @var list */ public const VALID_SCOPES = [ self::SCOPE_OFFERING, self::SCOPE_ACCOUNT, ]; /** * Build an intake question value object. * * @param int|null $offeringId The owning offering, or null for account-scoped questions. * @param list|null $options Choices for a `select` field. */ public function __construct( public readonly ?int $offeringId, public readonly string $label, public readonly string $fieldType = self::FIELD_TEXT, public readonly ?array $options = null, public readonly bool $isRequired = false, public readonly int $sortOrder = 0, public readonly bool $isActive = true, public readonly string $scope = self::SCOPE_OFFERING, public readonly ?int $id = null, ) {} public static function fromRow( \stdClass $row ): self { $options = null; if ( null !== $row->options && '' !== $row->options ) { $decoded = json_decode( Val::string( $row->options ), true ); $options = is_array( $decoded ) ? array_values( array_map( static fn( mixed $v ): string => Val::string( $v ), $decoded ) ) : null; } return new self( offeringId: Val::intOrNull( $row->offering_id ), label: Val::string( $row->label ), fieldType: Val::string( $row->field_type ), options: $options, isRequired: Val::bool( $row->is_required ), sortOrder: Val::int( $row->sort_order ), isActive: Val::bool( $row->is_active ), scope: Val::string( $row->scope ), id: Val::int( $row->id ), ); } /** * Returns a plain array representation of the question. * * @return array */ public function toArray(): array { return [ 'id' => $this->id, 'offering_id' => $this->offeringId, 'scope' => $this->scope, 'label' => $this->label, 'field_type' => $this->fieldType, 'options' => $this->options, 'is_required' => $this->isRequired, 'sort_order' => $this->sortOrder, 'is_active' => $this->isActive, ]; } }