Skip to content

Commit 59816bb

Browse files
authored
View Cleaning (#17018)
This cleans up the view component and extracts some traits.
1 parent aec1d04 commit 59816bb

15 files changed

+1006
-946
lines changed

src/Illuminate/View/Compilers/Concerns/CompilesLayouts.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@
66

77
trait CompilesLayouts
88
{
9+
/**
10+
* The name of the last section that was started.
11+
*
12+
* @var string
13+
*/
14+
protected $lastSection;
15+
916
/**
1017
* Compile the extends statements into valid PHP.
1118
*
@@ -31,6 +38,8 @@ protected function compileExtends($expression)
3138
*/
3239
protected function compileSection($expression)
3340
{
41+
$this->lastSection = trim($expression, "()'");
42+
3443
return "<?php \$__env->startSection{$expression}; ?>";
3544
}
3645

@@ -41,7 +50,7 @@ protected function compileSection($expression)
4150
*/
4251
protected function compileParent()
4352
{
44-
return ViewFactory::parentPlaceholder();
53+
return ViewFactory::parentPlaceholder($this->lastSection ?: '');
4554
}
4655

4756
/**

src/Illuminate/View/Compilers/Concerns/CompilesLoops.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ protected function compileForelse($expression)
2222

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

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

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

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

4343
/**
@@ -78,7 +78,7 @@ protected function compileForeach($expression)
7878

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

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

8383
return "<?php {$initLoop} foreach(\$__currentLoopData as {$iteration}): {$iterateLoop} ?>";
8484
}
@@ -124,7 +124,7 @@ protected function compileEndfor($expression)
124124
*/
125125
protected function compileEndforeach($expression)
126126
{
127-
return '<?php endforeach; $__env->popLoop(); $loop = $__env->getFirstLoop(); ?>';
127+
return '<?php endforeach; $__env->popLoop(); $loop = $__env->getLastLoop(); ?>';
128128
}
129129

130130
/**
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
<?php
2+
3+
namespace Illuminate\View\Concerns;
4+
5+
use Illuminate\Support\HtmlString;
6+
7+
trait ManagesComponents
8+
{
9+
/**
10+
* The components being rendered.
11+
*
12+
* @var array
13+
*/
14+
protected $componentStack = [];
15+
16+
/**
17+
* The original data passed to the component.
18+
*
19+
* @var array
20+
*/
21+
protected $componentData = [];
22+
23+
/**
24+
* The slot contents for the component.
25+
*
26+
* @var array
27+
*/
28+
protected $slots = [];
29+
30+
/**
31+
* The names of the slots being rendered.
32+
*
33+
* @var array
34+
*/
35+
protected $slotStack = [];
36+
37+
/**
38+
* Start a component rendering process.
39+
*
40+
* @param string $name
41+
* @param array $data
42+
* @return void
43+
*/
44+
public function startComponent($name, array $data = [])
45+
{
46+
if (ob_start()) {
47+
$this->componentStack[] = $name;
48+
49+
$this->componentData[$name] = $data;
50+
51+
$this->slots[$name] = [];
52+
}
53+
}
54+
55+
/**
56+
* Render the current component.
57+
*
58+
* @return string
59+
*/
60+
public function renderComponent()
61+
{
62+
$name = array_pop($this->componentStack);
63+
64+
return tap($this->make($name, $this->componentData($name))->render(), function () use ($name) {
65+
$this->resetComponent($name);
66+
});
67+
}
68+
69+
/**
70+
* Get the data for the given component.
71+
*
72+
* @param string $name
73+
* @return array
74+
*/
75+
protected function componentData($name)
76+
{
77+
$slot = ['slot' => new HtmlString(trim(ob_get_clean()))];
78+
79+
return array_merge($this->componentData[$name], $slot, $this->slots[$name]);
80+
}
81+
82+
/**
83+
* Start the slot rendering process.
84+
*
85+
* @param string $name
86+
* @return void
87+
*/
88+
public function slot($name)
89+
{
90+
if (ob_start()) {
91+
$this->slots[last($this->componentStack)][$name] = '';
92+
93+
$this->slotStack[last($this->componentStack)][] = $name;
94+
}
95+
}
96+
97+
/**
98+
* Save the slot content for rendering.
99+
*
100+
* @return void
101+
*/
102+
public function endSlot()
103+
{
104+
$current = last($this->componentStack);
105+
106+
$currentSlot = array_pop($this->slotStack[$current]);
107+
108+
$this->slots[$current][$currentSlot] = new HtmlString(trim(ob_get_clean()));
109+
}
110+
111+
/**
112+
* Reset the state for the given component.
113+
*
114+
* @param string $name
115+
* @return void
116+
*/
117+
protected function resetComponent($name)
118+
{
119+
unset($this->slots[$name]);
120+
unset($this->slotStack[$name]);
121+
unset($this->componentData[$name]);
122+
}
123+
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
<?php
2+
3+
namespace Illuminate\View\Concerns;
4+
5+
use Closure;
6+
use Illuminate\Support\Str;
7+
use Illuminate\Contracts\Events\Dispatcher;
8+
use Illuminate\Contracts\Container\Container;
9+
use Illuminate\Contracts\View\View as ViewContract;
10+
11+
trait ManagesEvents
12+
{
13+
/**
14+
* Register a view creator event.
15+
*
16+
* @param array|string $views
17+
* @param \Closure|string $callback
18+
* @return array
19+
*/
20+
public function creator($views, $callback)
21+
{
22+
$creators = [];
23+
24+
foreach ((array) $views as $view) {
25+
$creators[] = $this->addViewEvent($view, $callback, 'creating: ');
26+
}
27+
28+
return $creators;
29+
}
30+
31+
/**
32+
* Register multiple view composers via an array.
33+
*
34+
* @param array $composers
35+
* @return array
36+
*/
37+
public function composers(array $composers)
38+
{
39+
$registered = [];
40+
41+
foreach ($composers as $callback => $views) {
42+
$registered = array_merge($registered, $this->composer($views, $callback));
43+
}
44+
45+
return $registered;
46+
}
47+
48+
/**
49+
* Register a view composer event.
50+
*
51+
* @param array|string $views
52+
* @param \Closure|string $callback
53+
* @param int|null $priority
54+
* @return array
55+
*/
56+
public function composer($views, $callback, $priority = null)
57+
{
58+
$composers = [];
59+
60+
foreach ((array) $views as $view) {
61+
$composers[] = $this->addViewEvent($view, $callback, 'composing: ', $priority);
62+
}
63+
64+
return $composers;
65+
}
66+
67+
/**
68+
* Add an event for a given view.
69+
*
70+
* @param string $view
71+
* @param \Closure|string $callback
72+
* @param string $prefix
73+
* @param int|null $priority
74+
* @return \Closure|null
75+
*/
76+
protected function addViewEvent($view, $callback, $prefix = 'composing: ', $priority = null)
77+
{
78+
$view = $this->normalizeName($view);
79+
80+
if ($callback instanceof Closure) {
81+
$this->addEventListener($prefix.$view, $callback, $priority);
82+
83+
return $callback;
84+
} elseif (is_string($callback)) {
85+
return $this->addClassEvent($view, $callback, $prefix, $priority);
86+
}
87+
}
88+
89+
/**
90+
* Register a class based view composer.
91+
*
92+
* @param string $view
93+
* @param string $class
94+
* @param string $prefix
95+
* @param int|null $priority
96+
* @return \Closure
97+
*/
98+
protected function addClassEvent($view, $class, $prefix, $priority = null)
99+
{
100+
$name = $prefix.$view;
101+
102+
// When registering a class based view "composer", we will simply resolve the
103+
// classes from the application IoC container then call the compose method
104+
// on the instance. This allows for convenient, testable view composers.
105+
$callback = $this->buildClassEventCallback(
106+
$class, $prefix
107+
);
108+
109+
$this->addEventListener($name, $callback, $priority);
110+
111+
return $callback;
112+
}
113+
114+
/**
115+
* Build a class based container callback Closure.
116+
*
117+
* @param string $class
118+
* @param string $prefix
119+
* @return \Closure
120+
*/
121+
protected function buildClassEventCallback($class, $prefix)
122+
{
123+
list($class, $method) = $this->parseClassEvent($class, $prefix);
124+
125+
// Once we have the class and method name, we can build the Closure to resolve
126+
// the instance out of the IoC container and call the method on it with the
127+
// given arguments that are passed to the Closure as the composer's data.
128+
return function () use ($class, $method) {
129+
return call_user_func_array(
130+
[$this->container->make($class), $method], func_get_args()
131+
);
132+
};
133+
}
134+
135+
/**
136+
* Parse a class based composer name.
137+
*
138+
* @param string $class
139+
* @param string $prefix
140+
* @return array
141+
*/
142+
protected function parseClassEvent($class, $prefix)
143+
{
144+
if (! Str::contains($class, '@')) {
145+
return [$class, $this->classEventMethodForPrefix($prefix)];
146+
}
147+
148+
return explode('@', $class);
149+
}
150+
151+
/**
152+
* Determine the class event method based on the given prefix.
153+
*
154+
* @param string $prefix
155+
* @return string
156+
*/
157+
protected function classEventMethodForPrefix($prefix)
158+
{
159+
return Str::contains($prefix, 'composing') ? 'compose' : 'create';
160+
}
161+
162+
/**
163+
* Add a listener to the event dispatcher.
164+
*
165+
* @param string $name
166+
* @param \Closure $callback
167+
* @param int|null $priority
168+
* @return void
169+
*/
170+
protected function addEventListener($name, $callback, $priority = null)
171+
{
172+
if (is_null($priority)) {
173+
$this->events->listen($name, $callback);
174+
} else {
175+
$this->events->listen($name, $callback, $priority);
176+
}
177+
}
178+
179+
/**
180+
* Call the composer for a given view.
181+
*
182+
* @param \Illuminate\Contracts\View\View $view
183+
* @return void
184+
*/
185+
public function callComposer(ViewContract $view)
186+
{
187+
$this->events->fire('composing: '.$view->name(), [$view]);
188+
}
189+
190+
/**
191+
* Call the creator for a given view.
192+
*
193+
* @param \Illuminate\Contracts\View\View $view
194+
* @return void
195+
*/
196+
public function callCreator(ViewContract $view)
197+
{
198+
$this->events->fire('creating: '.$view->name(), [$view]);
199+
}
200+
}

0 commit comments

Comments
 (0)