One of the things that mildly annoys me after installing Laravel is dealing with migrations I'll never use—or worse, accidentally running them.
What do I mean? Well, when you create a new Laravel project using the installer, you get three migrations by default:
- The users migration
- The cache migration
- The jobs migration
The first one is great—your app will almost certainly have users. But the other two? You only need them if you're planning to use database-driven caching or queues.
So before running my migrations for the first time, I have to remember to remove those files. I usually forget, and those tables end up sitting empty in the database forever. Not the end of the world, but still... (though maybe there's a way to avoid creating them that I just don't know about).
Speaking of files I'll never use: the front-end files. I love using Laravel to build REST APIs. You could say headless Laravel is my favorite Laravel flavor. So, do I need the resources
folder or the Vite configuration file? Not really. I just remove them to keep the repo clean.
There are plenty of other common tasks after setting up Laravel—installing packages, configuring them, running a few commands... Wouldn't it be great to have all those steps predefined somewhere?
I know there are starter kits (and some really good ones!), but they're general solutions that might not offer the level of fine-tuning you need in certain situations. Good meals, definitely—but you can't hand-pick the ingredients.
Now, imagine having access to a thing that helps you run common post-Laravel installation tasks. Not a starter kit, but something closer to a starter kit builder.
I could tell it "Hey! I just installed Laravel. Now, I want to...":
- Remove the cache table migration
- Replace
CACHE_STORE=database
withCACHE_STORE=file
in my.env
file - Run the
install:api
artisan command - Remove all the frontend files:
resources
,package.json
,vite.config.js
. Heck, evenroutes/web.php
! I'll never use it! So also remove the lineweb: __DIR__.'/../routes/web.php',
frombootstrap/app.php
Interesting… this looks like a recipe with hand-picked ingredients. Let's take it further:
- Install Blueprint
- Grab this Gist and save it as
draft.yaml
- Then, run
blueprint:build
!
Oh, wait! Now I remember—Blueprint generates PHPUnit tests, but this time I wanted to use Pest. So before building, I needed to publish the configuration and swap out the test generator.
If only I had some kind of recipe to remind me of that step! (FORESHADOWING)
Again, I'm not looking for a generic setup. Sure, I could create a Laravel project, push it to a repo, and clone it whenever I need it. But different scenarios call for different setups.
I think that over the years, a dev collects many tiny bash and PHP snippets to run here and there. Let me share one with you that I put together to illustrate the case I mentioned above.
#!/bin/bash
# Exit on any error
set -e
echo "Starting..."
# ---
echo "Laravel: Create SQLite database"
touch database/database.sqlite
# ---
echo "Laravel: Remove cache table migration"
rm -f database/migrations/0001_01_01_000001_create_cache_table.php
# ---
echo "Laravel: Use file cache store"
php -r "require 'vendor/autoload.php'; (new \Illuminate\Filesystem\Filesystem)->replaceInFile('CACHE_STORE=database', 'CACHE_STORE=file', '.env');"
# ---
echo "Laravel: API setup"
php artisan install:api
# ---
echo "Laravel: Remove front-end files"
WEB_ROUTES_LINE=" web: __DIR__.'/../routes/web.php',\n"
php -r "require 'vendor/autoload.php'; (new \Illuminate\Filesystem\Filesystem)->replaceInFile(\"$WEB_ROUTES_LINE\", '', 'bootstrap/app.php');"
rm -rf resources
rm -f package.json
rm -f vite.config.js
rm -f routes/web.php
# ---
echo "Laravel: Remove default feature test"
rm -f tests/Feature/ExampleTest.php
# ---
echo "Duster: Install"
composer require tightenco/duster --dev
# ---
echo "Pest: Install Faker plugin"
composer require pestphp/pest-plugin-faker --dev
# ---
echo "Blueprint: Install"
composer require -W --dev laravel-shift/blueprint
# ---
echo "Blueprint: Install Additional Assertions"
composer require --dev jasonmccreary/laravel-test-assertions
# ---
echo "Blueprint: Publish config"
php artisan vendor:publish --tag=blueprint-config
# ---
echo "Blueprint: Generate Pest tests instead of PhpUnit tests"
php -r "require 'vendor/autoload.php'; (new \Illuminate\Filesystem\Filesystem)->replaceInFile('PhpUnitTestGenerator', 'PestTestGenerator', 'config/blueprint.php');"
# ---
echo "Blueprint: Ignore .blueprint file"
echo '/.blueprint' >> .gitignore
# ---
echo "Blueprint: Save Gist as draft.yaml"
curl https://gist.githubusercontent.com/nicodevs/0e889f237161010f478af66e164c79df/raw -o draft.yaml
# ---
echo "Blueprint: Build"
php artisan blueprint:build
# ---
echo "Laravel: Migrate fresh and seed"
php artisan migrate:fresh --seed
# ---
echo "Duster: Fix"
./vendor/bin/duster fix
# ---
echo "Done!"
A couple of notes:
- Most of the commands are quite simple, like removing a file or directory with
rm
, or running commands withphp artisan
. - I use cURL to grab a snippet from a Gist and save it as
draft.yaml
. - I use Laravel to replace strings in certain files, like replacing
CACHE_STORE=database
withCACHE_STORE=file
in my.env
file. Why use PHP and Laravel instead ofsed
? Well,sed
behaves differently between macOS and Linux, so I prefer not to risk it. Plus, being able to run small Laravel snippets from the command line is kind of cool.
For convenience, I've stored this bash file online on my site. Here:
https://nicodevs.com/recipe.txt
So, if I run this bash file in the root of my newly installed Laravel project, I'll have a fully scaffolded custom API. Thanks to Blueprint, I'll even get some basic CRUD tests! I just need to:
laravel new demo-api && cd demo-api
curl -L https://nicodevs.com/recipe.txt -o recipe.sh
chmod +x recipe.sh
./recipe.sh
Try it—it's super fun! In the installer prompts, choose: no starter kit, Pest as the testing framework, SQLite as the database, and say no to running migrations (the recipe takes care of that later).
Did it work? Great! Now, run:
php artisan test
My recipe.sh
is great (at least, I think it is!), but it's still a fixed set of steps. I copy and paste it, editing as needed—removing what I don't need and adding what I do. But when you need to fight with bash, the magic fades away.
Let's go back to the idea of this thing that helps you put together your starter kit.
What if it had a user interface to search for and select common ingredients for my recipe? Searching "Duster" could return:
- Duster: Install
- Duster: Publish GitHub Action
- Duster: Publish Husky Hooks
- Duster: Fix
...and I could check the ones I want—in this case, "Install" and "Fix."
Searching for Blueprint might return:
- Blueprint: Install
- Blueprint: Install Additional Assertions
- Blueprint: Publish config file
- Blueprint: Ignore .blueprint file
- Blueprint: Build
...and more. I could even pull in an ingredient that lets me download a file or compressed folder:
- File: Uncompress
- File: Download
- File URL: https://gist.githubusercontent.com/nicodevs/0e889f237161010f478af66e164c79df/raw
- Save As: draft.yaml
After selecting the ingredients I want, I could sort them. For example, the "File: Download" step that downloads a Gist and saves it as draft.yaml
needs to go before "Blueprint: Install."
Nothing matches exactly what I'm looking for? I can add my own ingredients and have them ready for my recipes. Maybe my custom ingredient "Show inspirational quote" runs php artisan inspire
.
Once I'm happy with the order of the ingredients, my recipe is ready! The starter kit creator shows me the final bash script. In essense will be pretty similar to the bash script I've included above.
The user can copy it, download it, or perhaps give it a cool name (money-maker-api
!) and publish it publicly to share with others.
Then, to run it locally, I can grab it from this thing and run:
laravel new demo-api && cd demo-api
curl -L https://mise.com/nicodevs/money-maker-api.txt -o recipe.sh
chmod +x recipe.sh
./recipe.sh
- A user interface makes this thing very user friendly. No need to battle with YAML or JSON files, no need to learn a new set of package methods, just interacting with the UI.
- I don't need to know bash. The starter kit creator has a big collection of bash instructions. And all the instructions are somewhat simple:
rm
,curl
,echo
,php artisan something
, etc. One liners more than long scripts. For crazy loops or super complex stuff, leverage on a Laravel command. - But even if I know just the basic commands to install Laravel and packages, I'll most likely understand the resulting recipe. I'll know what I'm about to run—no mysteries, no sneaky crypto miners (unless you install one on purpose!).
- It's shareable and extendable. I can share a link to the recipe or a link to the script.
- The Recipe Link (https://mise.com/nicodevs/foobar) will show the full UI. A guest could see the steps and download the bash script. A logged in user could add more steps, sort them, etc., and save a copy in their user account (https://mise.com/mattstauffer/foobar).
- The Script Link (https://mise.com/nicodevs/foobar.txt) will show the plain text version of the full script. Ideal to attach to bug reports, for example.
- This thing is actually a directory. It contains many, many ingredients. Keeping a clean directory is rewarding, but not an easy task. AI can help but, at the end of the day, a human should review additions and changes to the directory. Perhaps in the form of pull requests? Maybe!
- Now, I'm not sure if this is just a disadvantage. For the end user, it's nice to know there's someone checking the ingredients and making sure there is no
rm -rf /
in there.
I would love for this thing to exist. Am I describing Mise, the new project from Matt Stauffer? (Hi, Matt! 👋). I'm not sure!
Perhaps this is not even close to what Matt is looking for, but I wanted to share how I would do it and answer his questions!
Should it be YAML (cleaner) or PHP (easier to do custom stuff, easier to parse and write)?
The end result is bash. Thanks to the UI, the user doesn't need to worry about YAML, config files, or PHP setup scripts. But they can always use the UI to add custom steps, in bash, with the ability to use PHP—for example:
- Run this command:
php artisan app:setup-something
- Ping this webhook:
curl https://example.com/webhook
- Print a fake name:
php -r "require 'vendor/autoload.php'; echo fake()->name();"
Does "recipes" or "steps" make sense?
Absolutely! I called steps ingredients in this post.
How best to distribute "official" steps?
The project's repo will have a directory of checked ingredients. If someone wants to add an ingredient, they can create a pull request. The team will analyze the proposal and decide whether to accept it. If accepted, the ingredient will appear in the public directory.
The checked ingredients are part of the project’s codebase, not just stored in a private database. This means that anyone can pull the repo and run the app locally or self-host it, instead of using the website (mise.com).
How best to distribute steps and recipes that aren't "official"? How do we let people share their own?
Users can log in and create their own ingredients. By default, these are private, so "My wacky step" won't show up when other users search for "wacky."
However, users can share recipes, as explained earlier. A recipe will clearly show if a given step is official (checked by the team) or custom (created by the user).
Does namespacing steps make the most sense? For example,
step('duster/install')
runs theSteps/Duster/Install
PHP class?
Makes sense to have some kind of namespace, box, container or category to group ingredients ("Duster", "Blueprint", "File", etc).
It's also worth mentioning that recipes will have the username as the "namespace." It would be great to have "Login with GitHub" and make the username match GitHub's username for more recognition (https://mise.com/nicodevs).
Should it allow for user input?
If this means user input in the sense of terminal prompts, then yes! Running the bash command will allow it.
If this means accepting step parameters, also yes: some ingredients will require them (e.g., the URL of the file to download). Custom ingredients will need the entire command to execute.
Is there a better way, other than building this as a Laravel app, to make it as useful as possible for other tools (e.g., a modified Laravel installer or something else)?
Assuming these steps run after a Laravel installation and from the root of the project is a great advantage, because you can use the power of Laravel for steps.
But… if you think about it, nothing stops you from creating or consuming commands that assume other scenarios. For example, a recipe could have steps like npm create nuxt my-app
, npx nuxi module add icon
, and node my-setup.js
for a Nuxt project.
This was super fun. Thinking of recipes and ingredients made me think of miso, the Japanese dish (close to mise!). I hope all of this makes at least some sense. It feels like a spiritual successor to Lambo, but in app form. I'm not sure if this is the direction Mise will take, but happy to ping pong ideas any day!