Drupal → Создание простейшего модуля управления данными с помощью Entity API

24.04.2011

Ранее уже писал как управлять своими данными в Drupal 6. В этом посте покажу как сделать тоже самое в Drupal 7, но с применением модуля Entity API, который упрощает создание и администрирование своих сущностей.

Для примера создадим сущность Альбом (album) и административную страницу, на которой можно будет создать/отредактировать/удалить альбомы.

Административная страница

1. В .install файле модуля описываем таблицу, в которой будет храниться информация об альбомах:

/**
 * Implements hook_schema().
 */
function album_schema() {
  $schema['albums'] = array(
    'fields' => array(
      'aid' => array(
        'description' => 'Album ID',
        'type' => 'serial',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
      'title' => array(
        'description' => 'Album title',
        'type' => 'varchar',
        'length' => 100,
        'not null' => TRUE,
      ),
      'year' => array(
        'description' => 'Album year',
        'type' => 'int',
        'unsigned' => TRUE,
        'not null' => TRUE,
      ),
    ),
    'primary key' => array('aid'),
  );
 
  return $schema;
}

2. В хуке hook_entity_info() описываем сущность Альбом:

/**
 * Implements hook_entity_info().
 */
function album_entity_info() {
  return array(
    'album' => array(                               // Системное имя сущности
      'label' => t('Album'),                        // Человеко-понятное имя сущности
      'entity class' => 'Entity',                   // Класс сущности
      'controller class' => 'EntityAPIController',  // Контроллер сущности
      'base table' => 'albums',                     // Таблица, в которой хранится информация об альбомах
      'entity keys' => array(
        'id' => 'aid',                              // Название поля, в котором хранится id сущности (идентификатор альбома)
        'label' => 'title'                          // Название поля, в котором хранится label сущности (заголовок альбома)
      ),
      'admin ui' => array(
        'path' => 'admin/structure/albums'          // Путь, по которому будет доступна админка
      ),
      'access callback' => 'album_access',          // Имя функции, отвечающей за проверку прав доступа
      'module' => 'album',
    ),
  );
}

Благодаря тому, что мы указали entity class и controller class, модуль Entity API автоматически создаст для нашей сущности функции entity_create(), entity_save(), entity_delete(), entity_view() и entity_access().

3. Создаём функции album_access() и album_load() (это нужно для работы административной страницы):

/**
 * Access callback.
 */
function album_access($op, $entity, $account = NULL, $entity_type = 'album') {
  return user_access('administer site configuration'); // сущность будет доступна только администратору сайта
}

/**
 * Load album entity by id.
 */
function album_load($aid) {
  $result = entity_load('album', array($aid));
  return $result ? reset($result) : FALSE;
}

4. Описываем форму создания/редактирования сущности:

/**
 * Album entity form.
 */
function album_form($form, &$form_state, $album, $op = 'edit') {
  $form['title'] = array(
    '#title' => 'Название',
    '#description' => 'Название альбома',
    '#type' => 'textfield',
    '#default_value' => isset($album->title) ? $album->title : '',
    '#required' => true,
  );
 
  $form['year'] = array(
    '#title' => 'Год',
    '#description' => 'Год выхода альбома',
    '#type' => 'textfield',
    '#default_value' => isset($album->year) ? $album->year : '',
    '#required' => true,
    '#size' => 5,
  );
  
  $form['actions'] = array(
    '#type' => 'actions',
  );
  
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Сохранить',
  );
  
  return $form;
}

5. Добавляем каллбак для описанной выше формы:

/**
 * Album entity form submit.
 */
function album_form_submit(&$form, &$form_state) {
  $album = entity_ui_form_submit_build_entity($form, $form_state);
  $album->save();
  $form_state['redirect'] = 'admin/structure/albums';
}

Вот и всё. Исходники прилагаются.

Официальная документация по Entity API (довольно скудная).
Что такое сущности (видео).
Новая концепция сущностей (Entity) в Drupal 7.
Creating your own entities with Entity API
Как создать сущность в Drupal

Написанное актуально для
Drupal 7
Похожие записи

Комментарии

Очень полезно оказалось, спасибо

Vydrin_AP
20.06.2011, 18:41

Просто супер! Спасибо за статью! Уже третий день пытался сделать подобное, но никак не получалось.

Vydrin_AP
21.06.2011, 12:50

День добрый! Возник вопрос.

  $form['actions'] = array(
    '#type' => 'actions',
  );
 
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Сохранить',
  );

Это опечатка или сабмитов должно быть именно два?
И еще - сабмиты обязательно должны включаться в ['actions'] или это просто так сделано?

Заранее спасибо!

Vydrin_AP
21.06.2011, 12:52

Прошу прощения, не внимательно посмотрел код здесь, с ним все в порядке, но в прилагаемом для скачивания модуле как раз два сабмита - видимо опечатка.

  $form['actions'] = array(
    '#type' => 'actions',
  );

Принято в 7ке так делать, чтобы можно было легко добавлять дополнительные кнопки в форму не нарушая последовательность её элементов

Vydrin_AP
27.06.2011, 12:58

Спасибо andypost, я после этого коммента посмотрел ваше выступление про сущности, за него огромный респект ;)

А в прилагаемом архиве так и осталось два сабмита:

  $form['actions']['submit'] = array(
    '#type' => 'actions',
  );
  
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Сохранить',
  );
dansamara
26.07.2011, 18:56

Привет!

Смотрел Entity API, твой модуль, entity_test, но так и не понял, как сделать fieldable entity. В readme пишут "просто добавьте 'fieldable => true' в hook_entity_info, но чего-то оно работать не хочет - интерфейс полей не появляется.

Если без модуля EntityAPI, то всё ок, но хотелось бы с ним, он для ленивых :)

Ты это дело не смотрел?

dansamara
26.07.2011, 21:10

Всё конечно оказалось просто. Тупил из-за ошибки в коде.
Итак, для данного модуля нужно дополнить хук информации сущности:

/**
 * Реализация hook_entity_info()
 */
function album_entity_info() {
  return array(
    'album' => array(                               // Системное имя сущности
      'label' => t('Album'),                        // Человеко-понятное имя сущности
      'entity class' => 'Entity',                   // Класс сущности
      'controller class' => 'EntityAPIController',  // Контроллер сущности
      'base table' => 'albums',                     // Таблица, в которой хранится информация об альбомах
      'entity keys' => array(
        'id' => 'aid',                              // Название поля, в котором распологаются идентификаторы альбомов
        'label' => 'title'                          // Название поля, в котором распологаются названия альбомов
      ),
      'admin ui' => array(
        'path' => 'admin/structure/albums'          // Путь, по которому будет доступна админка
      ),
      'fieldable' => true,  // добавили эту строку
      'bundles' => array( // ...и этот массив
        'album' => array(
          'label' => t('album'),
          'admin' => array(
            'path' => 'admin/structure/albums',
            'access arguments' => array('administer site configuration'),
          ),
        ),
      ),
      'access callback' => 'album_access',          // Имя функции, отвечающей за проверку прав доступа
      'module' => 'album',
    ),
  );
}
dansamara
26.07.2011, 21:12

Ещё стоит добавить пункт меню для симпатичности:

/**
 * Реализация hook_menu()
 */
function album_menu() {
  $items['admin/structure/albums/list'] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  return $items;
}

И, для эстетов, сортировку встроенных полей:

/**
 * Реализация hook_field_extra_fields().
 */
function album_field_extra_fields() {
  $return = array();
  $return['album']['album'] = array(
    'form' => array(
      'title' => array(
        'label' => t('Title'),
        'description' => t('Album title'),
        'weight' => -10,
      ),
      'year' => array(
        'label' => t('Year'),
        'description' => t('Album year'),
        'weight' => -10,
      ),
    ),
  );
        
  return $return;
}

Эти пункты необязательны. То есть всё что нужно, это указать, что к сущности можно добавлять поля (fieldable = true) и прописать настойки для наборов добавляемых полей (bundles).

Может кто-то уже боролся с этой проблемой... Как можно комментарии подключить к своей сущности. Не вижу вариантов использования комментариев, встроенных в ядро, так как они заточены под ноды.

скорей всего никак, нужны комменты - создавайте свой тип материала

dansamara
31.08.2011, 20:25

Комменты никак не подключишь, т.к. они жёстко привязаны к нодам. См. например сохранение коммента. То есть даже не получиться сделать модуль-прослойку, которая бы хранила пару entity_id-comment_id.

Мдаа... Придется надеяться, что переделают в восьмерке.

dansamara
31.08.2011, 20:36

Ну можно создавать скрытую ноду, если очень надо :)

А как реализовать так, чтобы к каждому альбому можно было добавить список композиций? Каким образом реализовать такую сущность?

А можно небольшой пример как использовать модуль?

наверное так же с node reference. не работал с ним

а как сделать чтобы у сущности были ревизии? что-то в api найти никак не могу

Очень полезная статья, спасибо автору!
Для полного комплекта, по моему, не хватает только реализации вывода сущности на фронтэнд части сайта для показа посетителям

поспешил радоваться, форма сбрасывается

<?php
$q=db_select('albums','a');
//извлекаемые поля
$q->fields('a',array('title','year'));
//сортировка по году выпуска
$q->orderBy('a.year','asc');
//выполнение запроса
$res=$q->execute();

while($rec=$res->fetchAssoc()){
	echo $rec['title'].' - '.$rec['year'].'<br />';
}
?>

Как запихнуть таблицу в блок контента, чтобы сделать в дальнейшем кнопки сортировки?

Выводил до этого таблицу с помощью views, все здорово, но как допиливать кнопки?

Гость
06.04.2012, 13:19

Как добавить дополнительное поле, например Group? Добавил описание поля в соответствующие функции, но на выходе получил ошибку PDOException: SQLSTATE[42000].
При установки модуля таблица создалась как надо... код модуля:

http://pastebin.com/w8eZRRPn

С Друпалом только начинаю дружить...

Александр
10.04.2012, 00:34

Писал модуль с использование Entity API. Все получилось - очень интересная концепция. Но у меня проблема: не получается реализовать hook для сущности на мультиязычном сайте. Не подскажите в чем может быть проблема?

Александр
10.04.2012, 00:55

Аналогично Вашему примеру в описании сушности:

'admin ui' => array(
        'path' => 'admin/my_menu_item' 

Только что проверил: простым hook_menu тоже не получилось создать пункт меню. При этом на одноязычном сайте все окей.

Александр
10.04.2012, 01:06

В меню нет соответствующего пункта. Путь не регистрируется.

Александр
10.04.2012, 01:16

В вашем примере путь админки admin/structure/albums. Так вот у меня так такого мути нет. При попытке обратится к нему я попадаю admin/structure (в общем так и должно быть при условии, что путь admin/structure/albums не сущестует, поэтому я сделал вывод, что путь не регистрируется).

попробуй $items['albums'] = array(....
назвать по-другому
тоже такое было

Александр
10.04.2012, 01:19

Пробовал. Не помогло. При этом Devel (Entity Info) показывает сущность, где в качестве path указан описанный мною путь.

Так вот у меня так такого мути нет.

ну сбросьте кэш чтоли. мультиязычность тут не при чём

Александр
10.04.2012, 01:23

Спасибо Вам. Кеш я конечно сбрасывал уже и не раз. Пробовал на 3 разных сайтах запустить. И только на одном наработает, вот и решил, что мультиязычность. Все равно спасибо.

Непонятно из примера, как программно добавлять поля в сущность - если она состоит не только из заголовка и ID. И хрен где найдешь примеры

сущности в примере состоит не только из заголовка и id, есть поле year

А этот пример будет работать при добавлении полей через CCK - я правильно понимаю? Смущает сохранение одной строчкой - $album = entity_ui_form_submit_build_entity($form, $form_state);

Ну вы меня поняли. Через его премника - как правильно называть, FieldAPI ?

А что, album_form автоматом подхватывается, без описания? Или же просто не указали album_menu?
И если автоматом, то как быть, когда бандлов например два?

Мда, паршиво, entity_ui_form_submit_build_entity работает только со стандартно формируемым массивом формы.

Игорь
26.08.2012, 17:57

Добрый день!
Есть ещё такой модуль: http://drupal.org/project/eck - может с ним легче с сущностями работать? Про него можете что-нибудь сказать?

у меня тоже 'admin ui' не пашет :(
еще не понятно: модуль Entity API (http://drupal.org/project/entity) нужно ставить или нет?
ну и так же присоединяюсь к вопросу про форму - как она цепляется? потому что в названии функции имя модуля? А если у меня в одном модуле несколько сущностей, как мне с формами быть?

спасибо!

Гы, который день курю статью, а про Entity API сам себя запутал :) Спасть пора :)
Про 'admin ui' всем рекомендую курить http://drupal.org/node/1021576
Там как раз написано что функция формы должа иметь вид ENTITY_TYPE_form()
Курим дальше :)

Игорь
29.10.2012, 20:19

А как вывести в блоке - добавление bundle (новой сущности)?

Владимир
06.11.2012, 22:28

А почему при просмотре представления admin/structure/albums вызывается album_access, но переменная entity пустая?

Владимир
06.11.2012, 23:13

Ну а как же управлять правами доступа, если не знаешь о какой entity идет речь?

Владимир
06.11.2012, 23:21

Разве не должна вызываться функция album_access для каждой entity в представлении? Т.е. если у тебя 5 entity выводится в представлении то ф-ия вызывается 5 раз и в entity содержится каждая последующая entity. Разве не так это должно работать?

Владимир
06.11.2012, 23:36

Если у пользователя нет прав на entity, то получается представление (Views) ее все равно выведет, т.к. не проверяет права

admin/structure/albums это не представление, это административная страница

Владимир
06.11.2012, 23:44

Попробуй сделай представление - та же ситуация

http://drupal.org/node/1021576

This is the function named in 'access callback' property in the hook_entity_info() implementation, above. It should return TRUE if the provided user account should have access to administer your entity, and FALSE if not.

Владимир
07.11.2012, 00:06

"Управлять сущностью" довольно размытое понятие не говоря уже о том, что в описании вообще нет access callback на уровне сущности - только на уровне bundle. Давай представим, что пользователь имеет право видеть только альбомы созданные им, т.е. у нас появится поле типа author, как тогда поступить?

Владимир
07.11.2012, 00:08

Я не просто так спрашиваю, у меня такая же проблема. Вот одна из статей, которая поставляется вместе с модулем Entity, там такая же проблема... Уж не знаю проблема ли это, может все это должно фильтроваться другими методами, но для меня это как-то странно.

Владимир
07.11.2012, 19:47

Есть мысли? А то я себе уже всю голову сломал? :(

Для fieldable сущности кроме


comment-2460

Нужно также прописать добавления полей в форме:

function album_form($form, &$form_state, $album, $op = 'edit') {
  field_attach_form('album', $album, $form, $form_state);
  
  $form['title'] = array(
    '#title' => 'Название',
    '#description' => 'Название альбома',
    '#type' => 'textfield',
    '#default_value' => isset($album->title) ? $album->title : '',
    '#required' => true,
  );
 
  $form['year'] = array(
    '#title' => 'Год',
    '#description' => 'Год выхода альбома',
    '#type' => 'textfield',
    '#default_value' => isset($album->year) ? $album->year : '',
    '#required' => true,
    '#size' => 5,
  );
  
  $form['actions']['submit'] = array(
    '#type' => 'actions',
  );
  
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Сохранить',
  );
  
  return $form;
}
Андрей
19.01.2013, 00:50

Сделал свою сущность аналогичным образом, в .install файле в основной таблице есть поля целого типа и не обязательные, прописаны таким образом:

  $schema['book']['fields']['field_2_3'] = array(
      'description' => 'Statistical parameter of the book to the table of parameters.',
      'type' => 'int',
      'not null' => FALSE,
      'default' => NULL,   
  );  

Возникла проблема с такими полями - если их не заполняю в форме, то все равно в таблицу заносятся значения 0 вместо NULL и потом отображаются на странице отображения сущности.
Как с этим бороться, посоветуйте, пожалуйста, ибо уже перепробовал все что мог, вплоть до того, чтобы при сабмите делать unset всем таким form_state['values']['field_2_3'], которые были не заполнены. Все равно тут же в таблице базы появляются нолики!

Роман
28.01.2013, 14:56

Добрый день.
Вопросы, скорее всего, не совсем в тему, но, тем не менее, задам.

1. А вот если бы в данной сущности был не "Год альбома", а "Дата альбома", то каким образом можно к форме ввода/редактирования подключить виджет-календарик, с помощью которого можно было бы выбрать дату?

2. Каким образом можно сделать так, чтобы на форме просмотра альбомов метка содержала не название альбома, а, к примеру, название альбома + год выпуска ? Например, "Красный альбом (1987 год)" или "Белый альбом (1968 год)" ? Что для этого надо доопределить и переопределить ?

bernackiy.name
11.02.2013, 15:43

Касательно второго вопроса - если используете свой контроллер для работы с сущностью, объявите функцию:

protected function defaultLabel() {
        return $this->title.' ('.$this->year.' год)';
    }

Уверен, что и при использовании EntityAPIController это возможно.

Виктор Портон
17.02.2013, 00:18

Пункт в меню не появляется! См. также комментарий во второй строке в коде ниже.

function cleanetica_video2_menu() {
  $items['admin/structure/video'] = // если меняю здесь путь, пункт меню появляется
  array( 
    'title' => 'Video',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    //'type' => MENU_LOCAL_ACTION,
    'weight' => -10,
    'menu_name' => 'menu-admin-misc', // "Navigation" menu by default
    'access arguments' => array('administer site configuration'),
  );
  
  return $items;
}

function cleanetica_video2_entity_info() {
  return array(
    'cleanetica_video2' => array(                               // Системное имя сущности
      'label' => t('Video'),                        // Человеко-понятное имя сущности
      'entity class' => 'Entity',                   // Класс сущности
      'controller class' => 'EntityAPIController',  // Контроллер сущности
      'base table' => 'video',
      'entity keys' => array(
        'id' => 'id',                              // Название поля, в котором распологаются идентификаторы альбомов
        'label' => 'title'                          // Название поля, в котором распологаются названия альбомов
      ),
      'admin ui' => array(
        'path' => 'admin/structure/video'          // Путь, по которому будет доступна админка
      ),
      'access callback' => 'cleanetica_video2_access',          // Имя функции, отвечающей за проверку прав доступа
      'module' => 'cleanetica_video2',
      //'load hook' => 'cleanetica_video2_load',
    ),
  );
}

Видимо, cleanetica_video2_menu() и cleanetica_video2_entity_info() друг другу мешают, если в них прописан один путь 'admin/structure/video'. Что делать?!

Виктор Портон
17.02.2013, 00:36

Я решил проблему добавив в меню Misc ссылку на admin/structure/video вручную.

Добрый день! Прошу прощения за ламерский вопрос. Не могу разобраться с созданием сущности. Поставил модуль entity api. Сущность могу создать только с применением model api. Хотелось бы сделать как у Вас. И расскажите пожалуйста как создать административную страницу. Спасибо!

Я слышал про eck, просто хочу реализовать все как в примере, а при активации модуля Entity Api не получается создать сущность и административную страницу, подскажите пожалуйста как это сделать. Спасибо!

А можно создавать сразу несколько сущностей? Я хочу в схеме описать несколько таблиц, а в модуле работать с этими таблицами, но никак не получается создать формы для них и ссылки. И еще вопрос как подгружать данные в одну таблицу из другой (id)

Проблему с множественным созданием сущностей и работой с ними решил, все оказалось достаточно просто. Возник вопрос можно ли из одной формы добавлять данные сразу в несколько сущностей? стоит задача привязать несколько значений с характкристиками техники к магазинам.

Подскажите пожалуйста, как можно определить в схеме связанные таблицы, чтобы с ними можно было работать во views.

Если я создаю сущность с помощью модуля Entity API ,в которой будет только 2 bundles, надо ли описывать эти бандлы в hook_entity_info? Имеется ввиду часть в которой сущность указывается как бандл к другой сущности ("bundle of")

Всем привет! Подскажите пожалуйста как можно указать в entity_info для label поле даты в нормальном формате, в базе оно хранится в integer. Спасибо!

Игорь
25.03.2013, 14:49

как можно определить в схеме связанные таблицы

у таблиц должно быть общее поле, как например: node и user - общее uid

поле даты в нормальном формате

добавить поле дата в свою сущность. Хотя и с time() - очень удобно работать.

Игорь, про поле даты я не то имел ввиду. В entity info я указываю метку для таблицы, которая будет отображаться. 'label' => 'date'. date хранится в int. А хотелось бы отображать метку в понятном формате. Пока кроме как через jquery работать не хочет. Пытался всячески преобразовать метку напрямую, но результата не получил. И еще можно ли сформировать таблицу сущностей не из двух полей, а из нескольких?

Станислав
07.05.2013, 15:44

А если нужно сделать чтобы каждый пользователь мог редактировать и создавать только свои сущности, и не иметь доступ к чужим, подскажите способ, кто реализовывал подобное?

Игорь
07.05.2013, 21:31

Станислав - каждый пользователь мог редактировать и создавать только свои сущности

По-моему - нода вполне решит вашу задачу - зачем лезть в свои сущности?
Автор ноды редактирует только свою(и) странички(статьи).
Можно сделать сущность для роли, но не для каждого юзверя отдельную сущность?

Дмитрий
18.09.2013, 02:30

А как сделать что бы в списке альбомов, присутствовало поле год, аналогично как в примере для шестого друпала?

Роман
18.09.2013, 08:59

Думаю, здесь проще всего посмотреть, как это уже реализовано в несложных сущностях, которые уже есть или в ядре, или в дополнительных модулях.

Например, можно исследовать сущность, отвечающую за комментарии

Alexander
19.09.2013, 14:00

А вот что делать, если нужно добавить сортировку композиций в альбоме?

Виталий
29.08.2014, 21:35

А как привязать поле с таксомонией?

Просто благодарность.
Спасибо камрад за сайт, за твою работу.

добавил еще поле

 'tdate' => array(
        'description' => 'tdate',
        'type' => 'datetime',
         'mysql_type' => 'datetime',	
      ),

все ок, добавляет, сохраняет.
Все поля видны во Views, кроме этого поля даты. Как подружить его с Views?

Поставил, да, но не помогло. Пока копаюсь

Гость
08.01.2015, 15:00

function module_phone_form_submit(&$form, &$form_state) {
$phone = entity_ui_form_submit_build_entity($form, $form_state);
$phone->save();
$form_state['redirect'] = 'admin/structure/phone';
}

При выполнение текущего кода вылазит ошибка Fatal error: Call to undefined method stdClass::save() ПОМОГИИИИИИИИИИИИТЕ!

Гость
12.01.2015, 13:12

Разобрался =) в описании ентити инфо забыл добавить 'entity class' => 'Entity',

cheloveck1917
17.01.2015, 21:18

Столкнулся с небольшой странностью. У вас в модуле "Человеко-понятное имя сущности" - Album, а в админке пункт меню называется Albums, сделал по аналогии модуль и перевод к нему, но в админке на конце приписывается буква s, к примеру "Файлыs". Это глюк или так задумано? На каком этапе приписывается буква и как ее убрать?

Добрый день! Не могли бы подсказать, как в eck добавить поле типа дата? Просто пока решения не нашел, а очень нужно. Заранее спасибо!

Кирилл
13.08.2016, 10:28

Зато он полезен для порталов и срм систем, где требуются десятки модулей и сотни полей, а для обычных сайтов и интернет магазинов смысла нет, согласен.
я например создаю справочники на подобных сущностях, так как таксономия с большим числом данных не очень хорошо дружит, некоторые вспомогательные модули не работают

Добавить комментарий