Skip to content

Instantly share code, notes, and snippets.

@sauron
Last active June 15, 2021 02:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sauron/1c6b41d8d5d9a87c71501cb5b2dd049c to your computer and use it in GitHub Desktop.
Save sauron/1c6b41d8d5d9a87c71501cb5b2dd049c to your computer and use it in GitHub Desktop.
Step by step - Creating a Twill app - Part 1

Step by step - Creating a Twill app.

In this post, I'll create an app from the ground up. I'll assume you have PHP, Laravel and Composer. Of course a mysql DB for storing "everything." All the code it's here for reference.

Installation

Let's create a new app.

laravel new my-blog

Add homestead for easy development ;-)

cd my-blog
composer require laravel/homestead --dev
php vendor/bin/homestead make

and our main actor

composer require area17/twill:"1.2.*"

Now we are almost ready to start building our app. Let's verify that the configuration on the Homestead.yml is ok. I've changed the default sites for this:

sites:
  - map: my-blog.dev.a17.io
    to: "/home/vagrant/code/public"
  - map: admin.my-blog.dev.a17.io
    to: "/home/vagrant/code/public"

NOTE: An alternative way of creating the app is with these two commands

composer global require yanhaoli/create-twill-app
create-twill-app new blog

Running Provision your vagrant machine with vagrant up Add the IP and domain defined at Homestead.yaml to your /etc/hosts file.

192.168.10.44 my-blog.dev.a17.io
192.168.10.44 admin.my-blog.dev.a17.io

Now you should be able to visit your URL and receive the Laravel Homepage

This means that it is time for the magic touch. Jump into the VM and run the installation script

vagrant ssh
cd code
php artisan twill:install

It prompts for the superadmin email, password and password confirmation. Now we can visit the admin URL and see everything working.

Building the structure I need an Article entity which can have a Hero image, a title, a description and a flexible structure(Block Editor). Every article can be translated and have a specific URL(slug).

php artisan twill:module articles -TSMBR

Adapt the Table to our needs.

class CreateArticlesTables extends Migration
{
    public function up()
    {
        Schema::create('articles', function (Blueprint $table) {
            // this will create an id, a "published" column, and soft delete and timestamps columns
            createDefaultTableFields($table);
        });
        Schema::create('article_translations', function (Blueprint $table) {
            createDefaultTranslationsTableFields($table, 'article');
            $table->string('title', 200)->nullable();
            $table->text('description')->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_translations');
        Schema::dropIfExists('article_slugs');
        Schema::dropIfExists('articles');
    }
}

Create the DB table

php artisan migrate

Rearrange the model counterpart app/Models/Article.php

class Article extends Model
{
    use HasBlocks, HasTranslation, HasSlug, HasMedias, HasRevisions;
    protected $fillable = [
        'published',
    ];
    public $translatedAttributes = [
        'title',
        'description',
    ];
    public $slugAttributes = [
        'title',
    ];
    public $checkboxes = [
        'published'
    ]
}

Now we have the DB structure we can add the routes to routes/admin.php

Route::module('articles');

and the option to the Twill menu config/twill-navigation.php

    'articles' => [
        'title' => 'Articles',
        'module' => true
    ]

Make sure you have all the languages you want to handle on the config/translatable.php file.

return [
    'locales' => [
        'en',
        'es',
    ],

Our basic CMS for creating articles with translations is ready.

Hero Image For adding images, we need to decide if we are going to use local storage or S3. I'll use local storage. Later in the process, we will switch to S3 and Imgix. Hence, let's add what the manual says: for .env

MEDIA_LIBRARY_ENDPOINT_TYPE=local
MEDIA_LIBRARY_LOCAL_PATH=uploads/
MEDIA_LIBRARY_IMAGE_SERVICE=A17\Twill\Services\MediaLibrary\Local

Now let's make sure we have the mediasParams in the Article model.

    public $mediasParams = [
        'hero_image' => [
            'default' => [
                [
                    'name' => 'landscape',
                    'ratio' => 16 / 9,
                ]
            ]
        ]
    ];

also, the form field to upload the images articles/form.blade.php

    @formField('medias',[
        'name' => 'hero_image',
        'label' => 'Hero image',
    ])

Great! Now we can upload the Hero Image.

Block Editor (flexible structure) For this tutorial, I want the ability to add a gallery, an Image with text, a quote, and a paragraph. Let's go. Inside resources/views/admin/ create a folder called blocks. This folder contains all the Blocks for the CMS. Add the blocks (files containing the form fields we want to use for building an Article). Gallery resources/views/admin/blocks/gallery.blade.php

@formField('medias', [
    'name' => 'gallery',
    'label' => 'Gallery',
    'max' => 5,
    'note' => 'Minimum image width: 1500px'
])

Image with text resources/views/admin/blocks/image_with_text.blade.php

@formField('medias', [
    'name' => 'cover',
    'label' => 'Image',
    'note' => 'Minimum image width 1300px'
])
@formField('input', [
    'translated' => true,
    'name' => 'image_subtitle',
    'label' => 'Image Subtitle (translated)',
    'maxlength' => 250,
    'required' => true,
    'placeholder' => 'Description.',
    'type' => 'textarea'
])

Quote resources/views/admin/blocks/quote.blade.php

@formField('input', [
    'translated' => true,
    'name' => 'quote',
    'label' => 'Quote (translated)',
    'maxlength' => 250,
    'required' => true,
    'type' => 'textarea',
    'rows' => 3
])

Paragraph resources/views/admin/blocks/paragraph.blade.php

@formField('wysiwyg', [
    'translated' => true,
    'name' => 'paragraph',
    'label' => 'Paragraph',
    'maxlength' => 200,
    'editSource' => true,
    'note' => 'You can edit the source.',
])

Now with the blocks created Twill needs to know that these blocks in the config/twill.php like:

<?php
return [
    'block_editor' => [
        'block_single_layout' => 'layouts.block',
        'blocks' => [
            'gallery' => [
                'title' => 'Gallery',
                'icon' => 'image',
                'component' => 'a17-block-gallery',
            ],
            'image_with_text' => [
                'title' => 'Image with text',
                'icon' => 'image-text',
                'component' => 'a17-block-image_with_text',
            ],
            'quote' => [
                'title' => 'Quote',
                'icon' => 'quote',
                'component' => 'a17-block-quote',
            ],
            'paragraph' => [
                'title' => 'Paragraph',
                'icon' => 'text',
                'component' => 'a17-block-paragraph',
            ],
        ],
        'crops' => [
            'cover' => [
                'default' => [
                    [
                        'name' => 'default',
                        'ratio' => 1 / 1,
                        'minValues' => [
                            'width' => 100,
                            'height' => 100,
                        ],
                    ],
                ],
            ],
            'gallery' => [
                'default' => [
                    [
                        'name' => 'default',
                        'ratio' => 16 / 9,
                        'minValues' => [
                            'width' => 1024,
                            'height' => 768,
                        ],
                    ],
                ],
            ],
        ],
    ],
];

Please note that I've added a crops section. That is needed for the blocks that have images, without those the images are not stored. If we have RTFM correctly then we need to add the scripts to the project's package.json:

"scripts": {
    "twill-build": "npm run twill-copy-blocks && cd vendor/area17/twill && npm ci && npm run prod && cp -R public/* ${INIT_CWD}/public",
    "twill-copy-blocks": "npm run twill-clean-blocks && mkdir -p resources/assets/js/blocks/ && mkdir -p vendor/area17/twill/frontend/js/components/blocks/customs/ && cp -R resources/assets/js/blocks/ vendor/area17/twill/frontend/js/components/blocks/customs",
    "twill-clean-blocks": "rm -rf vendor/area17/twill/frontend/js/components/blocks/customs/*"
}

On the articles/form.blade.php we need to add the Block Editor selector.

@formField('block_editor')

Or

    @formField('block_editor', [
        'blocks' => ['gallery', 'image_with_text', 'quote', 'paragraph']
    ])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment