Skip to content

Camps ​

Els camps defineixen el formulari d'un Resource (o d'una Pàgina). Es declaren dins de fields(), agrupats en Section:

php
public function fields(): array
{
    return [
        Section::make('Content')
            ->columns(1)
            ->schema([
                Text::make('title')->rules('required', 'string', 'max:255'),
                Toggle::make('is_published')->default(false),
            ]),

        Section::make('Advanced')
            ->collapsed()
            ->schema([
                Time::make('publish_time')->displayFormat('H:i'),
            ]),
    ];
}

Section::make($label) accepta columns(int) per definir la graella del formulari i collapsed() per començar plegada. Cada secció conté un array de camps.

La classe base Field ​

Tots els camps hereten de Field i comparteixen aquests mètodes:

MètodeDescripció
label(string $label)Etiqueta mostrada al formulari. Per defecte, Str::headline() del nom del camp.
rules(string ...$rules)Regles de validació de Laravel, tal com les passaries a un Validator ('required', 'max:255', 'unique:posts,slug'...).
helperText(string $text)Línia curta d'ajuda sota el camp, per a comportament que l'etiqueta sola no deixa clar.
placeholder(string $text)Text de placeholder de l'input.
default(mixed $value)Valor amb què el formulari de creació arriba emplenat abans que l'usuari el toqui. El tipus depèn del camp (bool per a Toggle, string per a Select...).
visibleWhen(string $field, string $operator, mixed $value = null)Mostra el camp només quan un altre camp compleix una condició. Vegeu més avall.
visibleOn(string ...$operations)Mostra el camp només en una operació: create o edit.

La validació es recull automàticament de tots els camps del Resource via validationRules() (que ve del trait HasSchemaFields, compartit entre Resource i Page), no cal declarar cap FormRequest.

Visible només en crear o en editar ​

php
Text::make('confirmation')
    ->visibleOn('create')
    ->rules('required', 'string')

Un camp amb visibleOn('create') només apareix al formulari de creació, i les seves regles de validació només s'apliquen allà. El formulari d'un Relation manager no distingeix entre crear i editar, així que visibleOn() no hi filtra res.

Visibilitat condicional ​

php
Text::make('category_note')
    ->rules('required', 'string', 'max:255')
    ->visibleWhen('category', 'equals', 'announcement')

L'operador és un de filled, empty, equals o not_equals (equals/not_equals prenen un tercer argument, el valor a comparar), un conjunt petit i fix, no composable (sense and/or, sense niuar-ne). La condició es guarda com a dades dins de l'schema, no com a closure, perquè s'avalua dos cops: al client, perquè el camp aparegui i desaparegui en viu mentre s'omple el formulari, i al servidor, perquè un camp required que l'usuari mai ha vist no pugui bloquejar mai l'enviament, les seves regles se salten completament, no només es tornen opcionals, quan la seva condició és falsa contra les dades enviades.

Un camp dins d'un Repeater queda circumscrit a la seva pròpia fila: la condició mira camps germans de la mateixa fila, no el formulari sencer, i cada fila es valida independentment (la fila 1 pot requerir el seu url mentre la fila 2, amb label buit, no ho requereix).

php
Repeater::make('links')
    ->schema([
        Text::make('label')->rules('nullable', 'string', 'max:255'),
        Text::make('url')
            ->rules('required', 'url')
            ->visibleWhen('label', 'filled'),
    ])

Un camp amagat conserva el valor que ja tingui al client (no s'esborra automàticament), així que alternar una condició amunt i avall no fa perdre el que l'usuari ja havia escrit.

Catàleg de camps ​

Text ​

Entrada d'una línia.

php
Text::make('title')->rules('required', 'string', 'max:255')

masked() la converteix en un camp de contrasenya, que amaga el que s'escriu i evita que el navegador l'autocompleti:

php
Text::make('password')->masked()->rules('required', 'string', 'min:12')

Textarea ​

Entrada de text multilínia. Mateixa API que Text (només label()/rules()).

php
Textarea::make('body')->rules('nullable', 'string')

RichEditor ​

Editor WYSIWYG (Tiptap al frontend). El seu ~120 KB de JS només es carrega als panells que fan servir aquest camp. El contingut es sanititza al servidor en cada save() (via HtmlSanitizer), independentment del que enviï el client.

php
RichEditor::make('content')->rules('nullable', 'string')

Toggle ​

Interruptor booleà. Sense opcions pròpies més enllà de les de la classe base, default(false) és el default() genèric de Field, amb false com a valor per defecte del propi Toggle si no en declares cap altre.

php
Toggle::make('is_published')
    ->helperText('Published posts are visible on the public index.')
    ->default(false)

Select ​

Desplegable d'una sola opció.

php
Select::make('category')
    ->options(['news' => 'News', 'update' => 'Update', 'announcement' => 'Announcement'])
    ->nullable()
    ->rules('nullable', 'string')
MètodeDescripció
options(array<string, string> $options)Parelles valor => etiqueta.
nullable(bool $nullable = true)Permet deixar-lo buit al frontend.

Les claus poden ser enters, i el desplegable respecta l'ordre en què declares les opcions.

CheckboxList ​

Selecció múltiple sobre el mateix format d'opcions que Select. Quan hi ha més d'una opció, mostra una casella per marcar-les o desmarcar-les totes. Dins d'una Section plegada, la capçalera mostra quantes caselles hi ha marcades.

php
CheckboxList::make('notify_channels')
    ->options(['email' => 'Email', 'push' => 'Push', 'sms' => 'SMS'])
    ->rules('nullable', 'array')

Tags ​

Una llista lliure de paraules, que es desa com un array. L'usuari escriu un valor i el confirma amb Enter, coma o punt i coma, i cada valor queda com una etiqueta que es pot treure. Els duplicats s'ignoren.

php
Tags::make('keywords')
    ->itemRules('string', 'max:30')
    ->rules('nullable', 'array')

itemRules() són les regles de cada element, i rules() les de l'array sencer. Es pot fer servir tant en un formulari de Resource com al schema() d'una acció.

Placeholder ​

Un camp de només lectura que mostra un valor dins el formulari, sense cap entrada. El valor surt de les dades del formulari, així que normalment el calcules a mutateDataBeforeFill():

php
Placeholder::make('summary')->label('Summary')

Date i Time ​

php
Date::make('published_at')->displayFormat('d/m/Y')->rules('nullable', 'date')
Time::make('publish_time')->displayFormat('H:i')->rules('nullable', 'date_format:H:i')
MètodeDescripció
displayFormat(string $format)Format de visualització (sintaxi PHP date()). Y-m-d i H:i per defecte, respectivament.

Date es renderitza amb el DatePicker d'Arrel, que a més de la data admet un mínim, un màxim i dies deshabilitats. Aquestes opcions són propietats del component, no del camp PHP: les fas servir a les teves pàgines personalitzades.

File i Image ​

Image és un File amb type() diferent (image en lloc de file), comparteixen tota la resta de l'API.

php
Image::make('cover_path')
    ->directory('posts/covers')
    ->url(fn (Post $post): ?string => $post->cover_path === null ? null : "/storage/{$post->cover_path}")
    ->rules('nullable', 'image', 'max:4096')
MètodeDescripció
disk(string $disk)Disc de destinació final. local per defecte.
directory(string $directory)Subdirectori dins del disc.
url(Closure $callback)Resol un enllaç al fitxer que el registre ja té desat, perquè el formulari d'edició mostri un «veure fitxer actual». Rep el model i retorna l'enllaç, o null si no n'hi ha.

Com funciona la pujada: el frontend puja el fitxer a un directori temporal (arrel-temp, disc local) tan bon punt l'usuari el selecciona, abans de desar el formulari. En fer save(), Arrel mou el fitxer de arrel-temp cap a {disk}/{directory}/{nom} i només llavors escriu la ruta final a la base de dades (vegeu HasSchemaFields::moveUploadedFiles()). Els temporals que ningú arriba a desar es netegen amb php artisan arrel:prune-uploads (vegeu Instal·lació).

Repeater ​

Sub-schema repetible: una llista de grups de camps amb la mateixa forma.

php
Repeater::make('links')
    ->schema([
        Text::make('label')->rules('required', 'string', 'max:255'),
        Text::make('url')->rules('required', 'url'),
    ])
    ->rules('nullable', 'array')

schema() pren un array de Field (renderitzat recursivament: un Repeater pot contenir un altre Repeater). Les regles de validació de cada subcamp s'apliquen amb la notació de punts de Laravel per a arrays (links.*.label, links.*.url), generada automàticament, i respecten el visibleWhen() propi de cada fila.

Relation ​

Un desplegable cercable sobre un model relacionat. Té dos modes, segons si la relació és d'un sol valor o de molts:

Valor únic (belongsTo): el camp es diu com la columna de clau forana, no com el mètode de relació, perquè el Model::create()/update() normal el tracti com qualsevol altre atribut.

php
Relation::make('author_id')
    ->related(User::class)
    ->titleAttribute('name')
    ->searchColumns('name', 'email')
    ->query(fn (Builder $query): Builder => $query->whereNotNull('email_verified_at'))
    ->nullable()
    ->rules('nullable', 'exists:users,id')

Múltiple (belongsToMany, via ->multiple()): el camp es diu com el mètode de la relació (tags, és a dir $model->tags()), no com una columna, una taula pivot no té clau forana amb què anomenar-lo. Fes servir ->relationship('nom') quan el nom del camp i el del mètode hagin de diferir.

php
Relation::make('tags')
    ->multiple()
    ->related(Tag::class)
    ->titleAttribute('name')
    ->rules('array')
MètodeDescripció
related(class-string<Model> $model)El model relacionat. Obligatori.
titleAttribute(string $attribute)Atribut mostrat a cada opció del desplegable. name per defecte.
searchColumns(string ...$columns)Columnes contra les quals cerca el combobox. Per defecte, només titleAttribute().
query(Closure $callback)Restringeix quins registres es poden triar. També s'aplica en desar (vegeu més avall).
searchUsing(Closure $callback, ?Closure $refine = null)Substitueix la cerca per defecte del desplegable. Vegeu «Cerca a mida».
dependsOn(string $field)Les opcions depenen del valor d'un altre camp del formulari. Vegeu «Opcions dependents».
nullable(bool = true)Només rellevant en mode d'un sol valor.
multiple(bool = true)Canvia el camp a selecció múltiple (chips), sobre una relació belongsToMany.
relationship(string $name)Nom del mètode de relació, quan difereix del nom del camp. Per defecte, el mateix nom del camp.
itemRules(string ...$rules)Regles de validació per a cada element seleccionat en mode múltiple. Per defecte, integer més un exists:{taula},{clau} derivat de related().

L'abast s'aplica en desar ​

query() no només filtra el desplegable: també s'aplica quan es desa. Un registre que l'abast amaga és refusat amb un error de validació, encara que algú n'enviï l'identificador a mà. Això val per als formularis d'un Resource, d'un Relation manager i d'una acció.

El closure rep tres arguments: el query builder, el valor del camp de dependsOn() (o null si no n'hi ha), i el registre pare quan el camp és dins d'un Relation manager o d'una acció. Ha de mutar el builder que rep.

php
Relation::make('room_id')
    ->related(Room::class)
    ->query(function (Builder $query, mixed $dependsOn, ?Model $parent): void {
        $query->where('school_id', $parent?->school_id);
    })

Opcions dependents ​

dependsOn() fa que les opcions d'un camp es tornin a carregar quan canvia un altre camp del formulari. El valor d'aquest camp arriba com a segon argument del closure de query():

php
Relation::make('room_id')
    ->related(Room::class)
    ->dependsOn('building_id')
    ->query(function (Builder $query, mixed $buildingId): void {
        $query->where('building_id', $buildingId);
    })

Cerca a mida ​

Per defecte, el desplegable cerca amb un LIKE sobre searchColumns(). searchUsing() el substitueix per a columnes que no es poden comparar així, com una de xifrada:

php
Relation::make('student_id')
    ->related(Student::class)
    ->searchUsing(
        fn (Builder $query, string $term): Builder => $query->where('name_index', 'like', substr($term, 0, 3).'%'),
        refine: fn (Student $student, string $term): bool => str_contains(mb_strtolower($student->name), mb_strtolower($term)),
    )

El primer closure estreny els candidats a SQL (fins a 500). El segon, refine, decideix en PHP quins d'aquests candidats coincideixen realment. Sense refine, el resultat és el que el primer closure deixa al builder.

Com es desa una relació múltiple: en crear/actualitzar el registre, Arrel fa un sync() (reemplaçament complet, no una addició) contra la relació, abans que s'executin afterCreate()/afterUpdate(), així que aquests hooks sempre veuen la relació ja sincronitzada. Com que sync() reemplaça tot el conjunt de files pivot, qualsevol columna pivot extra es reinicia en les files resincronitzades, editar dades pivot és feina d'un Relation manager, no d'aquest camp. Una clau simplement absent del payload de la petició mai es toca, així que una actualització parcial no pot esborrar per accident una relació existent.

Referència ràpida ​

Camptype()Opcions pròpies
Texttextmasked()
TextareatextareaCap
RichEditorrich_editorCap
ToggletoggleCap
Selectselectoptions(), nullable()
CheckboxListcheckbox_listoptions()
TagstagsitemRules()
PlaceholderplaceholderCap
DatedatedisplayFormat()
TimetimedisplayFormat()
Filefiledisk(), directory(), url()
Imageimagedisk(), directory(), url()
Repeaterrepeaterschema()
Relationrelation / relation_multiplerelated(), titleAttribute(), searchColumns(), searchUsing(), query(), dependsOn(), nullable(), multiple(), relationship(), itemRules()

Tots els camps, a més, comparteixen helperText(), placeholder(), default(), visibleWhen() i visibleOn() de la classe base.