Building a Simple Blog with Flight PHP
This guide walks you through creating a basic blog using the Flight PHP framework. You'll set up a project, define routes, manage posts with JSON, and render them with the Latte templating engine—all showcasing Flight’s simplicity and flexibility. By the end, you’ll have a functional blog with a homepage, individual post pages, and a creation form.
Prerequisites
- PHP 7.4+: Installed on your system.
- Composer: For dependency management.
- Text Editor: Any editor like VS Code or PHPStorm.
- Basic knowledge of PHP and web development.
Step 1: Set Up Your Project
Start by creating a new project directory and installing Flight via Composer.
-
Create a Directory:
mkdir flight-blog cd flight-blog
-
Install Flight:
composer require flightphp/core
-
Create a Public Directory: Flight uses a single entry point (
index.php
). Create apublic/
folder for it:mkdir public
-
Basic
index.php
: Createpublic/index.php
with a simple “hello world” route:<?php require '../vendor/autoload.php'; Flight::route('/', function () { echo 'Hello, Flight!'; }); Flight::start();
-
Run the Built-in Server: Test your setup with PHP’s development server:
php -S localhost:8000 -t public/
Visit
http://localhost:8000
to see “Hello, Flight!”.
Step 2: Organize Your Project Structure
For a clean setup, structure your project like this:
flight-blog/
├── app/
│ ├── config/
│ └── views/
├── data/
├── public/
│ └── index.php
├── vendor/
└── composer.json
app/config/
: Configuration files (e.g., events, routes).app/views/
: Templates for rendering pages.data/
: JSON file for storing blog posts.public/
: Web root withindex.php
.
Step 3: Install and Configure Latte
Latte is a lightweight templating engine that integrates well with Flight.
-
Install Latte:
composer require latte/latte
-
Configure Latte in Flight: Update
public/index.php
to register Latte as the view engine:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'My Blog']); }); Flight::start();
-
Create a Layout Template: In
app/views/layout.latte
:<!DOCTYPE html> <html> <head> <title>{$title}</title> </head> <body> <header> <h1>My Blog</h1> <nav> <a href="/">Home</a> | <a href="/create">Create a Post</a> </nav> </header> <main> {block content}{/block} </main> <footer> <p>© {date('Y')} Flight Blog</p> </footer> </body> </html>
-
Create a Home Template: In
app/views/home.latte
:{extends 'layout.latte'} {block content} <h2>{$title}</h2> <ul> {foreach $posts as $post} <li><a href="/post/{$post['slug']}">{$post['title']}</a></li> {/foreach} </ul> {/block}
Restart the server if you got out of it and visit
http://localhost:8000
to see the rendered page. -
Create a Data File:
Use a JSON file to simulate a database for simplicity.
In
data/posts.json
:[ { "slug": "first-post", "title": "My First Post", "content": "This is my very first blog post with Flight PHP!" } ]
Step 4: Define Routes
Separate your routes into a config file for better organization.
-
Create
routes.php
: Inapp/config/routes.php
:<?php Flight::route('/', function () { Flight::view()->render('home.latte', ['title' => 'My Blog']); }); Flight::route('/post/@slug', function ($slug) { Flight::view()->render('post.latte', ['title' => 'Post: ' . $slug, 'slug' => $slug]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Create a Post']); });
-
Update
index.php
: Include the routes file:<?php require '../vendor/autoload.php'; use Latte\Engine; Flight::register('view', Engine::class, [], function ($latte) { $latte->setTempDirectory(__DIR__ . '/../cache/'); $latte->setLoader(new \Latte\Loaders\FileLoader(__DIR__ . '/../app/views/')); }); require '../app/config/routes.php'; Flight::start();
Step 5: Store and Retrieve Blog Posts
Add the methods to load and save posts.
-
Add a Posts Method: In
index.php
, add a method to load posts:Flight::map('posts', function () { $file = __DIR__ . '/../data/posts.json'; return json_decode(file_get_contents($file), true); });
-
Update Routes: Modify
app/config/routes.php
to use posts:<?php Flight::route('/', function () { $posts = Flight::posts(); Flight::view()->render('home.latte', [ 'title' => 'My Blog', 'posts' => $posts ]); }); Flight::route('/post/@slug', function ($slug) { $posts = Flight::posts(); $post = array_filter($posts, fn($p) => $p['slug'] === $slug); $post = reset($post) ?: null; if (!$post) { Flight::notFound(); return; } Flight::view()->render('post.latte', [ 'title' => $post['title'], 'post' => $post ]); }); Flight::route('GET /create', function () { Flight::view()->render('create.latte', ['title' => 'Create a Post']); });
Step 6: Create Templates
Update your templates to display posts.
-
Post Page (
app/views/post.latte
):{extends 'layout.latte'} {block content} <h2>{$post['title']}</h2> <div class="post-content"> <p>{$post['content']}</p> </div> {/block}
Step 7: Add Post Creation
Handle form submission to add new posts.
-
Create Form (
app/views/create.latte
):{extends 'layout.latte'} {block content} <h2>{$title}</h2> <form method="POST" action="/create"> <div class="form-group"> <label for="title">Title:</label> <input type="text" name="title" id="title" required> </div> <div class="form-group"> <label for="content">Content:</label> <textarea name="content" id="content" required></textarea> </div> <button type="submit">Save Post</button> </form> {/block}
-
Add POST Route: In
app/config/routes.php
:Flight::route('POST /create', function () { $request = Flight::request(); $title = $request->data['title']; $content = $request->data['content']; $slug = strtolower(str_replace(' ', '-', $title)); $posts = Flight::posts(); $posts[] = ['slug' => $slug, 'title' => $title, 'content' => $content]; file_put_contents(__DIR__ . '/../../data/posts.json', json_encode($posts, JSON_PRETTY_PRINT)); Flight::redirect('/'); });
-
Test It:
- Visit
http://localhost:8000/create
. - Submit a new post (e.g., “Second Post” with some content).
- Check the homepage to see it listed.
- Visit
Step 8: Enhance with Error Handling
Override the notFound
method for a better 404 experience.
In index.php
:
Flight::map('notFound', function () {
Flight::view()->render('404.latte', ['title' => 'Page Not Found']);
});
Create app/views/404.latte
:
{extends 'layout.latte'}
{block content}
<h2>404 - {$title}</h2>
<p>Sorry, that page doesn't exist!</p>
{/block}
Next Steps
- Add Styling: Use CSS in your templates for a better look.
- Database: Replace
posts.json
with a database like SQLite usingPdoWrapper
. - Validation: Add checks for duplicate slugs or empty inputs.
- Middleware: Implement authentication for post creation.
Conclusion
You’ve built a simple blog with Flight PHP! This guide demonstrates core features like routing, templating with Latte, and handling form submissions—all while keeping things lightweight. Explore Flight’s documentation for more advanced features to take your blog further!