Создание и сопровождение сайтов

Блог. Yii. Подсказки по Yii. Глава семнадцатая

Поиск

Подсказки по Yii. Глава семнадцатая.

  1. primaryKey.

    Если в таблице нет первичного ключа, например: `id` int(11) NOT NULL AUTO_INCREMENT + PRIMARY KEY (`id`) можно воспользоваться переопределением метода primaryKey() в модели.

    Например: есть таблица `users_tel_nums` с такой структурой:

    1. CREATE TABLE IF NOT EXISTS `users_tel_nums` (  
    2.   `userId` int(11) NOT NULL COMMENT 'Id пользователя',  
    3.   `number` varchar(20) NOT NULL COMMENT 'Номер телефона',  
    4.   KEY `userId` (`userId`)  
    5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

    Если в таблице будет храниться только один номер телефона пользователя, то метод primaryKey имеет вид:

    1. public function primaryKey() {  
    2.     return 'userId';  
    3. }  

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

    1. public function primaryKey() {  
    2.     return array('userId''number');  
    3. }  

     

  2. Поиск по связи HAS_MANY.

    Имеются: модель Objects (список объектов), Users (список пользователей) и UsersTelNums (список телефонов пользователей). С аналогичными названиями таблиц.

    Структура таблицы объектов `objects`:

    1. CREATE TABLE IF NOT EXISTS `objects` (  
    2.   `id` int(11) NOT NULL AUTO_INCREMENT,  
    3.   `status` tinyint(1) NOT NULL COMMENT 'Статус',  
    4.   ....  
    5.   `ownerId` int(11) NOT NULL COMMENT 'Id владельца объекта',  
    6.   ...  
    7.   PRIMARY KEY (`id`),  
    8.   KEY `ownerId` (`ownerId`)  
    9. ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;  

    Таблица users:

    1. CREATE TABLE IF NOT EXISTS `users` (  
    2.   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
    3.   `firstName` varchar(64) NOT NULL,  
    4.   ...  
    5.   PRIMARY KEY (`id`),  
    6.   UNIQUE KEY `email` (`email`)  
    7. ) ENGINE=InnoDB  DEFAULT CHARSET=utf8;  

    И таблица users_tel_nums:

    1. CREATE TABLE IF NOT EXISTS `users_tel_nums` (  
    2.   `userId` int(11) NOT NULL COMMENT 'Id пользователя',  
    3.   `number` varchar(20) NOT NULL COMMENT 'Номер телефона',  
    4.   KEY `userId` (`userId`)  
    5. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

    В модели UsersTelNums (таблица users_tel_nums) используется составной ключ в методе primaryKey. Это мы рассмотрели чуть выше.

    Метод relations модели Objects:

    1. public function relations() {  
    2.     return array(  
    3.         ...  
    4.         'user' => array(self::BELONGS_TO, 'Users''ownerId'),  
    5.         'usersTelNums' => array(self::HAS_MANY, 'UsersTelNums'array('userId' => 'ownerId')),  
    6.     );  
    7. }  

    Здесь таблица objects связывается по полю ownerId с первичным ключём (id) таблицы users. А таблица objects связывается по полю ownerId к userId (array('userId' => 'ownerId')) таблицы users_tel_nums.

    Когда структура и связи описаны разберём метод search модели Objects.

    В модели Objects объявим переменную ownerPhone:

    1. public $ownerPhone;  

    Добавим название поля:

    1. public function attributeLabels() {  
    2.     ...  
    3.     'ownerPhone' => 'Телефон(ы) владельца',  
    4. }  

    Будем сохранять введённое значение при поиске:

    1. public function rules() {  
    2.     ...  
    3.     array('ownerPhone''safe''on' => 'search'),  
    4. }  

    Сам метод search():

    1. public function search() {  
    2.     $criteria = new CDbCriteria;  
    3.   
    4.     $criteria->compare($this->getTableAlias().'.id'$this->id);  
    5.     $criteria->compare($this->getTableAlias().'.status'$this->status, true);  
    6.   
    7.     if ($this->ownerPhone) {  
    8.         $criteria->compare('usersTelNums.number'$this->ownerPhone, true);  
    9.     }  
    10.   
    11.     $criteria->order = $this->getTableAlias().'.id DESC';  
    12.     $criteria->with = array(  
    13.         'usersTelNums' => array(  
    14.             'select'=>'usersTelNums.number',  
    15.             'together'=>true  
    16.         ),  
    17.     );  
    18.   
    19.   
    20.     return new CActiveDataProvider($thisarray(  
    21.         'criteria' => $criteria,  
    22.         'pagination'=>array(  
    23.             'pageSize'=>50,  
    24.         ),  
    25.     ));  
    26. }  

    На что хотелось бы обратить внимание - это как в таком случае нужно указывать with для $criteria.

    Ну и плюс задание связи с моделью UsersTelNums:

    1. 'usersTelNums' => array(self::HAS_MANY, 'UsersTelNums'array('userId' => 'ownerId')),  

     

  3. CSRF и CMenu.

    Есть представление view.php просмотра данных какого-то объекта.

    На странице присутствует меню из ссылок на редактирование и удаление текущего объекта:

    1. $this->widget('zii.widgets.CMenu'array(  
    2.     'items'=>array(  
    3.         array('label'=>tc('Update object'), 'url'=>'#''linkOptions'=>array('submit'=>array('update','id'=>$model->id))),  
    4.         array('label'=>tc('Delete object'), 'url'=>'#''linkOptions'=>array('submit'=>array('delete','id'=>$model->id),'confirm'=>'Вы действительно хотите удалить выбранный элемент?')),  
    5.     ),  
    6. ));  

    Через какое-то время Вы решили добавить защиту от CSRF (http://www.yiiframework.com/doc/guide/1.1/ru/topics.security#sec-3)

    И клик по таким ссылкам начал выдавать ошибку 400 "The CSRF token could not be verified.".

    Решение проблемы - это добавить 'csrf' => true.

    Т.е в итоге получится так:

    1. $this->widget('zii.widgets.CMenu'array(    
    2.     'items'=>array(    
    3.         array('label'=>tc('Update object'), 'url'=>'#''linkOptions'=>array('submit'=>array('update','id'=>$model->id), 'csrf' => true)),    
    4.         array('label'=>tc('Delete object'), 'url'=>'#''linkOptions'=>array('submit'=>array('delete','id'=>$model->id),'confirm'=>'Вы действительно хотите удалить выбранный элемент?''csrf' => true)),    
    5.     ),    
    6. ));   

     

Обсудить статью на форуме