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