xandeadx.ru Блог музицирующего веб-девелопера

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

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

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

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

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

/**
 * Реализация 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() описываем сущность Альбом:

/**
 * Реализация 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'          // Путь, по которому будет доступна админка
      ),
      '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() (это нужно для работы административной страницы):

/**
 * Проверка прав доступа
 */
function album_access($op, $entity, $account = NULL, $entity_type = 'album') {
  return user_access('administer site configuration'); // сущность будет доступна только администратору сайта
}
 
/**
 * Возвращает альбом по его идентификатору
 */
function album_load($aid) {
  $result = entity_load('album', array($aid));
  return $result ? reset($result) : FALSE;
}

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

/**
 * Форма создания/редактирования альбома
 */
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. Добавляем каллбак для описанной выше формы:

/**
 * Сохранение информации об альбоме
 */
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.x
Похожие записи

Комментарии RSS

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

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

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

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

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

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

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

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

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

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

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

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

Привет!

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

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

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

нет ещё

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

/**
 * Реализация 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',
    ),
  );
}

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

/**
 * Реализация 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).

супер :)

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

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

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

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

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

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

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

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

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

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

сделал по примеру
album manager работает
возникла проблема при создании собственной таблицы
http://www.drupal.ru/node/79313

кэш сбрасывали?

Благодарю, все заработало!

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

<?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, все здорово, но как допиливать кнопки?

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

http://pastebin.com/w8eZRRPn

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

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

какой хук? как реализовываете?

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

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

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

что именно не получается?

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

в каком меню?

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

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

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

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

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

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

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

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

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

в drupal 7 нет cck

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

будет

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

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

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

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

http://www.innoraft.com/blog/use-entity-reference-your-custom-entities-w...

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

спасибо!

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

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

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

а что в ней должно быть?

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

вы на вопрос не ответили

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

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

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

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

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.

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

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

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

Для 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;
}

Сделал свою сущность аналогичным образом, в .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'], которые были не заполнены. Все равно тут же в таблице базы появляются нолики!

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

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

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

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

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

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

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

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'. Что делать?!

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

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

Попробуйте ECK

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

возможно нужно поставить модуль Date. Views из коробки не имеет хэндлеров для обработки полей datetime
https://www.drupal.org/node/1104286

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

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() ПОМОГИИИИИИИИИИИИТЕ!

что-то не так делаете

Логично)

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

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

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

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

Не вижу смысла в решении.

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

Оставить комментарий

Содержимое этого поля является приватным и не будет отображаться публично. Если у вас есть аккаунт в Gravatar, привязанный к этому e-mail адресу, то он будет использован для отображения аватара.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Доступные HTML теги: <a> <i> <b> <strong> <code> <ul> <ol> <li> <blockquote> <em> <s>
  • Строки и параграфы переносятся автоматически.
  • Подсветка кода осуществляется с помощью тегов: <code>, <css>, <html>, <ini>, <javascript>, <sql>, <php>. Поддерживаемые стили выделения кода: <foo>, [foo].

Подробнее о форматировании