Web tarayıcı satırından girilen değere aşağıda gösterilen sıra ile işlem yapılır:
Her bir hareket (Action) için, ait olduğu denetleyici sınıfı içinde tanımlanmış olan bir fonksiyon vardır. Bu fonksiyon, kendisine tahsis edildiği hareket devreye girdiğinde, veritabanı ile ilgili olanlar da dahil, gerekli tüm işlemleri yapar ve sonuçları ilgili görüntüye yönlendirir.
BG MVC uygulamasında kullanılan dizin ve dosya yapısının ayrıntıları aşağıda gösterilmektedir:
Yan tarafında mavi nokta ile gösterilen dizin ve dosyalar, haricen yüklenmiş yazılımları göstermektedir.
Web tarayıcı satırından girilen değere aşağıda gösterilen sıra ile işlem yapılır:
Web tarayıcı satırından hangi ifade girilirse girilsin, bu değere önce ana dizinde bulunan .htaccess dosyası işlem yapar ve akışı yine ana dizinde bulunan index.php dosyasına yönlendirir.
C:\wamp\www\bgmvc\.htaccess
Options -Indexes
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [L]
.htaccess dosyasından gelen sistem akışı ana dizinde bulunan index.php dosyasında devam eder.
C:\wamp\www\bgmvc\index.php
<?php
session_start(); // Oturum başlatma
// Sınıfı kullanımları
use \Core\{Config, Router, H};
use App\Models\Users;
use Dotenv\Dotenv;
// Sabit bildirimleri
define('FROOT', __DIR__); // Fiziksel dosya yol tanımlaması (C:\wamp\www\bgmvc)
define('DS', DIRECTORY_SEPARATOR);
// Composer tarafından yüklenen bileşenleri otomatik olarak yükleme
require_once('./vendor/autoload.php');
// Dotenv bileşeni oluşturma
$dotenv = Dotenv::createImmutable(__DIR__);
$dotenv->safeLoad();
// Otomatik sınıf dosyası yükleme fonksiyonu
spl_autoload_register(function($className){
$parts = explode('\\', $className);
$class = end($parts);
array_pop($parts);
$path = strtolower(implode(DS, $parts));
$path = FROOT . DS . $path . DS . $class . '.php';
if(file_exists($path)) {
include($path);
}
});
// Kullanıcı bağlı ise kullanıcı bilgilerini içeren bir nesne, değilse false değeri geri döndürür.
$current_user = Users::get_current_user();
// Config.php dosyasındaki Config sınıfı içindeki get() fonksiyonu ile
// ana dizinde bulunan env dosyasındaki root_dir değerini okuma
$root_dir = Config::get('root_dir'); // '/bgmvc/'
// Tanımlanan ROOT sabitine Lokal sunucu ise '/bgmvc/' değerini, uzak sunucu ise '/' değerini atar.
define('ROOT', ($_SERVER['SERVER_NAME']==='localhost') ? $root_dir : '/'); // Lokal='/bgmvc/' Live='/'
// Lokal sunucu ise '/bgmvc/' değerini, uzak sunucu ise '/' değerini URL değerinin başından siler.
// Lokal: '/bgmvc/' -> '', '/bgmvc/home' -> 'home' Live: '/' -> '' '/home' -> 'home'
$url = H::remove_root($_SERVER['REQUEST_URI']);
// var_dump($url); die();
// İlk giriş ise sadece boş bir dizi, değilse denetleyici (controller), hareket (action) ve parametreler içerir.
$current_page = $url; // '', 'home', '/home/details/5', 'activities/software', 'about'
// '/' değeri ile ayrılmış olan denetleyici, hareket ve parametre değerleri bir diziye atanır.
$url = explode ('/', $url);
Router::route($url); // Router'a yönlendirme
// C:\wamp\www\bgmvc\core\H.php
public static function remove_root($url) {
// '/bgmvc/' -> ''
// '/bgmvc/home' -> 'home'
// '/bgmvc/home/details/5' -> '/home/details/5'
if(ROOT != '/') { // En soldaki '/bgmvc/' (Lokal sunucu) veya '/' (Uzak sunucu) değerini kaldırma
$url = str_replace(ROOT, '', $url);
}
else { // '/' değerini kaldırma (Canlı sunucu) $url = '/' -> $url = ''
$url = ltrim($url, '/');
}
// $url değerinin en sağında yer alabilecek id=21&md=34 gibi değerleri silmek için
$url = preg_replace('/(\?.+)/', '', $url);
return $url;
}
index.php dosyasında sırayla aşağıdaki işlemler gerçekleştirilir:
C:\wamp\www\bgmvc\core\Router.php
<?php
namespace Core;
use Core\Session;
use App\Models\Users;
class Router {
public static function route($url) {
$error = false;
// $url[0] = 'Denetleyici'
$controller = !empty($url[0]) ? ucwords($url[0]) : Config::get('default_controller'); // $controller = 'Home'
$controller_name = $controller; // $controller_name = 'Home'
$controller = '\App\Controllers\\' . $controller . 'Controller'; // $controller = '\App\Controllers\HomeController'
// URL satırında sadece denetleyici adı girilmişse ve bu denetleyiciye ait index.php dosyası varsa,
// index değerini action olarak URL'ye eklemek için (İçinde index.php olmayan denetleyici dizinlerine girişi bloke etmek için)
$index_exist = file_exists(FROOT . DS . 'app' . DS . 'views' . DS . lcfirst($controller_name) . DS . 'index.php');
// $url içinde denetleyici ve hareket değeri mevcutsa, denetleyici değerini siler, hareket değeri dizi başına gelir.
array_shift($url); // $url[0] = 'Hareket'
// Seçilen yol içinde action var ise atanır, yoksa $controller_name'e ait index.php dosyası varsa 'index' atanır.
// Böylece diğer dizinler için mevcut bir index.php yoksa $action boş kalır ve method_exists() koşulunu geçemez.
$action = !empty($url[0]) ? $url[0] : ($index_exist ? 'index' : ''); // $action = 'index'
$action_name = $action; // $action_name = 'index'
$action .= "Action"; // $action = 'indexAction'
// $url içinde hareket değeri mevcutsa, hareket değerini siler, parametre değeri dizi başına gelir.
array_shift($url); // $url[0] = 'Parametre 1', $url[1] = 'Parametre 2', ...
// Denetleyici sınıfı veya hareket metodu mevcut değilse, $url dizisine sayfanın mevcut olmadığını gösteren bir değer atar.
if(!class_exists($controller) || !method_exists($controller, $action)) {
$url = ["nopage"];
$error = true;
}
else {
// acl kontrolü (acl.json dosyası ile menüde gösterilmeyen sayfalara erişimi engeller)
// Menüde gösterilen sayfalara erişimi engellemek için ayrıca işlem gerekir (if($current_user->acl=='admin')).
$grant_access = self::has_access($controller_name, $action_name);
// Kullanıcının giriş izni yoksa, $url dizisine giriş izni olmadığını gösteren bir değer atar.
if(!$grant_access) {
$url = ["noaccess"];
$error = true;
}
}
// Sınıf veya metod olmamasından (nopage) veya giriş izni olmamasından (noaccess) kaynaklı hata varsa,
// sayfayı hata gösterimine yönlendirecek şekilde, denetleyici ve hareket adını değiştirir.
if($error) {
$controller = str_replace($controller_name, Config::get('exceptions'), $controller);
$controller_name = Config::get('exceptions'); // $controller_name = 'Exceptions'
$action_name = 'index'; // $action_name = 'index'
$action = $action_name . 'Action'; // $action = 'indexAction'
}
// HomeController sınıfı cinsinden bir nesne oluşturur. Bu işlemi yaparken, HomeController sınıfının türetildiği Controller
// sınıfının __construct() fonksiyonunu otomatik olarak çağırarak, $controller_name ve $action_name değerlerini geçirir.
// $controller = '\App\Controllers\HomeController', $controller_name = 'Home', $action_name = 'index'
$controller_class = new $controller($controller_name, $action_name);
// HomeController sınıfındaki $action fonksiyonunu (indexAction) $url parametreleri ile çağırma
call_user_func_array([$controller_class, $action], $url); // $action = 'indexAction', $url = params
}
public static function redirect($location) {
if(!headers_sent()) {
header('Location: ' . ROOT . $location);
}
else {
echo '<script type="text/javascript">';
echo 'window.location.href = "'. ROOT . $location .'"';
echo '</script>';
echo '<nosript>';
echo '<meta http-equiv="refresh" content="0;url=' . ROOT . $location . '" />';
echo '</nosript>';
}
exit();
}
public static function perm_redirect($perm, $redirect, $msg = "Bu sayfaya giriş yapamazsınız.") {
$user = Users::get_current_user();
$allowed = $user && $user->has_permission($perm);
if(!$allowed) {
Session::msg($msg);
self::redirect($redirect);
}
}
public static function get_menu($menu) {
$menu_array = [];
$menu_file = file_get_contents(FROOT . DS . 'app/views/inc' . DS . $menu . '.json');
$acl = json_decode($menu_file, true);
foreach($acl as $key => $val) {
if(is_array($val)) {
$sub = [];
foreach($val as $k => $v) {
if(substr($k,0,9) == 'separator' && !empty($sub)) {
$sub[$k] = '';
continue;
}
else if($final_val = self::get_link($v)) {
$sub[$k] = $final_val;
}
}
if(!empty($sub)) {
$menu_array[$key] = $sub;
}
}
else {
if($final_val = self::get_link($val)) {
$menu_array[$key] = $final_val;
}
}
}
return $menu_array;
}
private static function get_link($val) {
// Harici bağlantı ise
if(preg_match('/https?:\/\//', $val) == 1) {
return $val;
}
else {
$uAry = explode('/', $val);
$controller_name = ucwords($uAry[0]);
$action_name = (isset($uAry[1]))? $uAry[1] : '';
if(self::has_access($controller_name, $action_name)) {
return $val;
}
return false;
}
}
public static function has_access($controller_name, $action_name='index') {
$acl_file = file_get_contents(FROOT . DS . 'app/views/inc' . DS . 'acl.json');
$acl = json_decode($acl_file, true);
$current_user_acls = ["Guest"];
$grant_access = false;
if(Session::exists('logged_in_user')) {
$current_user_acls[] = "LoggedIn";
$current_user_acls[] = ucwords(Users::get_current_user()->acl); // Veritabanındaki users tablosunda acl değeri tek ise
}
foreach($current_user_acls as $level) { // 'Guest', 'LoggedIn' ve 'Admin'
if(array_key_exists($level, $acl) && array_key_exists($controller_name, $acl[$level])) {
if(in_array($action_name, $acl[$level][$controller_name]) || in_array("*", $acl[$level][$controller_name])) {
$grant_access = true;
break;
}
}
}
// Girişi bloke edilen menü seçenekleri
foreach($current_user_acls as $level) {
$denied = $acl[$level]['denied'];
if(!empty($denied) && array_key_exists($controller_name, $denied) && in_array($action_name, $denied[$controller_name])) {
$grant_access = false;
break;
}
}
return $grant_access;
}
}
Router.php dosyasında sırayla aşağıdaki işlemler gerçekleştirilir:
core dizini altındaki Controller.php dosyası içinde tanımlanan Controller sınıfı, app/controllers dizini altındaki denetleyici dosyalarında yer alan tüm sınıfların türetildiği bir sınıftır.
HomeController sınıfındaki indexAction() fonksiyonu çalıştığında, Controller sınıfından türetilen HomeController sınıfı nesnesi yoluyla erişim sağladığı, Controller sınıfının __construct() fonksiyonu ile oluşturulan ve aynı sınıf içinde yer alan $view değişkenine aktarılan View sınıfından üretilen, nesne yoluyla View sınıfı içindeki render() fonksiyonunu kullanarak ilgili görüntü dosyasını ve şablon dosyasını tarayıcıda gösterir.
C:\wamp\www\bgmvc\core\Controller.php
<?php
namespace Core;
use Core\{View, Config, Request};
class Controller {
private $_controller_name, $_action_name;
public $view, $request;
public function __construct($controller, $action) {
$this->_controller_name = $controller;
$this->_action_name = $action;
$view_path = strtolower($controller) . '/' .$action;
$this->view = new View($view_path);
$this->view->set_layout(Config::get('default_layout'));
$this->request = new Request();
$this->onConstruct();
}
public function onConstruct(){}
}
HomeController sınıfındaki indexAction() fonksiyonu çalışır. Controller sınıfından türetilen HomeController sınıf nesnesi yoluyla erişim sağladığı Controller sınıfı içindeki $view değişkeni yoluyla View sınıfı içindeki render() fonksiyonunu kullanarak ilgili görüntü dosyasını ve şablon dosyasını tarayıcıda gösterir.
C:\wamp\www\bgmvc\app\controllers\HomeController.php
<?php
namespace App\Controllers;
use Core\{DB, Controller, H, Router, Session};
use App\Models\{Articles, Users};
class HomeController extends Controller {
public function indexAction($pageno=''){
$params = [
'columns' => "*",
'order' => 'articles.id DESC'
];
$this->view->total = Articles::find_total($params); // articles tablosundaki toplam kayıt sayısı
[$params, $page] = Articles::merge_with_pagination($params, $pageno, $this->view->total);
$this->view->limit = $params['limit'];
$this->view->page = $page;
$this->view->articles = Articles::find($params); // articles tablosundaki tüm kayıt bilgileri bir nesne dizisine aktarılır.
$this->view->heading = "Son makaleler";
$this->view->set_site_title('Son makaleler');
$this->view->render();
}
public function detailsAction($id) {
$params = [
'columns' => "*",
'conditions' => "articles.id = :id",
'bind' => ['id' => $id]
];
if($this->request->isPost()) {
$params['bind']['id'] = $this->request->get('article_id');
$article = Articles::find_first($params);
echo(json_encode($article));
}
else {
$article = Articles::find_first($params);
if(!$article) Router::redirect('exceptions/index/noarticle');
$this->view->article = $article;
unset($params['conditions']);
unset($params['bind']);
$params['order'] = 'articles.id DESC';
$this->view->articles = Articles::find($params);
$this->view->render();
}
}
}
HomeController.php dosyasındaki HomeController sınıfının indexAction fonksiyonunda sırayla aşağıdaki işlemler gerçekleştirilir:
Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, yapılan görüntü işlemlerini gerçekleştirmek için, View.php dosyasında tanımlı View sınıfı oluşturulur.
<?php
namespace Core;
use Core\{Config, Router};
class View {
public $articles, $total, $heading;
public $errors, $user, $header, $users, $article;
public $msg, $msg_txt, $limit, $page, $url;
private $_site_title = '', $_content = [], $_current_content, $_buffer, $_layout;
private $_default_view_path;
public function __construct($path = '') {
$this->_default_view_path = $path;
$this->_site_title = Config::get('default_site_title');
}
public function set_layout($layout) {
$this->_layout = $layout;
}
public function set_site_title($title) {
$this->_site_title = $title;
}
public function get_site_title() {
return $this->_site_title;
}
public function render($path = '') {
if(empty($path)) {
$path = $this->_default_view_path;
}
$layout_path = FROOT . DS . 'app' . DS . 'views' . DS . 'layouts' . DS . $this->_layout . '.php';
$full_path = FROOT . DS . 'app' . DS . 'views' . DS . $path . '.php';
if(!file_exists($full_path)) {
Router::redirect('exceptions/index/noview');
}
if(!file_exists($layout_path)) {
Router::redirect('exceptions/index/notemplate');
}
include($full_path);
include($layout_path);
}
public function start($key) {
if(empty($key)) {
Router::redirect('exceptions/index/nokey');
}
$this->_buffer = $key;
ob_start();
}
public function end() {
if(empty($this->_buffer)) {
Router::redirect('exceptions/index/nostart');
}
$this->_content[$this->_buffer] = ob_get_clean();
$this->_buffer = null;
}
public function content($key) {
if(array_key_exists($key, $this->_content)) {
echo $this->_content[$key];
}
else {
echo '';
}
}
public function inc($path) {
$full_path = FROOT . DS . 'app' . DS . 'views' . DS . $path . '.php';
if(file_exists($full_path)) {
include($full_path);
}
}
}
Görüntü sınıfı kullanımı
1. Controller.php içindeki Controller sınıfından türetilen herhangi bir denetleyici sınıfı içindeki bir hareket fonksiyonundan, Controller sınıfı içinde tanmlı view değişkeni yoluyla, View.php dosyasında tanımlı View sınıfı içindeki render() fonksiyonu çağrılır.
$this->view->render('home/index');
2. render() fonksiyonu, kendisine geçirilen parametre değerine (home/index) karşılık gelen dosya ile şablon dosyası mevcut ise, sırayla görünüme dahil eder:
C:\wamp\www\bgmvc\app\views\home\index.php
<?php
use Core\{H, Config};
use App\Models\{Users};
?>
<?php $this->start('content'); ?>
<?php
if(Config::get('main_slide')) {
$this->inc('inc/main_slide');
}
?>
<div class="p-3">
<nav class="navbar navbar-expand-lg bg-header">
<div class="container-fluid p-2">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#MainNavbar" aria-controls="MainNavbar" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="MainNavbar">
<ul class="navbar-nav">
<li><?=$this->heading?></li>
</ul>
<ul class="navbar-nav ms-auto mb-2 mb-lg-0 bg-fontsm">
<?php // Sayfada gösterilecek kayıt sayısı seçimi ?>
<form class="mt-2" action="<?=ROOT?><?=H::get_action()?>" method="post">
<label class="me-2" for="records_limit">Kayıt sayısı</label>
<select name="records_limit" id="records_limit" class="custom-select">
<?php foreach([4, 6, 8, 10] as $limit) : ?>
<option
<?php if(isset($_SESSION['records_limit']) && $_SESSION['records_limit'] == $limit) echo 'selected'; ?>
value="<?= $limit; ?>">
<?= $limit; ?>
</option>
<?php endforeach; ?>
</select>
</form>
</ul>
</div>
</div>
</nav>
<?php // Makalelerin gösterimi ?>
<div class="row row-cols-1 row-cols-md-3 row-cols-lg-4 row-cols-xl-5 row-cols-xxl-6 g-4">
<?php foreach($this->articles as $article): ?>
<div class="col">
<div class="card h-100 shadow">
<div class="card-body">
<h5 class="card-title"><?= $article->title ?></h5>
<div class="bg-card-text mb-2"><?= html_entity_decode($article->body) ?></div>
<a href="<?=ROOT?>home/details/<?=$article->id?>" class="text-info">Detaylar</a>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<?php // Sayfa yapısı oluşturma ?>
<?php $this->inc('inc/pager'); ?>
</div>
<?php $this->end(); ?>
Bu dosya web sitesinin temel HTML dosyasıdır.
Dosya içinde, aşağıdaki işlemler sırasıyla gerçekleştirilir:
C:\wamp\www\bgmvc\app\views\layouts\default.php
<?php use Core\{Config, Session}; ?>
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= $this->get_site_title(); ?></title>
<link rel="stylesheet" href="<?=ROOT?>public/css/bootstrap.min.css" media="screen" title="no title" charset="utf-8">
<link href="<?=ROOT?>public/css/main.css?v=<?=Config::get('version');?>" rel="stylesheet" type="text/css" media="screen" title="no title" charset="utf-8">
<?php $this->content('head'); ?>
</head>
<body>
<?php $this->inc('inc/main_menu'); ?>
<div class="container-fluid p-0">
<?=Session::display_session_alerts(); ?>
<?php $this->content('content'); ?>
</div>
<script src="<?=ROOT?>public/js/jquery-3.7.1.min.js"></script>
<script src="<?=ROOT?>public/js/popper.min.js"></script>
<script src="<?=ROOT?>public/js/bootstrap.bundle.min.js"></script>
<script src="<?=ROOT?>public/js/main.js"></script>
</body>
</html>
default.php dosyasından çağrılan main_menu.php dosyasında:
C:\wamp\www\bgmvc\app\views\layouts\main_menu.php
<?php
use Core\{H, Router};
global $current_user;
$menu = Router::get_menu('menu_acl');
?>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark p-4">
<div class="container-fluid">
<a class="navbar-brand" href="<?=ROOT?>">BG MVC</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#TopNavbar2" aria-controls="TopNavbar2" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="TopNavbar2">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<?php foreach($menu as $key => $val) :
if(is_array($val)): ?>
<li class="<?php echo H::active_class($key,'nav-item dropdown'); ?>">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false"><?=$key?></a>
<ul class="dropdown-menu">
<?php foreach($val as $k => $v):
echo H::nav_item($v, $k, true);
endforeach;
?>
</ul>
</li>
<?php else:
echo H::nav_item($val, $key);
endif;
?>
<?php endforeach; ?>
</ul>
<ul class="navbar-nav d-flex">
<?php if(!$current_user): ?>
<?= H::nav_item('register/login', 'Giriş yap'); ?>
<?php endif; ?>
<?php if($current_user): ?>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Merhaba <?= $current_user->fname;?></a>
<ul class="dropdown-menu dropdown-menu-right">
<?= H::nav_item('register/logout', 'Çıkış', true); ?>
</ul>
</li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>
main_menu.php dosyasından çağrılan menu_acl.json dosyasındaki içerik çağrılan kod satırına dahil edilir.
C:\wamp\www\bgmvc\app\menu_acl.json
{
"Ana sayfa" : "home",
"Faaliyetlerimiz" : {
"Yazılım" : "activities/software",
"Kodlama" : "activities/code",
"Tasarım" : "activities/design"
},
"Kod örnekleri" : "codes",
"Hakkımızda" : "about"
}
Web sayfasına ilk girişte sistemin akışı aşağıdaki sıra ile gerçekleşmektedir:
.htaccess
Web tarayıcı satırından hangi ifade girilirse girilsin, bu değere önce ana dizinde bulunan .htaccess dosyası işlem yapar ve akışı yine ana dizinde bulunan index.php dosyasına yönlendirir.
RewriteRule ^(.*)$ index.php/$1 [L]
index.php
Aşağıda gösterilen sınıf fonksiyonları ile işlem yapıldıktan sonra route() fonksiyonu çağrılır:
Router::route($url);
core/Router.php
acl.json dosyasından erişim izni kontrolü yapılır.
call_user_func_array() fonksiyonunu kullanarak, HomeController sınıfındaki $action fonksiyonunu (indexAction) $url parametreleri ile çağırır.
// HomeController sınıfındaki indexAction fonksiyonunu çağırma
call_user_func_array([$controller_class, $action], $url); // $action = 'indexAction', $url = params
app/controllers/HomeController.php
indexAction() fonksiyonu içinde, aşağıda gösterilen sınıf fonksiyonları ile işlem yapıldıktan sonra View sınıfı render() fonksiyonu çağrılır:
$this->view->render();
core/View.php
render() fonksiyonu içinde, app/views/home/index.php ve app/views/layout/default.php dosyaları sırasıyla görünüme dahil edilir.
include($full_path); // app/views/home/index.php
include($layout_path); // app/views/layout/default.php
app/views/home/index.php
View sınıfındaki start() ve end() fonksiyonları ile, default.php şablon dosyasında yer alacak olan içerik oluşturulur.
pager.php dosyası ile sayfalama sistemi oluşturulur.
$this->start('content');
.
.
.
$this->end();
app/views/layout/default.php
Sistemin ana HTML sayfası oluşturulur. .css ve .js dosyaları sisteme dahil edilir.
main_menu.php dosyası dahil edilir.
app/views/home/index.php dosyasında oluşturulan içerik, View sınıfındaki content() fonksiyonu ile görünüme dahil edilir.
$this->inc('inc/main_menu');
app/views/inc/main_menu.php
Router::get_menu() fonksiyonu ile menu_acl.json dosyası okunarak, sayfanın en yukarısında yer alan menü oluşturulur.
Router::get_menu('menu_acl');
app/views/inc/menu_acl.json
Menü değerlerini içerir.
Sistemin çalışma sırası aşağıdaki şekilde gösterilmektedir: