Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[5.x] Dictionary tag #10885

Merged
merged 4 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Dictionaries/BasicDictionary.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,17 @@ public function get(string $key): ?Item
}

public function options(?string $search = null): array
{
return collect($this->optionItems($search))
->mapWithKeys(fn (Item $item) => [$item->value() => $item->label()])
->all();
}

public function optionItems(?string $search = null): array
{
return $this
->getFilteredItems()
->when($search, fn ($collection) => $collection->filter(fn ($item) => $this->matchesSearchQuery($search, $item)))
->mapWithKeys(fn (Item $item) => [$item->value() => $item->label()])
->all();
}

Expand Down
7 changes: 7 additions & 0 deletions src/Dictionaries/Dictionary.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,11 @@ private function getInferredGqlType($value)

return GraphQL::string();
}

public function optionItems(?string $search = null): array
{
return collect($this->options($search))
->map(fn ($label, $value) => new Item($value, $label, $this->get($value)->extra()))
->all();
}
}
1 change: 1 addition & 0 deletions src/Providers/ExtensionServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ class ExtensionServiceProvider extends ServiceProvider
Tags\Collection\Collection::class,
Tags\Cookie::class,
Tags\Dd::class,
Tags\Dictionary\Dictionary::class,
Tags\Dump::class,
Tags\GetContent::class,
Tags\GetError::class,
Expand Down
62 changes: 62 additions & 0 deletions src/Tags/Dictionary/Dictionary.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Statamic\Tags\Dictionary;

use Statamic\Data\DataCollection;
use Statamic\Exceptions\DictionaryNotFoundException;
use Statamic\Facades\Dictionary as Dictionaries;
use Statamic\Query\ItemQueryBuilder;
use Statamic\Tags\Concerns;
use Statamic\Tags\Tags;

class Dictionary extends Tags
{
use Concerns\GetsQueryResults,
Concerns\OutputsItems,
Concerns\QueriesConditions,
Concerns\QueriesOrderBys,
Concerns\QueriesScopes;

protected $defaultAsKey = 'options';

/**
* {{ dictionary:* }} ... {{ /dictionary:* }}.
*/
public function wildcard($tag)
{
return $this->loop($tag);
}

/**
* {{ dictionary handle="" }} ... {{ /dictionary }}.
*/
public function index()
{
return $this->loop($this->params->pull('handle'));
}

private function loop($handle)
{
if (! $handle) {
return [];
}

$search = $this->params->pull('search');

if (! $dictionary = Dictionaries::find($handle, $this->params->all())) {
throw new DictionaryNotFoundException($handle);
}

$options = (new DataCollection($dictionary->optionItems($search)))
->map(fn ($item) => new DictionaryItem($item->toArray()))
->values();

$query = (new ItemQueryBuilder)->withItems($options);

$this->queryConditions($query);
$this->queryOrderBys($query);
$this->queryScopes($query);

return $this->output($this->results($query));
}
}
26 changes: 26 additions & 0 deletions src/Tags/Dictionary/DictionaryItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Statamic\Tags\Dictionary;

use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Support\Arr;
use Statamic\Data\ContainsSupplementalData;

class DictionaryItem implements Arrayable
{
use ContainsSupplementalData;

public function __construct(public array $data)
{
}

public function get($key, $default = null)
{
return Arr::get($this->toArray(), $key, $default);
}

public function toArray()
{
return array_merge($this->data, $this->supplements ?? []);
}
}
97 changes: 97 additions & 0 deletions tests/Tags/DictionaryTagTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace Tests\Tags;

use PHPUnit\Framework\Attributes\Test;
use Statamic\Facades\Parse;
use Statamic\Query\Scopes\Scope;
use Tests\PreventSavingStacheItemsToDisk;
use Tests\TestCase;

class DictionaryTagTest extends TestCase
{
use PreventSavingStacheItemsToDisk;

#[Test]
public function it_gets_countries()
{
$template = '{{ dictionary:countries limit="1" }}{{ value }}{{ /dictionary:countries }}';

$this->assertEquals('AFG', $this->tag($template));
}

#[Test]
public function it_gets_dictionary_by_handle()
{
$template = '{{ dictionary handle="countries" limit="1" }}{{ value }}{{ /dictionary }}';

$this->assertEquals('AFG', $this->tag($template));
}

#[Test]
public function it_gets_timezones()
{
$template = '{{ dictionary:timezones limit="1" }}{{ value }}{{ /dictionary:timezones }}';

$this->assertEquals('Africa/Abidjan', $this->tag($template));
}

#[Test]
public function it_can_search()
{
$template = '{{ dictionary:countries search="Alg" }}{{ value }}{{ /dictionary:countries }}';

$this->assertEquals('DZA', $this->tag($template));
}

#[Test]
public function it_pulls_extra_data_data()
{
$template = '{{ dictionary:countries search="Alg" }}{{ region }} - {{ iso2 }}{{ /dictionary:countries }}';

$this->assertEquals('Africa - DZ', $this->tag($template));
}

#[Test]
public function it_can_paginate()
{
$template = '{{ dictionary:countries paginate="4" }}{{ options | count }}{{ /dictionary:countries }}';

$this->assertEquals('4', $this->tag($template));

$template = '{{ dictionary:countries paginate="4" as="countries" }}{{ countries | count }}{{ /dictionary:countries }}';

$this->assertEquals('4', $this->tag($template));
}

#[Test]
public function it_can_be_filtered_using_conditions()
{
$template = '{{ dictionary:countries iso3:is="AUS" }}{{ name }}{{ /dictionary:countries }}';

$this->assertEquals('Australia', $this->tag($template));
}

#[Test]
public function it_can_be_filtered_using_a_query_scope()
{
app('statamic.scopes')[TestScope::handle()] = TestScope::class;

$template = '{{ dictionary:countries query_scope="test_scope" }}{{ name }}{{ /dictionary:countries }}';

$this->assertEquals('Australia', $this->tag($template));
}

private function tag($tag, $data = [])
{
return (string) Parse::template($tag, $data);
}
}

class TestScope extends Scope
{
public function apply($query, $params)
{
$query->where('iso3', 'AUS');
}
}
Loading