Last updated April 1, 2014. Created by heyrocker on October 11, 2012.
Edited by mikeryan, xtfer, layosis, vacho. 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
type: module
description: 'Manages example configuration.'
core: 8.x


example/example.module

The hook_menu_link_defaults() function defines an administrative menu entry for managing your configuration entities.

<?php
function example_menu_link_defaults() {
 
$links = array();
 
$links ['admin/config/system/example'] = array(
   
'link_title' => 'Example',
   
'description' => 'Configure examples',
   
'route_name' => 'example.list',
  );
  return
$links ;
}
?>

Routing

example/example.routing.yml

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

example.list:
  path: '/admin/config/system/example'
  defaults:
    _entity_list: 'example'
    _title: 'Example Configuration'
  requirements:
    _permission: 'administer site configuration'
example.add:
  path: '/admin/config/system/example/add'
  defaults:
    _entity_form: 'example.add'
    _title: 'Add example'
  requirements:
    _entity_access: 'example.add'
example.edit:
  path: '/admin/config/system/example/{example}'
  defaults:
    _entity_form: 'example.edit'
    _title: 'Edit example'
  requirements:
    _entity_access: 'example.edit'
example.delete:
  path: '/admin/config/system/example/{example}/delete'
  defaults:
    _entity_form: 'example.delete'
    _title: 'Delete example'
  requirements:
    _entity_access: 'example.delete'

example/example.local_actions.yml

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

example.add:
  route_name: 'example.add'
  title: 'Add Example'
  appears_on:
    - example.list

Entity type classes

example/lib/Drupal/example/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/lib/Drupal/example/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"),
*   controllers = {
*     "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
*     "list_builder" = "Drupal\example\Controller\ExampleListBuilder",
*     "form" = {
*       "add" = "Drupal\example\Form\ExampleFormController",
*       "edit" = "Drupal\example\Form\ExampleFormController",
*       "delete" = "Drupal\example\Form\ExampleDeleteForm"
*     }
*   },
*   config_prefix = "example",
*   admin_permission = "administer site configuration",
*   entity_keys = {
*     "id" = "id",
*     "label" = "label",
*     "uuid" = "uuid"
*   },
*   links = {
*     "edit-form" = "example.edit",
*     "delete-form" = "example.delete"
*   }
* )
*/
class Example extends ConfigEntityBase implements ExampleInterface {
 
/**
   * The Example ID.
   *
   * @var string
   */
 
public $id;
 
/**
   * The Example UUID.
   *
   * @var string
   */
 
public $uuid;
 
/**
   * 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.

Entity controller classes

example/lib/Drupal/example/Form/ExampleFormController.php

<?php
/**
* @file
* Contains \Drupal\example\Form\ExampleFormController.
*/
namespace Drupal\example\Form;
use
Drupal\Core\Entity\EntityInterface;
use
Drupal\Core\Entity\EntityFormController;
class
ExampleFormController extends EntityFormController {
 
/**
   * {@inheritdoc}
   */
 
public function form(array $form, array &$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' => 'example_load',
      ),
     
'#disabled' => !$example->isNew(),
    );
   
// You will need additional form elements for your custom properties.
   
return $form;
  }
 
/**
   * {@inheritdoc}
   */
 
public function save(array $form, array &$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['redirect'] = 'admin/config/system/example';
  }
 
/**
   * {@inheritdoc}
   */
 
public function delete(array $form, array &$form_state) {
   
$destination = array();
   
$request = $this->getRequest();
    if (
$request->query->has('destination')) {
     
$destination = drupal_get_destination();
     
$request->query->remove('destination');
    }
   
$form_state['redirect'] = array('admin/config/system/example/' . $this->entity->id() . '/delete', array('query' => $destination));
  }
}
?>

example/lib/Drupal/example/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/lib/Drupal/example/Form/ExampleDeleteForm.php

<?php
/**
* @file
* Contains \Drupal\example\Form\ExampleDeleteForm.
*/
namespace Drupal\example\Form;
use
Drupal\Core\Entity\EntityConfirmFormBase;
/**
* 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 getCancelRoute() {
    return array(
     
'route_name' => 'example.list',
    );
  }
 
/**
   * {@inheritdoc}
   */
 
public function getConfirmText() {
    return
$this->t('Delete');
  }
 
/**
   * {@inheritdoc}
   */
 
public function submit(array $form, array &$form_state) {
   
$this->entity->delete();
   
drupal_set_message($this->t('Category %label has been deleted.', array('%label' => $this->entity->label())));
   
$form_state['redirect'] = 'admin/config/system/example';
  }
}
?>

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

Comments

Isn't there a config file missing?

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