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 URLtitle('My Custom Pages'): Module titlemenu(): Adds entries to the sidebar menuaccess('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()andsetCss()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)