Skip to content

Commit c914d89

Browse files
Initial Commit
0 parents  commit c914d89

File tree

9 files changed

+1208
-0
lines changed

9 files changed

+1208
-0
lines changed

LICENSE

Lines changed: 674 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
In this tutorial, I'll show you how I created a "basic" custom field in Drupal 8. I won't go into detail about [PSR–4](https://drupal.org/node/1971198), [annotations](https://drupal.org/node/1882526) or [plugins](https://drupal.org/node/2087839) or this tutorial will be huge.
2+
3+
Instead, I'll add links to other websites that explain the concept further.
4+
5+
That being said, if you're looking for detailed documentation on the Field API in Drupal 8.
6+
7+
In Drupal 8, fields are not implemented using hooks like they are in Drupal 7. Instead, they are created using Drupal 8's new [Plugin API](https://drupal.org/node/2087839). This means that instead of implementing hooks, we define a class for a widget, formatter and field item. Most Drupal 7 field hooks like `hook_field_schema`, `hook_field_is_empty` and more; are now methods in classes.
8+
9+
### [](#s-step-1-implement-field-item "Permalink to this headline")Step 1: Implement Field Item
10+
11+
The first bit of work we need to do is define a field item class called `CustomFieldsItem` that extends the `FieldItemBase` class.
12+
13+
1\. In Drupal 8 classes are loaded using [PSR-4](https://drupal.org/node/1971198).
14+
15+
So, to define the `CustomFieldsItem` class, we need to create a `CustomFieldsItem.php` file and place it in `"module"/src/Plugin/Field/FieldType/CustomFieldsItem.php`
16+
17+
```php
18+
/**
19+
* @file
20+
* Contains \Drupal\custom_fields\Plugin\Field\FieldType\CustomFieldsItem.
21+
*/
22+
23+
namespace Drupal\custom_fields\Plugin\Field\FieldType;
24+
25+
use Drupal\Core\Field\FieldItemBase;
26+
use Drupal\Core\Field\FieldStorageDefinitionInterface;
27+
use Drupal\Core\TypedData\DataDefinition;
28+
29+
```
30+
31+
Then in the file we add a namespace `Drupal\custom_fields\Plugin\Field\FieldType` and three _use_ statements:
32+
33+
* `Drupal\Core\Field\FieldItemBase`.
34+
* `Drupal\Core\Field\FieldStorageDefinitionInterface` .
35+
* `Drupal\Core\TypedData\DataDefinition`.
36+
37+
2\. Now we need to define the actual field details like the field id, label, default widget and formatter etc.. This is equivalent of implementing `hook_field_info` in Drupal 7.
38+
39+
In Drupal 8 a lot, if not all, of the info hooks have been replaced by [annotations](https://drupal.org/node/1882526).
40+
41+
```php
42+
/**
43+
* Plugin implementation of the 'custom_fields' field type.
44+
*
45+
* @FieldType(
46+
* id = "custom_fields_code",
47+
* label = @Translation("CustomFields field"),
48+
* description = @Translation("This field stores code custom_fields in the database."),
49+
* default_widget = "custom_fields_default",
50+
* default_formatter = "custom_fields_default"
51+
* )
52+
*/
53+
class CustomFieldsItem extends FieldItemBase { }
54+
```
55+
56+
So instead of implementing `hook_field_info`, we define the field as an annotation inside of a comment above the class.
57+
58+
The annotation attributes are quite self-explanatory. Just make sure that the `default_widget` and `default_formatter` reference the widget and formatter annotation ID and not the class.
59+
60+
> If you want to learn more about annotations, check out the [Annotations-based plugins](https://drupal.org/node/1882526) documentation page on drupal.org.
61+
62+
3\. Now that we have our field item class, we need to define a few methods. The first one we'll look at is `schema()`.
63+
64+
In Drupal 7, when you create a custom field you define its schema using `hook_field_schema`. In Drupal 8, we define the schema by adding a `schema()` method to the `CustomFieldsItem` class.
65+
66+
> The [Schema API documentation](https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Database%21database.api.php/group/schemaapi/latest) provides a description of schema array structure and possible values. 
67+
68+
```php
69+
/**
70+
* {@inheritdoc}
71+
*/
72+
public static function schema(FieldStorageDefinitionInterface $field) {
73+
return array(
74+
'columns' => array(
75+
'source_description' => array(
76+
'type' => 'varchar',
77+
'length' => 256,
78+
'not null' => FALSE,
79+
),
80+
'source_code' => array(
81+
'type' => 'text',
82+
'size' => 'big',
83+
'not null' => FALSE,
84+
),
85+
'source_lang' => array(
86+
'type' => 'varchar',
87+
'length' => 256,
88+
'not null' => FALSE,
89+
),
90+
),
91+
);
92+
}
93+
```
94+
95+
4\. Now we need to add the `isEmpty()` method and define what constitutes an empty field item. This method is the same as implementing `hook_field_is_empty` in Drupal 7.
96+
97+
```php
98+
/**
99+
* {@inheritdoc}
100+
*/
101+
public function isEmpty() {
102+
$value = $this->get('source_code')->getValue();
103+
return $value === NULL || $value === '';
104+
}
105+
```
106+
107+
5\. The final method we'll add to the class is the `propertyDefinitions()` method.
108+
109+
```php
110+
/**
111+
* {@inheritdoc}
112+
*/
113+
static $propertyDefinitions;
114+
115+
/**
116+
* {@inheritdoc}
117+
*/
118+
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
119+
$properties['source_description'] = DataDefinition::create('string')
120+
->setLabel(t('CustomField description'));
121+
122+
$properties['source_code'] = DataDefinition::create('string')
123+
->setLabel(t('CustomField code'));
124+
125+
$properties['source_lang'] = DataDefinition::create('string')
126+
->setLabel(t('Programming Language'))
127+
->setDescription(t('CustomField code language'));
128+
129+
return $properties;
130+
}
131+
```
132+
133+
This method is used to define the type of data that exists in the field values. The "CustomFields field" has just three values: description, code and language. So I just added those values to the method as strings.
134+
135+
> Go to the [How Entity API implements Typed Data API](https://drupal.org/node/1795854) documentation on drupal.org to learn more about this.
136+
137+
Note: it needs to be updated to the PSR-4 specification, see [https://www.drupal.org/node/2128865](https://www.drupal.org/node/2128865) for more details._
138+
139+
### [](#s-step-2-implement-field-widget "Permalink to this headline")Step 2: Implement Field Widget
140+
141+
Now that we've defined the field item, let's create the field widget. We need to create a class called `CustomFieldsDefaultWidget` that extends the `WidgetBase` class.
142+
143+
1\. So create a `CustomFieldsDefaultWidget.php` file and add it to `"module"/src/Plugin/Field/FieldWidget/CustomFieldsDefaultWidget.php`.
144+
145+
```php
146+
/**
147+
* @file
148+
* Contains \Drupal\custom_fields\Plugin\Field\FieldWidget\CustomFieldsDefaultWidget.
149+
*/
150+
151+
namespace Drupal\custom_fields\Plugin\Field\FieldWidget;
152+
153+
use Drupal\Core\Field\FieldItemListInterface;
154+
use Drupal\Core\Field\WidgetBase;
155+
use Drupal\Core\Form\FormStateInterface;
156+
```
157+
158+
Make sure the file namespace is `Drupal\custom_fields\Plugin\Field\FieldWidget` and add the following three _use_ statements:
159+
160+
* `Drupal\Core\Field\FieldItemListInterface`.
161+
* `Drupal\Core\Field\WidgetBase`.
162+
* `Drupal\Core\Form\FormStateInterface`.
163+
164+
2\. Next, we need to define the widget using an annotation. This is the equivalent of using `hook_field_widget_info` in Drupal 7.
165+
166+
```php
167+
/**
168+
* Plugin implementation of the 'custom_fields_default' widget.
169+
*
170+
* @FieldWidget(
171+
* id = "custom_fields_default",
172+
* label = @Translation("CustomFields default"),
173+
* field_types = {
174+
* "custom_fields_code"
175+
* }
176+
* )
177+
*/
178+
class CustomFieldsDefaultWidget extends WidgetBase { }
179+
```
180+
181+
Just a heads up, make sure that the `field_types` attribute in the annotation references the field types using their ID. For this module, it is `custom_fields_code` because we added `id = "custom_fields_code",` to the `@FieldType` annotation.
182+
183+
3\. And finally, we need to define the actual widget form. We do this by adding a `formElement()` method to the `CustomFieldsDefaultWidget` class. This method is the same as using `hook_field_widget_form` in Drupal 7.
184+
185+
```php
186+
/**
187+
* {@inheritdoc}
188+
*/
189+
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
190+
191+
$element['source_description'] = array(
192+
'#title' => $this->t('Description'),
193+
'#type' => 'textfield',
194+
'#default_value' => isset($items[$delta]->source_description) ? $items[$delta]->source_description : NULL,
195+
);
196+
$element['source_code'] = array(
197+
'#title' => $this->t('Code'),
198+
'#type' => 'textarea',
199+
'#default_value' => isset($items[$delta]->source_code) ? $items[$delta]->source_code : NULL,
200+
);
201+
$element['source_lang'] = array(
202+
'#title' => $this->t('Source language'),
203+
'#type' => 'textfield',
204+
'#default_value' => isset($items[$delta]->source_lang) ? $items[$delta]->source_lang : NULL,
205+
);
206+
return $element;
207+
}
208+
```
209+
210+
Note: it needs to be updated to the PSR-4 specification, see [https://www.drupal.org/node/2128865](https://www.drupal.org/node/2128865) for more details._
211+
212+
### [](#s-step-3-implement-field-formatter "Permalink to this headline")Step 3: Implement Field Formatter
213+
214+
The final piece to the puzzle, is the field formatter, and we create it by defining a class called `CustomFieldsDefaultFormatter` that extends the `FormatterBase` class.
215+
216+
1\. Create a `CustomFieldsDefaultFormatter.php` file and add it to `"module"/src/Plugin/Field/FieldFormatter/CustomFieldsDefaultFormatter.php`.
217+
218+
```php
219+
/**
220+
* @file
221+
* Contains \Drupal\custom_fields\Plugin\field\formatter\CustomFieldsDefaultFormatter.
222+
*/
223+
224+
namespace Drupal\custom_fields\Plugin\Field\FieldFormatter;
225+
226+
use Drupal\Core\Field\FormatterBase;
227+
use Drupal\Core\Field\FieldItemListInterface;
228+
```
229+
230+
Make sure the file namespace is `Drupal\custom_fields\Plugin\Field\FieldFormatter` and add the following _use_ statements:
231+
232+
* `Drupal\Core\Field\FieldItemListInterface`.
233+
* `Drupal\Core\Field\FormatterBase`.
234+
235+
2\. Next, we need to define the formatter as an annotation. The same as we did for the widget and field type, this is the equivalent of using `hook_field_formatter_info`.
236+
237+
```php
238+
/**
239+
* Plugin implementation of the 'custom_fields_default' formatter.
240+
*
241+
* @FieldFormatter(
242+
* id = "custom_fields_default",
243+
* label = @Translation("CustomFields default"),
244+
* field_types = {
245+
* "custom_fields_code"
246+
* }
247+
* )
248+
*/
249+
class CustomFieldsDefaultFormatter extends FormatterBase { }
250+
```
251+
252+
3\. Now the only thing left to do is add the `viewElements()` method and define the actual field formatter. Again, this method is the same as using `hook_field_formatter_view` in Drupal 7.
253+
254+
```php
255+
/**
256+
* {@inheritdoc}
257+
*/
258+
public function viewElements(FieldItemListInterface $items, $langcode) {
259+
$elements = array();
260+
foreach ($items as $delta => $item) {
261+
// Render output using custom_fields_default theme.
262+
$source = array(
263+
'#theme' => 'custom_fields_default',
264+
'#source_description' => $item->source_description,
265+
'#source_code' => $item->source_code,
266+
);
267+
268+
$elements[$delta] = array('#markup' => \Drupal::service('renderer')->render($source));
269+
}
270+
271+
return $elements;
272+
}
273+
```
274+
275+
4\. Create a custom_fields.module file and add code that defines twig templates. The key of each item in the array is what you will need to call the template.
276+
277+
```php
278+
/**
279+
* Implements hook_theme().
280+
*/
281+
function custom_fields_theme() {
282+
return array(
283+
'custom_fields_default' => array(
284+
'variables' => array('source_description' => NULL, 'source_code' => NULL),
285+
'template' => 'custom-fields-default',
286+
),
287+
);
288+
}
289+
```
290+
291+
5\. In our module, inside of the templates folder, create a twig template. The name of the file has to match what you put into hook_theme() (make sure replace underscores with dashes). In this case, the file name would be `custom-fields-default.html.twig`. The reason for this is I didn't want to put a lot of logic or HTML code in the `viewElements()` method.
292+
293+
```twig
294+
{% if source_description %}
295+
<div class="snippets-description">{{ source_description }}</div>
296+
{% endif %}
297+
{% if source_code %}
298+
<pre>{{ source_code }}</pre>
299+
{% endif %}
300+
```
301+
302+
### [](#s-conclusion "Permalink to this headline")Conclusion
303+
304+
As stated earlier the biggest change in Drupal 8 is that fields are created using the [Plugin API](https://drupal.org/node/2087839) and not hooks. Once you understand that, the concept of creating a field is very similar to Drupal 7. A lot of the methods in Drupal 8 match the hooks in Drupal 7.

composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "sujanshresthanet/Custom-field-types-widgets-and-formatters",
3+
"description": "Custom field types, widgets, and formattersP",
4+
"homepage": "http://www.sujanshrestha.net",
5+
"license": "LGPL-3.0",
6+
"keywords": [
7+
"drupal",
8+
"drupal 9",
9+
"php",
10+
"custom field",
11+
"drupal module"
12+
],
13+
"authors": [
14+
{
15+
"name": "Sujan Shrestha",
16+
"role": "lead"
17+
}
18+
],
19+
"require": {
20+
"php": ">=7.0"
21+
},
22+
"prefer-stable": true
23+
}

custom_field.info.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
name: Custom field types, widgets, and formatters
2+
type: module
3+
description: Provides custom field types, widgets, and formatters for drupal 9
4+
package: Custom Fields
5+
core: 8.x
6+
core_version_requirement: ^8 || ^9
7+
8+
# Information added by Drupal.org packaging script on 2022-06-01
9+
version: '9.0.1'
10+
project: 'custom_fields'
11+
datestamp: 1654140056

custom_field.module

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
/**
3+
* @file
4+
* Contains custom_fields.module.
5+
*/
6+
/**
7+
* Implements hook_theme().
8+
*/
9+
function custom_fields_theme() {
10+
return array(
11+
'custom_fields_default' => array(
12+
'variables' => array('source_description' => NULL, 'source_code' => NULL),
13+
'template' => 'custom-fields-default',
14+
),
15+
);
16+
}

0 commit comments

Comments
 (0)