Skip to main content

Get Up and Running

This guide will help you create your first controller, view, and understand the MINI framework’s core concepts.
Make sure you’ve completed the installation before starting this guide.

Understanding the URL Structure

MINI’s URL structure is intuitive and self-explanatory. URLs directly map to controllers and their methods:
http://example.com/controller/method/parameter1/parameter2

URL Routing Examples

URLControllerMethodParameters
example.comhome.phpindex()none
example.com/homehome.phpindex()none
example.com/home/exampleOnehome.phpexampleOne()none
example.com/songssongs.phpindex()none
example.com/songs/edit/17songs.phpedit()17
The default controller is home and the default method is index(). Both are called when no specific controller or method is provided in the URL.

Project Structure

Understand the MINI directory structure:
mini/
├── application/
│   ├── config/
│   │   └── config.php           # Configuration file
│   ├── controller/
│   │   ├── home.php             # Home controller
│   │   ├── songs.php            # Songs controller (CRUD example)
│   │   └── problem.php          # Error controller
│   ├── core/
│   │   ├── application.php      # Application routing logic
│   │   └── controller.php       # Base controller class
│   ├── model/
│   │   └── model.php            # Database model
│   ├── view/
│   │   ├── _templates/          # Header and footer templates
│   │   ├── home/                # Views for home controller
│   │   └── songs/               # Views for songs controller
│   └── libs/
│       └── helper.php           # Helper functions (PDO debugger)
├── public/
│   ├── index.php                # Application entry point
│   ├── .htaccess                # URL rewriting rules
│   └── css/                     # CSS files
└── _install/
    └── *.sql                    # Database setup files

Create Your First Controller

Let’s create a simple “Blog” controller.
1

Create the Controller File

Create a new file: application/controller/blog.php
application/controller/blog.php
<?php

/**
 * Class Blog
 * Handles blog-related pages and actions
 */
class Blog extends Controller
{
    /**
     * PAGE: index
     * This is the main blog listing page
     * URL: http://yourproject/blog or http://yourproject/blog/index
     */
    public function index()
    {
        // Load views
        require APP . 'view/_templates/header.php';
        require APP . 'view/blog/index.php';
        require APP . 'view/_templates/footer.php';
    }

    /**
     * PAGE: article
     * This shows a single blog article
     * URL: http://yourproject/blog/article/5
     */
    public function article($id)
    {
        // For now, just pass the ID to the view
        // In a real app, you'd fetch article data from the database
        $article_id = $id;

        // Load views
        require APP . 'view/_templates/header.php';
        require APP . 'view/blog/article.php';
        require APP . 'view/_templates/footer.php';
    }
}
Controllers must extend the Controller base class, which provides database access via $this->db and model access via $this->model.
2

Create the View Directory

Create a directory for blog views:
mkdir application/view/blog
3

Create the Index View

Create application/view/blog/index.php:
application/view/blog/index.php
<div class="container">
    <h1>Blog</h1>
    <p>Welcome to the blog!</p>
    
    <h2>Recent Articles</h2>
    <ul>
        <li><a href="<?php echo URL; ?>blog/article/1">My First Article</a></li>
        <li><a href="<?php echo URL; ?>blog/article/2">Learning MINI Framework</a></li>
        <li><a href="<?php echo URL; ?>blog/article/3">PHP Best Practices</a></li>
    </ul>
</div>
The URL constant contains your application’s base URL, ensuring links work in any environment or sub-folder.
4

Create the Article View

Create application/view/blog/article.php:
application/view/blog/article.php
<div class="container">
    <h1>Article #<?php echo htmlspecialchars($article_id, ENT_QUOTES, 'UTF-8'); ?></h1>
    <p>This is article number <?php echo htmlspecialchars($article_id, ENT_QUOTES, 'UTF-8'); ?>.</p>
    <p>In a real application, this would display the actual article content from the database.</p>
    
    <a href="<?php echo URL; ?>blog"> Back to blog</a>
</div>
Always use htmlspecialchars() when outputting user-provided or dynamic content to prevent XSS attacks.
5

Test Your Controller

Open your browser and navigate to:
http://localhost/mini/blog
You should see your blog listing. Click on an article to see it in action!

Working with the Database

Now let’s enhance our blog to fetch real data from the database.
1

Create a Database Table

Add a blog posts table to your database:
CREATE TABLE post (
    id INT NOT NULL AUTO_INCREMENT,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (id)
);

-- Insert some sample data
INSERT INTO post (title, content) VALUES
('Getting Started with MINI', 'MINI is an extremely simple PHP framework...'),
('Database Operations', 'Working with PDO in MINI is straightforward...'),
('Clean URLs', 'MINI provides beautiful clean URLs out of the box...');
2

Add Model Methods

Edit application/model/model.php and add blog methods:
application/model/model.php
/**
 * Get all blog posts
 */
public function getAllPosts()
{
    $sql = "SELECT id, title, content, created_at FROM post ORDER BY created_at DESC";
    $query = $this->db->prepare($sql);
    $query->execute();
    
    return $query->fetchAll();
}

/**
 * Get a single blog post by ID
 * @param int $post_id
 */
public function getPost($post_id)
{
    $sql = "SELECT id, title, content, created_at FROM post WHERE id = :post_id LIMIT 1";
    $query = $this->db->prepare($sql);
    $parameters = array(':post_id' => $post_id);
    
    // For debugging, you can use:
    // echo Helper::debugPDO($sql, $parameters);
    
    $query->execute($parameters);
    
    return $query->fetch();
}
PDO automatically escapes all parameters, protecting against SQL injection. No need to manually sanitize input.
3

Update the Controller

Modify application/controller/blog.php to use the model:
application/controller/blog.php
public function index()
{
    // Get all posts from the database
    $posts = $this->model->getAllPosts();

    // Load views
    require APP . 'view/_templates/header.php';
    require APP . 'view/blog/index.php';
    require APP . 'view/_templates/footer.php';
}

public function article($id)
{
    // Get the specific post from the database
    $post = $this->model->getPost($id);

    // Load views
    require APP . 'view/_templates/header.php';
    require APP . 'view/blog/article.php';
    require APP . 'view/_templates/footer.php';
}
4

Update the Views

Update application/view/blog/index.php to display real data:
application/view/blog/index.php
<div class="container">
    <h1>Blog</h1>
    
    <?php if (!empty($posts)) { ?>
        <?php foreach ($posts as $post) { ?>
            <article style="margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid #ddd;">
                <h2>
                    <a href="<?php echo URL; ?>blog/article/<?php echo $post->id; ?>">
                        <?php echo htmlspecialchars($post->title, ENT_QUOTES, 'UTF-8'); ?>
                    </a>
                </h2>
                <p>
                    <?php echo htmlspecialchars(substr($post->content, 0, 150), ENT_QUOTES, 'UTF-8'); ?>...
                </p>
                <small>Posted on: <?php echo htmlspecialchars($post->created_at, ENT_QUOTES, 'UTF-8'); ?></small>
            </article>
        <?php } ?>
    <?php } else { ?>
        <p>No posts available.</p>
    <?php } ?>
</div>
Update application/view/blog/article.php:
application/view/blog/article.php
<div class="container">
    <?php if (isset($post) && $post) { ?>
        <article>
            <h1><?php echo htmlspecialchars($post->title, ENT_QUOTES, 'UTF-8'); ?></h1>
            <p><small>Posted on: <?php echo htmlspecialchars($post->created_at, ENT_QUOTES, 'UTF-8'); ?></small></p>
            <div>
                <?php echo nl2br(htmlspecialchars($post->content, ENT_QUOTES, 'UTF-8')); ?>
            </div>
        </article>
        <br>
        <a href="<?php echo URL; ?>blog"> Back to blog</a>
    <?php } else { ?>
        <p>Post not found.</p>
        <a href="<?php echo URL; ?>blog"> Back to blog</a>
    <?php } ?>
</div>
5

Test Database Integration

Refresh http://localhost/mini/blog to see real posts from the database!

Debugging PDO Queries

MINI includes a helpful PDO debugger. Use it to see the actual SQL being executed:
$sql = "SELECT id, title FROM post WHERE id = :post_id LIMIT 1";
$query = $this->db->prepare($sql);
$parameters = array(':post_id' => $post_id);

// Debug the query (shows the SQL with parameters replaced)
echo Helper::debugPDO($sql, $parameters);

$query->execute($parameters);
Output:
SELECT id, title FROM post WHERE id = '5' LIMIT 1

CRUD Operations Example

MINI includes a complete CRUD (Create, Read, Update, Delete) example in the songs controller. Study these files:
  • Controller: application/controller/songs.php
  • Model: application/model/model.php
  • Views: application/view/songs/

Quick CRUD Reference

public function addSong($artist, $track, $link)
{
    $sql = "INSERT INTO song (artist, track, link) VALUES (:artist, :track, :link)";
    $query = $this->db->prepare($sql);
    $parameters = array(':artist' => $artist, ':track' => $track, ':link' => $link);
    $query->execute($parameters);
}

Key Concepts

The Application Flow

1

Entry Point

All requests go through public/index.php, which loads the framework and creates an Application instance.
2

URL Parsing

The Application class (application/core/application.php) parses the URL and determines which controller and method to call.
3

Controller Loading

The specified controller is loaded from application/controller/, and the method is called with any URL parameters.
4

View Rendering

The controller loads views from application/view/, typically including header, content, and footer templates.

Security Best Practices

Output Escaping

Always use htmlspecialchars($data, ENT_QUOTES, 'UTF-8') when outputting dynamic content

PDO Prepared Statements

Use PDO with parameter binding for all database queries (automatic SQL injection protection)

Public Folder

Only public/ is web-accessible. Application code is protected from direct access

Input Validation

Always validate and sanitize user input on the server side

Common Patterns

Passing Data to Views

Simply create variables before requiring the view:
public function index()
{
    // Create variables
    $posts = $this->model->getAllPosts();
    $total_posts = count($posts);
    $page_title = "Blog Home";

    // Variables are available in the views
    require APP . 'view/_templates/header.php';
    require APP . 'view/blog/index.php';
    require APP . 'view/_templates/footer.php';
}

Redirecting

// Redirect to another page
header('location: ' . URL . 'blog');
exit();

Working with POST Data

public function create()
{
    if (isset($_POST["submit_add_post"])) {
        $title = $_POST["title"];
        $content = $_POST["content"];
        
        // Add to database
        $this->model->addPost($title, $content);
        
        // Redirect
        header('location: ' . URL . 'blog');
        exit();
    }
    
    // Show form
    require APP . 'view/_templates/header.php';
    require APP . 'view/blog/create.php';
    require APP . 'view/_templates/footer.php';
}

Next Steps

Study the Songs Example

Explore application/controller/songs.php for a complete CRUD implementation

Customize the Configuration

Review application/config/config.php to customize error reporting, database settings, and more

Add Your Own Models

Expand application/model/model.php with your own data access methods

Explore AJAX

Check out the AJAX example in the demo application

Getting Help

GitHub Issues

Report bugs or request features

Dev Metal Blog

Read in-depth tutorials and articles
Remember: MINI is intentionally simple. If you need more features, consider MINI2 or MINI3.

Build docs developers (and LLMs) love