Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Variants Field Bundle Laravel Package

unite-cms/variants-field-bundle

Adds a flexible variants field to Unite CMS, letting editors define and manage multiple product or content variants within a single field. Configure variant options and values to support structured, reusable variant data across content types.

View on GitHub
Deep Wiki
Context7

Getting Started

First, calibrate your expectations. Despite living in the same Composer ecosystem, unite-cms/variants-field-bundle is a Symfony bundle for Unite CMS, not a Laravel package. There is no service provider, no facade, no config/variants-field.php. With zero stars and zero dependents, treat it as reference source code rather than a dependency you composer require into a Laravel app.

That leaves two realistic adoption paths:

  1. Run Unite CMS as a separate Symfony service and consume its GraphQL API from Laravel over HTTP. The bundle lives in the CMS repo; your Laravel app only ever sees GraphQL.
  2. Read the bundle as a design reference and reimplement the "pick one of N field-group variants" pattern natively — a JSON column + a custom Filament/Nova field + validation rules.

Where to look first (in this order):

  • composer.json — the unite-cms/core-bundle constraint tells you which Unite generation this targets. This single line determines whether the field-type API you're reading is current or three majors stale.
  • README.md — likely the only documentation. Expect installation + one YAML example, nothing more.
  • Resources/config/services.* — the service definition and, critically, the DI tag used to register the field type (Unite registers field types via a tagged service, typically unite_cms.field_type). This is the extension point everything else hangs off.
  • The FieldType class itself — look for getType() (the GraphQL/identifier name) and getSettingsFormType() (the Symfony form that configures the field in the admin).
  • Any Resources/public/ or Resources/js/ assets — the admin-side variant picker. If absent or unbuilt, the field will be API-only and editors will hate it.

First use case to validate against: a "Hero" content block where an editor picks Image + Text, Video, or Text only, and each choice exposes a different set of inputs. If the bundle can't express that in under an hour, stop and reimplement.

Sanity-check the install with a schema dump before writing any content:

bin/console unite:schema:dump   # confirm the variant field type appears in the ContentType schema

If the type isn't in the schema, the service tag isn't being picked up — see Gotchas.

Implementation Patterns

1. Model the variant as a discriminated union, not a bag of optional fields.

The field type only selects a variant; it does not enforce that the fields belonging to the selected variant are present. Model each variant as its own field group and treat the selector as the discriminant:

type HeroBlock {
  variant: String!          # "image_text" | "video" | "text_only"
  imageText: ImageTextFields
  video: VideoFields
  textOnly: TextOnlyFields
}

Consumers then branch on variant and pick the matching group. This keeps the schema additive: new variants are new nullable groups plus a new enum value, and old content keeps rendering.

2. Register the field type the Symfony way, and copy the tag verbatim.

The bundle's own services.yml is your template for registering a derived field type (e.g. a "variants" field with your own preset variant list):

services:
    App\Unite\FieldType\PresetVariantsFieldType:
        tags:
            - { name: unite_cms.field_type }

Do not invent a tag name — copy it character-for-character from the bundle. Unite silently ignores unregistered tags, so a typo produces "field type not found" at schema-build time, not at boot.

3. Drive editor UX from the settings form, not from code.

Unite field types expose a Symfony FormType for per-field configuration (which variants are offered, labels, default selection). Push variant definitions into that form so content editors can add a variant without a deploy. Store the offered variants as field settings — but read the Gotchas section on why that's a migration hazard.

4. Consume from Laravel with fragments + a DTO, never raw arrays.

$query = <<<'GQL'
query ($slug: String!) {
  getHero(slug: $slug) {
    variant
    imageText { heading image { url } }
    video     { url poster { url } }
    textOnly  { body }
  }
}
GQL;

$data = Cache::remember("hero:{$slug}", 300, fn () =>
    Http::withToken(config('services.unite.token'))
        ->post(config('services.unite.endpoint'), ['query' => $query, 'variables' => compact('slug')])
        ->throw()->json('data.getHero')
);

$hero = HeroBlockData::fromGraphQl($data); // branches on ->variant in one place

Centralise the variant switch in a single fromGraphQl factory. If the switch leaks into Blade templates you will be editing twelve views the day a variant is renamed.

5. Treat variant renames as schema migrations.

Because the selector is typically a plain string/enum in content, a rename is a content migration, not a code change. Write the mapping once and script it:

// artisan tinker / a one-off command
foreach (Hero::where('variant', 'legacy_cta')->get() as $hero) {
    $hero->update(['variant' => 'image_text']);
}

6. Keep a contract test on the CMS schema.

public function test_unite_schema_still_exposes_variants(): void
{
    $schema = $this->unite()->introspect();
    $this->assertContains('image_text', $schema->enumValues('HeroVariant'));
}

This is your only early warning that someone edited a content type in the CMS admin and broke your Laravel app.

Gotchas and Tips

Abandonment is the headline risk. Zero stars, zero dependents, and a Symfony-1.x-era API surface. Pin the exact commit in composer.lock, vendor the source if you must deploy it, and budget for a fork. If you cannot read the whole FieldType class in one sitting, it's small enough to reimplement — and given the maintenance signal, that is usually the correct call.

Tag registration fails silently. A field type that isn't registered simply doesn't exist at schema-build time. Debug in this order: (1) bin/console debug:container --tag=unite_cms.field_type, (2) confirm the bundle is in config/bundles.php, (3) confirm Resources/config/services.yml is actually loaded by the bundle's DependencyInjection extension. A missing load() call in the extension is the classic cause.

Settings live in the content type definition, not the content. Changing "which variants are offered" in the field settings does not migrate existing documents. Old content can reference a variant that is no longer offered. Guard every render path with a fallback branch and a nullable-safe accessor — the field type gives you no validation for this.

Per-variant required fields are not enforced. The selector is a hint, not a constraint. Add explicit validation in Laravel before persisting, or accept that nulls will reach your templates. Prefer a match ($variant) with a default arm that throws in dev and degrades gracefully in prod.

Admin assets may not be built. Symfony bundles from this era often ship unbuilt JS under Resources/public. If the variant picker doesn't render, check whether Webpack Encore / assets:install picked up the bundle's assets, and whether the host Unite admin version matches the bundle's expected React/API version. Mismatched admin versions are the most common "field type renders blank" cause.

GraphQL nullability differs from your mental model. Variant sub-groups are almost certainly nullable even when the discriminant guarantees one is populated. Your generated types (Lighthouse, graphql-codegen, or hand-rolled DTOs) will reflect that, and every accessor needs a null path.

Extension points worth knowing before you fork: the FieldType interface (identifier + settings form), the tagged-service registration, and the settings FormType. Those three are the whole seam. If you reimplement the pattern in Laravel, the equivalent seam is a JSON column, a variant discriminator, a match-based DTO factory, and a Filament field that swaps sub-schemas with ->live() — roughly 150 lines, fully owned, and no abandoned dependency.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager