*/ private array $fields = array(); /** * Callables that return more fields, asked each time. * * @var list> */ private array $sources = array(); /** * Arrangement chosen by the store. * * @var FormLayout|null */ private ?FormLayout $layout = null; /** * Add or replace a field. * * @param FormField $field Field. */ public function add( FormField $field ): void { $this->fields[ $field->id() ] = $field; } /** * Add fields that are worked out when needed, such as one per stored definition. * * @param callable(): iterable $source Returns fields. */ public function add_source( callable $source ): void { $this->sources[] = $source; } /** * Arrange the fields. * * @param FormLayout|null $layout Layout, or null for each field's own place. */ public function set_layout( ?FormLayout $layout ): void { $this->layout = $layout; } /** * Every field, keyed by id. * * @return array */ public function all(): array { $fields = $this->fields; foreach ( $this->sources as $source ) { foreach ( $source() as $field ) { if ( $field instanceof FormField && ! isset( $fields[ $field->id() ] ) ) { $fields[ $field->id() ] = $field; } } } return $fields; } /** * A field's area and position, after the layout. * * @param FormField $field Field. * @return array{area: string, position: int} */ public function place( FormField $field ): array { $place = null === $this->layout ? null : $this->layout->place( $field ); return null === $place ? array( 'area' => $field->area(), 'position' => $field->position(), ) : $place; } /** * Remove a field. * * @param string $id Field id. */ public function remove( string $id ): void { unset( $this->fields[ $id ] ); } /** * One field. * * @param string $id Field id. */ public function get( string $id ): ?FormField { return $this->fields[ $id ] ?? ( $this->all()[ $id ] ?? null ); } /** * Fields that apply in a context, sorted by area then position. * * @param FormContext $context Context. * @param string|null $area Only this area. * @return list */ public function for_context( FormContext $context, ?string $area = null ): array { $places = array(); $fields = array(); foreach ( $this->all() as $id => $field ) { $place = $this->place( $field ); if ( ( null === $area || $place['area'] === $area ) && ( array() === $context->only || in_array( $field->id(), $context->only, true ) ) && $field->applies( $context ) ) { $places[ $id ] = array( FormField::AREA_MAIN === $place['area'] ? 0 : 1, $place['position'] ); $fields[] = $field; } } usort( $fields, static function ( FormField $a, FormField $b ) use ( $places ): int { return $places[ $a->id() ] <=> $places[ $b->id() ]; } ); return $fields; } }