Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New View Layout functionality for simple template functionality. #1729

Merged
merged 1 commit into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions system/View/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,29 @@ class View implements RendererInterface
*/
protected $viewsCount = 0;

/**
* The name of the layout being used, if any.
* Set by the `extend` method used within views.
*
* @var string
*/
protected $layout;

/**
* Holds the sections and their data.
*
* @var array
*/
protected $sections = [];

/**
* The name of the current section being rendered,
* if any.
*
* @var string
*/
protected $currentSection;

//--------------------------------------------------------------------

/**
Expand Down Expand Up @@ -211,6 +234,16 @@ public function render(string $view, array $options = null, $saveData = null): s
$output = ob_get_contents();
@ob_end_clean();

// When using layouts, the data has already been stored
// in $this->sections, and no other valid output
// is allowed in $output so we'll overwrite it.
if (! is_null($this->layout))
{
$layoutView = $this->layout;
$this->layout = null;
$output = $this->render($layoutView, $options, $saveData);
}

$this->logPerformance($this->renderVars['start'], microtime(true), $this->renderVars['view']);

if (CI_DEBUG && (! isset($options['debug']) || $options['debug'] === true))
Expand Down Expand Up @@ -376,6 +409,82 @@ public function getData()

//--------------------------------------------------------------------

/**
* Specifies that the current view should extend an existing layout.
*
* @param string $layout
*
* @return $this
*/
public function extend(string $layout)
{
$this->layout = $layout;
}

//--------------------------------------------------------------------

/**
* Starts holds content for a section within the layout.
*
* @param string $name
*/
public function section(string $name)
{
$this->currentSection = $name;

ob_start();
}

//--------------------------------------------------------------------

/**
*
*
* @throws \Zend\Escaper\Exception\RuntimeException
*/
public function endSection()
{
$contents = ob_get_clean();

if (empty($this->currentSection))
{
throw new \RuntimeException('View themes, no current section.');
}

// Ensure an array exists so we can store multiple entries for this.
if (! array_key_exists($this->currentSection, $this->sections))
{
$this->sections[$this->currentSection] = [];
}
$this->sections[$this->currentSection][] = $contents;

$this->currentSection = null;
}

//--------------------------------------------------------------------

/**
* Renders a section's contents.
*
* @param string $sectionName
*/
public function renderSection(string $sectionName)
{
if (! isset($this->sections[$sectionName]))
{
echo '';

return;
}

foreach ($this->sections[$sectionName] as $contents)
{
echo $contents;
}
}

//--------------------------------------------------------------------

/**
* Returns the performance data that might have been collected
* during the execution. Used primarily in the Debug Toolbar.
Expand Down
29 changes: 29 additions & 0 deletions tests/system/View/ViewTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,33 @@ public function testPerformanceNonLogging()
$this->assertEquals(0, count($view->getPerformanceData()));
}

public function testRenderLayoutExtendsCorrectly()
{
$view = new View($this->config, $this->viewsDir, $this->loader);

$view->setVar('testString', 'Hello World');
$expected = "<p>Open</p>\n<h1>Hello World</h1>";

$this->assertContains($expected, $view->render('extend'));
}

public function testRenderLayoutMakesDataAvailableToBoth()
{
$view = new View($this->config, $this->viewsDir, $this->loader);

$view->setVar('testString', 'Hello World');
$expected = "<p>Open</p>\n<h1>Hello World</h1>\n<p>Hello World</p>";

$this->assertContains($expected, $view->render('extend'));
}

public function testRenderLayoutSupportsMultipleOfSameSection()
{
$view = new View($this->config, $this->viewsDir, $this->loader);

$view->setVar('testString', 'Hello World');
$expected = "<p>First</p>\n<p>Second</p>";

$this->assertContains($expected, $view->render('extend_two'));
}
}
5 changes: 5 additions & 0 deletions tests/system/View/Views/extend.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?= $this->extend('layout') ?>

<?= $this->section('content') ?>
<h1><?= $testString ?></h1>
<?= $this->endSection() ?>
10 changes: 10 additions & 0 deletions tests/system/View/Views/extend_two.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?= $this->extend('layout') ?>

<?= $this->section('content') ?>
<p>First</p>
<?= $this->endSection() ?>


<?= $this->section('content') ?>
<p>Second</p>
<?= $this->endSection() ?>
3 changes: 3 additions & 0 deletions tests/system/View/Views/layout.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<p>Open</p>
<?= $this->renderSection('content') ?>
<p><?= $testString ?></p>
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/next.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Version |version|
Release Date: Not released

Highlights:
- New View Layouts provide simple way to create site site view templates.


The list of changed files follows, with PR numbers shown.
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/outgoing/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ View components are used to build what is returned to the user.
views
view_cells
view_renderer
view_layouts
view_parser
response
api_responses
Expand Down
74 changes: 74 additions & 0 deletions user_guide_src/source/outgoing/view_layouts.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
############
View Layouts
############

.. contents::
:local:
:depth: 2

CodeIgniter supports a simple, yet very flexible, layout system that makes it simple to use one or more
base page layouts across your application. Layouts support sections of content that can be inserted from
any view being rendered. You could create different layouts to support one-column, two-column,
blog archive pages, and more. Layouts are never directly rendered. Instead, you render a view, which
specifies the layout that it wants to extend.

*****************
Creating A Layout
*****************

Layouts are views like any other. The only difference is their intended usage. Layouts are the only view
files that would make use of the ``renderSection()`` method. This method acts as a placeholder for content.

::

<!doctype html>
<html>
<head>
<title>My Layout</title>
</head>
<body>
<?= $this->renderSection('content') ?>
</body>
</html>

The renderSection() method only has one argument - the name of the section. That way any child views know
what to name the content section.

**********************
Using Layouts in Views
**********************

Whenveer a view wants to be inserted into a layout, it must use the ``extend()`` method at the top of the file::

<?= $this->extend('default') ?>

The extend method takes the name of any view file that you wish to use. Since they are standard views, they will
be located just like a view. By default, it will look in the application's View directory, but will also scan
other PSR-4 defined namespaces. You can include a namespace to locate the view in particular namespace View directory::

<?= $this->extend('Blog\Views\default') ?>

All content within a view that extends a layout must be included within ``section($name)`` and ``endSection()`` method calls.
Any content between these calls will be inserted into the layout wherever the ``renderSection($name)`` call that
matches the section name exists.::

<?= $this->extend('default') ?>

<?= $this->section('content') ?>
<h1>Hello World!</h1>
<?= $this->endSection() ?>

The ``endSection()`` does not need the section name. It automatically knows which one to close.

******************
Rendering the View
******************

Rendering the view and it's layout is done exactly as any other view would be displayed within a controller::

public function index()
{
echo view('some_view');
}

The renderer is smart enough to detect whether the view should be rendered on its own, or if it needs a layout.