Creating a Craft CMS Field Type

Building a Craft CMS Dropdown Field Type with Static Data

The first Craft CMS field type we’ll build in this course is a dropdown that allows us to create a dropdown of world languages.

The first Craft CMS field type we’ll build in this course is a drop­down that allows us to cre­ate a drop­down of world languages.

We won’t pop­u­late the drop­down with remote data but instead, just store it in an array right in the mod­ule source code. I showed in the first video of the course how it worked.

Cre­at­ing the Field Type Class File

The first step in cre­at­ing the field type is to cre­ate the class file. We name the class file based on the name of the field type. We will call our field type World Lan­guages, and the class file name will be WorldLanguages.php. So, let’s cre­ate that file.

craft-module
├── README.md
└── src
    ├── CraftModule.php
    └── fields
        └── WorldLanguages.php

This file will be the class file for our field type, and it’s going to be the only file we need to cre­ate. Since our field type is rel­a­tive­ly sim­ple, we don’t need much code to make it hap­pen. The idea here is that we’re learn­ing the frame­work for cre­at­ing the field type, and then you can use that frame­work to build your imple­men­ta­tions as they are needed.

Field types in Craft can extend the base Field class in Craft. This gives every­thing we need to get start­ed. How­ev­er, we can also extend one of the in-built fields that come with Craft and lay­er our cus­tomiza­tion over top of them. 

In the case of a drop­down field, this is a good option because we don’t need to do a lot of heavy lift­ing. There will be very lit­tle code for us to write.

Look­ing at the includ­ed field types, I think we can just extend the Dropdown class and build on top of it.

Let’s pop­u­late the class file with the base code we need to get started. 

Our name­space for this field will be craftquest\fields since our name­space for the con­tain­ing mod­ule is craftquest.

<?php

namespace craftquest\fields;

We want to import the Drop­down class and then extend it with our own field type class.

<?php

namespace craftquest\fields;
use craft\fields\Dropdown;

class WorldLanguages extends Dropdown
{

}

Now we have the basics of our class file. Reload­ing our site shouldn’t result in any errors since we have valid class code, albeit use­less at this point.

Now let’s focus on the class meth­ods that we need to cre­ate to get things working. 

First, we’ll cre­ate the init method that ini­tial­izes our class file. 

<?php

namespace craftquest\fields;
use craft\fields\Dropdown;

class WorldLanguages extends Dropdown
{
	public function init()
	{
		parent::init();
	}
}

Inside of the init method we’ll run the par­ent class init method, too, since we’re extend­ing it. Lat­er on we’ll come back and call our class method to set the options for our drop­down field.

Next, let’s give our field type a name, so Craft can dis­play some­thing human-read­able in the UI. We do this with a sta­t­ic method called displayName, which returns a string.

<?php

namespace craftquest\fields;
use craft\fields\Dropdown;

class WorldLanguages extends Dropdown
{
	public function init()
	{
		parent::init();
	}

	
	public static function displayName(): string
	{
		return "World Languages";
	}

}

Let’s rebuild our Com­pos­er autoload files to get the class picked up. 

composer dump-autoload

Reg­is­ter­ing a Field Type

Before we can see our new field type in the Craft CMS con­trol pan­el as an option to choose when cre­at­ing a new field, we need to reg­is­ter it with Craft via an Event. 

We do that in the mod­ule class file. I like to cre­ate a pri­vate method for reg­is­ter­ing dif­fer­ent com­po­nents in a mod­ule. For this, we’ll cre­ate one called _registerCustomFieldTypes() and then call it in the init() method of the mod­ule class.

First, let’s import our field type class file:

use craftquest\fields\WorldLanguages;

Our base event class:

use Yii\base\Event

And the base Fields class:

use craft\services\Fields;

And final­ly, we need to import the RegisterComponentTypesEvent class as well. 

use craft\events\RegisterComponentTypesEvent;

So our end result is this:

use craftquest\fields\WorldLanguages;
use Yii\base\Event;
use craft\services\Fields;
use craft\events\RegisterComponentTypesEvent;

Now we can cre­ate our pri­vate class method to reg­is­ter the field type via an event: 

private function _registerCustomFieldTypes()
{
	Event::on(
		Fields::class,
		Fields::EVENT_REGISTER_FIELD_TYPES,
		function(RegisterComponentTypesEvent $event) {
			$event->types[] = WorldLanguages::class;
		}
	);
}

And now we call the pri­vate method from the module’s init() method:

$this->_registerCustomFieldTypes();

Now when we cre­ate a new field, we can see our field type appear as an option!

How­ev­er, it looks just like a stan­dard Drop­down field since that’s the one we’re extend­ing. The only dif­fer­ence right now is the name!

Let make it our own by pop­u­lat­ing it with the world lan­guages data.

Cre­at­ing the Lan­guages List

We are going to store our lan­guages in an array right in our code. It might make sense to have this attached to a ser­vice of some type, but we’ll keep it sim­ple for now.

When we extend the Drop­down class from Craft, we get access to the $options prop­er­ty. We need to add to that prop­er­ty the options for each lan­guage when we want to make available. 

Let’s start with our lan­guages array. This array is avail­able as a Github Gist and you can copy and paste into your code.

Let’s cre­ate a pro­tect­ed class method where we’ll process the lan­guages in the array and add them to the options for our dropdown. 

protected function setLanguages(): void
{
	
}

We set the return type as void, mean­ing that we don’t have any­thing at all to return. We are inter­cept­ing and updat­ing the options array via the event in Craft for reg­is­ter­ing ad adding a new field type.

The first thing we’ll do is drop in our array. Now, to be clear, hav­ing this array like this in the method isn’t ide­al, but we want to focus on build­ing our field type, and then we can always come back around and refac­tor this as needed. 

Let’s drop our array in at the top of the method.

protected function setLanguages(): void
{

	$languages = [ /*data here */ ];
	
	
}

Before we make the array data avail­able as options for the drop­down, let’s first clear the drop­down that we get from the base class, and add our default options:

protected function setLanguages(): void
{

	$languages = [ /*data here */ ];

	
	$this->options = [
		[
			'label' => 'Choose a language',
			'value' => '',
			'disabled' => true,
        ]
    ];
	
}

We need to get that data into a for­mat that the $options prop­er­ty in Craft expects, so it’ll pop­u­late a drop­down for us. We’ll iter­ate over the array and place the keys and val­ues as labels and values. 

protected function setLanguages(): void
{

	$languages = [ /*data here */ ];

	
	$this->options = [
		[
			'label' => 'Choose a language',
			'value' => '',
			'disabled' => true,
        ]
    ];
	

	foreach ($languages as $key => $value) {

    	$this->options[] = [
			'label' => $plan->name,
			'value' => $plan->uid
        ];
	
	}
	
}

Next, I want to over­ride yet anoth­er base class method to con­trol how our World Lan­guages field type func­tions. By default, the Drop­down field type has a set­tings area in the bot­tom of the field set­tings. With a drop­down field this is where you would add your drop­down field data. Since we’re pop­u­lat­ing this data from the array in the class method, then we don’t need (or want) the data to be updat­ed. Plus, with the way the set­tings field works, it would dou­ble our data if we saved the HTML fields data, and then our cus­tom field code would load that data again from the array. 

We want to avoid both sit­u­a­tions, so we’ll just over­ride the set­tings method and return noth­ing or null.

public function getSettingsHtml()
{
	return;
}

The next step is to call this method is our class init() method. We want to place it before the parent::init() method. Usu­al­ly, we want to call our own meth­ods in the init() method before we call our own. That way we’re get­ting the base code we extend and then over­ride it with our own. How­ev­er, in this case, we want to inject our code beforehand.

<?php

namespace craftquest\fields;
use craft\fields\Dropdown;

class WorldLanguages extends Dropdown
{
	public function init()
	{
		$this->setLanguages();
		parent::init();
	}
}

Now we can test our new cus­tom field type and see how it works!

Creating a Craft CMS Field Type is made up of the following videos: