Answers.Body: <p>Because of several reasons it won't work the way you are going to do it. The easiest approach is to return an object or an array instead of single value in your <code>normalize</code> functions that contains the value and the states. Craft does the same with nearly all fields. They values are objects with a <code>_toString</code> function so you don't even know you have an object rather than a string </p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-24T19:35:37.950Z
Answers.Text: Can you give me an example of what that array might look like?
Answers.UserDisplayName: kmgdev
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-24T21:24:11.633Z
Answers.Text: It can look however you like (?) `['foo' => $value, 'bar' => $states]` then you can do `entry.field.foo` to display the value and `entry.fieldHandle.bar.AL` to display `Alabama`
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-24T23:54:16.173Z
Answers.Text: If I return an array then I get an `Illegal offset type` php error when I try to save a field from the publish page. Here's my code with the array: https://gist.github.com/kgrote/290abdfc316ceab15134cd711ea0cf16
Answers.UserDisplayName: kmgdev
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-26T21:40:11.753Z
Answers.Text: It would be useful to provide a stack trace and the line of the error
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-27T19:50:51.697Z
Answers.Text: Here's the stack trace: https://gist.github.com/kgrote/397a3b2572db23f873f959a709421de2 The line of the error is the `'label' => $opts[$value]` item of the array inside the `normalizeValue()` function.
Answers.UserDisplayName: kmgdev
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-27T20:40:48.760Z
Answers.Text: That means your options doesn't have the index  `regions`. It's a bad idea to include a file that way to begin with. Anyway you need to make sure your options include the given index
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-28T19:58:39.717Z
Answers.Text: Why is it a bad idea to use an include?
Answers.UserDisplayName: kmgdev
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-28T20:33:55.190Z
Answers.Text: Because it will produce unnecessary overhead and it isn't a reliable functionality in modern PHP projects anymore to include a file an use its variables unless you return the array. You could create a config file with the settings for your field and do it that way
Answers.UserDisplayName: Robin Schambach
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-24T11:58:40.257Z
Answers.LastActivityDate: 2018-09-24T11:58:40.257Z
Answers.UserDisplayName: Robin Schambach
Answers.Body: <p>Robin's answer is technically correct but I wanted to add a little more detail on how I got it working.</p>
  
  <p><strong>TL;DR</strong></p>
  
  <pre><code>public function normalizeValue($value, ElementInterface $element = null)
  {
      if ( $value instanceof SingleOptionFieldData ) 
      {
          return $value;
      }
  
      $nakedOpts = array
      (
          "AL" =&gt; "Alabama", 
          "AK" =&gt; "Alaska", 
          "AS" =&gt; "American Samoa", 
          "AZ" =&gt; "Arizona", 
          "AR" =&gt; "Arkansas"
      );
  
      foreach( $nakedOpts as $optval =&gt; $label )
      {
          $isSelected = ($value == $optval) ? true : false;
          $this-&gt;options[] = new OptionData($label, $optval, $isSelected);
      }
  
      if( !empty($value) )
      {
          $value = new SingleOptionFieldData($nakedOpts[$value], $value, true);
          $value-&gt;setOptions($this-&gt;options);
      }
  
      return $value;
  }
  </code></pre>
  
  <p><strong>Full Explanation</strong></p>
  
  <p>I ended up using Craft's native <code>OptionData</code> and <code>SingleOptionFieldData</code> classes to convert my values and options into objects that Craft knows how to work with.</p>
  
  <p>So to start with I added these declarations at the top of my fieldtype file:</p>
  
  <pre><code>use craft\fields\data\OptionData;
  use craft\fields\data\SingleOptionFieldData;
  </code></pre>
  
  <p>Now in your fieldtype file you need to do two things:</p>
  
  <ol>
  <li>Convert your options to an array of <code>OptionData</code> objects</li>
  <li>Convert the <code>$value</code> to a <code>SingleOptionFieldData</code> object</li>
  </ol>
  
  <p>So we need to take our "naked" option array (our array of US states) and convert it to an array of objects, and we also need to mark which of the options is our selected value. The <code>OptionData</code> object accepts 3 parameters: <code>$label</code>, <code>$value</code>, and <code>$selected</code>:</p>
  
  <pre><code>$nakedOpts = array
  (
      "AL" =&gt; "Alabama", 
      "AK" =&gt; "Alaska", 
      "AS" =&gt; "American Samoa", 
      "AZ" =&gt; "Arizona", 
      "AR" =&gt; "Arkansas"
  );
  
  foreach( $nakedOpts as $optval =&gt; $label )
  {
      // is this array item the selected value?
      $isSelected = ($value == $optval) ? true : false;
      // create a new object for each item
      $this-&gt;options[] = new OptionData($label, $optval, $isSelected);
  }
  </code></pre>
  
  <p>So now we need to convert our field <code>$value</code> to an object so Craft can compare it to the objects in the options list. Again, <code>SingleOptionFieldData</code> accepts the parameters of <code>$label</code>, <code>$value</code>, and <code>$selected</code>:</p>
  
  <pre><code>$value = new SingleOptionFieldData($nakedOpts[$value], $value, true);
  </code></pre>
  
  <p>The <code>normalizeValue()</code> method gets called once when the field is loaded on the publish page, and again when the entry is saved. So you have to check if you've already converted the <code>$value</code> to an object and bypass the rest of the code if so:</p>
  
  <pre><code>if ( $value instanceof SingleOptionFieldData ) 
  {
      return $value;
  }
  </code></pre>
  
  <p>The advantage of using a Craft object for this instead of a regular array is that Craft already knows how to use this type of data in the template. So the tag <code>{{ entry.region }}</code> will output <code>AK</code> without having to add the <code>.value</code> attribute. And just like a native Craft dropdown field, you can get the label with <code>{{ entry.region.label }}</code>.</p>
  
  <p>To loop through all of your options in the template, just add this to <code>normalizeValue()</code> after you've converted the <code>$value</code> to an object and created your list of option objects:</p>
  
  <pre><code>$value-&gt;setOptions($this-&gt;options);
  </code></pre>
  
  <p>Now you can output a list of your options in your template:</p>
  
  <pre><code>{% for option in entry.region.options %}
      &lt;option value="{{ option }}"{% if option.selected %} selected{% endif %}&gt;{{ option.label }}&lt;/option&gt;
  {% endfor %}
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-07-04T22:07:36.850Z
Answers.Text: nice job on the explanations - these are bound to help others in future!
Answers.UserDisplayName: narration_sd
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2018-09-28T20:41:17.800Z
Answers.LastActivityDate: 2018-09-30T19:42:50.180Z
Answers.LastEditDate: 2018-09-30T19:42:50.180Z
Answers.UserDisplayName: kmgdev
Body: <p>I want to be able to output different values for my fieldtype depending on the twig attribute. So <code>{{ entry.myField }}</code> would output <code>AK</code> by default, but <code>{{ entry.myField.label }}</code> would output <code>Alaska</code>. Currently I'm using the <code>normalizeValue()</code> method to handle all this, but I need to know which attribute (if any) is being called by the twig template. How do I get that information?</p>
  
  <p>A very basic example of what I'm thinking of would be:</p>
  
  <pre><code>public function normalizeValue($value, ElementInterface $element = null)
  {
      $states = array(
          'AK' =&gt; 'Alaska',
          'AL' =&gt; 'Alabama'
      );
  
      if( twig.attribute == 'label' )
      {
          return $states[$value];
      }
      else
      {
          return $value;
      }
  }
  </code></pre>
  
Tags: <plugin-development><fieldtypes>
Title: How to Access the Twig Attribute Name from the Fieldtype Class
ThreadText: # How to Access the Twig Attribute Name from the Fieldtype Class
  
  (Asked by: username_0 on 2018-09-24)
  
  I want to be able to output different values for my fieldtype depending on the twig attribute. So `{{ entry.myField }}` would output `AK` by default, but `{{ entry.myField.label }}` would output `Alaska`. Currently I'm using the `normalizeValue()` method to handle all this, but I need to know which attribute (if any) is being called by the twig template. How do I get that information?
  
  A very basic example of what I'm thinking of would be:
  
  ```
  public function normalizeValue($value, ElementInterface $element = null)
  {
      $states = array(
          'AK' => 'Alaska',
          'AL' => 'Alabama'
      );
  
      if( twig.attribute == 'label' )
      {
          return $states[$value];
      }
      else
      {
          return $value;
      }
  }
  
  ```
  
  **Tags:** plugin-development, fieldtypes
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2018-09-24. Score: 2 | Accepted answer
  
  Because of several reasons it won't work the way you are going to do it. The easiest approach is to return an object or an array instead of single value in your `normalize` functions that contains the value and the states. Craft does the same with nearly all fields. They values are objects with a `_toString` function so you don't even know you have an object rather than a string
  
  > **Comments:**
  > **username_0**: Can you give me an example of what that array might look like?  
  > **username_1**: It can look however you like (?) `['foo' => $value, 'bar' => $states]` then you can do `entry.field.foo` to display the value and `entry.fieldHandle.bar.AL` to display `Alabama`  
  > **username_0**: If I return an array then I get an `Illegal offset type` php error when I try to save a field from the publish page. Here's my code with the array: https://gist.github.com/kgrote/290abdfc316ceab15134cd711ea0cf16  
  > **username_1**: It would be useful to provide a stack trace and the line of the error  
  > **username_0**: Here's the stack trace: https://gist.github.com/kgrote/397a3b2572db23f873f959a709421de2 The line of the error is the `'label' => $opts[$value]` item of the array inside the `normalizeValue()` function.  
  > **username_1**: That means your options doesn't have the index  `regions`. It's a bad idea to include a file that way to begin with. Anyway you need to make sure your options include the given index  
  > **username_0**: Why is it a bad idea to use an include?  
  > **username_1**: Because it will produce unnecessary overhead and it isn't a reliable functionality in modern PHP projects anymore to include a file an use its variables unless you return the array. You could create a config file with the settings for your field and do it that way  
  
  ---
  
  ### Answer by username_0 on 2018-09-28. Score: 4
  
  username_1's answer is technically correct but I wanted to add a little more detail on how I got it working.
  
  **TL;DR**
  
  ```
  public function normalizeValue($value, ElementInterface $element = null)
  {
      if ( $value instanceof SingleOptionFieldData ) 
      {
          return $value;
      }
  
      $nakedOpts = array
      (
          "AL" => "Alabama", 
          "AK" => "Alaska", 
          "AS" => "American Samoa", 
          "AZ" => "Arizona", 
          "AR" => "Arkansas"
      );
  
      foreach( $nakedOpts as $optval => $label )
      {
          $isSelected = ($value == $optval) ? true : false;
          $this->options[] = new OptionData($label, $optval, $isSelected);
      }
  
      if( !empty($value) )
      {
          $value = new SingleOptionFieldData($nakedOpts[$value], $value, true);
          $value->setOptions($this->options);
      }
  
      return $value;
  }
  
  ```
  
  **Full Explanation**
  
  I ended up using Craft's native `OptionData` and `SingleOptionFieldData` classes to convert my values and options into objects that Craft knows how to work with.
  
  So to start with I added these declarations at the top of my fieldtype file:
  
  ```
  use craft\fields\data\OptionData;
  use craft\fields\data\SingleOptionFieldData;
  
  ```
  
  Now in your fieldtype file you need to do two things:
  
  1. Convert your options to an array of `OptionData` objects
  2. Convert the `$value` to a `SingleOptionFieldData` object
  
  So we need to take our "naked" option array (our array of US states) and convert it to an array of objects, and we also need to mark which of the options is our selected value. The `OptionData` object accepts 3 parameters: `$label`, `$value`, and `$selected`:
  
  ```
  $nakedOpts = array
  (
      "AL" => "Alabama", 
      "AK" => "Alaska", 
      "AS" => "American Samoa", 
      "AZ" => "Arizona", 
      "AR" => "Arkansas"
  );
  
  foreach( $nakedOpts as $optval => $label )
  {
      // is this array item the selected value?
      $isSelected = ($value == $optval) ? true : false;
      // create a new object for each item
      $this->options[] = new OptionData($label, $optval, $isSelected);
  }
  
  ```
  
  So now we need to convert our field `$value` to an object so Craft can compare it to the objects in the options list. Again, `SingleOptionFieldData` accepts the parameters of `$label`, `$value`, and `$selected`:
  
  ```
  $value = new SingleOptionFieldData($nakedOpts[$value], $value, true);
  
  ```
  
  The `normalizeValue()` method gets called once when the field is loaded on the publish page, and again when the entry is saved. So you have to check if you've already converted the `$value` to an object and bypass the rest of the code if so:
  
  ```
  if ( $value instanceof SingleOptionFieldData ) 
  {
      return $value;
  }
  
  ```
  
  The advantage of using a Craft object for this instead of a regular array is that Craft already knows how to use this type of data in the template. So the tag `{{ entry.region }}` will output `AK` without having to add the `.value` attribute. And just like a native Craft dropdown field, you can get the label with `{{ entry.region.label }}`.
  
  To loop through all of your options in the template, just add this to `normalizeValue()` after you've converted the `$value` to an object and created your list of option objects:
  
  ```
  $value->setOptions($this->options);
  
  ```
  
  Now you can output a list of your options in your template:
  
  ```
  {% for option in entry.region.options %}
      <option value="{{ option }}"{% if option.selected %} selected{% endif %}>{{ option.label }}</option>
  {% endfor %}
  
  ```
  
  > **Comments:**
  > **username_2**: nice job on the explanations - these are bound to help others in future!  
  
  ---
  
  