В предыдущем уроке я рассказал как создать свою сущность. Сегодня я покажу, как связать сущность с модулем Views. Не забываем скачать его, если еще не сделали этого. Для решения данной задачи существует два способа и сейчас мы их рассмотрим.
Способ 1
Сразу отмечу, что данный способ подходит только если сущность была создана с помощью модуля Entity API. В хуке hook_entity_info() для нашей сущности устанавливаем views controller class:
'views controller class' => 'EntityDefaultViewsController'
Хук стал выглядеть следующим образом:
/**
* Implements hook_entity_info().
*/
function product_entity_info() {
$return = array(
'product' => array(
'label' => t('Product'),
'entity class' => 'Entity',
'controller class' => 'EntityAPIController',
'views controller class' => 'EntityDefaultViewsController',
'base table' => 'product',
'uri callback' => 'product_uri',
'fieldable' => TRUE,
'entity keys' => array(
'id' => 'id',
),
'bundles' => array(
'product' => array(
'label' => t('Product'),
'admin' => array(
'path' => 'admin/config/product/products',
'access arguments' => array('configure products settings'),
),
),
),
'view modes' => array(
'full' => array(
'label' => t('Full'),
'custom settings' => TRUE,
),
'administrator' => array(
'label' => t('Administrator'),
'custom settings' => TRUE,
),
),
'module' => 'product',
),
);
return $return;
}
Вот и все, чистим кеш и можем создавать вьюхи.
Способ 2
Для начала имплементируем хук hook_views_api():
/**
* Implements hook_views_api().
*/
function product_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'product') . '/views',
);
}
Далее в корне модуля создаем каталог views, а внутри него файл product.views.inc. Открываем этот файл и имплементируем в этом файле хук hook_views_data(). В этом хуке мы опишем доступные поля, фильтры, сортировки и связи:
/**
* Implements hook_views_data().
*/
function product_views_data() {
$data = array();
// Значение ключа 'group' будет использовано в UI для группировки полей,
// фильтров, сортировки и т.д.
$data['product']['table']['group'] = t('Product');
// Описываем базовую таблицу, из которой Views будет доставать данные.
$data['product']['table']['base'] = array(
'field' => 'id', // Поле идентификатора.
'title' => t('Product'),
);
// Указываем, что таблица хранит данные об сущности.
$data['product']['table']['entity type'] = 'product';
// Описываем поле "id".
$data['product']['id'] = array(
'title' => t('Product ID'), // Человеко-понятное название поля, которое отображается в UI.
'help' => t('The unique internal identifier of the product.'), // Описание поля, которое отображается в UI.
// Информация для вывода id.
'field' => array(
'handler' => 'product_handler_field_product', // Обработчик, который будет выводить значение поля.
'click sortable' => TRUE, // Используются для сортировки при отображении данных в таблице.
),
// Информация для принятия id в качестве аргумента.
'argument' => array(
'handler' => 'product_handler_argument_product_id', // Обработчик, который будет принимать аргумент, и фильтровать данные по нему.
'name field' => 'id', // Поле для отображения summary.
'numeric' => TRUE,
'validate type' => 'id',
),
// Информация для принятия id в качестве фильтра.
'filter' => array(
'handler' => 'views_handler_filter_numeric', // Обработчик, который будет фильтровать данные.
),
// Информация для сортировки по id.
'sort' => array(
'handler' => 'views_handler_sort', // Обработчик, который будет сортировать данные.
),
);
// Описываем поле "заголовок".
$data['product']['title'] = array(
'title' => t('Title'),
'help' => t('The product title.'),
// Информация для вывода заголовка.
'field' => array(
'field' => 'title',
'group' => t('Product'),
'handler' => 'product_handler_field_product',
'click sortable' => TRUE,
'link_to_product_default' => TRUE,
),
// Информация для принятия заголовка в качестве аргумента.
'argument' => array(
'handler' => 'views_handler_argument_string',
),
// Информация для принятия заголовка в качестве фильтра.
'filter' => array(
'handler' => 'views_handler_filter_string',
),
// Информация для сортировки по заголовку.
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Описываем поле "Дата создания".
$data['product']['created'] = array(
'title' => t('Post date'),
'help' => t('The date the product was created.'),
// Информация для вывода даты создания.
'field' => array(
'handler' => 'views_handler_field_date',
'click sortable' => TRUE,
),
// Информация для принятия даты создания в качестве фильтра.
'filter' => array(
'handler' => 'views_handler_filter_date',
),
// Информация для сортировки по дате создания.
'sort' => array(
'handler' => 'views_handler_sort_date',
),
);
// Описываем поле "Дата обновления".
$data['product']['changed'] = array(
'title' => t('Updated date'),
'help' => t('The date the product was last updated.'),
// Информация для вывода даты обновления.
'field' => array(
'handler' => 'views_handler_field_date',
'click sortable' => TRUE,
),
// Информация для принятия даты обновления в качестве фильтра.
'filter' => array(
'handler' => 'views_handler_filter_date',
),
// Информация для сортировки по дате обновления.
'sort' => array(
'handler' => 'views_handler_sort_date',
),
);
// Описываем поле "Статус".
$data['product']['status'] = array(
'title' => t('Product status'),
'help' => t('The workflow status of the product.'),
// Информация для вывода статуса.
'field' => array(
'handler' => 'product_handler_field_product_status',
'click sortable' => TRUE,
),
// Информация для принятия статуса в качестве фильтра.
'filter' => array(
'handler' => 'product_handler_filter_product_status',
),
// Информация для сортировки по статусу.
'sort' => array(
'handler' => 'views_handler_sort',
),
);
// Описываем поле "uid".
$data['product']['uid'] = array(
'title' => t('User uid'),
'help' => t('The user author. If you need more fields than the uid add the content: author relationship'),
// Информация для связи таблицы "product" с таблицей "users".
'relationship' => array(
'title' => t('Author'), // Человеко-понятное название связи, которое отображается в UI.
'help' => t('Relate product to the user who created it.'), // Описание связи, которое отображается в UI.
'handler' => 'views_handler_relationship',
'base' => 'users', // Название таблицы, которая будет присоединена, в данном прмиере это таблица "users".
'field' => 'uid', // По какому полю таблицы будут связаны.
'label' => t('author'), // Ключевое слово связи, которое используется в UI.
),
// Информация для вывода uid.
'field' => array(
'handler' => 'views_handler_field_user',
),
// Информация для принятия uid в качестве аргумента.
'argument' => array(
'handler' => 'views_handler_argument_numeric',
),
// Информация для принятия uid в качестве фильтра.
'filter' => array(
'handler' => 'views_handler_filter_user_name',
),
);
// Описываем поле с допустимыми операциями над продуктом.
$data['product']['operations'] = array(
// Информация для операций.
'field' => array(
'title' => t('Operations links'),
'help' => t('Display all the available operations links for the product.'),
'handler' => 'product_handler_field_product_operations',
),
);
return $data;
}
На что здесь стоит обратить внимание - на обработчики. В данном примере у нас используются стандартные вьюзовские и несколько самописных.
Обработчики Views
- views_handler_relationship;
- views_handler_field_date;
- views_handler_field_user;
- views_handler_argument_string;
- views_handler_filter_date;
- views_handler_filter_user_name;
- views_handler_filter_numeric;
- views_handler_filter_string;
- views_handler_sort;
- views_handler_sort_date.
Самописные обработчики
- product_handler_field_product;
- product_handler_field_product_status;
- product_handler_field_product_operations;
- product_handler_argument_product_id;
- product_handler_filter_product_status.
Следующим шагом будет написание наших обработчиков. В каталоге views нашего модуля создаем каталог handlers. В этом каталоге для каждого обработчика создаем файл, название которого совпадает с названием обработчика:
- product_handler_field_product.inc
- product_handler_field_product_status.inc
- product_handler_field_product_operations.inc
- product_handler_argument_product_id.inc
- product_handler_filter_product_status.inc
Содержимое product_handler_field_product.inc:
<?php
/**
* @file
* Contains the product id field handler.
*/
/**
* Field handler to provide simple renderer that allows linking to a product.
*/
class product_handler_field_product extends views_handler_field {
function init(&$view, &$options) {
parent::init($view, $options);
// Если выбрана опция "Выводить в виде ссылки", до достаем id продукта.
if (!empty($this->options['link_to_product'])) {
$this->additional_fields['id'] = array('table' => 'product', 'field' => 'id');
}
}
/**
* Обьявляем настройки для поля.
*/
function option_definition() {
$options = parent::option_definition();
$options['link_to_product'] = array('default' => 'none');
return $options;
}
/**
* Описываем форму с натсройками.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['link_to_product'] = array(
'#title' => t('Link this field to the original piece of product'),
'#description' => t("Enable to override this field's links."),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['link_to_product']),
);
parent::options_form($form, $form_state);
}
/**
* Выводим поле в виде ссылки на продукт.
*/
function render_link($data, $values) {
if (!empty($this->options['link_to_product']) && !empty($this->additional_fields['id'])) {
$id = $this->get_value($values, 'id');
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['path'] = 'product/' . $id;
}
return $data;
}
/**
* Выводим поле.
*/
function render($values) {
$value = $this->get_value($values);
return $this->render_link($this->sanitize_value($value), $values);
}
}
Содержимое product_handler_field_product_status.inc:
<?php
/**
* @file
* Contains the product status field handler.
*/
/**
* Field handler a product status into its readable form.
*/
class product_handler_field_product_status extends product_handler_field_product {
function render($values) {
$id = $this->get_value($values);
$value = product_status_get_title($id);
return $this->render_link($this->sanitize_value($value), $values);
}
}
Содержимое product_handler_field_product_operations.inc:
<?php
/**
* @file
* Contains the product's operations field handler.
*/
/**
* Field handler to present a product's operations links.
*/
class product_handler_field_product_operations extends views_handler_field {
function construct() {
parent::construct();
$this->additional_fields['id'] = 'id';
}
function option_definition() {
$options = parent::option_definition();
$options['add_destination'] = TRUE;
return $options;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['add_destination'] = array(
'#type' => 'checkbox',
'#title' => t('Add a destination parameter to edit and delete operation links so users return to this View on form submission.'),
'#default_value' => $this->options['add_destination'],
);
}
function query() {
$this->ensure_my_table();
$this->add_additional_fields();
}
function render($values) {
$id = $this->get_value($values, 'id');
$links = array();
$links['edit'] = array(
'title' => t('Edit'),
'href' => 'product/' . $id . '/edit',
'html' => FALSE,
);
$links['delete'] = array(
'title' => t('Delete'),
'href' => 'product/' . $id . '/delete',
'html' => FALSE,
);
if ($this->options['add_destination']) {
$links['edit']['query'] = drupal_get_destination();
$links['delete']['query'] = drupal_get_destination();
}
return theme('links', array('links' => $links, 'attributes' => array('class' => array('links', 'inline', 'operations'))));
}
}
Содержимое product_handler_argument_product_id.inc:
<?php
/**
* @file
* Provide a product id argument handler.
*/
/**
* Argument handler to accept a product id.
*/
class product_handler_argument_product_id extends views_handler_argument_numeric {
/**
* Override the behavior of title().
*/
function title_query() {
$titles = array();
$result = db_query("SELECT p.title FROM {product} p WHERE p.id IN (:ids)", array(':ids' => $this->value));
foreach ($result as $product) {
$titles[] = check_plain($product->title);
}
return $titles;
}
}
Содержимое product_handler_filter_product_status.inc:
<?php
/**
* @file
* Provide a product status filter handler.
*/
/**
* Filter by product status.
*/
class product_handler_filter_product_status extends views_handler_filter_in_operator {
function get_value_options() {
if (!isset($this->value_options)) {
$this->value_title = t('Status');
$this->value_options = array();
foreach (product_status_get_title() as $key => $title) {
$this->value_options[$key] = $title;
}
}
}
}
Далее открываем файл product.info и добавляем в него следующие строки:
dependencies[] = views
files[] = views/handlers/product_handler_field_product.inc
files[] = views/handlers/product_handler_field_product_status.inc
files[] = views/handlers/product_handler_field_product_operations.inc
files[] = views/handlers/product_handler_filter_product_status.inc
files[] = views/handlers/product_handler_argument_product_id.inc
Вот и все. Теперь наша сущность связана с Views.