Building full-stack with Leaf MVC
Leaf MVC is a minimalistic PHP framework built for developers who need a simple and elegant toolkit to create full-featured web applications.
Full-stack applications typically combine both the front-end and back-end in a single, cohesive system. With Leaf MVC, you get all the power of Leaf for handling requests while seamlessly rendering views using Blade or integrating with modern front-end frameworks like React, Vue or Svelte.
Getting started
You can create a new MVC app using the create
command on the Leaf CLI.
leaf create my-app --mvc
composer create-project leafs/mvc:v4.0-beta my-app
This will create a new Leaf MVC app in the my-app
directory. You can then navigate to the my-app
directory and run the app using the serve
command.
cd my-app
php leaf serve
Your app is now running! Open http://localhost:5500
in your browser.
Project Structure
Leaf MVC, like the rest of Leaf, is built for makers—focused, lightweight, and flexible. It keeps only the essentials, giving you everything you need without the extra baggage. Here’s the basic structure of a Leaf MVC app:
├───app
│ ├── controllers
│ ├── database
│ ├── models
│ ├── routes
│ └── views
│ └── errors
└───public
└───assets
├── css
└── img
Leaf MVC keeps things simple, ensuring you focus on building rather than configuration. Here’s a quick breakdown of the key directories:
- app/ – This is where all your application logic lives, including controllers, models, views, and routes. Your database files also reside here.
- public/ – Contains publicly accessible files like bundled CSS, JavaScript, and images. This is the only directory exposed to the browser.
Most of your work will happen in the app directory, with a typical request starting with a route, which calls a controller, which interacts with a model, and finally renders a view—this is the MVC cycle in action.
Don’t worry if you’re new to MVC! Just remember: every request starts with a route and ends with a response.
Building your first app
As a maker, the easiest way to get started with your app is by building a Coming Soon, Early Access, or Pre-Launch page. This gives you something real to share while you build, helping you gather interest and early users. Let’s create a simple pre-launch page in Leaf MVC!
1 The routing bit
In Leaf MVC, routes are defined in the app/routes
directory. You’ll see an index.php file along with some files that start with _
. These are partials, and Leaf automatically loads them to help you organize routes in a way that fits your project.
For our pre-launch page, let’s create a new route inside app/routes/_prelaunch.php
. Open the file and add this:
<?php
app()->view('/prelaunch', 'prelaunch');
Okay, looks like some magic is happening here. Let's break it down:
app()
is a helper function that gives you access to the Leaf app instance, it is available from anywhere in your app.view()
is a method that you can use to create a route that renders a Blade view. The first argument is what the user enters in the URL, and the second argument is the name of the view file to render.
2 The view bit
We’ve set up the route, but if we navigate to /prelaunch
now, we’ll hit an error—because we haven’t created the prelaunch view yet!
To fix this, let’s create a new Blade file inside the app/views
directory. Name it prelaunch.blade.php
, and this is where we’ll define our pre-launch page.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Coming Soon</title>
<style>
body { text-align: center; padding: 50px; }
h1 { font-size: 2.5rem; }
p { font-size: 1.2rem; color: #555; }
input { padding: 10px; width: 250px; }
button { padding: 10px 15px; background: #333; color: #fff; border: none; cursor: pointer; }
</style>
</head>
<body>
<h1>Something amazing is coming soon!</h1>
<p>Sign up to be the first to know when we launch.</p>
<form action="/store" method="post">
<input type="email" name="email" placeholder="Enter your email">
<button type="submit">Notify Me</button>
</form>
</body>
</html>
The most important part of this page is the form. We’re keeping it simple—it just collects an email address and submits it to a route that will handle the email. We’ll create that route in a moment. Remember how we set up a route earlier? We’ll follow the same approach!
3 Handling the form submission
This is the final piece of our pre-launch page, but also the most involved. We need to create a route that calls a controller, validates the email, and saves it to a database using a model. This will take us through the full MVC cycle, plus a bit of setup.
Let’s start by defining the route!
<?php
app()->view('/prelaunch', 'prelaunch');
app()->post('/store', 'SubscribersController@store');
We used the post()
method because we only want POST requests to hit this route. The second argument specifies the controller and method that will handle the request.
To generate the controller, we can use the Leaf CLI:
php leaf g:controller subscribers
This will create a new controller in the app/controllers
directory. Open the SubscribersController.php
file and add the store
method.
<?php
namespace App\Controllers;
class SubscribersController extends Controller
{
public function store()
{
// handle the email //
}
}
We're almost there! We need to validate the email and save it to a database. Validation is pretty simple with Leaf, we can use the validate()
method on our request and pass in the rules we want to validate against.
<?php
namespace App\Controllers;
class SubscribersController extends Controller
{
public function store()
{
if (!$data = request()->validate(['email' => 'email'])) {
// validation failed, redirect back with errors //
return response()->with('errors', request()->errors())->redirect('/prelaunch');
}
// save the email
}
}
Great job so far! Now, let's save the email to a database using a model. First, we'll generate a Subscriber model:
php leaf g:model subscriber
We don’t need to modify the model—Leaf keeps things simple. But before we can store anything, we need to connect our database. Open your .env file and add your database credentials:
DB_CONNECTION=mysql
DB_HOST=xxx
DB_PORT=xxx
DB_DATABASE=xxx
DB_USERNAME=xxx
DB_PASSWORD=xxx
After updating your credentials, restart your server with: php leaf serve
.
Now, we need to create our database table. Leaf MVC makes this seamless with schema files, a simpler way to define and manage your database structure. Let’s set up our schema next!
php leaf g:schema subscribers
The name of the schema file should be the same as your table name. This will create a new schema file in the app/database
directory. Open the file and add the columns you want in your table.
columns:
email: string
Here, we are telling Leaf to add a column where we can store the email. We can now run the migration to create the table.
php leaf db:migrate
4 Saving the email
We can now save the email to the database using our Subscriber
model.
<?php
namespace App\Controllers;
use App\Models\Subscriber;
class SubscribersController extends Controller
{
public function store()
{
if (!$data = request()->validate(['email' => 'email'])) {
// validation failed, redirect back with errors
return response()->withFlash('errors', request()->errors())->redirect('/prelaunch');
}
$subscriber = new Subscriber;
$subscriber->email = $data['email'];
$subscriber->save();
return response()
->withFlash('success', 'Nice, we will send a mail when we launch!')
->redirect('/prelaunch');
}
}
You can use the withFlash()
method to send a message to the next request. This is useful for sending messages to the user after a redirect. We can now test our app by navigating to the /prelaunch
page and submitting an email.
5 Deploying your app
We have built a simple pre-launch page using Leaf MVC. You can now deploy your app to a server using a service like Heroku, Fly.io a VPS like DigitalOcean, or even a shared hosting service like Sevalla.
What to read next
Now that you have built a simple pre-launch page, the next step is to get you familiar with the basics of building a full-stack application with Leaf. So you can build and launch your next big idea fast.
Routing
Learn more about routing in Leaf MVC, dynamic routes, middleware and more.
Using Controllers
Controllers are the 'C' in MVC, and separate your logic from your views.
Using Models
Models are the 'M' in MVC, and let you interact with your database programmatically.
Frontend
Learn about SSR, SPA, and how to use Leaf with your favorite frontend framework.