Milk Admin

Creating Pages and Links in Modules

Revision: 2025/10/16

How to use the MyPages module to create custom pages with menus and navigation

This guide shows how to create pages and navigation systems within MilkAdmin modules using the practical example of the MyPages module.

Basic Module Structure

A MilkAdmin module is composed of:

  • Module: Handles the logic and actions of the module
  • Controller (optional): For complex routing
  • Views: Templates for visualization
  • Assets (optional): CSS, JS, images

Step 1: Basic Module with RequestAction

Let's start by creating a simple module without a separate router. The directory must be structured like this:

milkadmin/Modules/MyPages/
├── MyPagesModule.php
└── views/
    └── my_view_page.php

Basic Module

The module MyPagesModule.php shows how to use RequestAction attributes to define pages and the new configure() method:

<?php
namespace Modules\MyPages;

use App\Abstracts\AbstractModule;
use App\Attributes\RequestAction;
use App\Response;
use Builders\LinksBuilder;

class MyPagesModule extends AbstractModule {

    protected function configure($rule): void
    {
        $rule->page('mypages')
             ->title('My Custom Pages')
             ->menu('My Custom Pages', '', 'bi bi-file-earmark-post-fill', 10)
             ->access('registered')
    }

     #[RequestAction('home')]
    public function my_home_page() {
        Response::render(__DIR__ . '/views/my_post_page.php', ['title' => 'My Home Page']);
    }
}

Key Concepts

The configure() Method

The configure($rule) method uses a fluent interface to set up the module:

  • page('mypages'): Unique identifier in the URL
  • title('My Custom Pages'): Module title
  • menu(): Adds entries to the sidebar menu
  • access('registered'): Sets access level (registered, admin, public)

The RequestAction Attribute

The #[RequestAction('action_name')] attribute defines actions accessible via URL:

  • Syntax: #[RequestAction('page1')]
  • Resulting URL: ?page=mypages&action=page1
  • When no separate Controller is present, this attribute allows defining pages directly in the module

The bootstrap() Function

The bootstrap() function is fundamental for configuring common elements:

  • Called every time the module is loaded
  • Used for configurations common to all pages
  • Ideal for loading custom CSS/JS (though you can also use setJs() and setCss() in configure())
  • JavaScript and CSS files, header links are now configured automatically via configure()

Basic View

Create views/my_view_page.php to display pages:

<?php !defined('MILK_DIR') && die(); // Prevent direct access ?>
<div class="container mt-4">
    <div class="card">
        <div class="card-header">
            <h3><?php _p($title); ?></h3>
        </div>
        <div class="card-body">
            <p>This is an example page from the MyPages module.</p>
        </div>
    </div>
</div>

Step 2: Adding a Controller for Complex Pages

The module is already functional. However, now let's create another group of pages with a sidebar menu. First, let's create another file called MyPagesController.php

Now we need to move the Functions from Module to Controller

<?php
namespace Modules\MyPages;

use App\Abstracts\AbstractController;
use App\Attributes\RequestAction;
use App\Response;
use Builders\LinksBuilder;

class MyPagesController extends AbstractController {
     #[RequestAction('home')]
    public function my_home_page() {
        Response::render(__DIR__ . '/views/my_post_page.php', ['title' => 'My Home Page']);
    }

    #[RequestAction('other-page')]
    public function my_page_2() {
        Response::render(__DIR__ . '/views/vertical_menu.php', ['title' => 'My other-page', 'links' => $this->links()]);
    }

    #[RequestAction('page3')]
    public function p3() {
        Response::render(__DIR__ . '/views/vertical_menu.php', ['title' => 'My Page 3', 'links' => $this->links()]);
    }

    private function links() {
        $navbar = LinksBuilder::create()
            ->addGroup('Menu', 'My Menu')
                ->add('Other Pages', '?page='.$this->page.'&action=other-page')
                ->add('Page 3', '?page='.$this->page.'&action=page3')
            ->render('sidebar');
        return $navbar;
    }
}

The Module becomes simpler, remove the functions with RequestAction. The router will be loaded automatically:

<?php
namespace Modules\MyPages;

use App\Abstracts\AbstractModule;

class MyPagesModule extends AbstractModule {

    /**
     * Configure the module
     */
    protected function configure($rule): void
    {
        $rule->page('mypages')
             ->title('My Custom Pages')
             ->menu('My Custom Pages', '', 'bi bi-file-earmark-post-fill', 10)
             ->access('registered')
             ->addHeaderLink('Home', '?page=mypages', 'bi bi-house-fill')
             ->addHeaderLink('Other Pages', '?page=mypages&action=other-page', 'bi bi-gear-fill');
    }

}

addHeaderLink is used to add links to the header. The first parameter is the link text, the second is the link URL, and the third is the link icon.

Then create views/vertical_menu.php for the layout:

<?php
!defined('MILK_DIR') && die(); // Prevent direct access
?>
<div class="container-fluid px-0">
<div class="row">
    <div class="col-md-2 bg-light">
        <?php _ph($links); ?>
    </div>
    <div class="col-md-10 bg-white">
        <div class="card mt-4">
            <div class="card-header">
                <h5><?php _p($title); ?></h5>
            </div>
            <div class="card-body">
                Page content goes here...
            </div>
        </div>
    </div>
</div>
</div>

Note: addGroup() optimizes the menu for mobile devices by grouping entries.

How Response::render Works

Response::render is the main method for displaying pages. It can accept different formats:

Render with View File

// Pass variables to the view
Response::render(__DIR__ . '/views/my_view_page.php', [
    'title' => 'My Page Title',
    'content' => 'Page content here'
]);

Helper Functions for Views:

  • _p($var): Prints the variable safely (HTML escape)
  • _ph($var): Prints HTML without escape (for trusted HTML content)
Loading...