View renderer
Learn about view renderer
This library is abandoned. Please consider using a different library.
Rendering views
The view renderer is the central part of the rendering system and it’s
represented by an instance of the Opis\View\ViewRenderer
class.
Let’s assume we have the following template for a blog article and we want
to associate it with a view instance named blog.article
.
article.php
<div class="article">
<h2><?= $title ?></h2>
<div><?= $content ?></div>
</div>
In order to be able to render that view, we must first provide a way to resolve the view’s name to the location
of our article template. This is done with the help of the handle
method.
use Opis\View\{
ViewRenderer, View
};
$renderer = new ViewRenderer();
$renderer->handle('blog.article', function(){
return '/path/to/article.php';
});
To test that our view name is resolved correctly, we could pass it to the
resolveViewName
method.
echo $renderer->resolveViewName('blog.article'); //> /path/to/article.php
Now, we can start rendering article views, by using the render
method.
$article = new View('blog.article', [
'title' => 'My first article',
'content' => 'This is my first article'
]);
echo $renderer->render($article);
The renderView
method can be used as a shortcut for the above example.
echo $renderer->renderView('blog.article', [
'title' => 'My first article',
'content' => 'This is my first article'
]);
Result:
<div class="article">
<h2>My first article</h2>
<div>This is my first article</div>
</div>
Resolving view names
Having to resolve each view name individually is an overkill task when dealing with a considerable amount of names. The solution to this problem is to split the view name into segments, and make some of those segments available for being referenced in your callback function.
$renderer->handle('blog.{viewName}' function($viewName) {
return '/path/to/' . $viewName . '.php';
});
Now we can render multiple views, named differently, and resolve them only in as single place.
$renderer->renderView('blog.article', [
'title' => 'Some title',
'content' => 'Some content'
]);
$render->renderView('blog.comment', [
'date' => 'Now',
'comment' => 'Hello, World!'
]);
You could also add a regex constraint - with the help of the where
and whereIn
methods -
to filter which view names you are trying to resolve.
// Handle blog.article and blog.comment
$renderer->handle('blog.{viewName}' function($viewName) {
return '/path/to/' . $viewName . '.php';
})
->whereIn('viewName', ['article', 'comment']);
// Handle blog.list and blog.user
$renderer->handle('blog.{viewName}' function($viewName) {
return '/other/path/to/' . $viewName . '.php';
})
->whereIn('viewName', ['list', 'user']);
// Handle blog.1, blog.24, blog.2018 etc.
$renderer->handle('blog.{viewName}' function($viewName) {
return '/some/path/to/blog' . $viewName . '.php';
})
->where('viewName', '[1-9][0-9]*');
Use the filter
method to add a custom filter callback.
The filter callback must return either true
or false
.
// Handle blog.1, blog.24, blog.2018 etc.,
// only when the template file exists
$renderer->handle('blog.{viewName}' function($viewName) {
return '/some/path/to/blog' . $viewName . '.php';
})
->where('viewName', '[1-9][0-9]*')
->filter(function($viewName){
return file_exists('/some/path/to/blog' . $viewName . '.php');
});
Overriding views
Let’s face it: no matter how awesome your website is, sooner or later you will want to change how your site’s content is presented to the users. The classical workflow is to dig up into your template files, change the code inside them, and then publish the changes. While this seems to be the most logical and practical solution, it’s far from being an ideal one.
Let’s take into consideration, once again, the template for the blog.article
view.
article.php
<div class="article">
<h2><?= $title ?></h2>
<div><?= $content ?></div>
</div>
Our task is to change the div
’s class attribute content toblog-article
, and the
h2
tag into an h1
tag. Of course, we could make this changes directly into the article.php
template file, but remember, we don’t want to take it on that road. Instead, we will create another
template file and make our changes there.
custom-article.php
<div class="blog-article">
<h1><?= $title ?></h1>
<div><?= $content ?></div>
</div>
Now, we’re ready to override our blog.article
view. We do this
by adding a new rule, with the help of the handle
method,
specifying a higher priority for this rule than the priority of the one we wish to
override. The default priority for a handling rule is 0
.
// Handle blog.article and blog.comment
$renderer->handle('blog.{viewName}' function($viewName) {
return '/path/to/' . $viewName . '.php';
}) // default priority of 0
->whereIn('viewName', ['article', 'comment']);
// Override blog.article
$renderer->handle('blog.article' function() {
return '/path/to/custom-article.php';
}, 1); // priority 1
// Test it
echo $renderer->renderView('blog.article', [
'title' => 'Foo',
'content' => 'Bar'
]);
Result:
<div class="blog-article">
<h1>Foo</h1>
<div>Bar</div>
</div>