Что она делает? Модель разделяет область кода от представления, отделяя код приложения.
Модель это общая точка доступа к базе данных, а именно, к определенной таблице в базе данных. По умолчанию, каждая модель использует таблицу, чье имя – это множественное число от имени модели, например: модель 'User' использует таблицу 'users'. Модель может также включать в себя правила верификации данных, информацию об ассоциациях, и индивидуальные методы к таблице, которую использует. Вот как простая модель 'User' может выглядить в Cake:
<?php
//AppModel дает вам всю функциональность моделей Cake
class User extends AppModel
{
// Всегда полезно включать эту переменную.
var $name = 'User';
// Это используется для верификации данных, подробнее в соответсвующей главе.
var $validate = array();
// Вы также можете объявить ассоциации.
// Посмотрите раздел 6.3 для более подробной информации.
var $hasMany = array('Image' =>
array('className' => 'Image')
);
// Вы также можете включить собственные функции:
function makeInactive($uid)
{
//Вставьте свой код сюда...
}
}
?>
Со стороны PHP, модели это классы расширения App Model? класса. App Model? класс изначально объявлен в директории cake/, но если вы хотите создать собственный, поместите его в app/app_model.php. Он должен включать в себя методы, которые будут делиться двумя или более моделями. Он сам по себе расширит класс Модели которая находится в стандартной библиотеке Cake в cake/libs/model.php.
Этот раздел будет рассказывать о самых частоиспользуемых функциях моделей Cake, не стоит забывать, что полезно использовать http://api.cakephp.org для полной справки.
Пример индивидуального метода таблицы в модели – это пара методов для скрывания/отображения постов в блоге
<?php
class Post extends AppModel
{
var $name = 'Post';
function hide ($id=null)
{
if ($id)
{
$this->id = $id;
$this->saveField('hidden', '1');
}
}
function unhide ($id=null)
{
if ($id)
{
$this->id = $id;
$this->saveField('hidden', '0');
}
}
}
?>
Ниже несколько стандартных способов извлечь ваши данные, используя модель:
Возвращает определенные поля количеством не больше $limit, соответсвующих $conditions, начинается просмотр со страницы $page (по умолчанию 1). $conditions должна выглядить как бы она выглядела в SQL-запросе: $conditions="race='wookie' AND thermal_detonators > 3", например.
Когда опция $recursive установлена на целое значение выше 1, операция findAll() попытается вернуть модели ассоциированные с найденным операцией findAll(). Если у ваших данных несколько владельцев, то рекурсив операции findAll() в вашей модели Property возвратит все ассоциированные модели.
Возвращает определенные (или же все, если нету специфики) поля из первой записи, соответствующие $conditions.
Если опция $recursive установлена на целое значение между 1 и 3, операция find() попытается вернуть модели ассоциированные с найденными операцией find(). Рекурсивный поиск может работать на три уровня вглубь. Если у ваших данных несколько владельцев, рекурсивная операция find() в вашей модели Property вернет ассоциативные модели до трех уровней вглубь.
Эти волшебные функции можно использовать как ярлык для поиска в ваших таблицах строчки с указанным полем, и указанным значением. Примерами (использования в контроллере) могут быть:
<?
$this->Post->findByTitle('My First Blog Post');
$this->Author->findByLastName('Rogers');
$this->Property->findAllByState('AZ');
$this->Specimen->findAllByKingdom('Animalia');
?>
Вовзращает масив с соседними моделями (только с определенными полями), определенными по $field и $value, отсортированными по SQL-условиям, $conditions.
Это полезно в случае, когда вы хотите разместить ссылки «Предыдущее» и «Следующее», что позволят пользователям путешествовать по каким-то упорядоченным, последовательным данным в вашей модели. Это работает только с полями, имеющими нумерацию или датирование.
<?
class ImagesController extends AppController
{
function view($id)
{
// Скажем мы хотим показать изображение...
$this->set('image', $this->Image->find("id = $id");
// Но также мы хотим чтобы у нас были предыдущие и следующие изображения...
$this->set('neighbours', $this->Image->findNeighbours(null, 'id', $id);
}
}
?>
Возвращает как строку одно поле из первой записи, соответствующей $conditions отсортированных по $order.
Возвращает количество записей, что соответствуют данному условию.
Это функция — ярлык к получению списка пар ключевых значений — в особенности для создания тега select в html из списка ваших моделей. Используйте параметры $conditions, $order и $limit также как в findAll(). $keyPath и $valuePath для указания модели, где именно искать ключи и значения для генерирования списка. Например, если вы хотите сгенерировать список ролей, базируемый на модели Role. Полный запрос может выглядеть похоже на это:
<?
$this->set(
'Roles',
$this->Role->generateList(null, 'role_name ASC', null, '{n}.Role.id', '{n}.Role.role_name')
);
?>
Используйте эту функцию, чтобы получить поля и их значения из ныне загруженной записи, или записи определенной по $id.
Пожалуйста отметьте что операция read() сделает выборку только с первого уровня ассоциативных моделей, не обращая внимания на значение $recursive в модели. Чтобы получить дополнительные уровни, используйте find() и findAll().
Настраеваемые SQL-запросы можно сделать, используя query() и методы execute() модели. Разница между ними в том, что query() используется для настройки SQL-запросов (результаты которой возвращаются), а execute() используется для настройки SQL-команд (которые не требует возврата результатов).
<?php
class Post extends AppModel
{
var $name = 'Post';
function posterFirstName() {
$ret = $this->query("SELECT first_name FROM posters_table WHERE poster_id = 1");
$firstName = $ret[0]['first_name'];
return $firstName;
}
}
?>
Большинство модельных поисковиков подразумевают вовлечение пересылки параметров условий разными способами. Простейший способ приблизиться к этому – использовать фрагмент SQL-выражения WHERE, но если вам нужно больше контроля, вы можете использовать масивы. Масивы яснее и легче читать, а также легче строить запросы. Этот синтаксис также разбивает элементы запроса (поля, значения, операторы и т.д.) на более понятные, манипулируемые части. Это позволяет Cake генерировать более результативные запросы, гарантируя подходящий синтаксис SQL.
Самое простой масивный запрос выглядит так:
Структура даже не требует объяснений: это найдет любой пост, название которого будет соответсвовать This is a post. Отметьте, что мы могли использовать только title как имя поля, но создавая запросы, полезно указывать и имя модели, так как это улучшает четкость кода и помогает предотвратить несостыковки в будущем. Что насчет других типов соответствий? Все так же просто. Скажем мы хотим найти все посты где в названии нет This is a post:
Все что мы добавили это '<>' перед выражением. Cake может проанализировать любой действительный оператор SQL, включая выражения как LIKE, BETWEEN или REGEX, так же как вы оставляете место между оператором и выражением или значением. Одно исключение в стиле (...) соответсвия. Скажем вы хотите найти посты, названия которых соответсвуют одному из списка значений:
Добавление дополнительных фильтров в условия это так же просто как добавления дополнительных пар ключ/значение в масив:
<?
array
(
"Post.title" => array("First post", "Second post", "Third post"),
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
?>
<?
array
("or" =>
array
(
"Post.title" => array("First post", "Second post", "Third post"),
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks"))
)
)
?>
<?
array
("Author.name" => "Bob", "or" => array
(
"Post.title" => "LIKE %magic%",
"Post.created" => "> " . date('Y-m-d', strtotime("-2 weeks")
)
)
?>
Чтобы сохранить что-либо, вам нужно снабдить сохранение данными, которые нужно записать. Данные передаваемые в метод save() должны быть размещены в следующей форме:
<?
Array
(
[ModelName] => Array
(
[fieldname1] => 'value'
[fieldname2] => 'value'
)
)
?>
В случае, если вам нужно чтобы ваши данные были отправлены в контроллер таким способ, будет проще использовать HTML Хелпер, потому что он создает элементы формы, называя их именно так, как ожидает этого Cake. Вы не обязаны его использовать, однако: только убедитесь, что имена элементов формы выглядят как data[Modelname][fieldname]. Однако, использование $html->input('Model/fieldname') — проще.
Форма данных автоматически форматируется и помещается в $this->data в ваш контроллер, так что сохранение ваших данных из веб-формы – это дело одного клика. Функция редактирования для вашего собственного контроллера может выглядить так:
<?
function edit($id) {
//Заметьте: Нужная модель автоматически загружена для нас в $this->Property.
// Проверяем есть ли у нас форма для данных...
if (empty($this->data)) {
$this->Property->id = $id;
$this->data = $this->Property->read();//размножаем поля формы для данной строки
} else {
// Вот здесь мы пытаемся сохранить наши данные. Автоматическая верификация.
if ($this->Property->save($this->data['Property'])) {
//Выводить сообщение и перенаправление.
$this->flash('Ваша информация сохранена.', '/properties/view/'.$this->data['Property']['id'], 2);
}
//если в каких-то полях данные не правильные или сохранение провалилось, форма сообщит
}
}
?>
Другие полезные функции сохранения:
Удаляет модель, выбранную по $id.
Если эта модель ассоциирована с другими моделями, и ключ зависимости был установлен в ассоциативном масиве, этот метод также удалил те ассоциативные модели, если $cascade присвоено значение true.
Возвращает true при успешном выполнении.
Используется для сохранения значения отдельного поля.
Возвращает ID записи, которая создана последней.
Мы добавили некоторые колбеки моделей, которые позволяют вам добраться до кода до или после определенной операции модели. Чтобы получить эту функциональность в ваших приложениях, используйте следующие параметры и перепишите эти функции в ваши модели Cake.
beforeFind() колбек вызывается сразу перед началом операции поиска. Поместить любой передующий поиску код сюда. Когда вы перепишете это в свою модель, верните значение true, когда захотите чтобы начался поиск, или false, когда захотите отменить его.
Используйте этот колбек, чтобы модифицировать результаты, которые вернулись после операции поиска, или впишите любой другой после-поисковый код. Параметры для этой функции – вернувшиеся результаты из операции поиска модели, а вернувшееся значение – это модифицированные результаты.
Используйте этот колбек для модифицирования данных перед их проверкой. Это также используется для добавления дополнительных, более сложных правил проверки, используя Model::invalidate(). В этом контексте, модель данных доступна через $this->data. Эта функция также должна возвращать true, иначе выполнение save() будет отменено.
Поместите любой передующий сохранению код в эту функцию. Эта функция выполняется сразу после проверки данных (только в случае что данные были проверены, иначе save() отменяется, а этот колбек не выполняется), но перед тем как данные сохранены. Это функция также должна возвращать true если вы хотите, чтобы операция сохранения продолжалась, и false если хотите чтобы была отменена.
Одно из использований beforeSave может быть формирования времени и даты для сохранения в специфическом месте базы данных:
<?
// Дата/время поля созданы HTML-хелпером:
// Этот код будет виден в отображении
$html->dayOptionTag('Event/start');
$html->monthOptionTag('Event/start');
$html->yearOptionTag('Event/start');
$html->hourOptionTag('Event/start');
$html->minuteOptionTag('Event/start');
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
// Функция колбека модели, используемая для того, чтобы сшить
// данные о дате в месте для сохранения.
// Этот код будет виден в модели Event:
function beforeSave()
{
$this->data['Event']['start'] = $this->_getDate('Event', 'start');
return true;
}
function _getDate($model, $field)
{
return date('Y-m-d H:i:s', mktime(
intval($this->data[$model][$field . '_hour']),
intval($this->data[$model][$field . '_min']),
null,
intval($this->data[$model][$field . '_month']),
intval($this->data[$model][$field . '_day']),
intval($this->data[$model][$field . '_year'])));
}
?>
Поместите любой код, который будет выполнятся после каждого сохранения в колбеке.
Поместите любой передующий удалению код в эту функцию.Это функция должна возвращать true если вы хотите чтобы удаление продолжилось или false, чтобы отменилось.
Поместите любой код, который будет выполнятся после каждого удаления в этот метод колбека.
Есть несколько специальных переменных, которые вы можете установить, если хотите получить доступ к функциональности Cake, создавая свою модель:
Если эта модель относится к таблице базы данных, и главный ключ (primary key) не называется 'id', используйте эту переменную, чтобы сообщить Cake'у имя главного ключа.
Это устанавливает число уровней, которые вы хотите чтобы Cake выбирал в операциях find() и findAll().
Представьте что у вас есть группы (Groups) в которых множество пользователей (Users), у которых будет много статей (Articles).
Model::recursive options
$recursive = 0 Cake делает выборку данных группы
$recursive = 1 Cake делает выборку группы и ассоциирует ее с пользователями
$recursive = 2 Cake делает выборку группы, ассоциирует ее с пользователями, а пользователи ассоциируются со статьями
Говорит Cake'у делать ли доступным транзакции для этой модели (например начать/совершить/откатить). Значение логического типа. Доступно только для баз данных с поддержкой.
Если таблица базы данных, которую вы хотите использовать называется не во множественном числе имени модели (и вы не хотите переименовывать таблицу), укажите этой переменной имя таблицы, которые вы хотите использовать в этой модели.
Масив, используемый для проверки входящих в эту модель данных. См. главу «Верификация данных».
Помниче что настройки базы данных можно устанавливать в /app/config/database.php? Используйте эту переменную, чтобы переключаться между ними – просто используйте имя переменной с настройками соединения с базой данных, которую вы создали в конфигурационном файле. По умолчанию, вы должны были догадаться, 'default'.
Одна из самых сильных возможностей Cake PHP? это построение отношений предоставленных моделью. В Cake PHP?, связи между таблицами управляются через ассоциации. Ассоциации — это клей между логическими единицами, состоящими в отношениях.
Есть четыре типа ассоциаций в Cake PHP?:
Когда ассоциации между моделями объявлены, Cake автоматически сделает выборку моделей пребывающих в отношении с моделью, с которой вы работаете. Например, если модель Post имеет отношения к модели Author используя тип ассоциации hasMany, делая запрос к $this->Post->findAll() в контроллере будет сделана как выборка записей Post, так и всех записей Author, к которым они относятся.
Чтобы коректно использовать ассоциации, лучше следовать схеме присвоения имен Cake PHP?. Если вы следуете этой схеме, вы можете пользоваться скаффолдингом для визуализации данных вашего приложения, потому что скаффолдинг вылавливает и использует ассоциации между моделями. Конечно же вы всегда можете настроить ассоциации моделей для работы, если не используете схему присвоения имен Cake PHP?, но мы прибережем эти советы на потом. А пока, давайте просто следовать схеме. Схема присвоения имен, которая нас волнует – это сторонние ключи, имена моделей и таблиц.
Вот обзор того, что Cake ожидает от имен этих разных элементов: (взгляните на «Соглашение Cake» для подробной информации о присвоении имен)
Скаффолдинг Cake PHP? ожидает, что ваши ассоциации построены в том же порядке, что и колонки. То есть, если у меня есть Articles с типом ассоциаций belongsTo, то есть принадлежит трем другим моделям (Author, Editor и Publisher), мне понадобятся три ключа: author_id, editor_id и publisher_id. Скаффолдинг ожидает, что ваши ассоциации происходят в том же порядке, что и ключи в таблице (наприер, сначала Author, второй Editor и последний Publisher).
Чтобы показать как некоторые из этих ассоциаций работают, давайте продолжим использовать приложение блога как пример. Представьте, что мы собираемся создать простую систему управления пользователями блога. Я предполагаю это будет не для того, чтобы следить за пользователями, а для того чтобы у каждого пользователя был ассоциированный профиль (Profile, пользователь hasOne профиль). Пользователи также смогут создавать комментарии оставляя ассоциацию с ними (пользователь hasMany комментарии). Сделав пользовательскую систему, мы переходим к тому, чтобы позволить постам относится к тег-объектам, используя тип ассоциаций hasAndBelongsToMany (имеетИПринадлежитМногим, то есть пост имеет и принадлежит многим тегам).
Чтобы установить эту ассоциацию, мы предполагаем что вы уже создали модели User и Profile. Чтобы объявить hasOne-ассоциацию между ними, нам понадобится добавить масив к модели, чтобы сообщить Cake'у как они будут относится. Вот как это выглядит:
/app/models/user.php hasOne
<?php
class User extends AppModel
{
var $name = 'User';
var $hasOne = array('Profile' =>
array('className' => 'Profile',
'conditions' => '',
'order' => '',
'dependent' => true,
'foreignKey' => 'user_id'
)
);
}
?>
Масив $hasOne это то, что Cake использует для создания ассоциации между User и Profile моделями. Каждый ключ в масиве позволяет настроить ассоциацию:
Теперь когда мы выполняем запросы find() или findAll(), используя Profile модель, мы также увидим здесь и ассоциативную модель User:
<?
$user = $this->User->read(null, '25');
print_r($user);
?>
Теперь пользователь может увидить свой профиль, нам нужно объявить ассоциацию так, чтобы пользователь мог видеть только свой профиль. Это делается использованием типа ассоциаций belongsTo. В модели Profile, мы делаем следующее:
<?
/app/models/profile.php belongsTo
<?php
class Profile extends AppModel
{
var $name = 'Profile';
var $belongsTo = array('User' =>
array('className' => 'User',
'conditions' => '',
'order' => '',
'foreignKey' => 'user_id'
)
);
}
?>
Масив $belongsTo это то, что Cake использует для создания ассоциаций между User и Profile моделями. Каждый ключ в масиве позволяет вам настраивать ассоциацию:
Теперь, когда мы выполняем запросы find() или findAll(), используя Profile модель, мы должны увидить нашу ассоциированную модель User:
<?
$profile = $this->Profile->read(null, '4');
print_r($profile);
?>
Теперь User и Profile модели ассоциированы и работают как надо, давайте создадим систему, в которой записи пользователей будут ассоциированы с записями комментариев. Это делается в модели User так:
/app/models/user.php hasMany
<?php
class User extends AppModel
{
var $name = 'User';
var $hasMany = array('Comment' =>
array('className' => 'Comment',
'conditions' => 'Comment.moderated = 1',
'order' => 'Comment.created DESC',
'limit' => '5',
'foreignKey' => 'user_id',
'dependent' => true,
'exclusive' => false,
'finderQuery' => ''
)
);
// Вот отношение hasOne, которое мы объявили раньше...
var $hasOne = array('Profile' =>
array('className' => 'Profile',
'conditions' => '',
'order' => '',
'dependent' => true,
'foreignKey' => 'user_id'
)
);
}
?>
Масив $hasMany это то, что использует Cake для создания ассоциаций между User и Comment моделями. Каждый ключ масива позволяет вам настроить ассоциацию:
Теперь когда мы выполняем запросы find() и findAll(), используя модель User, мы должны увидить все ассоциированные комментарии:
<?
$user = $this->User->read(null, '25');
print_r($user);
?>
Хоть мы и не документируем процес, но было бы очень полезно объявить ассоциацию Comment belongsTo User, чтобы каждая из моделей видела друг друга.
Теперь когда вы освоили простейшие ассоциации, давайте перейдем к последнему типу ассоциаций hasAndBelongsToMany (или HABTM). Эта последняя ассоциация – самая сложная, но также и одна из самых полезных. Ассоциация НАВТМ полезна когда у вас есть две модели, которые связаны между собой связной таблицей. Связная таблица содержит индивидуальные строки, которые относятся друг к другу.
Разница между hasMany и hasAndBelongsToMany в том, что с hasMany ассоциированная модель не общая. Если User hasMany Comments, это *только* пользователь ассоциирован с теми комментариями. С НАВТМ, ассоциированная модель – общая. Это хорошо для того, что мы будем делать дальше: ассоциировать модель Post с моделью Tag. Пока модель Tag принадлежит модели Post, мы не хотим чтобы она была 'истощена', мы хотим продолжить, ассоциировать ее с другими постами.
Для этого там нужно установить коректные таблицы для это ассоциации. Конечно нам понадобится таблица tags для модели Tag, и таблица posts для постов, но также нам будет нужна связная таблица для этой ассоциации. Схема присвоения имен для связных таблиц НАВТМ это [имя первой модели во множественном числе]_[имя второй модели во множественном числе], где имена моделей должны идти в алфавитном порядке:
Связные таблицы HABTM должны состояить минимум из двух сторонних ключей моделей, которые они связывают. Для нашего примера, post_id и tag_id это все что нам нужно.
Вот как будут выглядить дампы SQL для нашего Posts HABTM Tags примера:
--
-- Table structure for table `posts`
--
CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL auto_increment,
`user_id` int(10) default NULL,
`title` varchar(50) default NULL,
`body` text,
`created` datetime default NULL,
`modified` datetime default NULL,
`status` tinyint(1) NOT NULL default '0',
PRIMARY KEY (`id`)
) TYPE=MyISAM;
-- --------------------------------------------------------
--
-- Table structure for table `posts_tags`
--
CREATE TABLE `posts_tags` (
`post_id` int(10) unsigned NOT NULL default '0',
`tag_id` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`post_id`,`tag_id`)
) TYPE=MyISAM;
-- --------------------------------------------------------
--
-- Table structure for table `tags`
--
CREATE TABLE `tags` (
`id` int(10) unsigned NOT NULL auto_increment,
`tag` varchar(100) default NULL,
PRIMARY KEY (`id`)
) TYPE=MyISAM;
Давайте объявим ассоциацию в модели Post с нашими настройками таблиц:
/app/models/post.php hasAndBelongsToMany
<?php
class Post extends AppModel
{
var $name = 'Post';
var $hasAndBelongsToMany = array('Tag' =>
array('className' => 'Tag',
'joinTable' => 'posts_tags',
'foreignKey' => 'post_id',
'associationForeignKey'=> 'tag_id',
'conditions' => '',
'order' => '',
'limit' => '',
'unique' => true,
'finderQuery' => '',
'deleteQuery' => '',
)
);
}
?>
Масив $hasAndBelongsToMany это что Cake использует для создания ассоциации между моделями Post и Tag. Каждый ключ таблицы позволяет вам настроить ассоциацию:
Теперь выполняя find() или findAll() запросы, используя модель Post, мы должны увидеть также ассоциированную модель Tag:
<?
$post = $this->Post->read(null, '2');
print_r($post);
?>
Важно запомнить одну вещь при работе с ассоциативными моделями. Это то, что сохранение данных должно происходить через соответствующую модель Cake. Если сохраняете новый пост и ассоциированные с ним комментарии, тогда вам нужно использовать обе модели Post и Comment во время операции сохранения.
Если ни одна из ассоциированных моделей в системе еще не существует (например, вы хотите сохранить новый пост и соответствующий комментарий одновременно), вам нужно сначала сохранить главную, родительскую модель. Чтобы понять как это работает, давайте представим, что у нас есть действие в нашем Post Controller? которое управляет сохранением новых постов и соответствующих комментариев. Пример ниже подразумевает, что вы воздали один пост и один комментарий.
/app/controllers/posts_controller.php (частично)
function add()
{
if (!empty($this->data))
{
//Мы можем сохранить данные поста:
//они должны быть в $this->data['Post']
$this->Post->save($this->data);
//Теперь нам нужно сохранить данные комментария
//Но сначала нам нужно узнать ID
//поста, который мы только что сохранили...
$post_id = $this->Post->getLastInsertId();
//Теперь мы добавляем эту информацию в сохраняемые данные
//и сохраняем комментарий.
$this->data['Comment']['post_id'] = $post_id;
//Поскольку у нас Post hasMany Comments, мы можем получить доступ
//к модели комментария через модель Post:
$this->Post->Comment->save($this->data);
}
}
/app/controllers/posts_controller.php (partial)
//Вот как будет выглядеть ссылка, если мы используем ее для передачи параметра...
function addComment($post_id)
{
if (!empty($this->data))
{
//Вы можете захотеть сделать данные $post_id более защищенными,
//но этого будет достаточно для работающего примера..
$this->data['Comment']['post_id'] = $post_id;
//Поскольку у нашего Post hasMany Comments, мы можем получить доступ
//доступ к модели Comment через модель Post:
$this->Post->Comment->save($this->data);
}
}
Если ID был пропущен как скрытый элемент формы, вы можете захотеть назвать поле (если используете HTML хелпер) это находится в конце данных, где и должно быть:
If the ID for the post is at $post['Post']['id']...
<?php echo $html->hidden('Comment/post_id', array('value' => $post['Post']['id'])); ?>
Форма для создания постов
<h1>Write a New Post</h1>
<table>
<tr>
<td>Title:</td>
<td><?php echo $html->input('Post/title')?></td>
</tr>
<tr>
<td>Body:<td>
<td><?php echo $html->textarea('Post/body')?></td>
</tr>
<tr>
<td colspan="2">
<?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
<?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
<?php echo $html->submit('Save Post')?>
</td>
</tr>
</table>
Форма как она есть сейчас будет просто создавать записи в Post. Давайте добавим кое-какой код, который позволит нам привязывать данный пост к одному или нескольким тегам:
/app/views/posts/add.thtml (Дополнительный код для ассоциирования с тегами)
<h1>Write a New Post</h1>
<table>
<tr>
<td>Title:</td>
<td><?php echo $html->input('Post/title')?></td>
</tr>
<tr>
<td>Body:</td>
<td><?php echo $html->textarea('Post/body')?></td>
</tr>
<tr>
<td>Related Tags:</td>
<td><?php echo $html->selectTag('Tag/Tag', $tags, null, array('multiple' => 'multiple')) ?>
</td>
</tr>
<tr>
<td colspan="2">
<?php echo $html->hidden('Post/user_id', array('value'=>$this->controller->Session->read('User.id')))?>
<?php echo $html->hidden('Post/status' , array('value'=>'0'))?>
<?php echo $html->submit('Save Post')?>
</td>
</tr>
</table>
Для запроса $this->Post->save() в контроллере, чтобы сохранить связи между этим новым постом и ассоциацией с тегами, имя поля должно быть в форме "Tag / Tag?" (отображаемое имя атрибута будет выглядить как 'data[ИмяМодели][ИмяМодели][]'). Подтвержденные данные должы быть одним ID или масивом ID-номеров связанных записей. Поскольку мы используем возможность выбора не одного тега, то подтвержденные данные для Tag / Tag? будут масивом ID-номеров.
Переменная $tags здесь – просто масив где ключи это ID-номера возможных тегов, а значения это отображаемые имена тегов в многоэлементном выборе.
Смена ассоцииаций на лету, используя bindModel() и unbindModel()
Вы можете случайно захотеть изменить информацию об ассоциациях моделей в необычных ситуациях, пока создаете свое приложение. Если настройка ассоциаций в файле модели дает слишком много (или недостаточно) информации, вы можете использовать две функции моделей чтобы связать и развязать ассоцииации моделей.
Давайте сделаем несколько моделей так что мы сможем посмотреть как bindModel() и unbindModel() работают. Мы начнем с двух моделей:
leader.php и follower.php
<?php
class Leader extends AppModel
{
var $name = 'Leader';
var $hasMany = array(
'Follower' => array(
'className' => 'Follower',
'order' => 'Follower.rank'
)
);
}
?>
<?php
class Follower extends AppModel
{
var $name = 'Follower';
}
?>
Теперь, в Leader Controller?, мы можем использовать find() в модели Leader. Как вы можете увидить выше, ассоциативный масив в модели Leader объявляет ассоциацию Leader hasMany Followers. Для демонстрации замысла давайте используем unbindModel() чтобы убрать эту ассоциацию.
leaders_controller.php (частично)
function someAction()
{
//Это делает выборку Leaders и ассоциированные с ними Followers
$this->Leader->findAll();
//Давайте уберем hasMany...
$this->Leader->unbindModel(array('hasMany' => array('Follower')));
//Теперь используя функцию поиска вернем Leaders, но уже без Followers
$this->Leader->findAll();
//ЗАМЕЧАНИЕ: unbindModel распространится только на следующую функцию поиска..
//Дополнительный запрос поиска будет использовать настроенную ассоциированную информацию.
//Мы уже использовали функцию findAll() после unbindModel(), так что это сделает выборку
//Leaders с ассоциированными Followers еще раз...
$this->Leader->findAll();
}
Функция unbindModel() работает также с остальными ассоциациями: просто измените названия типа ассоциаций и имя класса модели. Основное применение unbindModel():
Общий unbindModel() пример
$this->Model->unbindModel(array('associationType' => array('associatedModelClassName')));
Теперь мы успешно убрали ассоциацию на лету, давайте добавим. Наша модель Leader нуждается в какой-то ассоциации с Principles. Файл модели для модели Principles голый, за исключением одной переменной $name выражения. Давайте ассоциируем Principles с нашей Leader на лету (но только для следующего запроса функции поиска):
leaders_controller.php (частично)
funciton anotherAction()
{
//Нету никакой Leader hasMany Principles в leader.php-файле модели, так что
//поиск здесь просто сделает выборку Leaders.
$this->Leader->findAll();
//Давайте используем bindModel(), чтобы добавить ассоциацию к Principles модели:
$this->Leader->bindModel(
array('hasMany' => array(
'Principle' => array(
'className' => 'Principle'
)
)
)
);
//Теперь когда мы сделали коректную ассоциацию, мы можем использовать простую функцию поиска
//чтобы сделать выборку Leaders с ассоциированными Principles:
$this->Leader->findAll();
}
Функция bindModel() может быть удобной при создании новых ассоциаций, но она также может быть полезна если вы хотите изменить сортировку или другие параметры в данной ассоциации на лету.
Вот теперь это есть у вас. Основное использование bindModel это герметизация обычного ассоциативного масива внутрь масива, чьи ключи названы после типа ассоциации, которую вы пытаетесь создать:
//Общий bindModel() пример
<?
$this->Model->bindModel(
array('associationName' => array(
'associatedModelClassName' => array(
// обычные ассоциативные ключи здесь...
)
)
)
);
?>
Пожалуйста заметьте что в ваших таблицах должны быть коректно прописаны ключи (или ассоциативные масивы соответственно настроеные) чтобы связывать модели на лету.
А можно еще немного поподробней на счет ассоциаций? Какие нибудь другие примеры. К примеру когда используются и именно в каких случаях использовать эти ассоциации. Например. Когда нужно найти все записи одного автора можно же просто прописать «where name='Вася Пупкин'":
hasOne
belongsTo
hasMany
hasAndBelongsToMany
Ваши примеры просто тяжелы на восприятия или может быть только для меня. Заранее благодарен.
не понятно насчет Модели posts_tags
и сохранения в таблицу
posts_tags
Смена ассоциаций на лету, используя bindModel() и unbindModel()
Я так полагаю это подзаголовок ;)
в пункте
«Настройка запросов SQL с query()"
вместо $firstName = $ret[0]['first_name'];
надо $firstName = $ret[0][0]['first_name'];