diff --git a/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php b/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php index eeb79dd7c3b8..2e818cb94ad7 100644 --- a/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php +++ b/src/Illuminate/View/Compilers/Concerns/CompilesComponents.php @@ -174,6 +174,20 @@ protected function compileProps($expression) "; } + /** + * Compile the aware statement into valid PHP. + * + * @param string $expression + * @return string + */ + protected function compileAware($expression) + { + return " \$__value) { + \$__consumeVariable = is_string(\$__key) ? \$__key : \$__value; + \$\$__consumeVariable = is_string(\$__key) ? \$__env->getConsumableComponentData(\$__key, \$__value) : \$__env->getConsumableComponentData(\$__value); +} ?>"; + } + /** * Sanitize the given component attribute value. * diff --git a/src/Illuminate/View/Concerns/ManagesComponents.php b/src/Illuminate/View/Concerns/ManagesComponents.php index f83b2301b142..19dc00a2775d 100644 --- a/src/Illuminate/View/Concerns/ManagesComponents.php +++ b/src/Illuminate/View/Concerns/ManagesComponents.php @@ -24,6 +24,13 @@ trait ManagesComponents */ protected $componentData = []; + /** + * The component data for the component that is currently being rendered. + * + * @var array + */ + protected $currentComponentData = []; + /** * The slot contents for the component. * @@ -81,16 +88,23 @@ public function renderComponent() { $view = array_pop($this->componentStack); - $data = $this->componentData(); - - $view = value($view, $data); + $this->currentComponentData = array_merge( + $previousComponentData = $this->currentComponentData, + $data = $this->componentData() + ); - if ($view instanceof View) { - return $view->with($data)->render(); - } elseif ($view instanceof Htmlable) { - return $view->toHtml(); - } else { - return $this->make($view, $data)->render(); + try { + $view = value($view, $data); + + if ($view instanceof View) { + return $view->with($data)->render(); + } elseif ($view instanceof Htmlable) { + return $view->toHtml(); + } else { + return $this->make($view, $data)->render(); + } + } finally { + $this->currentComponentData = $previousComponentData; } } @@ -115,6 +129,36 @@ protected function componentData() ); } + /** + * Get an item from the component data that exists above the current component. + * + * @param string $key + * @param mixed $default + * @return mixed|null + */ + public function getConsumableComponentData($key, $default = null) + { + if (array_key_exists($key, $this->currentComponentData)) { + return $this->currentComponentData[$key]; + } + + $currentComponent = count($this->componentStack); + + if ($currentComponent === 0) { + return value($default); + } + + for ($i = $currentComponent - 1; $i >= 0; $i--) { + $data = $this->componentData[$i] ?? []; + + if (array_key_exists($key, $data)) { + return $data[$key]; + } + } + + return value($default); + } + /** * Start the slot rendering process. * @@ -163,4 +207,16 @@ protected function currentComponent() { return count($this->componentStack) - 1; } + + /** + * Flush all of the component state. + * + * @return void + */ + protected function flushComponents() + { + $this->componentStack = []; + $this->componentData = []; + $this->currentComponentData = []; + } } diff --git a/tests/Integration/View/BladeTest.php b/tests/Integration/View/BladeTest.php index b3d57f8f7776..80280dfe3c4d 100644 --- a/tests/Integration/View/BladeTest.php +++ b/tests/Integration/View/BladeTest.php @@ -76,6 +76,32 @@ public function tested_nested_anonymous_attribute_proxying_works_correctly() $this->assertSame('', trim($view)); } + public function test_consume_defaults() + { + $view = View::make('consume')->render(); + + $this->assertSame('

Menu

+
Slot: A, Color: orange, Default: foo
+
Slot: B, Color: red, Default: foo
+
Slot: C, Color: blue, Default: foo
+
Slot: D, Color: red, Default: foo
+
Slot: E, Color: red, Default: foo
+
Slot: F, Color: yellow, Default: foo
', trim($view)); + } + + public function test_consume_with_props() + { + $view = View::make('consume', ['color' => 'rebeccapurple'])->render(); + + $this->assertSame('

Menu

+
Slot: A, Color: orange, Default: foo
+
Slot: B, Color: rebeccapurple, Default: foo
+
Slot: C, Color: blue, Default: foo
+
Slot: D, Color: rebeccapurple, Default: foo
+
Slot: E, Color: rebeccapurple, Default: foo
+
Slot: F, Color: yellow, Default: foo
', trim($view)); + } + protected function getEnvironmentSetUp($app) { $app['config']->set('view.paths', [__DIR__.'/templates']); diff --git a/tests/Integration/View/templates/components/menu-item.blade.php b/tests/Integration/View/templates/components/menu-item.blade.php new file mode 100644 index 000000000000..e5b856c2bee3 --- /dev/null +++ b/tests/Integration/View/templates/components/menu-item.blade.php @@ -0,0 +1,2 @@ +@aware(['color' => 'red', 'default' => 'foo']) +
Slot: {{ $slot }}, Color: {{ $color }}, Default: {{ $default }}
diff --git a/tests/Integration/View/templates/components/menu.blade.php b/tests/Integration/View/templates/components/menu.blade.php new file mode 100644 index 000000000000..b79b5b16f545 --- /dev/null +++ b/tests/Integration/View/templates/components/menu.blade.php @@ -0,0 +1,6 @@ +

Menu

+A +B +{{ $slot }} +E +F diff --git a/tests/Integration/View/templates/consume.blade.php b/tests/Integration/View/templates/consume.blade.php new file mode 100644 index 000000000000..ced9b232daca --- /dev/null +++ b/tests/Integration/View/templates/consume.blade.php @@ -0,0 +1,15 @@ +@isset($color) + + +C +D + + +@else + + +C +D + + +@endisset