Module & Architecture Development
Drupal Composer, Custom Entities, & Plugins
Jimmy Candan
/jənˈdən/
- Coding 9 yrs
- Drupal 6 yrs
- Love wife and 2 kids
- From L.A., Live in Chattanooga
Agenda
- Drupal Composer
- Custom Entities
- Custom Field Plugin
- Packagist repository
- Require
- Require Dev
- Install or Update
- Lock
- Vendor
# composer.json
{
"require": {
"guzzlehttp/guzzle": "^6.0",
},
}
# composer.json
{
"require": {
"guzzlehttp/guzzle": "^6.0"
},
"require-dev": {
"behat/mink": "~1.7"
}
}
$ composer install
OR
$ composer install --no-dev
proj_name/
├── composer.json
├── composer.lock
└── vendor/
├── bin/
│ ├── behat -> ../behat/behat/bin/behat
├── behat/
└── guzzlehttp/
└── guzzle/
$ composer update
OR
$ composer update behat/behat
$ composer require guzzlehttp/guzzle
$ composer require --dev behat/mink
Composer
A modern PHP dependency manager
Drupal Composer
Manage Drupal root and contrib modules with a modern dependency manager
$ composer create-project
{
"require": {
"drupal/admin_toolbar": "^1.22"
}
}
composer.json
$ composer create-project \
drupal-composer/drupal-project:8.x-dev
$ composer create-project \
drupal-composer/drupal-project:8.x-dev \
proj_name --stability dev --no-interaction
$ composer create-project \
drupal-composer/drupal-project:8.x-dev \
proj_name --stability dev --no-interaction
$ cd proj_name
$ composer create-project \
drupal-composer/drupal-project:8.x-dev \
proj_name --stability dev --no-interaction
$ cd proj_name
$ composer require drupal/admin_toolbar
Diff Drupal Composer
Things that come preconfigured for Drupal.
- Drupal repository
- Drush and Console
Behat- Drupal root
- Installer paths
{
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
{
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"drupal/core": "~8.4",
"drupal/console": "^1.0.2",
"drush/drush": "^9.0.0"
. . .
},
{
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"drupal/core": "~8.4",
"drupal/console": "^1.0.2",
"drush/drush": "^9.0.0"
. . .
},
"require-dev": {
"behat/mink": "~1.7",
. . .
},
$ composer require drupal/admin_toolbar
proj_name/
├── composer.json
├── composer.lock
├── vendor/
└── web/
├── autoload.php
├── core/
├── modules/
├── profiles/
├── sites/
├── themes/
├── index.php
├── robots.txt
├── update.php
└── web.config
{
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"drupal/core": "~8.4",
"drupal/console": "^1.0.2",
"drush/drush": "^9.0.0"
. . .
},
"require-dev": {
"behat/mink": "~1.7",
. . .
},
"extra": {
"installer-paths": {
"web/modules/contrib/{$name}": ["type:drupal-module"],
"web/themes/contrib/{$name}": ["type:drupal-theme"]
}
}
}
NOTE:
The CLI tools are in the vendor directory.
$ cd web
$ ../vendor/bin/drush <command>
For drush, avoid the inconvenience of typing ../vendor/bin/drush by installing drush-launcher.
../vendor/bin/drush
Composer Merge
Custom modules can have their own dependencies
-
composer.json
tells composer this must be available
-
mymodule.info.yml
tells drupal this must be installed
$ cd my_project
$ composer require wikimedia/composer-merge-plugin
# project_root/composer.json
{
"extra": {
"merge-plugin": {
"include": [
"web/modules/custom/*/composer.json"
]
}
}
}
name: My Custom Module
type: module
description: Does something awesome.
core: 8.x
package: Custom
dependencies:
- state_machine
# my_project/web/modules/custom/my_custom_module/composer.json
{
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"drupal/state_machine": "^1.0@beta"
},
}
$ cd my_project
# my_custom_module/composer.json
{
"require": {
"drupal/state_machine": "^1.0@beta"
},
}
My Fitness App
Requirements
Users can:
- Select a workout
- Play through workout's exercise's countdown timer
- Start, stop, skip exercises in a workout
Admins can:
- Create exercises with description and video
- Create workouts with exercises
- Specify the time for each exercise in the workout
Exercise
Exercise
Exercise
Workout
Workout
count: 3
count: 2
Some Questions
- Do we Node or custom Content Entity?
- Where do we field the relationship?
- Where do we field the time?
1. Do we Node or Content Entity?
- What is the difference?
- When do we choose Entity?
2 Types of Entities
- Configuration Entity
- Content Entity
Read the Docs (drupal.org/docs/8/api/entity-api)
Configuration Entity
Main difference
- Keeps configuration in the database
- No fields
Content Entity
Examples
- Node
- Comment
- User
- Taxonomy Term
When should you go custom?
Source: Comment by yareckon
lullabot.com/articles/module-monday-entity-construction-kit#comment-1931105250
- Nodes are for document-like things
- Consider custom when:
- More "behaviory" (e.g. Bookable Rooms)
- Units of Inventory (e.g. Products)
- Node's title, body, author, and multitude of settings options are overkill
- Client simply does not consider it "CONTENT"
New Content Type or Full-fledged Content Entity?
LET'S SEE IT IN ACTION
$ ../vendor/bin/drupal generate:module
Drupal Console
Generate Module
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
Enter the module Path [modules/custom]:
>
Enter module description [My Awesome Module]:
> Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
Enter the module Path [modules/custom]:
>
Enter module description [My Awesome Module]:
> Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.
Enter package name [Custom]:
>
Enter Drupal Core version [8.x]:
>
Do you want to generate a .module file (yes/no) [yes]:
>
Define module as feature (yes/no) [no]:
>
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
Enter the module Path [modules/custom]:
>
Enter module description [My Awesome Module]:
> Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.
Enter package name [Custom]:
>
Enter Drupal Core version [8.x]:
>
Do you want to generate a .module file (yes/no) [yes]:
>
Define module as feature (yes/no) [no]:
>
Do you want to add a composer.json file to your module (yes/no) [yes]:
>
Would you like to add module dependencies (yes/no) [no]:
>
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
Enter the module Path [modules/custom]:
>
Enter module description [My Awesome Module]:
> Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.
Enter package name [Custom]:
>
Enter Drupal Core version [8.x]:
>
Do you want to generate a .module file (yes/no) [yes]:
>
Define module as feature (yes/no) [no]:
>
Do you want to add a composer.json file to your module (yes/no) [yes]:
>
Would you like to add module dependencies (yes/no) [no]:
>
Do you want to generate a unit test class (yes/no) [yes]:
> no
Do you want to generate a themeable template (yes/no) [yes]:
> no
$ ../vendor/bin/drupal generate:module
// Welcome to the Drupal module generator
Enter the new module name:
> Fitness
Enter the module machine name [fitness]:
>
Enter the module Path [modules/custom]:
>
Enter module description [My Awesome Module]:
> Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.
Enter package name [Custom]:
>
Enter Drupal Core version [8.x]:
>
Do you want to generate a .module file (yes/no) [yes]:
>
Define module as feature (yes/no) [no]:
>
Do you want to add a composer.json file to your module (yes/no) [yes]:
>
Would you like to add module dependencies (yes/no) [no]:
>
Do you want to generate a unit test class (yes/no) [yes]:
> no
Do you want to generate a themeable template (yes/no) [yes]:
> no
Do you confirm generation? (yes/no) [yes]:
>
<?php
/**
* @file
* Contains fitness.module.
*/
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Implements hook_help().
*/
function fitness_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the fitness module.
case 'help.page.fitness':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.') . '</p>';
return $output;
default:
}
}
name: 'Fitness'
type: module
description: 'Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.'
core: 8.x
package: 'Custom'
Generate Content Entity
$ ../vendor/bin/drupal generate:entity:content
$ ../vendor/bin/drupal generate:entity:content
// Welcome to the Drupal Content Entity generator
Enter the module name [admin_toolbar]:
> fitness
$ ../vendor/bin/drupal generate:entity:content
// Welcome to the Drupal Content Entity generator
Enter the module name [admin_toolbar]:
> fitness
Enter the class of your new content entity [DefaultEntity]:
> WorkoutEntity
Enter the machine name of your new content entity [workout_entity]:
> workout
Enter the label of your new content entity [Workout entity]:
> Workout
$ ../vendor/bin/drupal generate:entity:content
// Welcome to the Drupal Content Entity generator
Enter the module name [admin_toolbar]:
> fitness
Enter the class of your new content entity [DefaultEntity]:
> WorkoutEntity
Enter the machine name of your new content entity [workout_entity]:
> workout
Enter the label of your new content entity [Workout entity]:
> Workout
Enter the base-path for the content entity routes [/admin/structure]:
>
$ ../vendor/bin/drupal generate:entity:content
// Welcome to the Drupal Content Entity generator
Enter the module name [admin_toolbar]:
> fitness
Enter the class of your new content entity [DefaultEntity]:
> WorkoutEntity
Enter the machine name of your new content entity [workout_entity]:
> workout
Enter the label of your new content entity [Workout entity]:
> Workout
Enter the base-path for the content entity routes [/admin/structure]:
>
Do you want this (content) entity to have bundles (yes/no) [no]:
>
Is your entity translatable (yes/no) [yes]:
> no
Is your entity revisionable (yes/no) [yes]:
> no
2. Where do I field the relationship?
Entity Reference Field in Workouts
Exercise
Exercise
Exercise
Workout
Workout
count: 3
count: 2
3. Where do I specify the time?
Exercise
Exercise
Exercise
Workout
00:40
00:30
00:25
3. Where do I specify the time?
Ooh, that's a bit, er, uh? Hmm.
Field Collection?
Plugins
Paragraphs?
Wait, doesn't Wordpress use plugins?
Wordpress plugins are the equivalent of Drupal modules
Drupal 8 still has modules
So, what are Plugins for us?
Plugins are built inside modules
$ ../vendor/bin/drupal list | grep generate:plugin
$ ../vendor/bin/drupal list | grep generate:plugin
generate:plugin:block (gpb) Generate a plugin block
generate:plugin:ckeditorbutton (gpc) Generate CKEditor button plugin.
generate:plugin:condition (gpco) Generate a plugin condition.
generate:plugin:field (gpf) Generate field type, widget and formatter plugins.
generate:plugin:fieldformatter (gpff) Generate field formatter plugin.
generate:plugin:fieldtype (gpft) Generate field type plugin.
generate:plugin:fieldwidget (gpfw) Generate field widget plugin.
generate:plugin:imageeffect (gpie) Generate image effect plugin.
generate:plugin:imageformatter (gpif) Generate image formatter plugin.
generate:plugin:mail (gpm) Generate a plugin mail
generate:plugin:migrate:process (gpmp) Generate a migrate process plugin
generate:plugin:migrate:source (gpms) Generate a migrate source plugin
generate:plugin:rest:resource (gprr) Generate plugin rest resource
generate:plugin:rulesaction (gpra) Generate a plugin rule action
generate:plugin:skeleton (gps) Generate an implementation of a skeleton plugin
generate:plugin:type:annotation (gpta) Generate a plugin type with annotation discovery
generate:plugin:type:yaml (gpty) Generate a plugin type with Yaml discovery
generate:plugin:views:field (gpvf) Generate a custom plugin view field.
$
$ ../vendor/bin/drupal generate:plugin:field
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
Enter the field widget plugin class name [ExampleWidgetType]:
> EntityRelationshipFieldWithTimeWidget
Enter the field widget plugin label [Entity relationship field with time widget]:
>
Enter the field widget plugin id [entity_relationship_field_with_time_widget]:
>
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
Enter the field widget plugin class name [ExampleWidgetType]:
> EntityRelationshipFieldWithTimeWidget
Enter the field widget plugin label [Entity relationship field with time widget]:
>
Enter the field widget plugin id [entity_relationship_field_with_time_widget]:
>
Enter the field formatter plugin class name [ExampleFormatterType]:
> EntityRelationshipFieldWithTimeFormatter
Enter the field formatter plugin label [Entity relationship field with time formatter]:
>
Enter the field formatter plugin id [entity_relationship_field_with_time_formatter]:
>
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
Enter the field widget plugin class name [ExampleWidgetType]:
> EntityRelationshipFieldWithTimeWidget
Enter the field widget plugin label [Entity relationship field with time widget]:
>
Enter the field widget plugin id [entity_relationship_field_with_time_widget]:
>
Enter the field formatter plugin class name [ExampleFormatterType]:
> EntityRelationshipFieldWithTimeFormatter
Enter the field formatter plugin label [Entity relationship field with time formatter]:
>
Enter the field formatter plugin id [entity_relationship_field_with_time_formatter]:
>
Enter the field type the formatter and widget plugin can be used with
[entity_relationship_field_with_time]:
>
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
Enter the field widget plugin class name [ExampleWidgetType]:
> EntityRelationshipFieldWithTimeWidget
Enter the field widget plugin label [Entity relationship field with time widget]:
>
Enter the field widget plugin id [entity_relationship_field_with_time_widget]:
>
Enter the field formatter plugin class name [ExampleFormatterType]:
> EntityRelationshipFieldWithTimeFormatter
Enter the field formatter plugin label [Entity relationship field with time formatter]:
>
Enter the field formatter plugin id [entity_relationship_field_with_time_formatter]:
>
Enter the field type the formatter and widget plugin can be used with
[entity_relationship_field_with_time]:
>
Enter the default field widget of the field type plugin
[entity_relationship_field_with_time_widget]:
>
Enter the default field formatter of field type plugin
[entity_relationship_field_with_time_formatter]:
>
$ ../vendor/bin/drupal generate:plugin:field
// Welcome to the Drupal Field Plugin generator
Enter the module name [admin_toolbar]:
> fitness
Field type plugin class name [ExampleFieldType]:
> EntityRelationshipFieldWithTime
Enter the field type plugin label [Entity relationship field with time]:
>
Enter the field type plugin id [entity_relationship_field_with_time]:
>
Enter the field type plugin description [My Field Type]:
> A custom Entity Relationship Field with attached hours, minutes, seconds field.
Enter the field widget plugin class name [ExampleWidgetType]:
> EntityRelationshipFieldWithTimeWidget
Enter the field widget plugin label [Entity relationship field with time widget]:
>
Enter the field widget plugin id [entity_relationship_field_with_time_widget]:
>
Enter the field formatter plugin class name [ExampleFormatterType]:
> EntityRelationshipFieldWithTimeFormatter
Enter the field formatter plugin label [Entity relationship field with time formatter]:
>
Enter the field formatter plugin id [entity_relationship_field_with_time_formatter]:
>
Enter the field type the formatter and widget plugin can be used with
[entity_relationship_field_with_time]:
>
Enter the default field widget of the field type plugin
[entity_relationship_field_with_time_widget]:
>
Enter the default field formatter of field type plugin
[entity_relationship_field_with_time_formatter]:
>
Do you confirm generation? (yes/no) [yes]:
>
LET'S SEE SOME CODE!
/*
* @file web/modules/custom/fitness/composer.json
*/
/*
* @file web/modules/custom/fitness/composer.json
*/
{
. . .
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
/*
* @file web/modules/custom/fitness/composer.json
*/
{
. . .
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
}
/*
* @file web/modules/custom/fitness/composer.json
*/
{
. . .
"repositories": [
{
"type": "composer",
"url": "https://packages.drupal.org/8"
}
],
"require": {
"drupal/hms_field": "1.x-dev"
}
$ cd my-fitness-app/
$ cd my-fitness-app/
$ composer update drupal/hms_field
$ cd my-fitness-app/
$ composer update drupal/hms_field
$ ls web/modules/contrib/
admin_toolbar hms_field
name: 'Fitness'
type: module
description: 'Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.'
core: 8.x
package: 'Custom'
name: 'Fitness'
type: module
description: 'Provides Exercise and Workout entities, and custom Entity Relationship Field with Time.'
core: 8.x
package: 'Custom'
dependencies:
- hms_field
use Drupal\Core\Field\FieldItemBase;
/**
* Plugin implementation of the 'entity_relationship_field_with_time' field type.
*
* @FieldType(
* id = "entity_relationship_field_with_time",
* label = @Translation("Entity relationship field with time"),
* description = @Translation("A custom Entity Relationship Field with attached hours, minutes, seconds field."),
* default_widget = "entity_relationship_field_with_time_widget",
* default_formatter = "entity_relationship_field_with_time_formatter"
* )
*/
class EntityRelationshipFieldWithTime extends FieldItemBase {}
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
/**
* Plugin implementation of the 'entity_relationship_field_with_time' field type.
*
* @FieldType(
* id = "entity_relationship_field_with_time",
* label = @Translation("Entity relationship field with time"),
* description = @Translation("A custom Entity Relationship Field with attached hours, minutes, seconds field."),
* default_widget = "entity_relationship_field_with_time_widget",
* default_formatter = "entity_relationship_field_with_time_formatter",
* category = @Translation("Reference"),
* list_class = "\Drupal\Core\Field\EntityReferenceFieldItemList",
* )
*/
class EntityRelationshipFieldWithTime extends EntityReferenceItem {}
class EntityReferenceWithTimeFieldType extends EntityReferenceItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
}
class EntityReferenceWithTimeFieldType extends EntityReferenceItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
return $schema;
}
class EntityReferenceWithTimeFieldType extends EntityReferenceItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$properties['time'] = DataDefinition::create('integer')
->setLabel(new TranslatableMarkup('Time'));
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
return $schema;
}
class EntityReferenceWithTimeFieldType extends EntityReferenceItem {
/**
* {@inheritdoc}
*/
public static function propertyDefinitions(FieldStorageDefinitionInterface $field_definition) {
$properties = parent::propertyDefinitions($field_definition);
$properties['time'] = DataDefinition::create('integer')
->setLabel(new TranslatableMarkup('Time'));
return $properties;
}
/**
* {@inheritdoc}
*/
public static function schema(FieldStorageDefinitionInterface $field_definition) {
$schema = parent::schema($field_definition);
$schema['columns']['time'] = [
'type' => 'int',
'length' => 11,
];
return $schema;
}
use Drupal\Core\Field\WidgetBase;
class EntityRelationshipFieldWithTimeWidget extends WidgetBase { }
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
class EntityReferenceWithTimeFieldWidget extends EntityReferenceAutocompleteWidget {
}
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
class EntityReferenceWithTimeFieldWidget extends EntityReferenceAutocompleteWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
}
}
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
class EntityReferenceWithTimeFieldWidget extends EntityReferenceAutocompleteWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
return $element;
}
}
use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget;
class EntityReferenceWithTimeFieldWidget extends EntityReferenceAutocompleteWidget {
/**
* {@inheritdoc}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$element = parent::formElement($items, $delta, $element, $form, $form_state);
$element['time'] = [
'#type' => 'hms',
'#title' => $items[$delta]->get('time')->getDataDefinition()->getLabel(),
];
return $element;
}
}
use Drupal\Core\Field\FormatterBase;
class EntityRelationshipFieldWithTimeFormatter extends FormatterBase {
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
class EntityReferenceWithTimeFieldFormatter extends EntityReferenceEntityFormatter {
}
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
class EntityReferenceWithTimeFieldFormatter extends EntityReferenceEntityFormatter {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $field_items, $langcode) {
}
}
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
class EntityReferenceWithTimeFieldFormatter extends EntityReferenceEntityFormatter {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $field_items, $langcode) {
$elements = parent::viewElements($field_items, $langcode);
return $elements;
}
}
use Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceEntityFormatter;
class EntityReferenceWithTimeFieldFormatter extends EntityReferenceEntityFormatter {
/**
* {@inheritdoc}
*/
public function viewElements(FieldItemListInterface $field_items, $langcode) {
$elements = parent::viewElements($field_items, $langcode);
foreach ($elements as $delta => $item) {
$items = [];
$additional_field = $field_items[$delta]->get('time')->getValue();
$items[] = [
'#theme' => 'hms',
'#format' => 'hh:mm:ss',
'#value' => $additional_field,
];
$items[] = $elements[$delta];
$elements[$delta] = $items;
}
return $elements;
}
}
Summary
- Drupal Composer
- drupal root = web/
- ../vendor/bin/drush
- sets up composer.json for drupal magic
- Composer merge
- Ability to read sub-directory composer.json
- Require dependencies from custom module
- List dependencies in info.yml
- Drupal console is badass
- Generate entities, plugins, and more
- Plugins
- Field Widget/Formatter, Image Formatter, etc
- OOP allowed us to build on top of Ent Ref
Questions?
June 2019
drupalcampchattanooga.com
D8: Composer, Entities, and Complex Relationships
By James Candan
D8: Composer, Entities, and Complex Relationships
We'll review some of my findings while learning and building web applications in Drupal 8. Not solely for enterprise content management, but consider applications for business management, intranets, mobile app backends, SaaS, and more. We'll look at some excellent combinations of well supported modules and glue code to create flexible architectures.
- 2,124