原文地址: https://www.drupal.org/node/2192175
原文版本: September 23, 2014.
This page provides an example of how to create a content entity type, with administration management pages, for Drupal 8.
Note: We try to improve the module as well as the documentation in sync with the development process of core. It is intended for usage with the latest dev version. You can also have a peek in core modules such as node and comment which make use of the ContentEntityType.
If you run into troubles while creating your own Content Entity be sure to check your watchdog log. Additionally inspect your apache log too. It will inform you about common mistakes such as missing use-statements for which Drupal 8 will only response with a white screen of death/WSOD (HTTP 500) in this case.
Set up module and admin menu entry
foo_bar/foo_bar.info.yml
name: Foo Bartype: moduledescription: 'Provides FooBar entity.'package: FooBarcore: 8.xdependencies: - entity
foo_bar/foo_bar.module
The hook_menu_link_defaults() function defines an administrative menu entry for managing your foo_bar entity.
<?php/** * @file * Contains Drupal\foo_bar\foo_bar.module *//** * Implements hook_permission(). */function foo_bar_permission() { return array( 'delete_foo_bar' => array( 'title' => t('Delete entity content.'), ), 'add_foo_bar' => array( 'title' => t('Add entity content'), ), 'view_foo_bar' => array( 'title' => t('View entity content'), ), 'edit_foo_bar' => array( 'title' => t('Edit entity content'), ), 'admin_foo_bar' => array( 'title' => t('Administer settings'), ), );}/** * Implements hook_theme(). */function bookmark_theme() { /* * You can implement this hook if you don't want an annoying watchdog message in your log. * However, it isn't required to implement it. */}?>
Routing
foo_bar/foo_bar.routing.yml
The routing.yml file defines the routes for the management pages: view, list, add, edit, delete and settings.
foo_bar.view: path: '/foo-bar/{foo_bar}' defaults: _entity_view: 'foo_bar' _title: 'Foo Bar Content' requirements: _permission: 'view_foo_bar'foo_bar.list: path: '/foo-bar/list' defaults: _entity_list: 'foo_bar' _title: 'Foo Bar Content List' requirements: _permission: 'view_foo_bar'foo_bar.add: path: '/foo-bar/add' defaults: _entity_form: foo_bar.add _title: 'Add foo bar Content' requirements: _permission: 'add_foo_bar'foo_bar.edit: path: '/foo-bar/{foo_bar}/edit' defaults: _entity_form: foo_bar.edit _title: 'Edit foo bar content' requirements: _permission: 'edit_foo_bar'foo_bar.delete: path: '/foo-bar/{foo_bar}/delete' defaults: _entity_form: foo_bar.delete _title: 'Delete foo bar Content ' requirements: _permission: 'delete_foo_bar'foo_bar.settings: path: 'admin/structure/foo_bar_settings' defaults: _form: '\Drupal\foo_bar\Entity\Form\FooBarSettingsForm' _title: 'FooBar Settings' requirements: _permission: 'admin_foo_bar'
foo_bar/foo_bar.links.action.yml
This makes the "Add" link appear on the List page.
foo_bar.add: route_name: foo_bar.add title: 'Add Foo Bar Content' appears_on: - foo_bar.list
foo_bar/foo_bar.links.task.yml
The "View/Edit/Delete" tabs will appear on the entity view page. The "Settings" tab will appear on the entity settings page.
foo_bar.settings_tab: route_name: foo_bar.settings title: 'Settings' base_route: foo_bar.settingsfoo_bar.view: route_name: foo_bar.view base_route: foo_bar.view title: 'View'foo_bar.page_edit: route_name: foo_bar.edit base_route: foo_bar.view title: Editfoo_bar.delete_confirm: route_name: foo_bar.delete base_route: foo_bar.view title: Delete weight: 10
Entity type classes
foo_bar/src/FooBarInterface.php
All additional fields, which are not entity_keys must be defined with a getter and a setter in your content entity interface.
<?php/** * @file * Contains \Drupal\foo_bar\FooBarInterface. */namespace Drupal\foo_bar;use Drupal\Core\Entity\EntityInterface;use Drupal\Core\Entity\EntityTypeInterface;/** * Provides an interface defining a Foo Bar entity. */interface FooBarInterface extends EntityInterface { /** * Returns the identifier. * * @return int * The entity identifier. */ public function id(); /** * Returns the entity UUID (Universally Unique Identifier). * * The UUID is guaranteed to be unique and can be used to identify an entity * across multiple systems. * * @return string * The UUID of the entity. */ public function uuid(); /** * Return the Value of Foo Bar Field. * * @return string * The content of the field. */ public function getFooBarField(); /** * Defines the base fields of the entity type. * * @param string $entity_type * Name of the entity type * * @return \Drupal\Core\Field\FieldDefinitionInterface[] * An array of entity field definitions, keyed by field name. */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type);}?>
foo_bar/src/Entity/FooBar.php
This file defines the content entity class.
If you want to use the same form for "add" and "edit" then simply use "default" for both in the @ContentEntityType Annotation.
Note: hook_schema() was deprecated. It is only required to implement baseFieldDefinitions() for your database schema.
<?php/** * @file * Contains \Drupal\foo_bar\Entity\FooBar. */namespace Drupal\foo_bar\Entity;use Drupal\Core\Entity\EntityStorageInterface;use Drupal\Core\Field\FieldDefinition;use Drupal\Core\Entity\ContentEntityBase;use Drupal\foo_bar\FooBarInterface;use Drupal\Core\Entity\EntityTypeInterface;/** * Defines the Foo Bar entity. * * @ContentEntityType( * id = "foo_bar", * label = @Translation("FooBar entity"), * handlers = { * "view_builder" = "Drupal\Core\Entity\EntityViewBuilder", * "list_builder" = "Drupal\foo_bar\Entity\Controller\FooBarListBuilder", * "form" = { * "add" = "Drupal\foo_bar\Entity\Form\FooBarFormController", * "edit" = "Drupal\foo_bar\Entity\Form\FooBarFormController", * "delete" = "Drupal\foo_bar\Entity\Form\FooBarDeleteForm" * }, * "translation" = "Drupal\content_translation\ContentTranslationController" * }, * base_table = "foo_bar", * admin_permission = "admin_foo_bar", * fieldable = TRUE, * translatable = TRUE, * entity_keys = { * "id" = "fbid", * "label" = "name", * "uuid" = "uuid" * }, * links = { * "edit-form" = "foo_bar.edit", * "delete-form" = "foo_bar.delete" * }, * field_ui_base_route = "foo_bar.settings", * ) */class FooBar extends ContentEntityBase implements FooBarInterface { /** * {@inheritdoc} */ public function id() { return $this->get('fbid')->value; } /** * {@inheritdoc} */ public function getFooBarField() { return $this->foo_bar_field->value; } /** * {@inheritdoc} */ public static function preCreate(EntityStorageInterface $storage_controller, array &$values) { parent::preCreate($storage_controller, $values); $values += array( 'user_id' => \Drupal::currentUser()->id(), ); } /** * {@inheritdoc} */ public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { $fields['fbid'] = FieldDefinition::create('integer') ->setLabel(t('ID')) ->setDescription(t('The ID of the FooBar entity.')) ->setReadOnly(TRUE); $fields['uuid'] = FieldDefinition::create('uuid') ->setLabel(t('UUID')) ->setDescription(t('The UUID of the FooBar entity.')) ->setReadOnly(TRUE); $fields['langcode'] = FieldDefinition::create('language') ->setLabel(t('Language code')) ->setDescription(t('The language code of theFooBar entity.')); $fields['name'] = FieldDefinition::create('string') ->setLabel(t('Name')) ->setDescription(t('The name of the FooBar entity.')) ->setTranslatable(TRUE) ->setPropertyConstraints('value', array('Length' => array('max' => 32))) ->setSettings(array( 'default_value' => '', 'max_length' => 255, 'text_processing' => 0, )) ->setDisplayOptions('view', array( 'label' => 'above', 'type' => 'string', 'weight' => -6, )) ->setDisplayOptions('form', array( 'type' => 'string', 'weight' => -6, )) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); $fields['type'] = FieldDefinition::create('string') ->setLabel(t('Type')) ->setDescription(t('The bundle of the FooBar entity.')) ->setRequired(TRUE); $fields['user_id'] = FieldDefinition::create('entity_reference') ->setLabel(t('User ID')) ->setDescription(t('The ID of the associated user.')) ->setSettings(array('target_type' => 'user')) ->setTranslatable(TRUE); $fields['foo_bar_field'] = FieldDefinition::create('string') ->setLabel(t('First FooBar Field')) ->setDescription(t('One field of the FooBar entity.')) ->setTranslatable(TRUE) ->setPropertyConstraints('value', array('Length' => array('max' => 32))) ->setSettings(array( 'default_value' => '', 'max_length' => 255, 'text_processing' => 0, )) ->setDisplayOptions('view', array( 'label' => 'above', 'type' => 'string', 'weight' => -5, )) ->setDisplayOptions('form', array( 'type' => 'string', 'weight' => -5, )) ->setDisplayConfigurable('form', TRUE) ->setDisplayConfigurable('view', TRUE); return $fields; }}?>
Controllers
foo_bar/src/Entity/Form/FooBarFormController.php
Define the form for adding and editing FooBar entity content. It is called by the '_entity_form' definition in the routing.
Note: You only need to add elements to the $fields[] which are not defined as setRequired(TRUE) and are without setDisplayOptions('form', array()) in your baseFieldDefinitions() .
<?php/** * @file * Definition of Drupal\foo_bar\Entity\Form\FooBarFormController. */namespace Drupal\foo_bar\Entity\Form;use Drupal\Core\Entity\ContentEntityForm;use Drupal\Core\Language\Language;use Drupal\Core\Form\FormStateInterface;/** * Form controller for the foo_bar entity edit forms. */class FooBarFormController extends ContentEntityForm { /** * Overrides Drupal\Core\Entity\EntityFormController::form(). */ public function form(array $form, FormStateInterface $form_state) { /* @var $entity \Drupal\foo_bar\Entity\FooBar */ $form = parent::form($form, $form_state); $entity = $this->entity; $form['user_id'] = array( '#type' => 'textfield', '#title' => 'UID', '#default_value' => $entity->user_id->target_id, '#size' => 60, '#maxlength' => 128, '#required' => TRUE, '#weight' => -10, ); $form['langcode'] = array( '#title' => t('Language'), '#type' => 'language_select', '#default_value' => $entity->getUntranslated()->language()->id, '#languages' => Language::STATE_ALL, ); $form['type'] = array( '#type' => 'hidden', '#default_value' => $entity->getEntityTypeId(), ); return $form; } /** * Overrides \Drupal\Core\Entity\EntityFormController::submit(). */ public function submit(array $form, FormStateInterface $form_state) { // Build the entity object from the submitted values. $entity = parent::submit($form, $form_state); $form_state->setRedirect('foo_bar.list'); return $entity; } /** * Overrides Drupal\Core\Entity\EntityFormController::save(). */ public function save(array $form, FormStateInterface $form_state) { $entity = $this->entity; $entity->save(); }}?>
foo_bar/src/Entity/Form/FooBarDeleteForm.php
Confirmation form when deleting foo_bar content
<?php/** * @file * Contains \Drupal\foo_bar\Entity\Form\FooBarDeleteForm */namespace Drupal\foo_bar\Entity\Form;use Drupal\Core\Entity\ContentEntityConfirmFormBase;use Drupal\Core\Url;use Drupal\Core\Form\FormStateInterface;/** * Provides a form for deleting a foo_bar entity. */class FooBarDeleteForm extends ContentEntityConfirmFormBase { /** * {@inheritdoc} */ public function getQuestion() { return t('Are you sure you want to delete entity %name?', array('%name' => $this->entity->label())); } /** * {@inheritdoc} */ public function getCancelUrl() { return new Url('foo_bar.list'); } /** * {@inheritdoc} */ public function getConfirmText() { return t('Delete'); } /** * {@inheritdoc} */ public function submit(array $form, FormStateInterface $form_state) { $this->entity->delete(); watchdog('content', '@type: deleted %title.', array('@type' => $this->entity->bundle(), '%title' => $this->entity->label())); $form_state->setRedirect('foo_bar.list'); }}?>
foo_bar/src/Entity/Controller/FooBarListBuilder.php
Define header and row content for the FooBar listing. The 'Operations' links are added automatically from the 'links' definition in the entityType annotation when the parent functions are called.
<?php/** * @file * Contains \Drupal\foo_bar\Entity\Controller\FooBarListBuilder. */namespace Drupal\foo_bar\Entity\Controller;use Drupal\Core\Entity\EntityInterface;use Drupal\Core\Entity\EntityListBuilder;/** * Provides a list controller for foo_bar entity. */class FooBarListBuilder extends EntityListBuilder { /** * {@inheritdoc} */ public function buildHeader() { $header['id'] = t('FooBarID'); $header['label'] = t('Label'); $header['foo-bar_field'] = t('FooBarField'); return $header + parent::buildHeader(); } /** * {@inheritdoc} */ public function buildRow(EntityInterface $entity) { /* @var $entity \Drupal\foo_bar\Entity\FooBar */ $row['id'] = $entity->id(); $row['label'] = l($this->getLabel($entity), 'foo-bar/' . $entity->id()); $row['foo_bar_field'] = $entity->getFooBarField(); return $row + parent::buildRow($entity); }}?>
Field Settings
foo_bar/src/Entity/Form/FooBarSettingsForm.php
Create a settings form for FooBar. Fields can be manage from here.
<?php/** * @file * Defines Drupal\foo_bar\Entity\Form\FooBarSettingsForm. */namespace Drupal\foo_bar\Entity\Form;use Drupal\Core\Form\FormBase;use Drupal\Core\Form\FormStateInterface;class FooBarSettingsForm extends FormBase { /** * Returns a unique string identifying the form. * * @return string * The unique string identifying the form. */ public function getFormId() { return 'foo_bar_settings'; } /** * Form submission handler. * * @param array $form * An associative array containing the structure of the form. * @param array $form_state * An associative array containing the current state of the form. */ public function submitForm(array &$form, FormStateInterface $form_state) { // Empty implementation of the abstract submit class. } /** * Define the form used for FooBar settings. * @return array * Form definition array. * * @param array $form * An associative array containing the structure of the form. * @param array $form_state * An associative array containing the current state of the form. */ public function buildForm(array $form, FormStateInterface $form_state) { $form['foo_bar_settings']['#markup'] = 'Settings form for FooBar. Manage field settings here.'; return $form; }}?>