diff --git a/src/Tempest/View/Attributes/AttributeFactory.php b/src/Tempest/View/Attributes/AttributeFactory.php index 46c7224ef..20b56b18c 100644 --- a/src/Tempest/View/Attributes/AttributeFactory.php +++ b/src/Tempest/View/Attributes/AttributeFactory.php @@ -13,6 +13,7 @@ public function make(View $view, string $name, ?string $value): Attribute { return match(true) { $name === ':if' => new IfAttribute(), + $name === ':elseif' => new ElseIfAttribute(), $name === ':else' => new ElseAttribute(), $name === ':foreach' => new ForeachAttribute($view, $value), $name === ':forelse' => new ForelseAttribute(), diff --git a/src/Tempest/View/Attributes/ElseAttribute.php b/src/Tempest/View/Attributes/ElseAttribute.php index c0a421b91..d9a5ba9a0 100644 --- a/src/Tempest/View/Attributes/ElseAttribute.php +++ b/src/Tempest/View/Attributes/ElseAttribute.php @@ -15,17 +15,25 @@ public function apply(Element $element): Element { $previous = $element->getPrevious(); + $previousCondition = false; - if ( - ! $previous instanceof GenericElement - || ! $previous->hasAttribute('if') - ) { - throw new Exception('No valid if condition found in preceding element'); + if (! $previous instanceof GenericElement) { + throw new Exception("Invalid preceding element before :else"); } - $condition = $previous->getAttribute('if'); + // Check all :elseif and :if conditions for previous elements + // If one of the previous element's conditions is true, we'll stop. + // We won't have to render this :else element + while ( + $previousCondition === false + && $previous instanceof GenericElement + && ($previous->hasAttribute('if') || $previous->hasAttribute('elseif')) + ) { + $previousCondition = (bool) ($previous->getAttribute('if') ?? $previous->getAttribute('elseif')); + $previous = $previous->getPrevious(); + } - if ($condition) { + if ($previousCondition) { return new EmptyElement(); } diff --git a/src/Tempest/View/Attributes/ElseIfAttribute.php b/src/Tempest/View/Attributes/ElseIfAttribute.php new file mode 100644 index 000000000..e7dd5eb2f --- /dev/null +++ b/src/Tempest/View/Attributes/ElseIfAttribute.php @@ -0,0 +1,52 @@ +getPrevious(); + $previousCondition = false; + + if (! $previous instanceof GenericElement) { + throw new Exception("Invalid preceding element before :elseif"); + } + + // Check all :elseif and :if conditions for previous elements + // If one of the previous element's conditions is true, we'll stop. + // We won't have to render this :elseif element + while ( + $previousCondition === false + && $previous instanceof GenericElement + && ($previous->hasAttribute('if') || $previous->hasAttribute('elseif')) + ) { + $previousCondition = (bool) ($previous->getAttribute('if') ?? $previous->getAttribute('elseif')); + $previous = $previous->getPrevious(); + } + + $currentCondition = (bool) $element->getAttribute('elseif'); + + // For this element to render, the previous conditions need to be false, + // and the current condition must be true + if ($previousCondition === false && $currentCondition === true) { + return $element; + } + + return new EmptyElement(); + } +} diff --git a/tests/Integration/View/TempestViewRendererTest.php b/tests/Integration/View/TempestViewRendererTest.php index 4b271e53d..1f97e5c59 100644 --- a/tests/Integration/View/TempestViewRendererTest.php +++ b/tests/Integration/View/TempestViewRendererTest.php @@ -49,6 +49,44 @@ public function test_if_attribute(): void ); } + public function test_elseif_attribute(): void + { + $this->assertSame( + '
A
', + $this->render(view('
A
B
None
')->data(a: true, b: true)), + ); + + $this->assertSame( + '
A
', + $this->render(view('
A
B
None
')->data(a: true, b: false)), + ); + + $this->assertSame( + '
B
', + $this->render(view('
A
B
None
')->data(a: false, b: true)), + ); + + $this->assertSame( + '
None
', + $this->render(view('
A
B
None
')->data(a: false, b: false)), + ); + + $this->assertSame( + '
C
', + $this->render(view('
A
B
C
None
')->data(a: false, b: false, c: true)), + ); + + $this->assertSame( + '
B
', + $this->render(view('
A
B
C
None
')->data(a: false, b: true, c: true)), + ); + + $this->assertSame( + '
None
', + $this->render(view('
A
B
C
None
')->data(a: false, b: false, c: false)), + ); + } + public function test_else_attribute(): void { $this->assertSame(