Skip to content

[6.x] Publish forms#11787

Merged
jasonvarga merged 36 commits intouifrom
publish-forms
May 20, 2025
Merged

[6.x] Publish forms#11787
jasonvarga merged 36 commits intouifrom
publish-forms

Conversation

@jasonvarga
Copy link
Member

@jasonvarga jasonvarga commented May 13, 2025

Note

This PR contains a handful of todos, commented out, and broken bits. Since it's going to the ui branch, we'll sort them out there. I just want the majority of it to be contained in a PR.

Publish Form

You can create a basic publish form without having to think about Vue or Blade.

You will need a route and a controller. The controller will need to getting the blueprint and values, and storing the values.

For example, you might want to create a publish form for an Eloquent model.

use Statamic\Facades\Blueprint;

class Thing extends Model
{
    public function values()
    {
        return [
            'name' => $this->name,
            'description' => $this->description,
        ];       
    }
    
    public function blueprint()
    {
        return Blueprint::make(...);
    }
}
Route::get('things/{thing}', [ThingController::class, 'edit'])->name('thing.edit');
Route::patch('things/{thing}', [ThingController::class, 'update'])->name('thing.update');
use Illuminate\Support\Request;
use Statamic\CP\PublishForm;

class ThingController
{
    public function edit(Thing $thing)
    {
        return PublishForm::make($thing->blueprint())
            ->values($thing->values())
            ->submittingTo(cp_route('thing.update', $thing));
    }
    
    public function update(Request $request, Thing $thing)
    {
        $values = PublishForm::make($thing->blueprint())->submit($request->values);
        
        $thing->update($values);
    }
}

Vue Components

If you have more complex needs, you can create your own Vue component using a number of composable elements.

The SavePipeline is a composable way to save the form using different steps. In this example there are three steps but the only required one is the Request step, which will handle the form submission and validation.

<script setup>
import { ref, useTemplateRef } from 'vue';
import { Header, Button, PublishContainer, PublishTabs, SavePipeline } from 'statamic';
const { Pipeline, Request, BeforeSaveHooks, AfterSaveHooks } = SavePipeline;

const props = defineProps(['blueprint', 'initialValues', 'initialMeta']);

const container = useTemplateRef('container');
const values = ref(props.initialValues);
const meta = ref(props.initialMeta);
const errors = ref({});
const saving = ref(false);

function save() {
    new Pipeline()
        .provide({ container, errors, saving })
        .through([
            new BeforeSaveHooks('entry'),
            new Request('/your-url', 'patch', { values: values.value }),
            new AfterSaveHooks('entry'),
        ])
        .then((response) => {
            Statamic.$toast.success('Saved');
        });
}
</script>

<template>
    <Header :title="title">
        <Button variant="primary" text="Save" @click="save" :disabled="saving" />
    </Header>
    <PublishContainer
        ref="container"
        name="myform"
        :blueprint="blueprint"
        :values="values"
        :meta="meta"
        :errors="errors"
        @updated="values = $event"
    >
        <PublishTabs />
    </PublishContainer>
</template>
@extends('statamic::layout')

@section('content')
    <your-publish-form
        :blueprint="{{ Js::from($blueprint) }}"
        :initial-values="{{ Js::from($values) }}"
        :initial-meta="{{ Js::from($meta) }}"
    ></your-publish-form>
@endsection

Container, Tabs, Sections, etc

The publish container is the main store of the form. All the values, metadata, errors, etc will be held in there.

The tabs component will render the standard tabbed based form layout automatically based on the provided blueprint.

You can customize the content of the tabs, but the default is the PublishSections component which again will just render the form appropriately.

The props and slots available to customize these elements will be described in more detail in the eventual docs.

Difference from existing forms

With this, you no longer have to deal with prop- and event-ception, passing props all the way down through tabs/sections/fields. It's now automatic just by having the Vue component existing on the page.

Also..

This PR also

  • adds some this.$something helpers to the Statamic class so you can do Statamic.$something when using composition api
  • Moves around conditional field logic so that it works from the composition api based components
  • Fieldtypes get id prop now
  • Removes WithField wrappers from inputs (that's where a big chunk of the diffs are coming from)

jasonvarga added 30 commits May 7, 2025 10:16
# Conflicts:
#	resources/js/components/ui/Slider.vue
- section provider becomes fields provider
- fields provider accepts prefixes for recursion
- recursion works now. values/meta/errors/previews are pulled straight from the store by dotted keys
- field conditions work now
- field ids are provided to the fieldtype components as an `id` prop rather than a `fieldId` computed
- replicators hooked up
- group fieldtype hooked up
- grid stacked mode hooked up
…er worked. This'll make the upcoming refactor easier.
# Conflicts:
#	resources/js/components/ui/Select/Select.vue
#	resources/js/components/ui/Slider.vue
#	resources/js/components/ui/index.js
Pass disabled as a prop.
Style disable based on prop, not sub-control position.
The cva classes are now a computed so that it'll update when props change, such as disabled.
Adjust slots so you can specify combinations of label and actions.
- Wire up terms publish form with new stuff.
- New composition api live preview component with only necessary props. (Values etc come from container)
- Container component and store adds more props and things like origin values/meta, localized fields, etc.
- Tab slot is actions rather than sidebar.
- Localization picker moved into a component. It'll get reused by entries form.
- Publish field component can sync/desync and track localized fields
- Publish "Components" component can just be dropped into the form. No need to pass any props, slot scope, or loops.
- Input component gets a disabled prop.
- Text fieldtype passes disabled and id
- Slug fieldtype passes disabled
- $wait util is moved into its own file
- Expose this.$events as Statamic.$events
- Fix/improve recursiveness in grid/rep/bard/group by passing around field/meta prefixes.
- Get rid of preview stuff in favor of getting it through the stores.
- Hook up more grid/rep/bard/group
@jasonvarga jasonvarga marked this pull request as ready for review May 20, 2025 17:44
@jasonvarga jasonvarga merged commit 11aa3fb into ui May 20, 2025
2 of 17 checks passed
@jasonvarga jasonvarga deleted the publish-forms branch May 20, 2025 17:44
duncanmcclean added a commit that referenced this pull request Jun 3, 2025
…wrapper

We removed the `<WithField>` wrapper in #11787, but didn't remove the label/description props we passed to it.
@jasonvarga jasonvarga mentioned this pull request Jun 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant