diff --git a/app/Liquid/Filters/Data.php b/app/Liquid/Filters/Data.php index 5b1f92f..61343b2 100644 --- a/app/Liquid/Filters/Data.php +++ b/app/Liquid/Filters/Data.php @@ -19,4 +19,24 @@ class Data extends FiltersProvider { return json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } + + /** + * Find an object in a collection by a specific key-value pair + * + * @param array $collection The collection to search in + * @param string $key The key to search for + * @param mixed $value The value to match + * @param mixed $fallback Optional fallback value if no match is found + * @return mixed The matching object or fallback value + */ + public function find_by(array $collection, string $key, mixed $value, mixed $fallback = null): mixed + { + foreach ($collection as $item) { + if (is_array($item) && isset($item[$key]) && $item[$key] === $value) { + return $item; + } + } + + return $fallback; + } } diff --git a/tests/Feature/PluginInlineTemplatesTest.php b/tests/Feature/PluginInlineTemplatesTest.php index ef2b96d..7451d93 100644 --- a/tests/Feature/PluginInlineTemplatesTest.php +++ b/tests/Feature/PluginInlineTemplatesTest.php @@ -172,4 +172,60 @@ LIQUID $this->assertStringContainsString('This is a test', $result); $this->assertStringContainsString('class="simple"', $result); } + + public function test_plugin_with_find_by_filter(): void + { + $plugin = Plugin::factory()->create([ + 'markup_language' => 'liquid', + 'render_markup' => <<<'LIQUID' +{% template user_info %} +
+

{{ user.name }}

+

Age: {{ user.age }}

+
+{% endtemplate %} + +{% assign found_user = collection | find_by: 'name', 'Ryan' %} +{% render "user_info", user: found_user %} +LIQUID + , + 'data_payload' => [ + 'collection' => [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ], + ], + ]); + + $result = $plugin->render('full'); + + // Should render the user info for Ryan + $this->assertStringContainsString('Ryan', $result); + $this->assertStringContainsString('Age: 35', $result); + $this->assertStringContainsString('class="user"', $result); + } + + public function test_plugin_with_find_by_filter_and_fallback(): void + { + $plugin = Plugin::factory()->create([ + 'markup_language' => 'liquid', + 'render_markup' => <<<'LIQUID' +{{ collection | find_by: 'name', 'ronak', 'Not Found' }} +LIQUID + , + 'data_payload' => [ + 'collection' => [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ], + ], + ]); + + $result = $plugin->render('full'); + + // Should return the fallback value + $this->assertStringContainsString('Not Found', $result); + } } diff --git a/tests/Unit/Liquid/Filters/DataTest.php b/tests/Unit/Liquid/Filters/DataTest.php index ffb4088..131dc24 100644 --- a/tests/Unit/Liquid/Filters/DataTest.php +++ b/tests/Unit/Liquid/Filters/DataTest.php @@ -53,3 +53,83 @@ test('json filter does not escape slashes', function () { expect($filter->json($data))->toBe('{"url":"https://example.com/path"}'); }); + +test('find_by filter finds object by key-value pair', function () { + $filter = new Data(); + $collection = [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ]; + + $result = $filter->find_by($collection, 'name', 'Ryan'); + expect($result)->toBe(['name' => 'Ryan', 'age' => 35]); +}); + +test('find_by filter returns null when no match found', function () { + $filter = new Data(); + $collection = [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ]; + + $result = $filter->find_by($collection, 'name', 'ronak'); + expect($result)->toBeNull(); +}); + +test('find_by filter returns fallback when no match found', function () { + $filter = new Data(); + $collection = [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ]; + + $result = $filter->find_by($collection, 'name', 'ronak', 'Not Found'); + expect($result)->toBe('Not Found'); +}); + +test('find_by filter finds by age', function () { + $filter = new Data(); + $collection = [ + ['name' => 'Ryan', 'age' => 35], + ['name' => 'Sara', 'age' => 29], + ['name' => 'Jimbob', 'age' => 29], + ]; + + $result = $filter->find_by($collection, 'age', 29); + expect($result)->toBe(['name' => 'Sara', 'age' => 29]); +}); + +test('find_by filter handles empty collection', function () { + $filter = new Data(); + $collection = []; + + $result = $filter->find_by($collection, 'name', 'Ryan'); + expect($result)->toBeNull(); +}); + +test('find_by filter handles collection with non-array items', function () { + $filter = new Data(); + $collection = [ + 'not an array', + ['name' => 'Ryan', 'age' => 35], + null, + ]; + + $result = $filter->find_by($collection, 'name', 'Ryan'); + expect($result)->toBe(['name' => 'Ryan', 'age' => 35]); +}); + +test('find_by filter handles items without the specified key', function () { + $filter = new Data(); + $collection = [ + ['age' => 35], + ['name' => 'Ryan', 'age' => 35], + ['title' => 'Developer'], + ]; + + $result = $filter->find_by($collection, 'name', 'Ryan'); + expect($result)->toBe(['name' => 'Ryan', 'age' => 35]); +}); diff --git a/tests/Unit/Liquid/InlineTemplatesTest.php b/tests/Unit/Liquid/InlineTemplatesTest.php index 4535740..bf68ddf 100644 --- a/tests/Unit/Liquid/InlineTemplatesTest.php +++ b/tests/Unit/Liquid/InlineTemplatesTest.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Tests\Unit\Liquid; use App\Liquid\FileSystems\InlineTemplatesFileSystem; +use App\Liquid\Filters\Data; use App\Liquid\Tags\TemplateTag; use Keepsuit\Liquid\Environment; use Keepsuit\Liquid\Exceptions\LiquidException; @@ -27,6 +28,7 @@ class InlineTemplatesTest extends TestCase ); $this->environment->tagRegistry->register(TemplateTag::class); $this->environment->tagRegistry->register(RenderTag::class); + $this->environment->filterRegistry->register(Data::class); } public function test_template_tag_registers_template(): void