feat: allow liquid filters in for control flow statement
Some checks failed
tests / ci (push) Has been cancelled

This commit is contained in:
Benjamin Nussbaum 2025-08-28 19:20:55 +02:00
parent f777e850b1
commit bcbb1be1da
2 changed files with 111 additions and 0 deletions

View file

@ -230,6 +230,22 @@ class Plugin extends Model
$template $template
); );
// Convert for loops with filters to use temporary variables
// This handles: {% for item in collection | filter: "key", "value" %}
// Converts to: {% assign temp_filtered = collection | filter: "key", "value" %}{% for item in temp_filtered %}
$template = preg_replace_callback(
'/{%\s*for\s+(\w+)\s+in\s+([^|]+)\s*\|\s*([^}]+)%}/',
function ($matches) {
$variableName = trim($matches[1]);
$collection = trim($matches[2]);
$filter = trim($matches[3]);
$tempVarName = '_temp_' . uniqid();
return "{% assign {$tempVarName} = {$collection} | {$filter} %}{% for {$variableName} in {$tempVarName} %}";
},
$template
);
return $template; return $template;
} }

View file

@ -0,0 +1,95 @@
<?php
declare(strict_types=1);
use App\Models\Plugin;
/**
* Tests for the Liquid where filter functionality
*
* The preprocessing in Plugin::applyLiquidReplacements() converts:
* {% for item in collection | filter: "key", "value" %}
* to:
* {% assign _temp_xxx = collection | filter: "key", "value" %}{% for item in _temp_xxx %}
*/
test('where filter works when assigned to variable first', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
{% liquid
assign json_string = '[{"t":"2025-08-26 01:48","v":"4.624","type":"H"},{"t":"2025-08-26 08:04","v":"0.333","type":"L"}]'
assign collection = json_string | parse_json
%}
{% assign tides_h = collection | where: "type", "H" %}
{% for tide in tides_h %}
{{ tide | json }}
{%- endfor %}
LIQUID
,
]);
$result = $plugin->render('full');
// Should output the high tide data
$this->assertStringContainsString('"t":"2025-08-26 01:48"', $result);
$this->assertStringContainsString('"v":"4.624"', $result);
$this->assertStringContainsString('"type":"H"', $result);
// Should not contain the low tide data
$this->assertStringNotContainsString('"type":"L"', $result);
});
test('where filter works directly in for loop with preprocessing', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
{% liquid
assign json_string = '[{"t":"2025-08-26 01:48","v":"4.624","type":"H"},{"t":"2025-08-26 08:04","v":"0.333","type":"L"}]'
assign collection = json_string | parse_json
%}
{% for tide in collection | where: "type", "H" %}
{{ tide | json }}
{%- endfor %}
LIQUID
,
]);
$result = $plugin->render('full');
// Should output the high tide data
$this->assertStringContainsString('"t":"2025-08-26 01:48"', $result);
$this->assertStringContainsString('"v":"4.624"', $result);
$this->assertStringContainsString('"type":"H"', $result);
// Should not contain the low tide data
$this->assertStringNotContainsString('"type":"L"', $result);
});
test('where filter works directly in for loop with multiple matches', function () {
$plugin = Plugin::factory()->create([
'markup_language' => 'liquid',
'render_markup' => <<<'LIQUID'
{% liquid
assign json_string = '[{"t":"2025-08-26 01:48","v":"4.624","type":"H"},{"t":"2025-08-26 08:04","v":"0.333","type":"L"},{"t":"2025-08-26 14:30","v":"4.8","type":"H"}]'
assign collection = json_string | parse_json
%}
{% for tide in collection | where: "type", "H" %}
{{ tide | json }}
{%- endfor %}
LIQUID
,
]);
$result = $plugin->render('full');
// Should output both high tide data entries
$this->assertStringContainsString('"t":"2025-08-26 01:48"', $result);
$this->assertStringContainsString('"t":"2025-08-26 14:30"', $result);
$this->assertStringContainsString('"v":"4.624"', $result);
$this->assertStringContainsString('"v":"4.8"', $result);
// Should not contain the low tide data
$this->assertStringNotContainsString('"type":"L"', $result);
});