🖼️ View
The View class in PageBlocks is a wrapper around the Fenom templating engine, providing a simple yet powerful way to render templates: MODX chunks, .tpl files, @INLINE templates, and more.
🚀 Quick Start
You can render a template with data:
use Boshnik\PageBlocks\Facades\View;
View::make('@INLINE <p>{$title}</p>', ['title' => 'Homepage']);Or using the helper function:
view('file:templates/base.tpl', ['title' => 'Homepage']);💡 Controller Example
Route::get('/', function () {
return view('file:templates/base', ['name' => 'PageBlocks']);
// => looks for: core/App/elements/templates/base.tpl
});🔧 Snippet Example
Let's say you have a MyProfile snippet that displays the current user's profile:
<?php
$user = $modx->user;
return view('file:blocks/profile.tpl', [
'username' => $user->get('username'),
'email' => $user->getOne('Profile')->get('email'),
]);Chunk core/App/elements/blocks/profile.tpl:
<div class="profile">
<h2>{$username}</h2>
<p>Email: {$email}</p>
</div>📄 Template Types
| Where it looks | Example |
|---|---|
Inline template code (@INLINE) | view('@INLINE Hello, {$name}') |
File template (@FILE, file:) | view('@FILE blocks/card.tpl') |
MODX template (template:) | view('template:base') |
Default: first modChunk, then file | view('header') |
If no extension is specified,
.tplis automatically added for files.
⚙️ Settings
| Setting | Description | Default Value |
|---|---|---|
pageblocks_elements_path | Path to file templates relative to core/ folder. Used for @FILE and file: templates. | App/elements/ |
pageblocks_file_elements_only | If true, templates are loaded only from files, MODX chunks are ignored. Improves performance. | false |
pageblocks_fenom_auto_reload | Automatic template recompilation when files change. Can be disabled in production for better performance. | true |
📁 Template Storage
When using file: or @FILE, the path is relative to the pageblocks_elements_path directory.
Example:
view('file:blocks/article.tpl');
// => looks for: core/App/elements/blocks/article.tpl📦 Available Data
🧠 $modx
Templates have access to the global $modx object, identical to the one in PHP code. This allows accessing resources, current user, and other system data directly from templates:
🔹 Current Document
{$modx->resource->id} {* Current page ID *}
{$modx->resource->pagetitle} {* Document title *}
{$modx->resource->get('template')} {* Getting any field *}🔹 Current User
{$modx->user->username}
{$modx->user->id}
{$modx->user->get('email')}
{$modx->user->getGravatar()}💬 Flash Messages
Passed through $_SESSION['pageblocks']['flash']. Available in templates:
old_input- previous form field valueserrors- short error messages per field (one error per field)errors_full- all error messages per fieldsuccess_message- success notificationerror_message- error notification
Examples:
{if $errors.email}
<p class="error">{$errors.email}</p>
{/if}
<input type="text" name="email" value="{$old_input.email}">
{if $success_message}
<div class="alert alert-success">{$success_message}</div>
{/if}
{if $error_message}
<div class="alert alert-danger">{$error_message}</div>
{/if}To display all errors:
{foreach $errors_full as $field => $list}
{foreach $list as $msg}
<p class="error">{$msg}</p>
{/foreach}
{/foreach}💡 All flash data is cleared after first display.
🧩 Modifiers
PageBlocks extends Fenom's capabilities with modifiers - small functions that can be applied directly in templates. They make templates more concise and flexible.
🛠 How to Add Custom Modifiers
You can add custom modifiers in:
core/App/Helpers/fenom/modifiers.phpThis file should return an array like:
return [
'upper' => function($value) {
return mb_strtoupper($value);
},
'users' => function () {
return query('modUser')
->alias('user')
->select('user.*,profile.*')
->where(['active' => 1])
->join('modUserProfile', 'profile', 'user.id = profile.internalKey')
->get();
},
];Calling Modifiers in Templates
- As a value filter:
<h2>{$title|upper}</h2>- As a function:
{users()}This will return a list of all active users with their profiles.
Inside the function,
$this->modxis available - the current MODX instance.
🔧 Default Modifiers
PageBlocks includes many useful modifiers out of the box:
| Name | Description |
|---|---|
route | Get path by route name: {route('home')} |
lang | Get translation from dictionary: {lang('title')} |
config | Get config from Config or MODX: `{'site_name' |
url | Generate link by resource ID: `{1 |
lexicon | Get phrase from MODX lexicon |
resource | Get field or all data of a resource by ID |
user | Get user field by ID or current user |
chunk | Insert MODX chunk: `{'header' |
snippet | Execute MODX snippet: `{'getResources' |
button | Automatic button generation |
notags | Remove HTML tags: {notags($text)} |
isloggedin | Check if user is logged in |
ismember | Check if user is in a group |
phone | Clean string to phone number format |
pbJson | Simplified call to pbJson snippet for serialization |
cssToHead | Add CSS to <head> |
jsToBottom | Add JS to document bottom |
preg_replace | Regex replacement |
print | Output debug information in <pre> |
⚡ Pseudo-Tags
You can create custom pseudo-tags (inline functions) that work like regular Fenom functions:
{icon 'edit' class='icon-sm'}These tags are convenient replacements for long insert or include statements, especially for repetitive HTML fragments like icons, buttons, or badges.
📁 Storage Location
Create a file:
core/App/Helpers/fenom/inline_tags.phpIt should return an array of names and functions:
return [
'icon' => function($params) {
$name = array_shift($params);
$attrs = [];
foreach ($params as $key => $value) {
$attrs[] = htmlspecialchars($key).'="'.htmlspecialchars($value).'"';
}
$attrStr = $attrs ? ' ' . implode(' ', $attrs) : '';
$path = MODX_BASE_PATH . "assets/icons/{$name}.svg";
if (!file_exists($path)) {
return "<!-- icon '{$name}' not found -->";
}
$svg = file_get_contents($path);
return preg_replace('/<svg\b(.*?)>/', '<svg$1' . $attrStr . '>', $svg, 1);
},
];✨ Template Examples
{icon 'edit' class='icon-sm text-muted'}
{icon 'user' class='icon-lg' aria-hidden='true'}These functions don't require
insertorincludedeclarations, are easier to read, and scale well.
🧱 Block Tags
Block tags allow wrapping template content in conditional blocks. Content inside will only be displayed if the condition is met.
This is especially useful for controlling access to template parts - like showing menus only to logged-in users or admins.
✅ Usage Example
{auth}
<a href="/profile">Profile</a>
{/auth}
{guest}
<a href="/login">Login</a>
{/guest}In this example:
{auth}block works only for authenticated users{guest}block - only for guests
🧩 How to Add a Block Tag
Block tags are loaded from:
core/App/Helpers/fenom/block_tags.phpYou can add a new tag like superadmin that shows content only for user with ID = 1:
return [
'superadmin' => function (array $params, $content) {
if ($this->modx->user->id === 1) {
return $content;
}
},
];📦 Usage Example
{superadmin}
<div class="alert alert-danger">
You're a super-admin!
</div>
{/superadmin}Content inside will only show if the user is a super-admin.
📘 How It Works
A block tag is a function that receives:
$params- parameters passed to the tag$content- content between opening and closing tags
🌍 Global Variables
Fenom supports global variables accessible from any template. Useful for static data, settings, or common values like project name, support email, assets path etc.
🛠 How It Works
File core/App/Helpers/fenom/data.php should return an array with needed data:
return [
'package' => 'PageBlocks',
'support_email' => 'support@example.com',
];This array will be available in templates as global object $.pb.
📦 Template Usage Example
{$.pb.package} {* Outputs: PageBlocks *}
{$.pb.support_email} {* Outputs: support@example.com *}Global variables are in the pb scope, so access them via {$.pb.key}.
🔐 PHP Functions
By default Fenom supports a limited set of safe PHP functions callable directly in templates:
✅ Default Available Functions
| Function | Purpose |
|---|---|
count() | Count array elements |
is_string() | Check if variable is a string |
is_array() | Check if variable is an array |
is_numeric() | Check if variable is a number |
is_int() | Check for integer |
constant() | Get constant value |
is_object() | Check if variable is an object |
gettype() | Get variable type |
is_double() | Check for float |
strtotime() | Convert string to timestamp |
json_encode() | Convert data to JSON |
json_decode() | Decode JSON string |
ip2long() | IP → integer |
long2ip() | Integer → IP |
strip_tags() | Remove HTML tags |
nl2br() | Convert \n to <br> |
explode() | Split string by delimiter |
implode() | Join array into string |
🧠 Usage Example
{count($items)} items
{json_encode(['foo' => 'bar'])}
{strtotime("2025-01-01")}
{strip_tags($html)}➕ Allowing Additional Functions
You can allow other safe functions:
core/App/Helpers/fenom/php_functions.php:
return [
'lcfirst',
'mb_strlen',
'htmlspecialchars',
];⚠️ Security
Never allow dangerous functions like:
eval,exec,system,shell_execfile_get_contents,fopen,unlink
They could lead to data leaks or remote code execution.
Only allow pure, predictable functions that don't interact with filesystem, network or environment.
🧪 Output Processing
The third argument of View::make() or view() helper is an output processor.
Useful for:
🔹 Removing HTML
view('user-card.tpl', ['user' => $user], function ($buffer) {
return strip_tags($buffer);
});📄 Result: clean text without
<div>,<a>etc. - good for text emails.
🔹 HTML Minification
view('page.tpl', [], function ($buffer) {
return preg_replace('/\s{2,}|\n/', '', $buffer);
});📉 Removes spaces and newlines - speeds up page load.
🔹 Automatic Censorship
view('comment.tpl', ['text' => $comment], function ($buffer) {
return str_ireplace(['fool', 'idiot'], '[censored]', $buffer);
});🛡️ Useful for user content moderation.
🔹 Replacing Absolute Paths
view('page.tpl', [], function ($buffer) {
return str_replace('https://example.com/assets/', '/assets/', $buffer);
});🔁 Helpful when moving sites between environments.
🔹 SEO: Adding rel="nofollow" to All Links
view('page.tpl', [], function ($buffer) {
return preg_replace('/<a (.*?)href="(.*?)"(.*?)>/i', '<a $1href="$2" rel="nofollow"$3>', $buffer);
});🔍 Protects against SEO spam, especially in comments or guest content.
🧮 Query Builder
Templates have access to the query() helper - a wrapper for pbQuery. You can run database queries directly from Fenom templates.
📊 Counting Records
{query('modResource')->where(['published' => 1])->count()}📋 Get Resource List
{set $resources = query('modResource')
->where(['template' => 4])
->sortBy('menuindex')
->limit(5)
->fetchAll()
}
<ul>
{foreach $resources as $res}
<li><a href="{$res.uri}">{$res.pagetitle}</a></li>
{/foreach}
</ul>🔀 Using join
{set $users = query('modUser')
->alias('u')
->where(['u.active' => 1])
->join('modUserProfile', 'p', 'u.id = p.internalKey')
->select('u.username, p.fullname')
->sortBy('u.username')
->fetchAll()
}
{foreach $users as $user}
<p>{$user.username} ({$user.fullname})</p>
{/foreach}🔍 Find Record by ID
{set $resource = query('modResource')->find(10)}
{if $resource}
<h2>{$resource->pagetitle}</h2>
{/if}📂 Get Categories Array
{set $categories = query('modCategory')->fetchAll()}
<ul>
{foreach $categories as $cat}
<li>{$cat.category}</li>
{/foreach}
</ul>🔧 Using value() or pluck()
{query('modResource')->where(['id' => 5])->value('pagetitle')}{set $titles = query('modResource')
->where(['parent' => 0])
->pluck('pagetitle')
}
<pre>{$titles|print}</pre>📁 File Structure
App/
└── Helpers/
└── fenom/
├── modifiers.php ← Modifiers
├── inline_tags.php ← Pseudo-tags
├── block_tags.php ← Block functions
├── data.php ← Global data
└── php_functions.php ← Allowed PHP functions🧼 Cache Clearing
When you clear cache via MODX manager, this automatically runs:
View::clearCache();This clears all compiled Fenom templates.