Skip to content

Commit 37cb345

Browse files
authored
[10.x] Fix loss of attributes after calling child component (#49216)
* Store and restore original attributes when rendering component * Move attribute assertion to mock This is needed because the original attributes are now restored after rendering a component. * Assert withAttribute params on child component prop * Fix assertion * Add assertion to ensure original attributes are restored
1 parent a91c3f4 commit 37cb345

File tree

3 files changed

+70
-2
lines changed

3 files changed

+70
-2
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ public static function compileClassComponentOpening(string $component, string $a
6767
{
6868
return implode("\n", [
6969
'<?php if (isset($component)) { $__componentOriginal'.$hash.' = $component; } ?>',
70+
'<?php if (isset($attributes)) { $__attributesOriginal'.$hash.' = $attributes; } ?>',
7071
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>',
7172
'<?php $component->withName('.$alias.'); ?>',
7273
'<?php if ($component->shouldRender()): ?>',
@@ -94,6 +95,10 @@ public function compileEndComponentClass()
9495
$hash = array_pop(static::$componentHashStack);
9596

9697
return $this->compileEndComponent()."\n".implode("\n", [
98+
'<?php endif; ?>',
99+
'<?php if (isset($__attributesOriginal'.$hash.')): ?>',
100+
'<?php $attributes = $__attributesOriginal'.$hash.'; ?>',
101+
'<?php unset($__attributesOriginal'.$hash.'); ?>',
97102
'<?php endif; ?>',
98103
'<?php if (isset($__componentOriginal'.$hash.')): ?>',
99104
'<?php $component = $__componentOriginal'.$hash.'; ?>',

tests/View/Blade/BladeComponentTagCompilerTest.php

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,7 @@ public function testAttributesTreatedAsPropsAreRemovedFromFinalAttributes()
715715
$component->shouldReceive('shouldRender')->once()->andReturn(true);
716716
$component->shouldReceive('resolveView')->once()->andReturn('');
717717
$component->shouldReceive('data')->once()->andReturn([]);
718-
$component->shouldReceive('withAttributes')->once();
718+
$component->shouldReceive('withAttributes')->with(['attributes' => new ComponentAttributeBag(['other' => 'ok'])])->once();
719719

720720
Component::resolveComponentsUsing(fn () => $component);
721721

@@ -730,7 +730,57 @@ public function testAttributesTreatedAsPropsAreRemovedFromFinalAttributes()
730730
eval(" ?> $template <?php ");
731731
ob_get_clean();
732732

733-
$this->assertNull($attributes->get('userId'));
733+
$this->assertSame($attributes->get('userId'), 'bar');
734+
$this->assertSame($attributes->get('other'), 'ok');
735+
}
736+
737+
public function testOriginalAttributesAreRestoredAfterRenderingChildComponentWithProps()
738+
{
739+
$container = new Container;
740+
$container->instance(Application::class, $app = m::mock(Application::class));
741+
$container->instance(Factory::class, $factory = m::mock(Factory::class));
742+
$container->alias(Factory::class, 'view');
743+
$app->shouldReceive('getNamespace')->never()->andReturn('App\\');
744+
$factory->shouldReceive('exists')->never();
745+
746+
Container::setInstance($container);
747+
748+
$attributes = new ComponentAttributeBag(['userId' => 'bar', 'other' => 'ok']);
749+
750+
$containerComponent = m::mock(Component::class);
751+
$containerComponent->shouldReceive('withName')->with('container')->once();
752+
$containerComponent->shouldReceive('shouldRender')->once()->andReturn(true);
753+
$containerComponent->shouldReceive('resolveView')->once()->andReturn('');
754+
$containerComponent->shouldReceive('data')->once()->andReturn([]);
755+
$containerComponent->shouldReceive('withAttributes')->once();
756+
757+
$profileComponent = m::mock(Component::class);
758+
$profileComponent->shouldReceive('withName')->with('profile')->once();
759+
$profileComponent->shouldReceive('shouldRender')->once()->andReturn(true);
760+
$profileComponent->shouldReceive('resolveView')->once()->andReturn('');
761+
$profileComponent->shouldReceive('data')->once()->andReturn([]);
762+
$profileComponent->shouldReceive('withAttributes')->with(['attributes' => new ComponentAttributeBag(['other' => 'ok'])])->once();
763+
764+
Component::resolveComponentsUsing(fn ($component) => match ($component) {
765+
TestContainerComponent::class => $containerComponent,
766+
TestProfileComponent::class => $profileComponent,
767+
});
768+
769+
$__env = m::mock(\Illuminate\View\Factory::class);
770+
$__env->shouldReceive('startComponent')->twice();
771+
$__env->shouldReceive('renderComponent')->twice();
772+
773+
$template = $this->compiler([
774+
'container' => TestContainerComponent::class,
775+
'profile' => TestProfileComponent::class,
776+
])->compileTags('<x-container><x-profile {{ $attributes }} /></x-container>');
777+
$template = $this->compiler->compileString($template);
778+
779+
ob_start();
780+
eval(" ?> $template <?php ");
781+
ob_get_clean();
782+
783+
$this->assertSame($attributes->get('userId'), 'bar');
734784
$this->assertSame($attributes->get('other'), 'ok');
735785
}
736786

@@ -797,3 +847,11 @@ public function render()
797847
return 'input';
798848
}
799849
}
850+
851+
class TestContainerComponent extends Component
852+
{
853+
public function render()
854+
{
855+
return 'container';
856+
}
857+
}

tests/View/Blade/BladeComponentsTest.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public function testComponentsAreCompiled()
1717
public function testClassComponentsAreCompiled()
1818
{
1919
$this->assertSame('<?php if (isset($component)) { $__componentOriginal2dda3d2f2f9b76bd400bf03f0b84e87f = $component; } ?>
20+
<?php if (isset($attributes)) { $__attributesOriginal2dda3d2f2f9b76bd400bf03f0b84e87f = $attributes; } ?>
2021
<?php $component = Illuminate\Tests\View\Blade\ComponentStub::class::resolve(["foo" => "bar"] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>
2122
<?php $component->withName(\'test\'); ?>
2223
<?php if ($component->shouldRender()): ?>
@@ -36,6 +37,10 @@ public function testEndComponentClassesAreCompiled()
3637

3738
$this->assertSame('<?php echo $__env->renderComponent(); ?>
3839
<?php endif; ?>
40+
<?php if (isset($__attributesOriginal79aef92e83454121ab6e5f64077e7d8a)): ?>
41+
<?php $attributes = $__attributesOriginal79aef92e83454121ab6e5f64077e7d8a; ?>
42+
<?php unset($__attributesOriginal79aef92e83454121ab6e5f64077e7d8a); ?>
43+
<?php endif; ?>
3944
<?php if (isset($__componentOriginal79aef92e83454121ab6e5f64077e7d8a)): ?>
4045
<?php $component = $__componentOriginal79aef92e83454121ab6e5f64077e7d8a; ?>
4146
<?php unset($__componentOriginal79aef92e83454121ab6e5f64077e7d8a); ?>

0 commit comments

Comments
 (0)