Академический Документы
Профессиональный Документы
Культура Документы
Last updated on
14 August 2017
Overview
Drupal 8 ships with a big library of base classes which allow you to work with your very own content. When
it comes to content entities you want to use Fields. It is important to understand Fields as that is where your
entities store their data.
FieldTypes
boolean
changed
created
decimal
email
entity_reference
float
integer
language
map
password
string
string_long
timestamp
uri
uuid
comment
datetime
file
image
link
list_float
list_integer
list_string
path
telephone
text
text_long
text_with_summary
Whenever you want to represent data in a way Drupal doesn't provide; you might want to create a new field
type for your data.
Let's say you have a content entity which holds sensitive data. The creator of this content can allow specific
users to access the entity via a password which differs for every user. If we talk in database tables you want
to create something like this:
| entity_id | uid | password |
-----------------------------------
| 1 | 1 | 'helloworld' |
| 1 | 2 | 'goodbye' |
As you can see our entity with the id 1 has two different passwords for two different users. So how can we
implement it in Drupal without having to create the table all by hand? We create a new field type.
Because Drupal implements the field logic as Plugin, we always have a base class that we can inherit from
to make it work in Drupal. For a new field type you want to create the following folder structure in your
module:
modules/custom/MODULENAME/src/Plugin/Field/FieldType
It is quite a long path and a little bit annoying but it makes it easier for Drupal to handle all the different
functions that can coexist in your modules.
/**
* @file
* Contains \Drupal\MODULENAME\Plugin\Field\FieldType\EntityUserAccessField
*/
namespace Drupal\MODULENAME\Plugin\Field\FieldType;
use Drupal\Core\Field\FieldItemBase;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
/**
* @FieldType(
* id = "entity_user_access",
* label = @Translation("Entity User Access"),
* description = @Translation("This field stores a reference to a user and a
password for this user on the entity."),
* )
*/
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition)
{
//ToDo: Implement this.
}
}
As you can see a field type looks very similar to a content entity. Actually, there is no real difference
between those two but this is a topic for another node ;)
@FieldType: This calls the Annotation class FieldType from the Drupal library
id: This is the machine name of our field type so we can reuse it. Be sure not to override any
predefined names from php and such.
label: This is can be a user readable translation for the machinename.
description: If the label is not enough you can also add a description for the field type.
Second, our class extends FieldItemBase which makes us implement two methods so we can use this field
type properly:
propertyDefinitions(): This method is similar to a baseFieldDefinition of a content entity (it is not the
same!). We can define data which appears on entity forms where this field type is being used.
schema(): On entities, this method is deprecated but we still have it on fields. This method should
implement the data representation of the field in a database. It can differ from the properties.
Because it is not that obvious what to write down on these methods let us add some code to them for the
convenience.
$properties['password'] = DataDefinition::create('string')
->setLabel(t('Password'))
->setDescription(t('A password saved in plain text. That is not save dude!'));
$properties['created'] = DataDefinition::create('timestamp')
->setLabel(t('Created Time'))
->setDescription(t('The time that the entry was created'));
return $properties;
}
It is also possible to save the user id via a DataReferenceDefinition this might be covered here in the future.
$schema = array(
'columns' => $columns,
'indexes' => array(),
'foreign keys' => array(),
);
return $schema;
}
The schema() is necessary to make Drupal know about how to save our data. The schema columns need
to be a subset of the properties defined in propertyDefinitions().
We now have a whole new field type created. It does not have any logic on it how to handle any data input
but it can be used on any content entity as a field. If you want you can use it as a baseField on an entity:
$fields['entity_user_access'] = BaseFieldDefinition::create('entity_user_access')
->setLabel(t('Entity User Access'))
->setDescription(t('Specify passwords for any user that want to see this entity.'))
->setCardinality(-1); // Ensures that you can have more than just one member
return $fields;
}
BaseFieldDefinition::create(): You have to use the machine name of the field type in the create()
setCardinality(-1): The cardinality represents the amount of field data one entity can have. E.g. if we
write a 2 in it only two users could access the entity, 3 would be 3 users and so on. -1 represents
infinite users.
FieldWidget
If you have custom data you might event want custom representation of this data. Widgets are used to
represent how the user can input this custom data on forms. E.g
if an integer has to be submitted in the form but the user can only check a checkbox
if you want autocompletion for your data
if password input has to be done via a special gui
et cetera.
/**
* @file
* Contains \Drupal\MODULENAME\Plugin\Field\FieldWidget\EntityUserAccessWidget
*/
namespace Drupal\MODULENAME\Plugin\Field\FieldWidget;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Plugin implementation of the 'entity_user_access_w' widget.
*
* @FieldWidget(
* id = "entity_user_access_w",
* label = @Translation("Entity User Access - Widget"),
* description = @Translation("Entity User Access - Widget"),
* field_types = {
* "entity_user_access",
* },
* multiple_values = TRUE,
* )
*/
Did you notice already? Drupal 8 uses this style of code over and over again if you want to implement
features. There is an annotation and a base class you have to inherit. Yay, Drupal can use it!
If you now want to use this widget with your field type you have to edit the annotation of the field type like
this
// ...
/**
* @FieldType(
* id = "entity_user_access",
* label = @Translation("Entity User Access"),
* description = @Translation("This field stores a reference to a user and a
password for this user on the entity."),
* default_widget = "entity_user_access_w",
* )
*/
// ...
Yeah all done! No, wait nothing happens yet because we have to implement the formElement() in our
widget.
$element['passwordlist'] = array(
'#type' => 'password',
'#title' => t('Password'),
'#description' => t('Select a password for the user'),
);
return $element;
}
If you now open a form with this widget then you will see at least two input fields. One is a select for the
user and the other is a password field. If you want to implement how the data is being saved then you have
to implement validation methods on this widget or on the entity form. See Drupal 8 Form API for more info.
By now you have done a most of the work regarding a custom field. If you don't understand what is going
on then just try out the code or have a look at the core modules for deeper knowledge on the topic.
FieldFormatters
The last thing that is missing is the representation of the data in the so called view mode of an entity - by the
way the widget is the form mode. This is most common if you call an entity via
yourdrupalpage.com/myentity/1/view
As there is not so much to talk about here we will come directly to the code. Under
modules/custom/MODULENAME/src/Plugin/Field/FieldFormatter create
EntityUserAccessFormatter.php
/**
* @file
* Contains
\Drupal\MODULENAME\Plugin\Field\FieldFormatter\EntityUserAccessFormatter.
*/
namespace Drupal\MODULENAME\Plugin\Field\FieldFormatter;
use Drupal\Core\Field\FieldItemListInterface;
use Drupal\Core\Field\FormatterBase;
/**
* Plugin implementation of the 'entity_user_access_f' formatter.
*
* @FieldFormatter(
* id = "entity_user_access_f",
* label = @Translation("Entity User Access - Formatter"),
* description = @Translation("Entity User Access - Formatter"),
* field_types = {
* "entity_user_access",
* }
* )
*/
return $elements;
}
}
This example has a very similar annotation as the widget so we do not need to talk about it that much. The
viewElements() just shows the username of the saved user id of our field type. The access implementation
has to be done in the entity. So this implementation will show all the user names which have a password on
the entity.