<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>

CC BY-SA 4.0
2018-09-24T19:35:37.950Z
Can you give me an example of what that array might look like?
kmgdev
CC BY-SA 4.0
2018-09-24T21:24:11.633Z
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`
Robin Schambach
CC BY-SA 4.0
2018-09-24T23:54:16.173Z
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
kmgdev
CC BY-SA 4.0
2018-09-26T21:40:11.753Z
It would be useful to provide a stack trace and the line of the error
Robin Schambach
CC BY-SA 4.0
2018-09-27T19:50:51.697Z
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.
kmgdev
CC BY-SA 4.0
2018-09-27T20:40:48.760Z
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
Robin Schambach
CC BY-SA 4.0
2018-09-28T19:58:39.717Z
Why is it a bad idea to use an include?
kmgdev
CC BY-SA 4.0
2018-09-28T20:33:55.190Z
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
Robin Schambach
CC BY-SA 4.0
2018-09-24T11:58:40.257Z
2018-09-24T11:58:40.257Z
Robin Schambach
<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>

CC BY-SA 4.0
2019-07-04T22:07:36.850Z
nice job on the explanations - these are bound to help others in future!
narration_sd
CC BY-SA 4.0
2018-09-28T20:41:17.800Z
2018-09-30T19:42:50.180Z
2018-09-30T19:42:50.180Z
kmgdev
<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>

<plugin-development><fieldtypes>
How to Access the Twig Attribute Name from the Fieldtype Class
# 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

Robin'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:

Convert your options to an array of OptionData objects
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.label }}
{% endfor %}

> **Comments:**
> **username_2**: nice job on the explanations - these are bound to help others in future!  

---

