Skip to content

Commit

Permalink
Merge 818c56a into b2926ed
Browse files Browse the repository at this point in the history
  • Loading branch information
haringsrob committed Feb 25, 2022
2 parents b2926ed + 818c56a commit 2c07863
Show file tree
Hide file tree
Showing 13 changed files with 407 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/src/.vuepress/sidebar.js
Expand Up @@ -291,6 +291,10 @@ module.exports = [
{
"title": "Adding fields to the create modal",
"path": "/guides/adding-fields-to-the-create-modal.html",
},
{
"title": "Prefill block editor from template",
"path": "/guides/prefill-block-editor-from-template.html",
}
],
},
Expand Down
131 changes: 131 additions & 0 deletions docs/src/guides/prefill-block-editor-from-template.md
@@ -0,0 +1,131 @@
---
pageClass: twill-doc
---

# Prefill a block editor from a selection of templates

Objectives:

- Create a new module with a `template` field
- Prefill the block editor for new items according to the selected template

Versions used at the time of writing:

| | Version |
|:-----------|:-------:|
| PHP | 8.0 |
| Laravel | 8.x |

## Create the new module

```
php artisan twill:make:module articles -B
```

We'll make sure to enable blocks on the module, everything else is optional. In this example, we won't be using
translations, but they can be added with minor changes.


## Update the migration

We'll add the `template` field to the generated migration:

<<< @/src/guides/prefill-block-editor-from-template/2021_09_19_131244_create_articles_tables.php{16}

Then, we'll run the migrations:
```
php artisan migrate
```

and add the module to our `routes/admin.php` and `config/twill-navigation.php`.


## Update the model

In this example, we imagine 3 templates that our authors can choose from:

- Full Article: an original article on our blog
- Linked Article: a short article to summarize and share interesting articles from other blogs
- Empty: a blank canvas


We'll start by adding our new field to the fillables:

`app/Models/Article.php`

<<< @/src/guides/prefill-block-editor-from-template/Article.php#fillable{6}

Then, we'll define some constants for our template options:

<<< @/src/guides/prefill-block-editor-from-template/Article.php#constants

We'll add an attribute accessor to get the template name for the currently selected template value:

<<< @/src/guides/prefill-block-editor-from-template/Article.php#accessor

This will be useful in our `create.blade.php` view below.

## Add the `template` field to the create modal

When running `php artisan twill:make:module`, we get a `form.blade.php` to define the main form for our module. In addition, it's also possible to redefine the fields that are displayed in the create modal, before the form:

![01-create-modal](./prefill-block-editor-from-template/create-modal.png)

We'll copy Twill's built-in view from `vendor/area17/twill/views/partials/create.blade.php` into our project, then add our `template` field:

`resources/views/admin/articles/create.blade.php`

<<< @/src/guides/prefill-block-editor-from-template/articles_create.blade.php

## Create some blocks

```
php artisan twill:make:block article-header
php artisan twill:make:block article-paragraph
php artisan twill:make:block article-references
php artisan twill:make:block linked-article
```

::: details blocks/article-header.blade.php
<<< @/src/guides/prefill-block-editor-from-template/blocks_article-header.blade.php
:::

::: details blocks/article-paragraph.blade.php
<<< @/src/guides/prefill-block-editor-from-template/blocks_article-paragraph.blade.php
:::

::: details blocks/article-references.blade.php
<<< @/src/guides/prefill-block-editor-from-template/blocks_article-references.blade.php
:::

::: details blocks/linked-post.blade.php
<<< @/src/guides/prefill-block-editor-from-template/blocks_linked-post.blade.php
:::

## Add the editor to our form

We'll add the block editor field to our form:

`resources/views/admin/articles/form.blade.php`

<<< @/src/guides/prefill-block-editor-from-template/articles_form.blade.php{10-12}

## Prefill the blocks on create

With this, all that's needed is to initialize the block editor from the selected template. We'll update our model to add the prefill operation:

`app/Models/Article.php`

<<< @/src/guides/prefill-block-editor-from-template/Article.php#prefill

Then, we'll hook into the repository's `afterSave()`:

<<< @/src/guides/prefill-block-editor-from-template/ArticleRepository.php{22-29}

The check on `$object->wasRecentlyCreated` ensures the prefill operation will only run when the record is first created.

## Finished result

And there we have it Ñ a templating mechanism for our block editor:

![02-edit-form](./prefill-block-editor-from-template/final.png)
@@ -0,0 +1,34 @@
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;

class CreateArticlesTables extends Migration
{
public function up()
{
Schema::create('articles', function (Blueprint $table) {
createDefaultTableFields($table);

$table->string('title', 200)->nullable();
$table->text('description')->nullable();
$table->integer('position')->unsigned()->nullable();
$table->text('template')->nullable();
});

Schema::create('article_slugs', function (Blueprint $table) {
createDefaultSlugsTableFields($table, 'article');
});

Schema::create('article_revisions', function (Blueprint $table) {
createDefaultRevisionsTableFields($table, 'article');
});
}

public function down()
{
Schema::dropIfExists('article_revisions');
Schema::dropIfExists('article_slugs');
Schema::dropIfExists('articles');
}
}
98 changes: 98 additions & 0 deletions docs/src/guides/prefill-block-editor-from-template/Article.php
@@ -0,0 +1,98 @@
<?php

namespace App\Models;

use A17\Twill\Models\Behaviors\HasBlocks;
use A17\Twill\Models\Behaviors\HasFiles;
use A17\Twill\Models\Behaviors\HasMedias;
use A17\Twill\Models\Behaviors\HasPosition;
use A17\Twill\Models\Behaviors\HasRevisions;
use A17\Twill\Models\Behaviors\HasSlug;
use A17\Twill\Models\Behaviors\Sortable;
use A17\Twill\Models\Model;
use A17\Twill\Repositories\BlockRepository;

class Article extends Model implements Sortable
{
use HasBlocks;
use HasSlug;
use HasMedias;
use HasFiles;
use HasRevisions;
use HasPosition;

// #region constants
public const DEFAULT_TEMPLATE = 'full_article';

public const AVAILABLE_TEMPLATES = [
[
'value' => 'full_article',
'label' => 'Full Article',
'block_selection' => ['article-header', 'article-paragraph', 'article-references'],
],
[
'value' => 'linked_article',
'label' => 'Linked Article',
'block_selection' => ['article-header', 'linked-article'],
],
[
'value' => 'empty',
'label' => 'Empty',
'block_selection' => [],
],
];

// #endregion constants

public const AVAILABLE_BLOCKS = ['article-header', 'article-paragraph', 'article-references', 'linked-article'];

// #region fillable
protected $fillable = [
'published',
'title',
'description',
'position',
'template',
];

// #endregion fillable

public $slugAttributes = [
'title',
];

// #region accessor
public function getTemplateLabelAttribute()
{
$template = collect(static::AVAILABLE_TEMPLATES)->firstWhere('value', $this->template);

return $template['label'] ?? '';
}

// #endregion accessor

// #region prefill
public function getTemplateBlockSelectionAttribute()
{
$template = collect(static::AVAILABLE_TEMPLATES)->firstWhere('value', $this->template);

return $template['block_selection'] ?? [];
}

public function prefillBlockSelection()
{
$i = 1;

foreach ($this->template_block_selection as $blockType) {
app(BlockRepository::class)->create([
'blockable_id' => $this->id,
'blockable_type' => static::class,
'position' => $i++,
'content' => '{}',
'type' => $blockType,
]);
}
}

// #endregion prefill
}
@@ -0,0 +1,30 @@
<?php

namespace App\Repositories;

use A17\Twill\Repositories\Behaviors\HandleBlocks;
use A17\Twill\Repositories\Behaviors\HandleSlugs;
use A17\Twill\Repositories\Behaviors\HandleMedias;
use A17\Twill\Repositories\Behaviors\HandleFiles;
use A17\Twill\Repositories\Behaviors\HandleRevisions;
use A17\Twill\Repositories\ModuleRepository;
use App\Models\Article;

class ArticleRepository extends ModuleRepository
{
use HandleBlocks, HandleSlugs, HandleMedias, HandleFiles, HandleRevisions;

public function __construct(Article $model)
{
$this->model = $model;
}

public function afterSave($object, $fields)
{
parent::afterSave($object, $fields);

if ($object->wasRecentlyCreated) {
$object->prefillBlockSelection();
}
}
}
@@ -0,0 +1,39 @@
@formField('input', [
'name' => $titleFormKey ?? 'title',
'label' => $titleFormKey === 'title' ? twillTrans('twill::lang.modal.title-field') : ucfirst($titleFormKey),
'required' => true,
'onChange' => 'formatPermalink'
])

@if ($item->template ?? false)
{{--
On update, we show the selected template in a disabled field.
For simplicity, templates cannot be modified once an item has been created.
--}}

@formField('input', [
'name' => 'template_label',
'label' => 'Template',
'disabled' => true,
])
@else
{{--
On create, we show a select field with all possible templates.
--}}

@formField('select', [
'name' => 'template',
'label' => 'Template',
'default' => \App\Models\Article::DEFAULT_TEMPLATE,
'options' => \App\Models\Article::AVAILABLE_TEMPLATES,
])
@endif

@if ($permalink ?? true)
@formField('input', [
'name' => 'slug',
'label' => twillTrans('twill::lang.modal.permalink-field'),
'ref' => 'permalink',
'prefix' => $permalinkPrefix ?? ''
])
@endif
@@ -0,0 +1,13 @@
@extends('twill::layouts.form')

@section('contentFields')
@formField('input', [
'name' => 'description',
'label' => 'Description',
'maxlength' => 100
])

@formField('block_editor', [
'blocks' => \App\Models\Article::AVAILABLE_BLOCKS,
])
@stop
@@ -0,0 +1,18 @@
@twillBlockTitle('Article Header')
@twillBlockIcon('text')
@twillBlockGroup('app')

@formField('input', [
'name' => 'subtitle',
'label' => 'Subtitle',
])

@formField('input', [
'name' => 'author',
'label' => 'Author',
])

@formField('input', [
'name' => 'reading_time',
'label' => 'Estimated Reading Time',
])
@@ -0,0 +1,10 @@
@twillBlockTitle('Article Paragraph')
@twillBlockIcon('text')
@twillBlockGroup('app')

@formField('wysiwyg', [
'name' => 'text',
'label' => 'Text',
'placeholder' => 'Text',
'toolbarOptions' => ['bold', 'italic', 'link', 'clean'],
])

0 comments on commit 2c07863

Please sign in to comment.