DALT.PHP
Guides

Building a Blog

Complete tutorial - build a blog from scratch with DALT.PHP

Let's build a complete blog application from scratch using DALT.PHP. You'll learn routing, database queries, CRUD operations, and security best practices.

What You'll Build

A fully functional blog with:

  • List all posts
  • View single post
  • Create new post
  • Edit existing post
  • Delete post
  • Basic validation

Prerequisites

  • DALT.PHP installed and running
  • Completed the Quick Start guide
  • Basic understanding of PHP and SQL

Time Required: 30-45 minutes

Step 1: Create the Database Table

First, create a migration for the posts table.

Generate Migration

php artisan make:migration create_posts_table

This creates a file like database/migrations/20240315120000_create_posts_table.sql.

Write the SQL

Open the migration file and replace the content with:

-- Migration: create_posts_table
-- Created: 2024-03-15

CREATE TABLE IF NOT EXISTS posts (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    title VARCHAR(255) NOT NULL,
    body TEXT NOT NULL,
    published BOOLEAN DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Index for faster queries
CREATE INDEX IF NOT EXISTS idx_posts_published ON posts(published);

Run Migration

php artisan migrate

You should see:

Running migration: 20240315120000_create_posts_table.sql
✓ Success

Ran 1 migrations.

Step 2: Create Controllers

Now create controllers for each blog operation.

Create Controllers Directory

mkdir -p app/Http/controllers/posts

Index Controller (List All Posts)

Create app/Http/controllers/posts/index.php:

<?php

$db = App::resolve(Core\Database::class);

$posts = $db->query('SELECT * FROM posts ORDER BY created_at DESC')->get();

view('posts/index.view.php', [
    'posts' => $posts,
    'title' => 'All Posts'
]);

Show Controller (View Single Post)

Create app/Http/controllers/posts/show.php:

<?php

$db = App::resolve(Core\Database::class);

$post = $db->query('SELECT * FROM posts WHERE id = ?', [$_GET['id']])->findOrFail();

view('posts/show.view.php', [
    'post' => $post,
    'title' => $post['title']
]);

Create Controller (Show Form)

Create app/Http/controllers/posts/create.php:

<?php

view('posts/create.view.php', [
    'title' => 'Create Post'
]);

Store Controller (Save New Post)

Create app/Http/controllers/posts/store.php:

<?php

$db = App::resolve(Core\Database::class);

// Validate input
$errors = [];

if (empty($_POST['title'])) {
    $errors['title'] = 'Title is required';
}

if (empty($_POST['body'])) {
    $errors['body'] = 'Body is required';
}

if (!empty($errors)) {
    view('posts/create.view.php', [
        'errors' => $errors,
        'old' => $_POST
    ]);
    exit;
}

// Insert post
$db->query('INSERT INTO posts (title, body, published) VALUES (?, ?, ?)', [
    $_POST['title'],
    $_POST['body'],
    isset($_POST['published']) ? 1 : 0
]);

// Redirect with success message
Core\Session::flash('success', 'Post created successfully!');
header('Location: /posts');
exit;

Edit Controller (Show Edit Form)

Create app/Http/controllers/posts/edit.php:

<?php

$db = App::resolve(Core\Database::class);

$post = $db->query('SELECT * FROM posts WHERE id = ?', [$_GET['id']])->findOrFail();

view('posts/edit.view.php', [
    'post' => $post,
    'title' => 'Edit Post'
]);

Update Controller (Save Changes)

Create app/Http/controllers/posts/update.php:

<?php

$db = App::resolve(Core\Database::class);

// Validate input
$errors = [];

if (empty($_POST['title'])) {
    $errors['title'] = 'Title is required';
}

if (empty($_POST['body'])) {
    $errors['body'] = 'Body is required';
}

if (!empty($errors)) {
    $post = $db->query('SELECT * FROM posts WHERE id = ?', [$_GET['id']])->findOrFail();
    view('posts/edit.view.php', [
        'post' => $post,
        'errors' => $errors
    ]);
    exit;
}

// Update post
$db->query('UPDATE posts SET title = ?, body = ?, published = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?', [
    $_POST['title'],
    $_POST['body'],
    isset($_POST['published']) ? 1 : 0,
    $_GET['id']
]);

Core\Session::flash('success', 'Post updated successfully!');
header('Location: /posts');
exit;

Destroy Controller (Delete Post)

Create app/Http/controllers/posts/destroy.php:

<?php

$db = App::resolve(Core\Database::class);

$db->query('DELETE FROM posts WHERE id = ?', [$_POST['id']]);

Core\Session::flash('success', 'Post deleted successfully!');
header('Location: /posts');
exit;

Step 3: Add Routes

Register all blog routes in routes/routes.php:

<?php

global $router;

// Existing routes
$router->get('/', 'welcome.php');

// Blog routes
$router->get('/posts', 'posts/index.php');
$router->get('/posts/create', 'posts/create.php');
$router->post('/posts', 'posts/store.php');
$router->get('/posts/{id}', 'posts/show.php');
$router->get('/posts/{id}/edit', 'posts/edit.php');
$router->patch('/posts/{id}', 'posts/update.php');
$router->delete('/posts/{id}', 'posts/destroy.php');

Route Order Matters! Put specific routes (/posts/create) before generic routes (/posts/{id}).

Step 4: Create Views

Create view templates for the blog interface.

Create Views Directory

mkdir -p resources/views/posts

Index View (List Posts)

Create resources/views/posts/index.view.php:

<!DOCTYPE html>
<html>
<head>
    <title><?= $title ?></title>
</head>
<body>
    <h1>Blog Posts</h1>
    
    <?php if (Core\Session::has('success')): ?>
        <p style="color: green;"><?= Core\Session::get('success') ?></p>
    <?php endif; ?>
    
    <a href="/posts/create">Create New Post</a>
    
    <?php if (empty($posts)): ?>
        <p>No posts yet. <a href="/posts/create">Create one!</a></p>
    <?php else: ?>
        <?php foreach ($posts as $post): ?>
            <article>
                <h2>
                    <a href="/posts/<?= $post['id'] ?>">
                        <?= htmlspecialchars($post['title']) ?>
                    </a>
                </h2>
                <p><?= htmlspecialchars(substr($post['body'], 0, 200)) ?>...</p>
                <small>
                    <?= $post['published'] ? 'Published' : 'Draft' ?> | 
                    <?= $post['created_at'] ?>
                </small>
            </article>
            <hr>
        <?php endforeach; ?>
    <?php endif; ?>
</body>
</html>

Show View (Single Post)

Create resources/views/posts/show.view.php:

<!DOCTYPE html>
<html>
<head>
    <title><?= htmlspecialchars($post['title']) ?></title>
</head>
<body>
    <a href="/posts"> Back to all posts</a>
    
    <article>
        <h1><?= htmlspecialchars($post['title']) ?></h1>
        <small>
            <?= $post['published'] ? 'Published' : 'Draft' ?> | 
            <?= $post['created_at'] ?>
        </small>
        
        <div>
            <?= nl2br(htmlspecialchars($post['body'])) ?>
        </div>
        
        <hr>
        
        <a href="/posts/<?= $post['id'] ?>/edit">Edit</a>
        
        <form method="POST" action="/posts/<?= $post['id'] ?>" style="display: inline;">
            <input type="hidden" name="_method" value="DELETE">
            <button type="submit" onclick="return confirm('Are you sure?')">Delete</button>
        </form>
    </article>
</body>
</html>

Create View (New Post Form)

Create resources/views/posts/create.view.php:

<!DOCTYPE html>
<html>
<head>
    <title>Create Post</title>
</head>
<body>
    <h1>Create New Post</h1>
    
    <a href="/posts"> Back to all posts</a>
    
    <form method="POST" action="/posts">
        <div>
            <label>Title:</label>
            <input type="text" name="title" value="<?= htmlspecialchars($old['title'] ?? '') ?>" required>
            <?php if (isset($errors['title'])): ?>
                <p style="color: red;"><?= $errors['title'] ?></p>
            <?php endif; ?>
        </div>
        
        <div>
            <label>Body:</label>
            <textarea name="body" rows="10" required><?= htmlspecialchars($old['body'] ?? '') ?></textarea>
            <?php if (isset($errors['body'])): ?>
                <p style="color: red;"><?= $errors['body'] ?></p>
            <?php endif; ?>
        </div>
        
        <div>
            <label>
                <input type="checkbox" name="published" <?= isset($old['published']) ? 'checked' : '' ?>>
                Published
            </label>
        </div>
        
        <button type="submit">Create Post</button>
    </form>
</body>
</html>

Edit View (Edit Post Form)

Create resources/views/posts/edit.view.php:

<!DOCTYPE html>
<html>
<head>
    <title>Edit Post</title>
</head>
<body>
    <h1>Edit Post</h1>
    
    <a href="/posts"> Back to all posts</a>
    
    <form method="POST" action="/posts/<?= $post['id'] ?>">
        <input type="hidden" name="_method" value="PATCH">
        
        <div>
            <label>Title:</label>
            <input type="text" name="title" value="<?= htmlspecialchars($post['title']) ?>" required>
            <?php if (isset($errors['title'])): ?>
                <p style="color: red;"><?= $errors['title'] ?></p>
            <?php endif; ?>
        </div>
        
        <div>
            <label>Body:</label>
            <textarea name="body" rows="10" required><?= htmlspecialchars($post['body']) ?></textarea>
            <?php if (isset($errors['body'])): ?>
                <p style="color: red;"><?= $errors['body'] ?></p>
            <?php endif; ?>
        </div>
        
        <div>
            <label>
                <input type="checkbox" name="published" <?= $post['published'] ? 'checked' : '' ?>>
                Published
            </label>
        </div>
        
        <button type="submit">Update Post</button>
    </form>
</body>
</html>

Step 5: Test Your Blog

Make sure your development server is running, then test all the functionality.

Start the Server

If not already running:

php artisan serve

You should see:

Starting development server: http://127.0.0.1:8000

Visit the Blog

Open http://localhost:8000/posts

You should see "No posts yet" with a link to create one.

Create a Post

  1. Click "Create New Post"
  2. Fill in title and body
  3. Check "Published" if you want
  4. Click "Create Post"

You should be redirected to the posts list with a success message.

View a Post

Click on a post title to view the full post.

Edit a Post

  1. Click "Edit" on a post
  2. Modify the content
  3. Click "Update Post"

Delete a Post

  1. Click "Delete" on a post
  2. Confirm the deletion

The post should be removed.

Security Notes

Your blog uses prepared statements to prevent SQL injection and htmlspecialchars() to prevent XSS attacks. These are built into the examples above - no extra configuration needed.

For production, add CSRF protection to forms:

// In routes
$router->post('/posts', 'posts/store.php')->only('csrf');

// In form
<?= csrf_field() ?>

What's Next

You've built a complete CRUD application with validation, flash messages, and security. From here you can add authentication, pagination, or build an API.

Done! You now understand routing, database queries, validation, and security basics.

On this page