Last updated February 6, 2016. Created on October 11, 2012.
Edited by joachim, justgable, TravisCarden, Kyna. Log in to edit this page.

This page provides an example of how to create a configuration entity type, with administration management pages, for Drupal 8. For an introduction to the concepts of simple configuration vs. configuration entities, see https://drupal.org/node/2120523

Set up module and admin menu entry

example/example.info.yml

name: Example
description: 'Manages example configuration.'
package: Example

type: module
core: 8.x

Routing

example/example.routing.yml

The routing.yml file defines the routes for the management pages: list, add, edit, delete.

entity.example.collection:
  path: '/admin/config/system/example'
  defaults:
    _entity_list: 'example'
    _title: 'Example Configuration'
  requirements:
    _permission: 'administer site configuration'

entity.example.add_form:
  path: '/admin/config/system/example/add'
  defaults:
    _entity_form: 'entity.example.add_form'
    _title: 'Add example'
  requirements:
    _permission: 'administer site configuration'

entity.example.edit_form:
  path: '/admin/config/system/example/{example}'
  defaults:
    _entity_form: 'example.edit'
    _title: 'Edit example'
  requirements:
    _permission: 'administer site configuration'

entity.example.delete_form:
  path: '/admin/config/system/example/{example}/delete'
  defaults:
    _entity_form: 'example.delete'
    _title: 'Delete example'
  requirements:
    _permission: 'administer site configuration'

example/example.links.action.yml

This makes the "Add" link appear on the List page.

entity.example.add_form:
  route_name: 'entity.example.add_form'
  title: 'Add Example'
  appears_on:
    - entity.example.collection

Entity type classes

example/src/ExampleInterface.php

Assuming that your configuration entity has properties, you will need to define some set/get methods on an interface.

<?php
/**
 * @file
 * Contains \Drupal\example\ExampleInterface.
 */

namespace Drupal\example;

use Drupal\Core\Config\Entity\ConfigEntityInterface;

/**
 * Provides an interface defining a Example entity.
 */
interface ExampleInterface extends ConfigEntityInterface {
  // Add get/set methods for your configuration properties here.
}
?>

example/src/Entity/Example.php

This file defines the configuration entity class.

<?php
/**
 * @file
 * Contains \Drupal\example\Entity\Example.
 */

namespace Drupal\example\Entity;

use Drupal\Core\Config\Entity\ConfigEntityBase;
use Drupal\example\ExampleInterface;

/**
 * Defines the Example entity.
 *
 * @ConfigEntityType(
 *   id = "example",
 *   label = @Translation("Example"),
 *   handlers = {
 *     "list_builder" = "Drupal\example\Controller\ExampleListBuilder",
 *     "form" = {
 *       "add" = "Drupal\example\Form\ExampleForm",
 *       "edit" = "Drupal\example\Form\ExampleForm",
 *       "delete" = "Drupal\example\Form\ExampleDeleteForm",
 *     }
 *   },
 *   config_prefix = "example",
 *   admin_permission = "administer site configuration",
 *   entity_keys = {
 *     "id" = "id",
 *     "label" = "label",
 *   },
 *   links = {
 *     "edit-form" = "/admin/config/system/example/{example}",
 *     "delete-form" = "/admin/config/system/example/{example}/delete",
 *   }
 * )
 */
class Example extends ConfigEntityBase implements ExampleInterface {

  /**
   * The Example ID.
   *
   * @var string
   */
  public $id;

  /**
   * The Example label.
   *
   * @var string
   */
  public $label;

  // Your specific configuration property get/set methods go here,
  // implementing the interface.
}
?>

The admin_permission key automatically allows all access for users with that permission. In case more logic is required, a custom access controller can be specified.

Configuration schema file

example/config/schema/example.schema.yml

example.example.*:
  type: config_entity
  label: 'Example config'
  mapping:
    id:
      type: string
      label: 'ID'
    label:
        type: label
        label: 'Label'

in example.schema.yml add the properties/attributes defined in \Drupal\example\Entity\Example

example.example.* is a configuration variable to refer to our class properties/attributes and you can specify different variable name for your entity by adding "config_prefix" e.g :


@ConfigEntityType(
..
... config_prefix = "variable_name" ...

then you can refer to it like the following :

example.variable_name.*: ....

for more information on Configuration schema see Configuration schema/metadata

Entity controller classes

example/src/Form/ExampleForm.php

<?php
/**
 * @file
 * Contains \Drupal\example\Form\ExampleForm.
 */

namespace Drupal\example\Form;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityForm;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Form\FormStateInterface;

class ExampleForm extends EntityForm {
 
  /**
   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
   *   The entity query.
   */
  public function __construct(QueryFactory $entity_query) {
    $this->entityQuery = $entity_query;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity.query')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);

    $example = $this->entity;

    $form['label'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#maxlength' => 255,
      '#default_value' => $example->label(),
      '#description' => $this->t("Label for the Example."),
      '#required' => TRUE,
    );
    $form['id'] = array(
      '#type' => 'machine_name',
      '#default_value' => $example->id(),
      '#machine_name' => array(
        'exists' => array($this, 'exist'),
      ),
      '#disabled' => !$example->isNew(),
    );

    // You will need additional form elements for your custom properties.

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $example = $this->entity;
    $status = $example->save();

    if ($status) {
      drupal_set_message($this->t('Saved the %label Example.', array(
        '%label' => $example->label(),
      )));
    }
    else {
      drupal_set_message($this->t('The %label Example was not saved.', array(
        '%label' => $example->label(),
      )));
    }

    $form_state->setRedirect('entity.example.collection');
  }

  public function exist($id) {
    $entity = $this->entityQuery->get('example')
      ->condition('id', $id)
      ->execute();
    return (bool) $entity;
  }
}
?>

example/src/Controller/ExampleListBuilder.php

<?php
/**
 * @file
 * Contains \Drupal\example\Controller\ExampleListBuilder.
 */

namespace Drupal\example\Controller;

use Drupal\Core\Config\Entity\ConfigEntityListBuilder;
use Drupal\Core\Entity\EntityInterface;

/**
 * Provides a listing of Example.
 */
class ExampleListBuilder extends ConfigEntityListBuilder {

  /**
   * {@inheritdoc}
   */
  public function buildHeader() {
    $header['label'] = $this->t('Example');
    $header['id'] = $this->t('Machine name');
    return $header + parent::buildHeader();
  }

  /**
   * {@inheritdoc}
   */
  public function buildRow(EntityInterface $entity) {
    $row['label'] = $this->getLabel($entity);
    $row['id'] = $entity->id();

    // You probably want a few more properties here...

    return $row + parent::buildRow($entity);
  }

}
?>

example/src/Form/ExampleDeleteForm.php

<?php
/**
 * @file
 * Contains \Drupal\example\Form\ExampleDeleteForm.
 */

namespace Drupal\example\Form;

use Drupal\Core\Entity\EntityConfirmFormBase;
use Drupal\Core\Url;
use Drupal\Core\Form\FormStateInterface;

/**
 * Builds the form to delete a Example.
 */

class ExampleDeleteForm extends EntityConfirmFormBase {

  /**
   * {@inheritdoc}
   */
  public function getQuestion() {
    return $this->t('Are you sure you want to delete %name?', array('%name' => $this->entity->label()));
  }

  /**
   * {@inheritdoc}
   */
  public function getCancelUrl() {
    return new Url('entity.example.collection');
  }

  /**
   * {@inheritdoc}
   */
  public function getConfirmText() {
    return $this->t('Delete');
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->entity->delete();
    drupal_set_message($this->t('Category %label has been deleted.', array('%label' => $this->entity->label())));

    $form_state->setRedirectUrl($this->getCancelUrl());
  }
}
?>

Looking for support? Visit the Drupal.org forums, or join #drupal-support in IRC.

Comments

ivanjaros’s picture

Isn't there a config file missing?

vacho’s picture

In file example/example.routing.yml

this code:

example.add:
  path: '/admin/config/system/example/add'
  defaults:
    _entity_form: example.add
    _title: 'Add example'
  requirements:
    _entity_access: 'example.add'

y replace by
example.add:
  path: '/admin/config/system/example/add'
  defaults:
    _entity_form: example.add
    _title: 'Add example'
  requirements:
    _permission: 'administer content types'

similar to what I found at Node entity.
Otherwise I can not "add" because I do not find prevent permissions where set
jienckebd’s picture

Great tutorial!

In example/src/Entity/Example.php, I think:

"list_builder" = "Drupal\example\ExampleListBuilder",

should be:

"list_builder" = "Drupal\example\Controller\ExampleListBuilder",

Kyna’s picture

Nice tuto !

And I confirm :
"list_builder" = "Drupal\example\ExampleListBuilder",
"list_builder" = "Drupal\example\Controller\ExampleListBuilder",

And :
public function submitForm(array $form, FormStateInterface $form_state) {
public function submitForm(array &$form, FormStateInterface $form_state) {

I have edited this tutorial for fix this errors.

andypost’s picture

all over core list builders libe in module namespace
It's nothing about controller - it is Builder

joachim’s picture

It looks like the 'add' and 'edit' form properties in the entity annotation have been replaced with 'default' for the common form.