Skip to content

Commit

Permalink
View Cleaning (#17018)
Browse files Browse the repository at this point in the history
This cleans up the view component and extracts some traits.
  • Loading branch information
taylorotwell authored Dec 28, 2016
1 parent aec1d04 commit 59816bb
Show file tree
Hide file tree
Showing 15 changed files with 1,006 additions and 946 deletions.
11 changes: 10 additions & 1 deletion src/Illuminate/View/Compilers/Concerns/CompilesLayouts.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@

trait CompilesLayouts
{
/**
* The name of the last section that was started.
*
* @var string
*/
protected $lastSection;

/**
* Compile the extends statements into valid PHP.
*
Expand All @@ -31,6 +38,8 @@ protected function compileExtends($expression)
*/
protected function compileSection($expression)
{
$this->lastSection = trim($expression, "()'");

return "<?php \$__env->startSection{$expression}; ?>";
}

Expand All @@ -41,7 +50,7 @@ protected function compileSection($expression)
*/
protected function compileParent()
{
return ViewFactory::parentPlaceholder();
return ViewFactory::parentPlaceholder($this->lastSection ?: '');
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/Illuminate/View/Compilers/Concerns/CompilesLoops.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ protected function compileForelse($expression)

$initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);";

$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getFirstLoop();';
$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();';

return "<?php {$empty} = true; {$initLoop} foreach(\$__currentLoopData as {$iteration}): {$iterateLoop} {$empty} = false; ?>";
}
Expand All @@ -37,7 +37,7 @@ protected function compileEmpty($expression)
{
$empty = '$__empty_'.$this->forElseCounter--;

return "<?php endforeach; \$__env->popLoop(); \$loop = \$__env->getFirstLoop(); if ({$empty}): ?>";
return "<?php endforeach; \$__env->popLoop(); \$loop = \$__env->getLastLoop(); if ({$empty}): ?>";
}

/**
Expand Down Expand Up @@ -78,7 +78,7 @@ protected function compileForeach($expression)

$initLoop = "\$__currentLoopData = {$iteratee}; \$__env->addLoop(\$__currentLoopData);";

$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getFirstLoop();';
$iterateLoop = '$__env->incrementLoopIndices(); $loop = $__env->getLastLoop();';

return "<?php {$initLoop} foreach(\$__currentLoopData as {$iteration}): {$iterateLoop} ?>";
}
Expand Down Expand Up @@ -124,7 +124,7 @@ protected function compileEndfor($expression)
*/
protected function compileEndforeach($expression)
{
return '<?php endforeach; $__env->popLoop(); $loop = $__env->getFirstLoop(); ?>';
return '<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>';
}

/**
Expand Down
123 changes: 123 additions & 0 deletions src/Illuminate/View/Concerns/ManagesComponents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

namespace Illuminate\View\Concerns;

use Illuminate\Support\HtmlString;

trait ManagesComponents
{
/**
* The components being rendered.
*
* @var array
*/
protected $componentStack = [];

/**
* The original data passed to the component.
*
* @var array
*/
protected $componentData = [];

/**
* The slot contents for the component.
*
* @var array
*/
protected $slots = [];

/**
* The names of the slots being rendered.
*
* @var array
*/
protected $slotStack = [];

/**
* Start a component rendering process.
*
* @param string $name
* @param array $data
* @return void
*/
public function startComponent($name, array $data = [])
{
if (ob_start()) {
$this->componentStack[] = $name;

$this->componentData[$name] = $data;

$this->slots[$name] = [];
}
}

/**
* Render the current component.
*
* @return string
*/
public function renderComponent()
{
$name = array_pop($this->componentStack);

return tap($this->make($name, $this->componentData($name))->render(), function () use ($name) {
$this->resetComponent($name);
});
}

/**
* Get the data for the given component.
*
* @param string $name
* @return array
*/
protected function componentData($name)
{
$slot = ['slot' => new HtmlString(trim(ob_get_clean()))];

return array_merge($this->componentData[$name], $slot, $this->slots[$name]);
}

/**
* Start the slot rendering process.
*
* @param string $name
* @return void
*/
public function slot($name)
{
if (ob_start()) {
$this->slots[last($this->componentStack)][$name] = '';

$this->slotStack[last($this->componentStack)][] = $name;
}
}

/**
* Save the slot content for rendering.
*
* @return void
*/
public function endSlot()
{
$current = last($this->componentStack);

$currentSlot = array_pop($this->slotStack[$current]);

$this->slots[$current][$currentSlot] = new HtmlString(trim(ob_get_clean()));
}

/**
* Reset the state for the given component.
*
* @param string $name
* @return void
*/
protected function resetComponent($name)
{
unset($this->slots[$name]);
unset($this->slotStack[$name]);
unset($this->componentData[$name]);
}
}
200 changes: 200 additions & 0 deletions src/Illuminate/View/Concerns/ManagesEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
<?php

namespace Illuminate\View\Concerns;

use Closure;
use Illuminate\Support\Str;
use Illuminate\Contracts\Events\Dispatcher;
use Illuminate\Contracts\Container\Container;
use Illuminate\Contracts\View\View as ViewContract;

trait ManagesEvents
{
/**
* Register a view creator event.
*
* @param array|string $views
* @param \Closure|string $callback
* @return array
*/
public function creator($views, $callback)
{
$creators = [];

foreach ((array) $views as $view) {
$creators[] = $this->addViewEvent($view, $callback, 'creating: ');
}

return $creators;
}

/**
* Register multiple view composers via an array.
*
* @param array $composers
* @return array
*/
public function composers(array $composers)
{
$registered = [];

foreach ($composers as $callback => $views) {
$registered = array_merge($registered, $this->composer($views, $callback));
}

return $registered;
}

/**
* Register a view composer event.
*
* @param array|string $views
* @param \Closure|string $callback
* @param int|null $priority
* @return array
*/
public function composer($views, $callback, $priority = null)
{
$composers = [];

foreach ((array) $views as $view) {
$composers[] = $this->addViewEvent($view, $callback, 'composing: ', $priority);
}

return $composers;
}

/**
* Add an event for a given view.
*
* @param string $view
* @param \Closure|string $callback
* @param string $prefix
* @param int|null $priority
* @return \Closure|null
*/
protected function addViewEvent($view, $callback, $prefix = 'composing: ', $priority = null)
{
$view = $this->normalizeName($view);

if ($callback instanceof Closure) {
$this->addEventListener($prefix.$view, $callback, $priority);

return $callback;
} elseif (is_string($callback)) {
return $this->addClassEvent($view, $callback, $prefix, $priority);
}
}

/**
* Register a class based view composer.
*
* @param string $view
* @param string $class
* @param string $prefix
* @param int|null $priority
* @return \Closure
*/
protected function addClassEvent($view, $class, $prefix, $priority = null)
{
$name = $prefix.$view;

// When registering a class based view "composer", we will simply resolve the
// classes from the application IoC container then call the compose method
// on the instance. This allows for convenient, testable view composers.
$callback = $this->buildClassEventCallback(
$class, $prefix
);

$this->addEventListener($name, $callback, $priority);

return $callback;
}

/**
* Build a class based container callback Closure.
*
* @param string $class
* @param string $prefix
* @return \Closure
*/
protected function buildClassEventCallback($class, $prefix)
{
list($class, $method) = $this->parseClassEvent($class, $prefix);

// Once we have the class and method name, we can build the Closure to resolve
// the instance out of the IoC container and call the method on it with the
// given arguments that are passed to the Closure as the composer's data.
return function () use ($class, $method) {
return call_user_func_array(
[$this->container->make($class), $method], func_get_args()
);
};
}

/**
* Parse a class based composer name.
*
* @param string $class
* @param string $prefix
* @return array
*/
protected function parseClassEvent($class, $prefix)
{
if (! Str::contains($class, '@')) {
return [$class, $this->classEventMethodForPrefix($prefix)];
}

return explode('@', $class);
}

/**
* Determine the class event method based on the given prefix.
*
* @param string $prefix
* @return string
*/
protected function classEventMethodForPrefix($prefix)
{
return Str::contains($prefix, 'composing') ? 'compose' : 'create';
}

/**
* Add a listener to the event dispatcher.
*
* @param string $name
* @param \Closure $callback
* @param int|null $priority
* @return void
*/
protected function addEventListener($name, $callback, $priority = null)
{
if (is_null($priority)) {
$this->events->listen($name, $callback);
} else {
$this->events->listen($name, $callback, $priority);
}
}

/**
* Call the composer for a given view.
*
* @param \Illuminate\Contracts\View\View $view
* @return void
*/
public function callComposer(ViewContract $view)
{
$this->events->fire('composing: '.$view->name(), [$view]);
}

/**
* Call the creator for a given view.
*
* @param \Illuminate\Contracts\View\View $view
* @return void
*/
public function callCreator(ViewContract $view)
{
$this->events->fire('creating: '.$view->name(), [$view]);
}
}
Loading

0 comments on commit 59816bb

Please sign in to comment.