Skip to content

Commit

Permalink
[10.x] Fix loss of attributes after calling child component (#49216)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
rojtjo committed Dec 1, 2023
1 parent a91c3f4 commit 37cb345
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 2 deletions.
5 changes: 5 additions & 0 deletions src/Illuminate/View/Compilers/Concerns/CompilesComponents.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ public static function compileClassComponentOpening(string $component, string $a
{
return implode("\n", [
'<?php if (isset($component)) { $__componentOriginal'.$hash.' = $component; } ?>',
'<?php if (isset($attributes)) { $__attributesOriginal'.$hash.' = $attributes; } ?>',
'<?php $component = '.$component.'::resolve('.($data ?: '[]').' + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>',
'<?php $component->withName('.$alias.'); ?>',
'<?php if ($component->shouldRender()): ?>',
Expand Down Expand Up @@ -94,6 +95,10 @@ public function compileEndComponentClass()
$hash = array_pop(static::$componentHashStack);

return $this->compileEndComponent()."\n".implode("\n", [
'<?php endif; ?>',
'<?php if (isset($__attributesOriginal'.$hash.')): ?>',
'<?php $attributes = $__attributesOriginal'.$hash.'; ?>',
'<?php unset($__attributesOriginal'.$hash.'); ?>',
'<?php endif; ?>',
'<?php if (isset($__componentOriginal'.$hash.')): ?>',
'<?php $component = $__componentOriginal'.$hash.'; ?>',
Expand Down
62 changes: 60 additions & 2 deletions tests/View/Blade/BladeComponentTagCompilerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -715,7 +715,7 @@ public function testAttributesTreatedAsPropsAreRemovedFromFinalAttributes()
$component->shouldReceive('shouldRender')->once()->andReturn(true);
$component->shouldReceive('resolveView')->once()->andReturn('');
$component->shouldReceive('data')->once()->andReturn([]);
$component->shouldReceive('withAttributes')->once();
$component->shouldReceive('withAttributes')->with(['attributes' => new ComponentAttributeBag(['other' => 'ok'])])->once();

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

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

$this->assertNull($attributes->get('userId'));
$this->assertSame($attributes->get('userId'), 'bar');
$this->assertSame($attributes->get('other'), 'ok');
}

public function testOriginalAttributesAreRestoredAfterRenderingChildComponentWithProps()
{
$container = new Container;
$container->instance(Application::class, $app = m::mock(Application::class));
$container->instance(Factory::class, $factory = m::mock(Factory::class));
$container->alias(Factory::class, 'view');
$app->shouldReceive('getNamespace')->never()->andReturn('App\\');
$factory->shouldReceive('exists')->never();

Container::setInstance($container);

$attributes = new ComponentAttributeBag(['userId' => 'bar', 'other' => 'ok']);

$containerComponent = m::mock(Component::class);
$containerComponent->shouldReceive('withName')->with('container')->once();
$containerComponent->shouldReceive('shouldRender')->once()->andReturn(true);
$containerComponent->shouldReceive('resolveView')->once()->andReturn('');
$containerComponent->shouldReceive('data')->once()->andReturn([]);
$containerComponent->shouldReceive('withAttributes')->once();

$profileComponent = m::mock(Component::class);
$profileComponent->shouldReceive('withName')->with('profile')->once();
$profileComponent->shouldReceive('shouldRender')->once()->andReturn(true);
$profileComponent->shouldReceive('resolveView')->once()->andReturn('');
$profileComponent->shouldReceive('data')->once()->andReturn([]);
$profileComponent->shouldReceive('withAttributes')->with(['attributes' => new ComponentAttributeBag(['other' => 'ok'])])->once();

Component::resolveComponentsUsing(fn ($component) => match ($component) {
TestContainerComponent::class => $containerComponent,
TestProfileComponent::class => $profileComponent,
});

$__env = m::mock(\Illuminate\View\Factory::class);
$__env->shouldReceive('startComponent')->twice();
$__env->shouldReceive('renderComponent')->twice();

$template = $this->compiler([
'container' => TestContainerComponent::class,
'profile' => TestProfileComponent::class,
])->compileTags('<x-container><x-profile {{ $attributes }} /></x-container>');
$template = $this->compiler->compileString($template);

ob_start();
eval(" ?> $template <?php ");
ob_get_clean();

$this->assertSame($attributes->get('userId'), 'bar');
$this->assertSame($attributes->get('other'), 'ok');
}

Expand Down Expand Up @@ -797,3 +847,11 @@ public function render()
return 'input';
}
}

class TestContainerComponent extends Component
{
public function render()
{
return 'container';
}
}
5 changes: 5 additions & 0 deletions tests/View/Blade/BladeComponentsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public function testComponentsAreCompiled()
public function testClassComponentsAreCompiled()
{
$this->assertSame('<?php if (isset($component)) { $__componentOriginal2dda3d2f2f9b76bd400bf03f0b84e87f = $component; } ?>
<?php if (isset($attributes)) { $__attributesOriginal2dda3d2f2f9b76bd400bf03f0b84e87f = $attributes; } ?>
<?php $component = Illuminate\Tests\View\Blade\ComponentStub::class::resolve(["foo" => "bar"] + (isset($attributes) && $attributes instanceof Illuminate\View\ComponentAttributeBag ? (array) $attributes->getIterator() : [])); ?>
<?php $component->withName(\'test\'); ?>
<?php if ($component->shouldRender()): ?>
Expand All @@ -36,6 +37,10 @@ public function testEndComponentClassesAreCompiled()

$this->assertSame('<?php echo $__env->renderComponent(); ?>
<?php endif; ?>
<?php if (isset($__attributesOriginal79aef92e83454121ab6e5f64077e7d8a)): ?>
<?php $attributes = $__attributesOriginal79aef92e83454121ab6e5f64077e7d8a; ?>
<?php unset($__attributesOriginal79aef92e83454121ab6e5f64077e7d8a); ?>
<?php endif; ?>
<?php if (isset($__componentOriginal79aef92e83454121ab6e5f64077e7d8a)): ?>
<?php $component = $__componentOriginal79aef92e83454121ab6e5f64077e7d8a; ?>
<?php unset($__componentOriginal79aef92e83454121ab6e5f64077e7d8a); ?>
Expand Down

0 comments on commit 37cb345

Please sign in to comment.