Skip to content

Commit

Permalink
Nested sections rendering (#4622)
Browse files Browse the repository at this point in the history
* Fix: Nested sections rendering

* Changed UG and applied PR suggestions
  • Loading branch information
iRedds authored May 5, 2021
1 parent 1472864 commit 2b5e848
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 8 deletions.
33 changes: 26 additions & 7 deletions system/View/View.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class View implements RendererInterface
*/
protected $layout;


/**
* Holds the sections and their data.
*
Expand All @@ -121,9 +122,18 @@ class View implements RendererInterface
* if any.
*
* @var string|null
* @deprecated
*/
protected $currentSection;

/**
* The name of the current section being rendered,
* if any.
*
* @var array<string>
*/
protected $sectionStack = [];

/**
* Constructor
*
Expand Down Expand Up @@ -227,7 +237,7 @@ public function render(string $view, array $options = null, bool $saveData = nul
// 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) && empty($this->currentSection))
if (! is_null($this->layout) && $this->sectionStack === [])
{
$layoutView = $this->layout;
$this->layout = null;
Expand Down Expand Up @@ -402,35 +412,44 @@ public function extend(string $layout)
/**
* Starts holds content for a section within the layout.
*
* @param string $name
* @param string $name Section name
*
* @return void
*
*/
public function section(string $name)
{
//Saved to prevent BC.
$this->currentSection = $name;
$this->sectionStack[] = $name;

ob_start();
}

/**
* Captures the last section
*
* @return void
* @throws RuntimeException
*/
public function endSection()
{
$contents = ob_get_clean();

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

$section = array_pop($this->sectionStack);

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

$this->currentSection = null;
$this->sections[$section][] = $contents;
}

/**
Expand Down
15 changes: 14 additions & 1 deletion tests/system/View/ViewTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ public function testRenderSaveDataCover()
$this->assertEquals(true, $this->getPrivateProperty($view, 'saveData'));
}

public function testRenderSaveDataUseAflterSaveDataFalse()
public function testRenderSaveDataUseAfterSaveDataFalse()
{
$view = new View($this->config, $this->viewsDir, $this->loader);
$view->setVar('testString', 'test');
Expand All @@ -387,4 +387,17 @@ public function testCachedAutoDiscoverAndRender()
// this second renderings should go thru the cache
$this->assertStringContainsString($expected, $view->render('Nested/simple', ['cache' => 10]));
}

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

$view->setVar('testString', 'Hello World');

$content = $view->render('nested_section');

$this->assertStringContainsString('<p>First</p>', $content);
$this->assertStringContainsString('<p>Second</p>', $content);
$this->assertStringContainsString('<p>Third</p>', $content);
}
}
14 changes: 14 additions & 0 deletions tests/system/View/Views/nested_section.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php $this->extend('layout'); ?>

<?php $this->section('content'); ?>
<p>Second</p>

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

<?php $this->endSection(); ?>

<?php $this->section('content'); ?>
<p>Third</p>
<?php $this->endSection(); ?>
2 changes: 2 additions & 0 deletions user_guide_src/source/changelogs/v4.1.2.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Enhancements:

Changes:

- Layouts in views now support nested sections.
- ``Response::getCookie`` now returns a ``Cookie`` instance instead of an array of cookie attributes.
- ``Response::getCookies`` now returns an array of ``Cookie`` instances instead of array of array of attributes.
- To eliminate warnings from modern browsers' consoles, empty samesite values will be defaulted to ``Lax`` on cookie dispatch.
Expand All @@ -30,6 +31,7 @@ Changes:

Deprecations:

- Deprecated ``Codeigniter\View\View::$currentSection`` property.
- Language strings and exceptions on invalid cookie samesite are deprecated for the ``CookieException``'s own exception message.
- Deprecated `CodeIgniter\Entity` in favor of `CodeIgniter\Entity\Entity`
- Deprecated cookie-related properties of ``Response`` in order to use the ``Cookie`` class.
Expand Down
12 changes: 12 additions & 0 deletions user_guide_src/source/outgoing/view_layouts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ matches the section name exists.::

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

Sections can contain nested sections::

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

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


******************
Rendering the View
******************
Expand Down

0 comments on commit 2b5e848

Please sign in to comment.