From bb1c8f1baef3d86d252e7d89a3f94f136f28ef87 Mon Sep 17 00:00:00 2001 From: Duncan McClean Date: Wed, 10 Jul 2024 11:21:49 +0100 Subject: [PATCH 1/4] Document dictionaries fieldtype --- content/collections/fieldtypes/dictionary.md | 194 ++++++++++++++++++ public/img/fieldtypes/icons/dictionary.svg | 1 + .../img/fieldtypes/screenshots/dictionary.png | Bin 0 -> 59673 bytes 3 files changed, 195 insertions(+) create mode 100644 content/collections/fieldtypes/dictionary.md create mode 100644 public/img/fieldtypes/icons/dictionary.svg create mode 100644 public/img/fieldtypes/screenshots/dictionary.png diff --git a/content/collections/fieldtypes/dictionary.md b/content/collections/fieldtypes/dictionary.md new file mode 100644 index 000000000..a9b8a4589 --- /dev/null +++ b/content/collections/fieldtypes/dictionary.md @@ -0,0 +1,194 @@ +--- +title: Dictionary +description: Choose from options provided by dictionaries. +intro: Give your users a list of options to choose from. Similar to the Select field, but allows you to read options from YAML or JSON files, or even hit external APIs. +screenshot: fieldtypes/screenshots/dictionary.png +options: + - + name: dictionary + type: array + description: | + Configure the dictionary to be used. You may also define any config values which should be passed along to the dictionary. The `dictionary` option accepts both string & array values: + + ```yaml + # When it's a dictionary without any config fields... + dictionary: countries + + # When it's a dictionary with config fields... + dictionary: + type: countries + region: Europe + ``` + + - + name: clearable + type: boolean + description: | + Allow deselecting any chosen option and making null a possible value. Default: `false`. + - + name: placeholder + type: string + description: | + Set the non-selectable placeholder text. Default: none. + - + name: default + type: string + description: | + Set the default option key. Default: none. + - + name: multiple + type: boolean + description: > + Allow multiple selections. Default: `false`. + - + name: searchable + type: boolean + description: > + Enable search with suggestions by typing in the select box. Default: `true`. +id: 9b14b5b8-6a7a-4db2-8533-9c78faa0e054 +--- +## Overview +At a glance, the Dictionary fieldtype is similar to the [Select fieldtype](/fieldtypes/select). However, with the Dictionary fieldtype, options aren't manually defined in a field's config, but rather returned from a PHP class (called a "dictionary"). + +This can prove to be pretty powerful, since it means you can read options from YAML or JSON files, or even hit an external API. It also makes it easier to share common select options between projects. + +## Built-in dictionaries +Statamic includes a few helpful dictionaries right out of the box: + +* Countries +* Currencies +* Timezones + +## Build your own dictionary +It's really easy to build your own dictionary, it only takes a few minutes: + +1. Generate a dictionary class using `php please`: + ```sh + php please make:dictionary Provinces + ``` + If you want to generate a dictionary for an addon, you should use the `--addon` parameter (`--addon=statamic/seo-pro`). + +2. In your `app/Dictionaries` directory (or `src/Dictionaries` in the case of an addon), you'll see a new `Provinces` dictionary has been generated: + + ```php + class Provinces extends Dictionary + + /** + * Returns a key/value array of options. + * + * @param string|null $search + * @return array + */ + public function options(?string $search = null): array + { + return $this->getItems() + ->when($search ?? false, function ($collection) use ($search) { + return $collection->filter(fn ($item) => str_contains($item['name'], $search)); + }) + ->mapWithKeys(fn ($item) => [$item['slug'] => $item['name']]) + ->all(); + } + + /** + * Returns a single option. + * + * @param string $key + * @return string|array + */ + public function get(string $key): string|array + { + return $this->getItems()->firstWhere('slug', $key); + } + + private function getItems(): Collection + { + return collect([ + ['name' => 'January', 'slug' => 'january'], + ['name' => 'February', 'slug' => 'february'], + ['name' => 'March', 'slug' => 'march'], + // ... + ]); + } + } + ``` + + * The `options` method should return a key/value array of all options. + * The `$search` variable will be provided if the user is searching options. Feel free to search the options in whatever way works for you. + * The `get` method should return a single option. + * This will be made available when the dictionary field's options are augmented. You're free to return whatever you need here. + * Optionally, you can also configure "config fields" for the dictionary which will be available in the dictionary's context: + + ```php + protected function fieldItems() + { + return [ + 'region' => [ + 'display' => __('Region'), + 'instructions' => __('statamic::messages.dictionaries_countries_region_instructions'), + 'type' => 'select', + 'options' => $this->getCountries()->unique('region')->pluck('region', 'region')->filter()->all(), + ], + ]; + } + + public function get(string $key): string|array + { + $region = $this->context['region']; + + // ... + } + ``` + +## Data Storage +Dictionary fields will store the "key" of the chosen option or options. + +For example, if a dictionary is returning these options: + +```php +public function options(?string $search = null): array +{ + return [ + 'jan' => 'January', + 'feb' => 'February', + 'mar' => 'March', + ]; +} +``` + +Your saved data will be: + +``` yaml +select: jan +``` + +## Templating +Dictionary fields will return the "option data" returned by the dictionary's `get` method. The shape of this data differs between dictionaries, but often it'll be an array of data. You can use the [`{{ dump }}` tag](/tags/dump) to determine which variables a dictionary provides. + +For example, using the built-in Countries dictionary, your template might look like this: + +```yaml +past_vacations: + - USA + - AUS + - CAN + - DEU + - GBR +``` + +``` + +``` + +```html + +``` diff --git a/public/img/fieldtypes/icons/dictionary.svg b/public/img/fieldtypes/icons/dictionary.svg new file mode 100644 index 000000000..414738237 --- /dev/null +++ b/public/img/fieldtypes/icons/dictionary.svg @@ -0,0 +1 @@ + diff --git a/public/img/fieldtypes/screenshots/dictionary.png b/public/img/fieldtypes/screenshots/dictionary.png new file mode 100644 index 0000000000000000000000000000000000000000..7ce65df73363ee3438b8e641d9d8f317c131a31e GIT binary patch literal 59673 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 DU)?zs literal 0 HcmV?d00001 From ce49ccd5e469be9c9ae8822b59a2dfb502e72119 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 25 Jul 2024 17:37:25 -0400 Subject: [PATCH 2/4] update fieldtype page and move custom dictionaries to its own page. --- .../extending-docs/dictionaries.md | 202 +++++++++++++++ content/collections/fieldtypes/dictionary.md | 241 ++++++++++-------- content/trees/navigation/extending_docs.yaml | 3 + 3 files changed, 335 insertions(+), 111 deletions(-) create mode 100644 content/collections/extending-docs/dictionaries.md diff --git a/content/collections/extending-docs/dictionaries.md b/content/collections/extending-docs/dictionaries.md new file mode 100644 index 000000000..4909fe10f --- /dev/null +++ b/content/collections/extending-docs/dictionaries.md @@ -0,0 +1,202 @@ +--- +id: d0668b6e-915b-46da-863e-51fec54b02e2 +blueprint: page +title: Dictionaries +template: page +intro: 'Dictionaries add options to the [Dictionary](/fieldtypes/dictionary) fieldtype.' +--- +## Overview + +Dictionaries come in two "flavors" depending on which class you extend from. + +With a `BasicDictionary`, you only really need to define the items. The options, searching, and GraphQL behavior is all handled automatically. + +If you need more control, you can either override methods, or extend the base `Dictionary` class and do it yourself. + +:::tip +You might not even need a custom dictionary. The native [File dictionary](/fieldtypes/dictionary#file) allows you simply provide a JSON or YAML file to use a source of options. +::: + + +## Basic Dictionaries + +You may create a dictionary using the following command, which will generate a class in the `App\Dictionaries` namespace. + +```shell +php please make:dictionary +``` + +You may generate it into an addon using the `--addon=vendor/package` option, which will generate it into your addon's `Dictionaries` namespace. + +```php + 'Alabama', 'value' => 'AL', 'capital' => 'Montgomery'], + ['label' => 'Alaska', 'value' => 'AK', 'capital' => 'Juneau'], + ['label' => 'Arizona', 'value' => 'AZ', 'capital' => 'Phoenix'], + // ... + ]; + } +} +``` + +### Item data + +In the example above, you can see that each item has a `label` and `value`. These will be used in the dropdown field. Any additional keys will be available within templates. + +Here we are returning a hardcoded array. But in reality you may be getting options from somewhere like a file, database, or an API: + +```php +protected function getItems(): array +{ + return Product::all()->toArray(); +} +``` + + +### Values and Labels + +By default, the `value` and `label` keys will be used. However, you may remap them: + +```php +protected function getItems(): array +{ + protected string $valueKey = 'abbr'; + protected string $labelKey = 'name'; + + return [ + ['name' => 'Alabama', 'abbr' => 'AL', 'capital' => 'Montgomery'], + // ... + ]; +} +``` + + +If you require more logic, you can override the `getItemValue` and/or `getItemLabel` methods: + +```php +protected function getItemLabel(array $item): string +{ + return $item['name'] . ' (' . $item['label'] . ')'; // "Alabama (AL)" +} +``` + +### Basic Search + +By default, when a user searches the field, a basic search will be performed by checking against each item's values. + +You may use the `searchable` property to narrow down which fields should be searched. + +```php +protected array $searchable = ['name', 'abbr']; +``` + +Alternatively, you may customize how the match is performed by overriding the `matchesSearchQuery` method: + +```php +protected function matchesSearchQuery(string $query, Item $item): bool +{ + return str_contains($item['name'], $query); +} +``` + +## Options + +The `options` method controls what is selectable within the fieldtype. This method should return an array of value/label pairs. + +```php +public function options(?string $search = null): array +{ + return [ + 'one' => 'Option One', + 'two' => 'Option Two', + ]; +} +``` + +This array's keys define what will be stored in the content. + +### Search + +The `options` method will be passed a `$search` string if the user is searching within the fieldtype. You should filter your options based on this search term. + +## Items + +The `get` method accepts a value (one of the option's keys) and should return an `Item` instance. + +An `Item` requires the value, label, and optionally an array of any additional data. + +In the following example we assume a product ID was saved to the content, the product name is the label, and price/sku is extra. + +```php +public function get(string $key): ?Item +{ + $product = Product::find($key); + + return new Item($key, $product->name, [ + 'price' => $product->price, + 'sku' => $product->sku, + ]); +} +``` + +## Config + +You may define config fields in order for the user to customize functionality of your dictionary. For example, if you are providing products, you may want to allow the user to select a category to narrow down the options. + +```php +protected function fieldItems() +{ + return [ + 'category' => [ + 'type' => 'select', + 'options' => ['clothing', 'accessories'] + ] + ]; +} +``` + +The user's configuration values will be available in your class within the `config` property. + +```php +$this->config['category']; +``` + +## GraphQL + +A dictionary will automatically get a GraphQL type named `Dictionary_YourClass`. Within it, you're able to query the item's fields, like so: + +```graphql +your_dictionary_field { + id + price +} +``` + +By default, the base `Dictionary` class will provide the GraphQL schema for nested fields automatically. It does this by looking up the first item. You may wish to override this and provide your own schema. + +```php +protected function getGqlFields(): array +{ + return [ + 'id' => [ + 'type' => GraphQL::nonNull(GraphQL::string()), + 'resolve' => fn (Item $item, $args, $context, $info) => $item['id']; + ], + 'price' => [ + 'type' => GraphQL::nonNull(GraphQL::int()), + 'resolve' => fn (Item $item, $args, $context, $info) => $item['price']; + ], + // ... + ]; +} +``` diff --git a/content/collections/fieldtypes/dictionary.md b/content/collections/fieldtypes/dictionary.md index a9b8a4589..5b35dc39c 100644 --- a/content/collections/fieldtypes/dictionary.md +++ b/content/collections/fieldtypes/dictionary.md @@ -19,12 +19,6 @@ options: type: countries region: Europe ``` - - - - name: clearable - type: boolean - description: | - Allow deselecting any chosen option and making null a possible value. Default: `false`. - name: placeholder type: string @@ -36,15 +30,10 @@ options: description: | Set the default option key. Default: none. - - name: multiple - type: boolean + name: max_items + type: integer description: > - Allow multiple selections. Default: `false`. - - - name: searchable - type: boolean - description: > - Enable search with suggestions by typing in the select box. Default: `true`. + Cap the number of selections. Setting this to 1 will change the UI. Default: null (unlimited). id: 9b14b5b8-6a7a-4db2-8533-9c78faa0e054 --- ## Overview @@ -52,107 +41,15 @@ At a glance, the Dictionary fieldtype is similar to the [Select fieldtype](/fiel This can prove to be pretty powerful, since it means you can read options from YAML or JSON files, or even hit an external API. It also makes it easier to share common select options between projects. -## Built-in dictionaries -Statamic includes a few helpful dictionaries right out of the box: - -* Countries -* Currencies -* Timezones - -## Build your own dictionary -It's really easy to build your own dictionary, it only takes a few minutes: - -1. Generate a dictionary class using `php please`: - ```sh - php please make:dictionary Provinces - ``` - If you want to generate a dictionary for an addon, you should use the `--addon` parameter (`--addon=statamic/seo-pro`). - -2. In your `app/Dictionaries` directory (or `src/Dictionaries` in the case of an addon), you'll see a new `Provinces` dictionary has been generated: - - ```php - class Provinces extends Dictionary - - /** - * Returns a key/value array of options. - * - * @param string|null $search - * @return array - */ - public function options(?string $search = null): array - { - return $this->getItems() - ->when($search ?? false, function ($collection) use ($search) { - return $collection->filter(fn ($item) => str_contains($item['name'], $search)); - }) - ->mapWithKeys(fn ($item) => [$item['slug'] => $item['name']]) - ->all(); - } - - /** - * Returns a single option. - * - * @param string $key - * @return string|array - */ - public function get(string $key): string|array - { - return $this->getItems()->firstWhere('slug', $key); - } - - private function getItems(): Collection - { - return collect([ - ['name' => 'January', 'slug' => 'january'], - ['name' => 'February', 'slug' => 'february'], - ['name' => 'March', 'slug' => 'march'], - // ... - ]); - } - } - ``` - - * The `options` method should return a key/value array of all options. - * The `$search` variable will be provided if the user is searching options. Feel free to search the options in whatever way works for you. - * The `get` method should return a single option. - * This will be made available when the dictionary field's options are augmented. You're free to return whatever you need here. - * Optionally, you can also configure "config fields" for the dictionary which will be available in the dictionary's context: - - ```php - protected function fieldItems() - { - return [ - 'region' => [ - 'display' => __('Region'), - 'instructions' => __('statamic::messages.dictionaries_countries_region_instructions'), - 'type' => 'select', - 'options' => $this->getCountries()->unique('region')->pluck('region', 'region')->filter()->all(), - ], - ]; - } - - public function get(string $key): string|array - { - $region = $this->context['region']; - - // ... - } - ``` - ## Data Storage Dictionary fields will store the "key" of the chosen option or options. -For example, if a dictionary is returning these options: +For example, a dictionary might have items such as: ```php -public function options(?string $search = null): array -{ - return [ - 'jan' => 'January', - 'feb' => 'February', - 'mar' => 'March', - ]; -} +'jan' => 'January', +'feb' => 'February', +'mar' => 'March', ``` Your saved data will be: @@ -162,7 +59,7 @@ select: jan ``` ## Templating -Dictionary fields will return the "option data" returned by the dictionary's `get` method. The shape of this data differs between dictionaries, but often it'll be an array of data. You can use the [`{{ dump }}` tag](/tags/dump) to determine which variables a dictionary provides. +Dictionary fields will return the "option data" returned by the dictionary's `get` method. The shape of this data differs between dictionaries and is outlined below. For example, using the built-in Countries dictionary, your template might look like this: @@ -192,3 +89,125 @@ past_vacations:

  • πŸ‡¬πŸ‡§ United Kingdom
  • ``` + +## Available Dictionaries +Statamic includes a few dictionaries straight out of the box. + +### File +This allows you point to a `json` or `yaml` file located in your `resources/dictionaries` directory to populate the options. + +Each option array should have `label` and `value` keys at the minimum. Any additional keys will be available when templating. + +You may redefine which keys are used for the labels and values by providing them to your fieldtype config. In the following example, `name` is the label and `id` is the value. + +```json +[ + {"name": "Apple", "id": "apple", "emoji": "🍎"}, + {"name": "Banana", "id": "banana", "emoji": "🍌"}, + {"name": "Cherry", "id": "cherry", "emoji": "πŸ’"}, + ... +] +``` + +```yaml +- + handle: fruit + field: + type: dictionary + dictionary: + type: file + filename: fruit.json + label: name # optional, defaults to "label" + value: id # optional, defaults to "value" +``` + +You may provide enhanced labels using basic Antlers syntax. For example, to include the emoji before the fruit name, you can do this: + +```yaml +label: '{{ emoji }} {{ name }}' +``` + +### Countries +This provides a list of countries with their ISO codes, region, subregion, and flag emoji. +```yaml +- + handle: countries + field: + type: dictionary + dictionary: + type: countries + region: 'oceania' # Optionally filter the countries by a region. + # Supported options are: africa, americas, asia, europe, oceania, polar +``` +```yaml +countries: + - USA + - AUS +``` +``` +{{ countries }} + {{ emoji }} {{ name }}, {{ iso2 }}, {{ iso3 }}, {{ region }}, {{ subregion }} +{{ /countries }} +``` +``` +πŸ‡ΊπŸ‡Έ United States, US, USA, Americas, Northern America +πŸ‡¦πŸ‡Ί Australia, AU, AUS, Oceania, Australia and New Zealand +``` + +### Timezones +This provides a list of timezones and their UTC offsets. + +```yaml +- + handle: timezones + field: + type: dictionary + dictionary: + type: timezones +``` +```yaml +timezones: + - America/New_York + - Australia/Sydney +``` +``` +{{ timezones }} + {{ name }} {{ offset }} +{{ /timezones }} +``` +``` +America/New_York -04:00 +Australia/Sydney +10:00 +``` + +### Currencies +This provides a list of currencies, with their codes, symbols, and decimals. + +```yaml +- + handle: currencies + field: + type: dictionary + dictionary: + type: currencies +``` +```yaml +currencies: + - USD + - HUF +``` +``` +{{ currencies }} + {{ name }}, {{ code }}, {{ symbol }}, {{ decimals }} +{{ /currencies }} +``` +``` +US Dollar, USD, $, 2 +Hungarian Forint, HUF, Ft, 0 +``` + +## Custom Dictionaries + +In many cases, using the native [File](#file) dictionary can be all you need for something custom. However, it's possible to create an entirely custom dictionary that could read from files, APIs, or whatever you can think of. + +[Find out how to create a custom dictionary](/extending/dictionaries) diff --git a/content/trees/navigation/extending_docs.yaml b/content/trees/navigation/extending_docs.yaml index fa518d8c8..194856feb 100644 --- a/content/trees/navigation/extending_docs.yaml +++ b/content/trees/navigation/extending_docs.yaml @@ -47,6 +47,9 @@ tree: - id: 7e4f5154-4499-40a4-929a-92fb81f10bb8 entry: aa6e0a79-9d3f-493b-92c9-df4d2257bc64 + - + id: 270cf2ba-ceb6-499d-b1f0-e548a1aed282 + entry: d0668b6e-915b-46da-863e-51fec54b02e2 - id: 044b4ef5-5809-4bde-8988-ca680199926d entry: b4b46ceb-9feb-4587-8f0d-2080511bf9e3 From be66e1c85ccae538b6306da56acafc7790a444e2 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 29 Jul 2024 10:08:19 -0400 Subject: [PATCH 3/4] mention csv --- content/collections/extending-docs/dictionaries.md | 2 +- content/collections/fieldtypes/dictionary.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/content/collections/extending-docs/dictionaries.md b/content/collections/extending-docs/dictionaries.md index 4909fe10f..eb0fc1651 100644 --- a/content/collections/extending-docs/dictionaries.md +++ b/content/collections/extending-docs/dictionaries.md @@ -14,7 +14,7 @@ With a `BasicDictionary`, you only really need to define the items. The options, If you need more control, you can either override methods, or extend the base `Dictionary` class and do it yourself. :::tip -You might not even need a custom dictionary. The native [File dictionary](/fieldtypes/dictionary#file) allows you simply provide a JSON or YAML file to use a source of options. +You might not even need a custom dictionary. The native [File dictionary](/fieldtypes/dictionary#file) allows you simply provide a JSON, YAML, or CSV file to use a source of options. ::: diff --git a/content/collections/fieldtypes/dictionary.md b/content/collections/fieldtypes/dictionary.md index 5b35dc39c..f8d6132c1 100644 --- a/content/collections/fieldtypes/dictionary.md +++ b/content/collections/fieldtypes/dictionary.md @@ -94,7 +94,7 @@ past_vacations: Statamic includes a few dictionaries straight out of the box. ### File -This allows you point to a `json` or `yaml` file located in your `resources/dictionaries` directory to populate the options. +This allows you point to a file located in your `resources/dictionaries` directory to populate the options. The file can be `json`, `yaml`, or `csv`. Each option array should have `label` and `value` keys at the minimum. Any additional keys will be available when templating. From d364d16e97788992811ae7abd532e16a0315e04c Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 29 Jul 2024 11:07:26 -0400 Subject: [PATCH 4/4] Add full example --- .../extending-docs/dictionaries.md | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/content/collections/extending-docs/dictionaries.md b/content/collections/extending-docs/dictionaries.md index eb0fc1651..4948ecbde 100644 --- a/content/collections/extending-docs/dictionaries.md +++ b/content/collections/extending-docs/dictionaries.md @@ -200,3 +200,54 @@ protected function getGqlFields(): array ]; } ``` + +## Full Example + +Here is an example dictionary class that will use the [MusicBrainz](https://musicbrainz.org/) API to create a dictionary of musicians/artists. + +```php +json(); + + return collect($response['artists'])->mapWithKeys(function ($artist) { + $label = $artist['name']; + + if ($disambiguation = $artist['disambiguation'] ?? null) { + $label .= ' ('.$disambiguation.')'; + } + + return [$artist['id'] => $label]; + })->all(); + } + + public function get(string $key): ?Item + { + return Cache::rememberForever('artist-'.$key, function () use ($key) { + $response = Http::get('https://musicbrainz.org/ws/2/artist/'.$key.'?fmt=json')->json(); + + return new Item($key, $response['name'], [ + 'name' => $response['name'], + 'disambiguation' => $response['disambiguation'] ?? null, + 'type' => $response['type'], + 'country' => $response['country'], + ]); + }); + } +} +```