Getting started with Laravel Blueprint
Originally posted
I recently discovered Laravel Blueprint, a code generation tool that creates multiple Laravel components (Models, Controller, Migrations etc) from a single yaml file.
What is Laravel Blueprint?
"Blueprint is an open-source tool for rapidly generating multiple Laravel components from a single, human readable definition."
What this means is you no longer have to manually generate files using php artisan ... commands and you can get straight into the deep code of your application.
If you've ever used the CMS Jekyll, you'll be familiar with the syntax, it's just a YAML file.
If you are unfamiliar with YAML syntax, all you need to know is that indentation is key, indent incorrectly and you'll have trouble. Watch those tabs!
Quick Blueprint commands
We'll dig into this later, but if you're here for a quick reference, these are the commands you need to know.
/* Generate a new draft.yaml file in a laravel project */
php artisan blueprint:new
/* Run blueprint to generate files based on your draft.yaml */
php artisan blueprint:build
/* Remove all the files generated by previous build command */
php artisan blueprint:erase
How to install Blueprint
If you've already got Laravel 10, it's easy to install Blueprint using a simple composer command.
composer require -W --dev laravel-shift/blueprint
php artisan blueprint:new
Optionally, you can also install the test package to run the generated unit tests.
composer require --dev jasonmccreary/laravel-test-assertions
And that's it for the installation steps!
Getting started
This is where you need a good idea of the structure of the project you're building, at the very least what Models you require, everything else can be added after.
You'll find in the root of your laravel installation a file called Draft.yml
, this is where you'll specify your data structure.
We're going to start with our Models:
models:
Post:
title: string
content: longtext
author: string
category: string
published_at: nullable timestamp
Comment:
content: longtext
author: string
published_at: nullable timestamp
You can see here that all we're doing is an indented list of two Models, Post and Comment, and listing out the fields and field types.
This is actually enough for you to be able to run the blueprint build command and see what's generated.
php artisan blueprint:build
From just specifying those models, blueprint has generated our Model files, the migrations and some factories!
But this is just the basics, we're going to extend this in stages.
Relationships
For our basic blog we've got posts and comments, but we need those comments to be associated with a blog post, they can't just exist without a parent model.
We do this by adding a relationship value to each model, similar to how you would specify a relationship in your Model files.
/* draft.yaml */
models:
Post:
title: string
content: longtext
author: string
category: string
published_at: nullable timestamp
relationships:
hasMany: Comment
Comment:
content: longtext
author: string
post_id: id foreign
published_at: nullable timestamp
relationships:
belongsTo: Post
We've just told blueprint that our two models have a relationship, Posts can have many Comments and a Comment must belong to a Post.
Notice that we have added the foreign id column post_id, this creates a foreign key constraint in the database.
Blueprint will assume you want to the foreign key to be from the posts table, if you wanted to specify a different table you could do that too with post_id: id foreign:other_posts_table
What if we extend for Authors too?
/* draft.yaml */
models:
Post:
title: string
content: longtext
author_id: id foreign
category: string
published_at: nullable timestamp
relationships:
hasMany: Comment
Comment:
content: longtext
post_id: id foreign
author_id: id foreign
published_at: nullable timestamp
relationships:
belongsTo: Post
Author:
name: string
relationships:
hasMany: Post, Comment
Here you can see we've added multiple relationships simply by using a comma seperated list.
Now you can refresh your generated files with the new relationships by running in your command line:
php artisan blueprint:erase && php artisan blueprint:build
Look closely at the generated migration files to see how the relationships are created, you can adjust your yaml file and rebuild as many times as you like.
Seeders
If you want to get playing with some dummy data quickly, blueprint will even create seeders for you.
Just add the seeders you want to generate after your models in your draft.yaml file, but make sure there are no spaces or tabs before the section.
/* draft.yaml */
models:
Post:
title: string
content: longtext
author_id: id foreign
category: string
published_at: nullable timestamp
relationships:
hasMany: Comment
Comment:
content: longtext
post_id: id foreign
author_id: id foreign
published_at: nullable timestamp
relationships:
belongsTo: Post
Author:
name: string
relationships:
hasMany: Post, Comment
seeders: Post, Comment, Author
Again refresh your generated files with the new seeders by running in your command line:
php artisan blueprint:erase && php artisan blueprint:build
Before you can start using the seeders, you need to run the database migrations as you would normally.
php artisan migrate && php artisan db:seed
If you use Tinker or Tinkerwell, you can also use these to test the seeders and see whats generated.
Controllers
If you plan on using blade templating it's a good idea to get Blueprint generating your controllers too, this is of course unnecessary if you are planning to use Livewire which as it's own Controller/View system.
/* draft.yaml */
controllers:
Post:
index:
query: all
render: post.index with:posts
Let's start with the basic index function, under your seeders you need a new section for controllers.
Here we are telling Blueprint for the model Post, we want an index function that runs fetch all posts query and returns a post.index blade file with the results of the query.
This may sound confusing at first, but when you look at the generated code, it's simply a standard function that returns all instances of the Post model to a view.
public function index()
{
$posts = Post::all();
return view('post.index', compact('posts'));
}
Let's take this further and create the whole Create, Read, Update and Delete process for the Post model.
controllers:
Post:
index:
query: all
render: post.index with:posts
create:
render: post.create
store:
validate: title, content, author
save: post
redirect: post.index
show:
render: post.show with:post
edit:
render: post.edit with:post
update:
validate: post
update: post
redirect: post.index
destroy:
delete: post
redirect: post.index