tobento / service-view
A flexible PHP view system.
Requires
- php: >=8.0
- tobento/service-dir: ^1.0
- tobento/service-filesystem: ^1.0.4
- tobento/service-macro: ^1.0
- tobento/service-tag: ^1.0.1
Requires (Dev)
- phpunit/phpunit: ^9.5
- twig/twig: ^3.0
- vimeo/psalm: ^4.0
README
The View Service is for creating and rendering views in a simple, yet flexible way.
Table of Contents
Getting started
Add the latest version of the view service project running this command.
composer require tobento/service-view
Requirements
- PHP 8.0 or greater
Highlights
- Framework-agnostic, will work with any project
- Decoupled design
- Easy to extend
Simple Example
Here is a simple example of how to use the View. We will assume the following directory stucture:
private/
views/
home.php
about.php
inc/
header.php
footer.php
public/
src/
app.css
js/
app.js
Create and render a view
use Tobento\Service\View\View; use Tobento\Service\View\PhpRenderer; use Tobento\Service\View\Data; use Tobento\Service\View\Assets; use Tobento\Service\Dir\Dirs; use Tobento\Service\Dir\Dir; $view = new View( new PhpRenderer( new Dirs( new Dir('home/private/views/'), ) ), new Data(), new Assets('home/public/src/', 'https://www.example.com/src/') ); echo $view->render('about', ['title' => 'About', 'description' => 'Lorem ipsum']);
The view template
<!DOCTYPE html> <html> <head> <title><?= $view->esc($title) ?></title> <?= $view->assets()->render() ?> <?php // assets can be included in every subview too. $view->asset('app.css'); $view->asset('js/app.js')->attr('async'); ?> </head> <body> <?= $view->render('inc/header') ?> <h1><?= $view->esc($title) ?></h1> <p><?= $view->esc($description) ?></p> <?= $view->render('inc/footer') ?> </body> </html>
Documentation
Data
use Tobento\Service\View\Data; $data = new Data(); // Set or add data. $data->set('key', 'value'); $data->add(['key' => 'value']); // Set or add data for specific view(s) only. $data->set('key', 'value', 'viewName'); $data->set('key', 'value', ['viewName', 'anotherViewName']); $data->add(['key' => 'value'], 'viewName'); $data->add(['key' => 'value'], ['viewName', 'anotherViewName']); // Get data by key. $key = $data->get('key'); $key = $data->get('key', 'defaultValue'); // Get all data. $allData = $data->all(); // Get all data for a specific view. $allData = $data->all('viewName'); // Rename data keys, add data and get them all. $allData = $data->rename(['key' => 'newKey'])->add(['bar' => 'foo'])->all();
Assets
use Tobento\Service\View\Assets; use Tobento\Service\View\Asset; use Tobento\Service\View\AssetsHandlerInterface; $assets = new Assets( assetDir: 'home/public/src/', assetUri: 'https://www.example.com/src/' ); // Assets handler might be used for minifying and/or combining scripts. // $assets->setAssetsHandler(AssetsHandlerInterface $assetsHandler); // Adding Assets $asset = new Asset( file: 'inc/styles.css', dir: '', uri: '', attributes: [ 'data-foo' => '1', ], order: 7, group: 'default' ); $assets->add($asset); // Creates and adds an Asset::class with default directory and uri set on Assets::class $assets->asset(file: 'inc/app.js'); $assets->asset(file: 'inc/app.js') ->dir('') // clear directory if needed ->uri('') // clear uri if needed ->group('footer') ->order(10) ->attr('data-foo', 'value') ->attr('async'); // Get all assets. $allAssets = $assets->all(); // Render the assets. This will only render placeholders. var_dump($assets->render()); // string(25) "<!-- assets="default" -->" var_dump($assets->render(group: 'footer')); // string(24) "<!-- assets="footer" -->" // Render the assets. var_dump($assets->flushing($assets->render(group: 'footer'))); // string(57) "<script src="inc/app.js" data-foo="value" async></script>" // You might clear all assets. $assets->clear();
Renderer
PHP Renderer
The PHP renderer uses native PHP language for views, no need to learn new syntax.
Furthermore, it uses the Dir Service which provides a simple way to organize your template files.
use Tobento\Service\View\PhpRenderer; use Tobento\Service\View\ViewNotFoundException; use Tobento\Service\Dir\Dirs; use Tobento\Service\Dir\Dir; $dirs = new Dirs( new Dir(dir: 'home/private/views/', priority: 5), new Dir(dir: 'home/private/theme/views/', priority: 10), ); $renderer = new PhpRenderer($dirs->sort()); // Render a view. try { echo $renderer->render('view', ['title' => 'Title']); } catch (ViewNotFoundException $e) { // } // Check if a view exists. if ($renderer->exists('view')) { // }
Chain Renderer
This renderer allows combining any number of other renderer. If the first renderer cannot handle rendering, the next renderer will try and so on.
use Tobento\Service\View\ChainRenderer; use Tobento\Service\View\PhpRenderer; use Tobento\Service\Dir\Dirs; use Tobento\Service\View\ViewNotFoundException; $renderer = new ChainRenderer( // new TwigRenderer(), new PhpRenderer(new Dirs()), ); // Render a view. try { echo $renderer->render('view'); } catch (ViewNotFoundException $e) { // } // Check if a view exists. if ($renderer->exists('view')) { // }
View
use Tobento\Service\View\PhpRenderer; use Tobento\Service\Dir\Dirs; use Tobento\Service\View\View; use Tobento\Service\View\Data; use Tobento\Service\View\Assets; $renderer = new PhpRenderer(new Dirs()); $assets = new Assets( assetDir: 'home/public/src/', assetUri: 'https://www.example.com/src/' ); $view = new View($renderer, new Data(), $assets); // Adding data $view->data(['key' => 'value']); $view->with(name: 'title', value: 'Title'); // Get data. $view->get('key'); $view->get('key', 'defaultValue'); $view->data()->get('key'); // Render a view. echo $view->render(view: 'inc/header', data: ['key' => 'value']); // Add a view by key. $view->add(key: 'inc.view', view: 'inc/view'); // Render the view added by key. echo $view->render(view: 'inc.view', data: ['key' => 'value']); // On render a specific view. $view->on('inc.view', function(array $data, ViewInterface $view): array { $data['key'] = 'value'; return $data; }); $view->on('comments.writing', function(array $data, ViewInterface $view): array { $view->add(key: 'comments.writing', view: 'comments/writing'); $data['text'] = 'Lorem ipsum'; return $data; }); // Get the assets $assets = $view->assets(); // Add an asset $view->asset('app.css');
Template
<!DOCTYPE html> <html> <head> <title><?= $view->esc($title) ?></title> <?= $view->assets()->render() ?> <?php // assets can be included in every subview too. $view->asset('app.css'); $view->asset('js/app.js')->attr('async'); ?> </head> <body> <?= $view->render('inc/header') ?> <h1><?= $view->esc($title) ?></h1> <p><?= $view->esc($description) ?></p> <?php if ($view->exists('inc/view')) { // or with view key 'inc.view' ?> <?= $view->render('inc/view') ?> <?php } else { ?> <p>Fallback</p> <?php } ?> </body> </html>
// Rendering once only. <?php if ($view->once(__FILE__)) { ?> <p>Lorem ipsum</p> <?php } ?>
Macros
With macros you can easily extend the view class with any function you need.
use Tobento\Service\View\PhpRenderer; use Tobento\Service\Dir\Dirs; use Tobento\Service\View\View; use Tobento\Service\View\Data; use Tobento\Service\View\Assets; $renderer = new PhpRenderer(new Dirs()); $assets = new Assets( assetDir: 'home/public/src/', assetUri: 'https://www.example.com/src/' ); $view = new View($renderer, new Data(), $assets); $view->macro('strtoupper', function($value) { return strtoupper($value); }); var_dump($view->strtoupper('lorem ipsum')); // string(11) "LOREM IPSUM"
Tags Attributes Macro
Tags Attributes might be useful to collect attributes for a specific tag.
use Tobento\Service\View\PhpRenderer; use Tobento\Service\Dir\Dirs; use Tobento\Service\View\View; use Tobento\Service\View\Data; use Tobento\Service\View\Assets; use Tobento\Service\View\TagsAttributes; use Tobento\Service\Tag\AttributesInterface; $renderer = new PhpRenderer(new Dirs()); $assets = new Assets( assetDir: 'home/public/src/', assetUri: 'https://www.example.com/src/' ); $view = new View($renderer, new Data(), $assets); $view->macro('attr', [new TagsAttributes(), 'get']); var_dump($view->attr('body') instanceof AttributesInterface); // bool(true)
Check out the Tag Service - Attributes Interface to learn more about it in general.