Контент


Описание полей Jelly

Стартовая статья про замечательный модуль Jelly не могла вместить всей многочисленной информации по использованию данного ORM‘а, поэтому планирую продолжать цикл «точечными» статьями. В данный момент попробую систематизировать знания, необходимые при объявлении моделей. В общем, поговорим про описание полей Jelly.

Вступление

Для начала вспомним, как мы описываем свойства модели:

class Model_Auth_User extends Jelly_Model
{
	public static function initialize(Jelly_Meta $meta)
        {
		$meta->name_key('username')
			->sorting(array('username' => 'ASC'))
			->fields(array(
			'id' => new Field_Primary,
			'username' => new Field_String(array(
				'unique' => TRUE,
				'rules' => array(
						'not_empty' => NULL,
						'max_length' => array(32),
						'min_length' => array(3),
						'regex' => array('/^[\pL_.-]+$/ui')
					)
				)),
			'password' => new Field_Password(array(
				'hash_with' => array(Auth::instance(), 'hash_password'),
				'rules' => array(
					'not_empty' => NULL,
					'max_length' => array(50),
					'min_length' => array(6)
				)
			)),
			'password_confirm' => new Field_Password(array(
				'in_db' => FALSE,
				'callbacks' => array(
					'matches' => array('Model_Auth_User', '_check_password_matches')
				),
				'rules' => array(
					'not_empty' => NULL,
					'max_length' => array(50),
					'min_length' => array(6)
				)
			)),
			'email' => new Field_Email(array(
				'unique' => TRUE
			)),
			'logins' => new Field_Integer(array(
				'default' => 0
			)),
			'last_login' => new Field_Timestamp,
			'tokens' => new Field_HasMany(array(
				'foreign' => 'user_token'
			)),
			'roles' => new Field_ManyToMany
		));
    }
}

Все происходит в статическом методе initialize(), где мы должны сохранить информацию о связанной с моделью таблице (имя таблицы, имена полей, связи). Для разных типов полей есть предопределенные классы, у каждого из них могут быть свои особенности в настройке и работе. Так как более-менее адекватного руководства я не нашел (плохо искал?), на свет появилась данная статья.

Общие свойства полей (класс Field_Core)

Все доступные классы полей являются предками класса Field_Core. Таким образом, все поля в моделях Jelly работают со следующими свойствами:

  • column — имя поля в БД. Обычно совпадает с именем поля в модели, но это необязательно.

    Если при создании поля в конструктор передавать строковое значение, а не массив (т.е. new Field_Primary('myid')), то это значение будет расцениваться как переданное значение свойства column.

  • primary — флаг первичного ключа (FALSE по умолчанию). Обычно явно не указывается, достаточно для нужного поля установить тип Field_Primary (первичный ключ).
  • unique — флаг уникальности поля (FALSE по умолчанию). Автоматически выставляется в TRUE для полей Field_Primary. Добавление этого флага означает, что при валидации модели для данного поля будет применен дополнительный callback _is_unique(). Фактически это приводит к дополнительному запросу БД.
  • in_db — флаг наличия в БД (TRUE по умолчанию). Если установить FALSE, получим аналог ignored_columns из ORM.
  • default — значение по умолчанию для поля (изначально NULL).
  • null — конвертировать или нет пустые (empty()) значения в NULL. По умолчанию FALSE.

Кроме этих свойств есть еще несколько, не относящихся к СУБД, но важных при описании полей.

  • description — описание поля. Оно напрямую нигде не используется, но может быть использовано, скажем, при построении форм (для расширенной подсказки о назначении поля). По умолчанию содержит пустую строку.
  • name — имя поля для генерации элемента формы (т.е. HTML-атрибут name).
  • label — имя поля в более удобном и понятном человеку формате, чем name. Обычно используется для тэгов LABEL формы. Если не указано явно, примет значение свойства column, обработанное методом inflector::humanize().
  • filters, rules, callbacks — эти три свойства используются для валидации полей. Там все стандартно, как и в ORM.

Логические поля (Field_Boolean)

Вроде бы тут все ясно — либо ДА, либо НЕТ. Но разработчики предпочли гибкость, в результате даже в таком простом поле есть что понастраивать:

  • true — значение, которое надо записывать в БД при положительном значении поля (масло масляное, ага). По умолчанию 1.
  • label_true — описание положительного значения (по умолчанию «Yes«). Может быть использовано при выводе значения на экран или для генерации формы.
  • false — аналогично свойству true, только действует для отрицательного значения поля. По умолчанию, естественно, равно 0.
  • label_false — как вы уже наверняка догадались, метка для свойства false. По умолчанию «No«.

Скорее всего вы эти свойства менять и не будете. Но сама возможность не может не радовать.

Метка времени (Field_Timestamp)

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

  • format — формат метки времени, в котором значение должно храниться в БД. Синтаксис можно посмотреть в описании функции date(). NULL по умолчанию, т.е. никакого преобразования не происходит.
  • pretty_format — формат метки времени, предназначенный для человеческих глаз. Данный формат используется при генерации элемента формы стандартным шаблоном Jelly. Согласитесь, не каждого обрадует значение ‘2010-09-09‘. Синтаксис аналогичен свойству format. Значение по умолчанию ‘r’ (дата в формате RFC 2822, например ‘Thu, 09 Sep 2010 12:00:00 +0300′).
  • auto_now_create — флаг, отвечающий за автоматическое заполнение поля текущей меткой времени при первоначальном сохранении (т.е. при создании записи). По умолчанию FALSE.
  • auto_now_update — аналогично auto_now_create, но действует при обновлении записи. По умолчанию FALSE.

Несмотря на все форматы, сама метка времени в поле хранится в виде целого числа (UNIX timestamp), т.е. можно не напрягать голову мыслями о текущем формате времени.

Числа с плавающей запятой (Field_Float)

Данный класс располагает специальным свойство places, который определяет количество знаков после запятой в итоговом числе. Если данное свойство отлично от NULL и является числом (is_numeric()), то значение будет округлено до places знаков.

Стоит обратить внимание, что отрицательные и дробные значения тоже являются числами, и вполне могут быть использованы в данном свойстве. Например, если установить places = 2.3, то значение будет округлено до двух знаков после запятой (дробная часть отбрасывается). Если же значение отрицательное, то округлять будут целую часть числа, например при places = -2.3 число 12345.6789 будет сохранено в базу как 12300. Это может быть полезно при форматировании дробных чисел.

Строковые и целочисленные поля (Field_String, Field_Text, Field_Integer)

Тут все без каких-либо особенностей, просто значение явно преобразовывается в нужный тип (строковый и числовой соответственно).

На данный момент я не нашел различий между Field_String и Field_Text в части реализации. В комментариях написано, что Field_Text предназначен для работы с большими текстами, возможно это еще в @TODO.

Поле ограниченного выбора (Field_Enum)

Данное поле предназначено для работы с неким заранее известным диапазоном значений. Например, это может быть пол (мужской/женский/не указан) или месяц года. Сами значения для выбора указываются в свойстве choices в виде массива. Можно использовать стандартный массив вида array('male', 'female', 'unknown'), а можно использовать осмысленные значения для ключей: array('1' => 'male', 2 => 'female', 0 => 'unknown'). Это удобно при генерации списков выбора (select list), когда пользователю показывается текстовое описание, за которым скрывается конкретный идентификатор значения в БД.

Если указанное пользователем значение в списке choices отсутствует, то оно будет заменено значением из default.

Вычисляемое поле (Field_Expression)

Очень интересный тип данных. Он позволяет перекладывать на СУБД функции вычисления каких-либо значений на основе полученных из таблицы данных. Например, у нас есть поле `birthday` (дата рождения). Чтобы получить возраст пользователя, создаем вычисляемое поле:

'age'   => new Field_Expression(array(
      'column' => DB::expr('(YEAR(CURRENT_DATE)-YEAR(birthday)) - (RIGHT(CURRENT_DATE,5)<RIGHT(birthday,5))')
      )),

В результате запрос к БД на выборку данных из таблицы будет содержать дополнительное поле ‘age‘, заполняемое содержимым нашего выражения. Так как поле физически в таблице не существует, параметр in_db автоматически устанавливается в FALSE.

Точнее, должно было содержать, по задумке авторов. На самом деле у меня Field_Expression не заработал, пока я не внес кое-какие поправки в класс Jelly_Meta_Core. Тикет создан, жду реакцию разработчиков.

Поле для хранения пароля (Field_Password)

Пароль по сути ничем не отличается от обычного строкового поля (поэтому Field_Password extends Field_String), за исключением уже привычной нам традиции шифровать значение при сохранении модели. Единственная доступная нам опция — свойство hash_with, которое содержит имя функции для шифрования (по умолчанию «sha1» — указывать без скобочек!).

Slug-поле (Field_Slug)

Долго ломал голову, как же по-русски назвать этот термин, ничего не придумал :) Slug — это такое нормализованная строка, где могут быть использованы только маленькие латинские буквы, дефисы и слэши (я привел мнение разработчиков Jelly). Все остальное заменяется на дефисы. Например, строка ‘Hello, World!‘ будет автоматом преобразована в ‘hello-world‘. Никаких свойств нет, хотя в принципе напрашивается как минимум возможность установки собственного диапазона допустимых символов.

Поле адреса электронной почты (Field_Email)

Обычное текстовое (Field_Text) поле, только автоматически будет применяться правило Validate::email() при сохранении.

Прячем объекты и массивы (Field_Serialized)

Данное поле позволяет работать с сериализованными (serialized) переменными. Зачем это надо? Бывает удобно хранить массивы или целые объекты в БД (например, конфигурация ACL или прочие структуры в том же духе). При заполнении значения происходит попытка восстановить (unserialize) объект, а при сохранении записи в БД — обратная запаковка (сериализация). Единственный минус (и то, возможно, только мне так кажется) — вызовы serialize() и unserialize() предваряются «собачкой» для подавления ошибок.

Загрузка файлов (Field_File)

Данное поле применимо для сохранения загруженных пользователем файлов. Имеется два параметра:

  • path — путь к директории для сохранения. Естественно, директория должна быть доступна для записи. Это обязательное поле, у которого нет значения по умолчанию.
  • delete_old_file — данный флаг используется при замене одного файла другим. Если TRUE (а это значение по умолчанию), то при удачной загрузке новый файл заменит старый не только в БД, но и на диске (т.е. старый файл будет удален физически).

Сам по себе файл лежит на диске, а в поле хранится его имя. Таким образом, доступ к файлу можно получить через $this->path.$this->value.

Пора заканчивать

На этом стандартные типы полей Jelly закончились. Остались неразобранными поля для описания связей, но это уже будет тема для отдельной статьи.

Google Bookmarks Digg Reddit del.icio.us Ma.gnolia Technorati Slashdot Yahoo My Web News2.ru БобрДобр.ru RUmarkz Ваау! Memori.ru rucity.com МоёМесто.ru Mister Wong

Опубликовано в Kohana3.

Теги: , , .


Комментарии (17)

Будьте в курсе обсуждения, подпишитесь на RSS ленту комментариев к этой записи.

  1. Max Kamashev пишет:

    Спасибо за обзор.

    В частности ваша статья помогла перевесить мой выбор в сторону Jelly вместо hive :)

    Очень хочется почитать про связи с формами и нестандартные поля, например картинки.

  2. Max Kamashev пишет:

    Собственно сам расскажу.

    В unstable ветке уже давно была реализована привязка к изображениям :)

  3. taggi пишет:

    Спасибо за статью, все отлично. Достаточно давно пользуюсь Jelly, один вопрос, куда девается 50 мс процессорного времени при создании объекта, когда запрос к базе выполняется за 0.3 мс ? На кой черт в объектах orm аккамулирется столько мусора по мимо данных выборки?

  4. biakaveron пишет:

    Скорее всего это связано с первоначальной инициализацией объекта, ведь создается объект Meta с информацией о модели и т.д. Замеряли, сколько уходит на повторное создание объекта того же класса (в рамках одного реквеста)? По идее засчет кэширования должно получиться значительно меньше.

  5. taggi пишет:

    2biakaveron
    Повторное создание конечно быстрей, но тоже далеко от идеала. Заметил, что на findfile тратится также 3/4 всего времени загрузки, лучше применять какой-нибудь ускоритель. С ORM пока борюсь кешированием, хотя если кешировать сами получаемые объекты, то тоже приходит абзац, из-за большого размера сериализация превращается в кошмар и по времени выигрыша практически нет.

  6. madmax пишет:

    А есть ли модуль авторизации/регистрации типа А1 на Jelly ? Auth не катит, у него поля не редактируются (

  7. madmax пишет:

    @Max Kamashev

    *В unstable ветке уже давно была реализована привязка к изображениям :) *

    Покажите пример, пожалуйста.

  8. biakaveron пишет:

    A1 поддерживает разные виды ORM :)

  9. Max Kamashev пишет:

    @madmax посмотрите вот на этот модуль http://github.com/raeldc/jelly-auth и для него даже существует демка http://github.com/rob/jelly-auth-demo

    По поводу image вот пример http://github.com/jonathangeiger/kohana-jelly/blob/unstable/classes/jelly/field/image.php , но я на ветку unstable не смог перейти, так как там они отказались от генерации форм, и переписал их компонент под себя.

    Если очень надо, то я выложу класс для изображений. Сейчас у меня гости и просто некогда :)

  10. xbagir пишет:

    @taggi
    А в сравнении под нагрузкой, кто себя лучше показывает родная ORM или Jelly ?

    @biakaveron
    Здорово! А я все по старинке юзаю старый вариант, где еще поддержки Спринга нет :)

    @Max Kamashev
    Спасибо! Если не сложно, выложите пожалуйста, интересно посмотреть.

    Вообще кохане не хватает сайта, со сборником рецептов.

    Иван, может организуете в своем блоке такую возможность? =)

  11. Max Kamashev пишет:

    @xbagir
    Написал статью как использовать изображения. Не думал что много выйдет :)
    http://uk0.us/2010/09/jelly-dobavlyaem-tip-polya-izobrazhenie/

  12. xbagir пишет:

    @Max Kamashev
    Спасибо большое.)) Только поправьте, пожалуйста, код — там теги местами проскакивают

  13. taggi пишет:

    2xbagir
    Родную ORM не пробывал, но скорость Jelly что-то совсем не радует. Пока справляюсь кешированием, подумываю не вернуться ли к обычным моделям, теряя в гибкости.

  14. uhamurad пишет:

    что то in_db не хочет работать. В контроллере заполняю поле, которое в модели отмечено как in_db=FALSE, а во view оно уже содержит NULL

  15. biakaveron пишет:

    @uhamurad
    А при чем тут in_db и NULL? Поля в принципе (не считая сохранения) обрабатываются одинаково, независимо от того, реальное оно или псевдо.

Продолжение обсуждения

  1. Jelly – Добавляем тип поля «Изображение» | Записочки ссылается на эту запись on 20 сентября 2010

    [...] дополнение к отличной статье о Jelly я решил рассказать о реализации типа поля [...]

  2. Объявление связей в Jelly | Изучаем Web ссылается на эту запись on 9 ноября 2010

    [...] недавно мы разбирали стандартные поля Jelly, теперь рассмотрим все, что нужно для описания связей. [...]



Можно включить подсветку кода: <code><pre lang="">...</pre></code>
Разрешены некоторые HTML теги

или используйте trackback.