How to Access the Twig Attribute Name from the Fieldtype Class 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' =&gt; 'Alaska',
        'AL' =&gt; 'Alabama'
    );

    if( twig.attribute == 'label' )
    {
        return $states[$value];
    }
    else
    {
        return $value;
    }
}
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" =&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;
}


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" =&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);
}


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-&gt;setOptions($this-&gt;options);


Now you can output a list of your options in your template:

{% for option in entry.region.options %}
    &lt;option value="{{ option }}"{% if option.selected %} selected{% endif %}&gt;{{ option.label }}&lt;/option&gt;
{% endfor %}