Создание надежного кода для полей, в седьмом Друпале
Это перевод моего блогпоста Writing robust code that uses fields, in Drupal 7
В семерке изменился способ прямого доступа к полям (cck в друпале 6.x). В шестерке мы пишем:
<?php
$field_val = $node->field_yourfield[0]['value'];
?>В семерке уже надо писать:
<?php
$field_val = $node->field_yourfield[LANGUAGE_NONE][0]['value'];
?>(во всяком случае, так рекомендуют писать официальные доки).
То есть, у нас появилось разделение значений поля по языкам.
Я уж не знаю, насколько это упростило создание кода, который хорошо работает с несколькими языками (сложных мультиязычных проектов в d7 я пока не делал) - надеюсь, упростило серьезно, но для "обычных" сайтов с одним активным языком этот подход добавляет некоторую головную боль разработчикам.
Проблема здесь в том, что надеяться на правильную работу константы LANGUAGE_NONE нельзя!
Если админ сайта включает модуль Locale, и, например, активирует англ. язык, ваш код надо будет менять на что-то типа:
<?php
$field_val = $node->field_yourfield['en'][0]['value'];
?>Очевидно, что программистам нужен более надежный способ доступа к значением полей, вне зависимсти от языковых настроек на сайте.
Первый подход: field_language()
Первый способ который я попробовал - определение активного языка для поля и ноды.
"Главный" язык контента:
http://api.drupal.org/api/drupal/developer--globals.php/global/language_...
Для поля:
http://api.drupal.org/api/drupal/modules--field--field.multilingual.inc/...
Но это работает не совсем так как я ожидал - когда ноды только создаются, если нода создается на англ. языке, все еще приходится использовать константу LANGUAGE_NONE. (Не могу дать 100% гарантию что это работает именно так, потому что уже прошло некоторое кол-во времени с момента моих разбирательств - но проблемы были в том что приходилось в разные этапы жизни ноды работать то с LANGUAGE_NONE то с ключом языка ('en', 'ru')
<?php
$language = field_language('node', $node, 'field_yourfield');
$field_val = $node->field_yourfield[$language][0]['value'];
?>Я погуглил и нашел
Второй подход: field_get_items()
http://www.davereid.net/content/hlkd7fotw-field-get-items
<?php
$field_val = field_get_items('node', $node, 'field_yourfield');
?>который работает неплохо. Но этот подход не совершенен - т.к. в одну строку кода не получится дернуть, например, второе значение в массиве multiple поля. (Я имею ввиду, что не получится сделать field_get_items('node', $node, 'field_yourfield')[1] - ну, во всяком случае, до php5.4) .
Так же, если вам надо быстрый доступ до пяти полей ноды, придется вызывать field_get_items() пять раз, по разу на каждое поле, а значит ваш код будет выглядеть... не идеально, мягко говоря.
и вот третий подход который я обнаружил, мне он показался самым удобным:
Третий подход: entity_metadata_wrapper
entity_metadata_wrapper это вспомогательный объект из клевого модуля Entity (fago - мой герой)
вот как выглядит его использование:
<?php
$obj = entity_metadata_wrapper('node', $node);
$field = $obj->field_yourfield->value();
?>ну, неплохо - но пока не супер :) Что действительно круто, так это то что можно используя entity_metadata_wrapper подгружать объекты reference (поля user reference, node reference) на лету:
<?php
$involved_users = array();
//grab usernames from user reference field of a node
$project = entity_metadata_wrapper('node', $node);
// field_users is user reference field
foreach ($project->field_users as $acc) {
$involved_users[] = $acc->value()->name;
}
var_dump($involved_users);
?>когда мы вызываем метод value(), entity_metadata_wrapper знает, что поле - это user reference поле, и подгружает нужный аккаунт пользователя на лету. В шестерке это выглядело бы так:
<?php
// Drupal6 code
$involved_users = array();
//grab usernames from user reference field of a node
// field_users is user reference field, $project is node
foreach ($project->field_users as $acc) {
$acc_object = user_load($acc['uid']);
$involved_users[] = $acc_object->name;
}
var_dump($involved_users);
?>Что еще интересно - $project->field_users это не массив, это объект, т.е. можно вызвать $project->field_users->value() чтобы получить массив всех аккаунтов на которое поле ссылается (говорим про multiple поле, опять же).
В то же время, этот объект поддерживает доступ как к массиву, т.е. можно использовать $project->field_users[0] или скормить field_users foreach'у как в примере выше. Если говорить языком PHP5, класс объекта поля реализует интерфейсы IteratorAggregate, ArrayAccess и Countable.
Еще немного примеров из readme.txt модуля entity:
<?php
$wrapper->author->mail = 'sepp@example.com';
?>Чтобы получить текстовое значение, очищенное для безопасного вывода на экран, можно использовать
$wrapper->title->value(array('sanitize' => TRUE));
(в примере - получение заголовка ноды или entity). Если свойство возвращается "очищенным" по умолчанию, например body у ноды, возможно понадобится получить "неочищенное" значение. Для этого есть опция 'decode', которая гарантирует что все теги будут убраны, а HTML сущности раскодированы:
$wrapper->body->value->value(array('decode' => TRUE));
Т.е. так возвращаются данные в виде в каком они должны показываться пользователю. Если вам надо совсем сырые данные, без процессинга:
$wrapper->body->value->raw();
Еще можно сохранять ноды (и сущности, конечно):
<?php
$node = node_load(323);
$wrapper = entity_metadata_wrapper('node', $wrapper);
$wrapper->title = 'New title for the node';
$wrapper->save();
?>Небольшая подсказка: для создания сущностей используем entity_create() или шорткат - entity_property_values_create_entity - которая принимает массив значений.
Я свй выбор сделал и в основном использую третий подход в своем коде.
Апдейт от Tom Nightingale:
Надо упомянуть что entity_metadata_wrapper работает только с полями которые "описали" свою структуру Entity API через свои хуки property_info(). Многие contrib модули все еще должны обновиться чтобы правильно заработать.

Comments
This quiver federation hand down expedition what is turned practically the seeds that conditions crate spot into grand tour self-locking bridewell betwixt their mother. http://azukev.com
Post new comment