Weave Code
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

Twill Laravel Package

area17/twill

Twill is an open-source Laravel package for building a custom CMS fast. It provides a polished admin UI with prebuilt features and Vue components, stays flexible and extensible, works headless or integrated, and lets you use your own models with no lock-in.

View on GitHub
Deep Wiki
Context7

Creating Blocks

In order to add a block editor to your module, add the block editor field to your module form:

:::tabs=currenttab.FormBuilder&items.FormBuilder|FormView|Directive:::

:::tab=name.FormBuilder:::

BlockEditor::make()

BlockEditor::make()
    ->blocks(['title', 'quote', 'text'])

:::#tab:::

:::tab=name.FormView:::

<x-twill::block-editor />

[@php](https://github.com/php)
    $blocks = [
        'title',
        'quote',
        'text'
    ];
[@endphp](https://github.com/endphp)

<x-twill::block-editor
    :blocks="$blocks"
/>

[@php](https://github.com/php)
    $excludeBlocks = [
        'title',
        'quote'
    ];
[@endphp](https://github.com/endphp)

<x-twill::block-editor
    :excludeBlocks="$excludeBlocks"
/>

:::#tab:::

:::tab=name.Directive:::

[@formField](https://github.com/formField)('block_editor', [
    'blocks' => ['title', 'quote', 'text', 'image', 'grid', 'test', 'publications', 'news']
])

[@formField](https://github.com/formField)('block_editor', [
    'excludeBlocks' => ['title', 'quote']
])

:::#tab:::

:::#tabs:::

Block component class

As of Twill 3, you can make use of Block component classes.

These are essentially regular Blade components, but they are also responsible for your Block's form and rendering!

You can generate Block components using the command:

php artisan twill:make:componentBlock Namespace/Name
php artisan twill:make:componentBlock namespace.name
php artisan twill:make:componentBlock name

These blocks will be placed under App\View\Components\Twill\Blocks.

While the rendering blade file looks the same, there is no longer a form blade file.

Instead, you define the form in your component class, the same way you can do module forms!

<?php

namespace App\View\Components\Twill\Blocks;

use A17\Twill\Services\Forms\Fields\Wysiwyg;
use A17\Twill\Services\Forms\Form;
use A17\Twill\Services\Forms\Fields\Input;
use A17\Twill\View\Components\Blocks\TwillBlockComponent;
use Illuminate\Contracts\View\View;

class Example extends TwillBlockComponent
{
    public function render(): View
    {
        return view('components.twill.blocks.example');
    }

    public function getForm(): Form
    {
        return Form::make([
            Input::make()->name('title'),
            Wysiwyg::make()->name('text')
        ]);
    }
}

Block helpers

By default, the class name will be used as your block name, and 'app' will be the default group.

These can be overwritten by overriding the following methods:

public static function getBlockTitle(?Block $block = null): string
{
    return Str::replace('Block', '', Str::afterLast(static::class, '\\'));
}

public static function getBlockGroup(): string
{
    return 'app';
}

public static function getBlockIcon(): string
{
    return 'text';
}

Crops

Usually we would define image crop's in the block_editor config, but with block components you can define them inline in your component like this:

public static function getCrops(): array
{
    return [
        'content_image' => [
            'default' => [
                [
                    'name' => 'default',
                    'ratio' => 16 / 9,
                    'minValues' => [
                        'width' => 100,
                        'height' => 100,
                    ],
                ],
            ]
        ]
    ];
}

Validation

As with default blocks, you can also validate fields:

public function getValidationRules(): array
{
    return [];
}

public function getTranslatableValidationRules(): array
{
    return [];
}

Rendering helpers

You have access to all the same variables as in a regular block, however with the components you have some additional helpers:

input and translated input

With components, you can directly access input values like this:

{{ $input('title') }}
{{ $translatedInput('title') }}

Image url

Getting an image url:

{{ $image('cover', 'default', ['h' => 100) }}

Repeaters

Looping over a repeater:

[@foreach](https://github.com/foreach) ($repeater('slider-item') as $repeaterItem)
  <li>
    <img src="{{ $repeaterItem->renderData->block->image('slider', 'desktop', ['h' => 850]) }}" alt="">
    {{ $repeaterItem->renderData->block->input('title') }}
  </li>
[@endforeach](https://github.com/endforeach)

Block component in packages or domain specific directories

If you want to register blocks from your package you can add:

\A17\Twill\Facades\TwillBlocks::registerComponentBlocks('\\Your\\Namespace\\Components\\Twill\\Blocks', __DIR__ . '/../../path/to/namespace');

This will register the namespace in your package or domain and load them!

Using blade files

Blocks and Repeaters are built on the same Block model and are created and defined in their respective folders. By default, Twill will look for Blade templates in views/twill/blocks for blocks and views/twill/repeaters for repeaters.

Blocks (and Repeaters) are exactly like a regular form, without any Blade layout or section. The templates take special annotations to add further customization. The title annotation is mandatory and Twill will throw an error if it is not defined.

Available annotations:

  • Provide a title with [@twillPropTitle](https://github.com/twillPropTitle) or [@twillBlockTitle](https://github.com/twillBlockTitle) or [@twillRepeaterTitle](https://github.com/twillRepeaterTitle) (mandatory)
  • Provide a dynamic title with [@twillPropTitleField](https://github.com/twillPropTitleField) or [@twillBlockTitleField](https://github.com/twillBlockTitleField) or [@twillRepeaterTitleField](https://github.com/twillRepeaterTitleField)
  • Provide an icon with [@twillPropIcon](https://github.com/twillPropIcon) or [@twillBlockIcon](https://github.com/twillBlockIcon) or [@twillRepeaterIcon](https://github.com/twillRepeaterIcon)
  • Provide a group with [@twillPropGroup](https://github.com/twillPropGroup) or [@twillBlockGroup](https://github.com/twillBlockGroup) or [@twillRepeaterGroup](https://github.com/twillRepeaterGroup) (defaults to app)
  • Provide a repeater trigger label with [@twillPropTrigger](https://github.com/twillPropTrigger) or [@twillRepeaterTrigger](https://github.com/twillRepeaterTrigger)
  • Provide a repeater max items with [@twillPropMax](https://github.com/twillPropMax) or [@twillRepeaterMax](https://github.com/twillRepeaterMax), [@twillRepeaterMax](https://github.com/twillRepeaterMax) can also be defined from the formField. See Repeater form field
  • Define a block or repeater as compiled with [@twillPropCompiled](https://github.com/twillPropCompiled) or [@twillBlockCompiled](https://github.com/twillBlockCompiled) or [@twillRepeaterCompiled](https://github.com/twillRepeaterCompiled)
  • Define a block or repeater component with [@twillPropComponent](https://github.com/twillPropComponent) or [@twillBlockComponent](https://github.com/twillBlockComponent) or [@twillRepeaterComponent](https://github.com/twillRepeaterComponent)

e.g.:

:::filename::: views/twill/blocks/quote.blade.php :::#filename:::

[@twillBlockTitle](https://github.com/twillBlockTitle)('Quote')
[@twillBlockIcon](https://github.com/twillBlockIcon)('text')

<x-twill::input 
    name="quote"
    type="textarea"
    label="Quote text"
    :maxlength="250"
    :rows="4"
/>

A more complex example would look like this:

:::filename::: views/twill/blocks/media.blade.php :::#filename:::

[@twillBlockTitle](https://github.com/twillBlockTitle)('Media')
[@twillBlockIcon](https://github.com/twillBlockIcon)('image')

<x-twill::medias
    name="image"
    label="Images"
    :max="20"
/>

<x-twill:files
    name="video"
    label="video"
    note="Video will overwrite previously selected images"
    :max="1"
/>

<x-twill::input
    name="caption"
    label="Caption"
    :maxlength="250"
    :translated="true"
/>

[@php](https://github.com/php)
    $options = [
        [
            'value' => 'cut',
            'label' => 'Cut'
        ],
        [
            'value' => 'fade',
            'label' => 'Fade In/Out'
        ]
    ];
[@endphp](https://github.com/endphp)

<x-twill::select
    name="effect"
    label="Transition effect"
    placeholder="Select transition effect"
    default="cut"
    :options="$options"
/>

<x-twill::color
    name="bg"
    label="Background color"
    note="Default is light grey (#E6E6E6)"
/>

<x-twill::input
    name="timing"
    label="Timing"
    note="Timing in ms (default is 4000ms)"
/>

Dynamic block titles

In Twill >= 2.5, you can use the [@twillBlockTitleField](https://github.com/twillBlockTitleField) directive to include the value of a given field in the title area of the blocks. This directive also accepts a hidePrefix option to hide the generic block title:

[@twillBlockTitle](https://github.com/twillBlockTitle)('Section')
[@twillBlockTitleField](https://github.com/twillBlockTitleField)('title', ['hidePrefix' => true])
[@twillBlockIcon](https://github.com/twillBlockIcon)('text')
[@twillBlockGroup](https://github.com/twillBlockGroup)('app')

<x-twill::input
    name="title"
    label="Title"
    :required="true"
/>
...

Create a block from an existing block template

Using php artisan twill:make:block {name} {baseBlock} {icon}, you can generate a new block based on a provided block as a base.

This example would create views/twill/blocks/exceptional-media.blade.php from views/twill/blocks/media.blade.php:

$ php artisan twill:make:block ExceptionalMedia media image

List existing blocks and repeaters

Using php artisan twill:list:blocks will list all blocks and repeaters. There are a few options:

  • -s|--shorter for a shorter table,
  • -b|--blocks for blocks only,
  • -r|--repeaters for repeaters only,
  • -a|--app for app blocks/repeaters only,
  • -c|--custom for app blocks/repeaters overriding Twill blocks/repeaters only,
  • -t|--twill for Twill blocks/repeaters only

List existing icons

php artisan twill:list:icons will list all icons available.

Using custom icons

Custom icons need to be named differently from default icons to avoid issues when creating the SVG sprites.

If you want to use custom icons in a block, you have to define the source directory's path in config/twill.php. Add it under block_editor.directories.source.icons key:

:::filename::: config/twill.php :::#filename:::

<?php

return [
    ...
    'block_editor' => [
        'directories' => [
            'source' => [
                'icons' => [
                    base_path('vendor/area17/twill/frontend/icons'),
                    resource_path('assets/admin/icons'), // or any other path of your choice
                ],
            ],
        ],
    ],
    ...
];

See also Default Configuration.

If the resource_path('assets/admin/icons') directory contains a my-custom-icon.svg file, you can use this icon in your block by using its basename: [@twillBlockIcon](https://github.com/twillBlockIcon)('my-custom-icon').

In order to make the icons appear in the CMS, you'll need to run php artisan twill:build

Use Block traits in your Model and Repository

Now, to handle the block data you must integrate it with your module. Use the Blocks traits in the Model and Repository associated with your module. If you generated that module from the CLI and did respond yes to the question asking you about using blocks, this should already be the case for you.

In your model, use HasBlocks:

:::filename::: app/Models/Article.php :::#filename:::

<?php

namespace App\Models;

use A17\Twill\Models\Behaviors\HasBlocks;
use A17\Twill\Models\Model;

class Article extends Model
{
    use HasBlocks;

    ...
}

In your repository, use HandleBlocks:

:::filename::: app/Repositories/ArticleRepository.php :::#filename:::

<?php

namespace App\Repositories;

use A17\Twill\Repositories\Behaviors\HandleBlocks;
use A17\Twill\Repositories\ModuleRepository;
use App\Models\Article;

class ArticleRepository extends ModuleRepository
{
    use HandleBlocks;

    ...
}
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport