Usage Guide
Getting the ViewFactory
The ViewFactory is registered as a singleton by ViewsServiceProvider. Retrieve it from the container:
use WPZylos\Framework\Views\ViewFactory;
// By alias
$views = $app->make('view');
// By class name
$views = $app->make(ViewFactory::class);
Or instantiate directly:
$views = new ViewFactory($context);
// or with a custom base path
$views = new ViewFactory($context, '/path/to/views');
If no $basePath is provided, it defaults to $context->path('resources/views').
Rendering Views
Use render() to immediately render a view template and return the HTML string:
$html = $views->render('admin.settings', [
'title' => 'Plugin Settings',
'options' => $options,
]);
echo $html;
View Naming Convention
Views use dot notation where dots are converted to directory separators:
| View Name | Resolved Path |
|---|---|
admin.settings | resources/views/admin/settings.php |
emails.welcome | resources/views/emails/welcome.php |
products.index | resources/views/products/index.php |
dashboard | resources/views/dashboard.php |
File Extension Resolution
The factory tries these extensions in order:
.php.html.php.twig.html.twig- Raw filename (no extension added)
The first matching file is used.
Deferred Rendering
Use make() to create a View instance without immediately rendering. This is useful when you need to build up data or pass the view object around:
$view = $views->make('admin.dashboard');
// Add data with chaining
$view->with('title', 'Dashboard')
->with('stats', $stats);
// Add multiple values at once
$view->with([
'notices' => $notices,
'user' => $currentUser,
]);
// Render explicitly
$html = $view->render();
// Or cast to string (triggers render automatically)
echo $view;
Sharing Data Globally
Use share() to make data available to every view rendered by this factory:
// Share a single value
$views->share('siteName', get_bloginfo('name'));
// Share multiple values
$views->share([
'currentUser' => wp_get_current_user(),
'isAdmin' => current_user_can('manage_options'),
'pluginVersion' => '2.0.0',
]);
Shared data is merged with per-view data. Per-view data takes precedence if keys overlap.
Checking View Existence
if ($views->exists('admin.custom-page')) {
echo $views->render('admin.custom-page', $data);
} else {
echo $views->render('admin.not-found');
}
Writing PHP Templates
PHP templates receive data as extracted local variables via extract(). Use WordPress escaping functions for output:
<!-- resources/views/admin/settings.php -->
<div class="wrap">
<h1><?= esc_html($title) ?></h1>
<form method="post">
<?php foreach ($options as $key => $option): ?>
<label for="<?= esc_attr($key) ?>">
<?= esc_html($option['label']) ?>
</label>
<input
type="text"
id="<?= esc_attr($key) ?>"
name="<?= esc_attr($key) ?>"
value="<?= esc_attr($option['value']) ?>"
/>
<?php endforeach; ?>
<?php wp_nonce_field('save_settings'); ?>
<?php submit_button(); ?>
</form>
</div>
Escaping in PHP Templates
Use WordPress's built-in escaping functions:
| Function | Purpose | Example |
|---|---|---|
esc_html() | HTML content | <?= esc_html($title) ?> |
esc_attr() | HTML attributes | value="<?= esc_attr($v) ?>" |
esc_url() | URLs | href="<?= esc_url($url) ?>" |
esc_textarea() | Textarea content | <?= esc_textarea($text) ?> |
wp_kses_post() | Allow safe HTML | <?= wp_kses_post($content) ?> |
Using Twig Templates
Twig is an optional dependency. To use it:
1. Install Twig
composer require twig/twig:^3.0
2. Register the TwigEngine
use WPZylos\Framework\Views\Engines\TwigEngine;
$twigEngine = new TwigEngine(
$views->getBasePath(), // Base path (same as ViewFactory)
'/path/to/cache', // Cache directory (null to disable)
false // Debug mode
);
$views->addEngine($twigEngine);
3. Create .twig Templates
{# resources/views/emails/welcome.twig #}
<h1>Welcome, {{ user.name }}!</h1>
<p>Thanks for joining {{ siteName }}.</p>
<ul>
{% for feature in features %}
<li>{{ feature }}</li>
{% endfor %}
</ul>
Twig has HTML autoescape enabled by default.
Accessing the Twig Environment
For advanced customization (adding globals, filters, functions):
$twig = $twigEngine->getEnvironment();
// Add a global
$twig->addGlobal('wp_version', get_bloginfo('version'));
// Add a custom filter
$twig->addFilter(new \Twig\TwigFilter('price', function ($value) {
return '$' . number_format($value, 2);
}));
Custom Template Engines
Implement EngineInterface to add support for any template format:
use WPZylos\Framework\Views\EngineInterface;
class MarkdownEngine implements EngineInterface
{
public function render(string $path, array $data = []): string
{
$content = file_get_contents($path);
// Process markdown and interpolate data
return $this->parseMarkdown($content, $data);
}
public function canRender(string $path): bool
{
return str_ends_with($path, '.md');
}
}
// Register your engine
$views->addEngine(new MarkdownEngine());
Custom engines are prepended to the engine list and are checked first during resolution.
Service Provider Registration
The ViewsServiceProvider automatically registers the ViewFactory:
$app->register(new ViewsServiceProvider());
// ViewFactory is now available as:
$views = $app->make('view');
$views = $app->make(ViewFactory::class);
The provider creates the ViewFactory with:
- Context from
$app->context() - Base path from
$app->paths()->path('@views')
DaisyUI CSS Helper
The CssHelper provides prefixed CSS class names for DaisyUI components, ensuring style isolation between plugins.
Getting the CssHelper
use WPZylos\Framework\Views\CssHelper;
// By alias
$css = $app->make('css');
// By class name
$css = $app->make(CssHelper::class);
Generating Prefixed Classes
// Single class
echo $css->cls('btn');
// Output: 'fcds-btn'
// Multiple classes
echo $css->cls('btn', 'btn-primary', 'btn-lg');
// Output: 'fcds-btn fcds-btn-primary fcds-btn-lg'
Admin Root Scope
Wrap admin pages in a DaisyUI-scoped root container:
// Open admin wrapper
echo $css->adminOpen();
// Output: <div id="fcds-admin" class="wrap">
// Your admin page content here...
// Close wrapper
echo $css->adminClose();
// Output: </div>
Using in Templates
<!-- resources/views/admin/settings.php -->
<?= $css->adminOpen() ?>
<div class="<?= esc_attr($css->cls('card', 'bg-base-100', 'shadow-xl')) ?>">
<div class="<?= esc_attr($css->cls('card-body')) ?>">
<h2 class="<?= esc_attr($css->cls('card-title')) ?>">Settings</h2>
<button class="<?= esc_attr($css->cls('btn', 'btn-primary')) ?>">
Save Changes
</button>
</div>
</div>
<?= $css->adminClose() ?>
JavaScript Mount Points
The JsMount helper renders mount points for Vue.js and React applications, with automatic data passing via window variables.
Getting JsMount
use WPZylos\Framework\Views\JsMount;
// By alias
$js = $app->make('js.mount');
// By class name
$js = $app->make(JsMount::class);
Vue.js Admin Page
Render a Vue app mount point inside the admin DaisyUI root scope:
// In your admin page callback
public function renderSettingsPage(): void
{
$js = $this->app->make('js.mount');
echo $js->adminMount('vue-settings-app', [
'apiUrl' => rest_url('my-plugin/v1/'),
'nonce' => wp_create_nonce('wp_rest'),
'options' => get_option('my_plugin_options', []),
]);
}
This outputs:
<div id="fcds-admin" class="wrap">
<div id="vue-settings-app"></div>
<script>window.mypluginData = {"apiUrl":"/wp-json/my-plugin/v1/","nonce":"abc123","options":[]};</script>
</div>
Your Vue app mounts to #vue-settings-app and reads config from window.mypluginData.
React Admin Page
The same helper works for React — just mount to the rendered element:
echo $js->adminMount('react-dashboard', [
'user' => wp_get_current_user()->display_name,
'apiBase' => rest_url('my-plugin/v1/'),
], 'myPluginDashboard');
In your React entry file:
const root = ReactDOM.createRoot(document.getElementById('react-dashboard'));
root.render(<App config={window.myPluginDashboard} />);
Frontend Mount Point
For frontend pages (no admin root wrapper):
echo $js->frontendMount('product-gallery', [
'products' => $products,
'currency' => 'USD',
]);
This outputs:
<div id="product-gallery"></div>
<script>window.mypluginData = {"products":[...],"currency":"USD"};</script>
Shortcode Mount Point
Use shortcodeMount() for WordPress shortcodes — it works identically to frontendMount():
public function registerShortcode(): void
{
add_shortcode('my_widget', [$this, 'renderWidget']);
}
public function renderWidget(array $atts): string
{
$js = $this->app->make('js.mount');
$id = 'my-widget-' . uniqid();
return $js->shortcodeMount($id, [
'title' => $atts['title'] ?? 'Widget',
'apiUrl' => rest_url('my-plugin/v1/widget'),
'nonce' => wp_create_nonce('wp_rest'),
]);
}
Custom Variable Names
By default, the JS variable name is derived from the plugin slug (e.g., mypluginData). Override it with the third parameter:
echo $js->adminMount('app', $data, 'MyCustomVar');
// Uses: window.MyCustomVar = {...};