{{tag>yii yii1 frameworks}}

====== Сборник советов для Yii1 ======

===== Своё сообщение при выводе ошибки – параметр message: =====

<code php>
public function rules() {  
    return array(  
        array('username, password', 'required', 'message' => 'Будьте любезны заполнить поле "{attribute}"'),  
    );  
}
</code>

===== Капча не обновляет код при ошибочном вводе =====

<code php>
public function actions() {  
    return array(  
        'captcha' => array(  
            'class' => 'CCaptchaAction',  
            'backColor' => 0xFFFFFF,  
            'testLimit'=> 1, //   
        ),  
    );  
}  
</code>

По-умолчанию, 'testLimit' установлен в значении 3. Поэтому код обновляется только после третьего неверного ввода.

===== Если при ajax запросе вам необходимо обновить часть страницы и загрузить все скрипты =====

<code php>
$this->renderPartial('index', array(  
        'criteria' => $criteria, ), false, true);  
</code>

Именно, последний параметр, установленный в true, сообщает что нужно отдавать страницу со всеми скриптами.

===== Полностью на AJAX =====

  * [[http://tpoxa.com/2011/04/19/fully-ajax-website-with-yii/|bbq]]

===== Меню на основе Cmenu, но вместо текста (label) необходимо изображение =====

==== 1 Способ ====

<code php>
$adminMenuItems = array(  
        array('label' => '<img src="'.Yii::app()->request->baseUrl.'/images/adminmenu/manage_ads.png" />',   
                'url'=>array('/apartments/backend/main/admin'),   
                'linkOptions'=>array('title' => Yii::t('module_apartments', 'Manage apartments'))  
            )  
);  
  
$this->widget('zii.widgets.CMenu', array(  
            'items'=>$adminMenuItems,                     
            'encodeLabel' => false,   
));  
</code>

==== 2 Способ ====

<code php>
$adminMenuItems = array(  
        array('label'=>'',   
                'url'=>array('/quicksearch/main/index'),   
                'template' => '<img src="'.Yii::app()->request->baseUrl.'/images/adminmenu/manage_ads.png" />'  
        )  
);  
  
$this->widget('zii.widgets.CMenu', array(  
    'items'=>$adminMenuItems,  
)); 
</code>

===== Передача параметров в JS скрипт =====

<code php>
$globalConfig = CJavaScript::encode(Yii::app()->params->toArray());   
Yii::app()->clientScript->registerScript('globalConfig', "var globalConfig = ".$globalConfig.";", CClientScript::POS_HEAD);  
</code>

<code php>
Yii::app()->clientScript->registerScript('testConfig', ' 
    if(globalConfig){ 
            alert(globalConfig["languages"]); 
    } 
', CClientScript::POS_END); 
</code>

===== Автоматическое обновление поля date в модели =====

<code php>
public function behaviors(){  
    return array(  
        'AutoTimestampBehavior' => array(  
            'class' => 'zii.behaviors.CTimestampBehavior',  
            'createAttribute' => 'date_updated',  
            'updateAttribute' => 'date_updated',  
        ),  
    );  
}  
</code>


===== Вставка фотографии из модели в GridView =====

<code php>
$this->widget('zii.widgets.grid.CGridView', array(  
    'dataProvider'=>$model->search(),  
    'filter'=>$model,  
    'columns'=>array(  
        array (  
            'name' => 'img',  
            'type' => 'image',  
            'value'=> 'url/to/image' /* например: 'Yii::app()->request->baseUrl."/".$data->sliderThumbPath."/".$data->img' - обязательно в ковычках */  
            'filter' => false,  
        ),  
        ...  
));
</code>

===== Задание имени сценария перед валидацией и его правила валидации =====

<code php>
$model->scenario = 'upload'; // Обязадельно должен объявляться до присвоения атрибутов  
$model->attributes = $_POST['form_name'];      
if($model->validate()) {  
    //...  
}  
</code>

в правилах валидации указываем сценарий **'on' => 'upload'**:

<code php>
public function rules() {  
    return array(  
        array(  
            'upload', 'file',  
            'types' => "{$this->supportExt}",  
            'maxSize' => $this->fileMaxSize,  
            'tooLarge' => Yii::t('module_slider', 'The file was larger than {size}MB. Please upload a smaller file.', array('{size}' => $this->fileMaxSize)),  
            'on' => 'upload',  
        ),  
    );  
} 
</code>

===== расширение для ресайза =====

  * http://www.yiiframework.com/extension/image/

<code php>
if(isset($_POST["{$this->modelName}"])){  
    $model->attributes = $_POST;                  
    $model->scenario = 'upload';  
      
    if($model->validate()) {  
        $model->upload = CUploadedFile::getInstance($model,'upload');                                 
        $model->img = md5(uniqid()).'.'.$model->upload->extensionName;                
          
        if($model->save()){  
            $model->upload->saveAs($model->uploadPath.'/'.$model->img);  
              
            Yii::import('application.extensions.image.Image');  
            $image = new Image($model->uploadPath.'/'.$model->img);  
            $image->resize($model->maxWidth, $model->maxHeight);  
            $image->save();  
              
            Yii::app()->user->setFlash(  
                    'mesSlider', tt('Image succesfullty added to slider.')  
            );  
            $model->unsetAttributes();                
        }  
    }  
}  
</code>

===== Информирование пользователя =====

  * [[yii:setFlash|Подробнее]]

<code php>
Yii::app()->user->setFlash(  
        'message', 'Image succesfullty added to slider.'  
);    
</code>

===== Вставка javascript и css на страницы =====

<code php>
// CSS
Yii::app()->clientScript->registerCssFile(Yii::app()->assetManager->publish(Yii::app()->basePath . '/css/').'css1.css', 'screen');  

// JS
// JQuery
Yii::app()->clientScript->registerCoreScript('jquery');  
// Custom script
Yii::app()->clientScript->registerScriptFile(Yii::app()->assetManager->publish(Yii::app()->basePath . '/js/'). '/jcarousel/lib/jquery.jcarousel.min.js', CClientScript::POS_END);  
</code>

Втавка в темплайт
<code php>
Yii::app()->clientScript->registerScript('loading', ' 
    $("#loading").bind("ajaxSend", function(){ 
        $(this).show(); 
    }).bind("ajaxComplete", function(){ 
        $(this).hide(); 
    }); 
', CClientScript::POS_READY);  
</code>

  * POS_READY значит, что скрипт выполнится в $(document).ready(function() { })
  * POS_LOAD - скрипт выполнится в window.onload
  * POS_END - вставка перед закрывающим тэгом body
  * POS_BEGIN - вставка перед открывающим тэгом body
  * POS_HEAD - вставка перед тэгом title

Статьи: 
  * [[http://www.yiiframework.com/forum/index.php/topic/11438-%D0%BF%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5-css-%D0%B2-%D0%BA%D0%BE%D0%BD%D1%82%D1%80%D0%BE%D0%BB%D0%B5%D1%80%D0%B5/|Подключение css в контролере]]
  * [[http://habrahabr.ru/post/150885/|Автоматическое подключение css и js файлов в Yii]]
===== Метод Action контроллера по-умолчанию =====

<code php>
class MainController extends CController {  
    public $defaultAction='test';  
      //....        
    public function actionTest() {  
    }    
}  
</code>

===== Редирект =====

<code php>
  $this->redirect(Yii::app()->homeUrl);  
</code>

===== Доступ к объектам контролера =====

<code php>
// Controller
$controllerId = Yii::app()->controller->id;  

// Получение ID контролера
$actionId = $this->getAction()->getId();  
$actionId = Yii::app()->controller->action->id;

// Обращение к модулю из чужого модуля или контролера
$module = Yii::app()->getModule('userGroups');
$module->mailMessages();


// Вызов чужого контроллера
$p = Yii::app()->createController('userGroups/user/login');
$act = $p[0]->createAction('login');
$act->run();

// Вызов чужого контроллера
// Если необходимо наличие _action в контролере
$controller = Yii::app()->createController('social/profile/friendsFeed')[0];
$action = $controller->createAction('friendsFeed');
$controller->runActionWithFilters($action, $controller->filters());

// Вызов чужого контроллера
// Если необходимо наличие _owner
Yii::app()->runController('social/profile/friendsFeed');

</code>

===== Вывод абсолютного пути =====

<code php>
$url = Yii::app()->createAbsoluteUrl('user/activation',array('activationKey'=>$activationKey));
</code>

===== Своя функция - валидатор на примере капчи =====

<code php>
public function rules() {  
    return array(  
        ...  
        array('verifyCode', 'required', 'on' => 'registration'),  
        array('verifyCode', 'captchaValidate', 'on' => 'registration'),  
    )  
}     
  
public function captchaValidate() {  
  $code = Yii::app()->controller->createAction('captcha')->getVerifyCode();  
    if ($code != $this->verifyCode)  
        $this->addError('verifyCode', 'Неверный код с капчи'); 
} 
</code>

===== Расширяем своё приложение на примере многоразового использования одного расширения для однотипных действий, на примере удаления записи =====


Объявляем расширение контролера в ext.test.testActionDelele
<code php>
<?php  
class testActionDelele extends CAction {  
    public $modelName;  
    public $redirectAfter = array('index');  
  
    public function run($id = null) {  
        if (id) {  
            CActiveRecord::model($this->modelName)->deleteByPk($id);  
        }  
          
        if(Yii::app()->getRequest()->getIsAjaxRequest()) {  
            Yii::app()->end(200, true);  
        }  
        else {  
            $this->getController()->redirect($this->redirectAfter);  
        }  
    }  
} 
</code>

Создаем общий метод
<code php>
<?php  
class testController extends CController {  
    public function actions() {  
        return array(  
            'deleteUser' => array(  
                'class' => 'ext.test.testActionDelele',  
                'modelName' => 'User',  
                'redirectAfter' => array('indexUsers'),  
            ),  
            'deletePost' => array(  
                'class' => 'ext.test.testActionDelele',  
                'modelName' => 'Post',  
                'redirectAfter' => array('indexPosts'),  
            ),  
        );  
    }   
} 
</code>

===== Jquery не работает после renderPartial. =====

<code php>
if($isFancyBox){  
    Yii::app()->clientscript->scriptMap['jquery.js'] = false;  
    Yii::app()->clientscript->scriptMap['jquery.min.js'] = false;  
    Yii::app()->clientscript->scriptMap['jquery-ui.min.js'] = false;  
      
    $this->renderPartial('form', array(  
        'model' => $model,  
    ), false, true);  
      
} else{  
    $this->render('form', array(  
        'model' => $model,  
    ));  
}
</code>

===== Фильтры. На примере сжатия страницы =====

**ext.compress.compressPage**
<code php>
<?php  
class compressPage extends CFilter {  
    protected function preFilter($filterChain) {  
        ob_start();  
        return parent::preFilter($filterChain);  
    }  
      
    protected function postFilter($filterChain) {  
        $html = ob_get_clean();  
        echo preg_replace("~>(\s+|\t+|\n+)<~", "><", $html);  
        parent::postFilter($filterChain);  
    }  
}  
</code>

В контолере
<code php>
public function filters() {  
    return array(  
        array(  
            'ext.compress.compressPage + view, index'  
        ),  
    );  
}
</code>

===== Зависимый select от другого select'а =====

  * [[yii:зависимые_select-списки|Зависимые select-списки в Yii]]

===== Сгруппированный раскрывающийся список =====

<code php>
echo CHtml::dropDownList('Cars', 'car_id', array(  
    'Toyota'=>array(  
        1=>'Avensis',  
        2=>'Camry',  
    ),  
    'Volvo'=>array(  
        3=>'S60',  
        4=>'XC70',  
    ),  
)); 
</code>

<code php>
$records = array(  
    array('id' => 1, 'name' => 'Yii', 'group' => 'Фреймворк'),  
    array('id' => 2, 'name' => 'Drupal', 'group' => 'Фреймворк'),  
    array('id' => 3, 'name' => 'Joomla', 'group' => 'Фреймворк'),  
    array('id' => 4, 'name' => 'Сodeigniter', 'group' => 'Фреймворк'),  
    array('id' => 5, 'name' => 'Mercurial', 'group' => 'Система контроля версий'),  
    array('id' => 6, 'name' => 'Git', 'group' => 'Система контроля версий'),  
    array('id' => 7, 'name' => 'SVN', 'group' => 'Система контроля версий'),  
);  
  
$dropDownList = CHtml::listData($records, 'id', 'name', 'group');   
echo CHtml::dropDownList('dropDownList', '', $dropDownList);  
</code>

===== Label, помеченный как обязательный для заполнения =====

<code php>
echo CHtml::activeLabel($model, 'name', array('required' => true));
</code>

===== CGridView. Добавляем раскрывающийся список для сортировки/поиска. =====

  * **'filter' => array(0 => 'Все', 1 => 'Продажа', 2 => 'Аренда'),**

<code php>
$this->widget('zii.widgets.grid.CGridView', array(    
    'id'=>'apartments-grid',    
    'dataProvider'=>$model->search(),    
    'filter'=>$model,    
    'columns'=>array(    
        ...    
        array(    
            'name' => 'type',    
            'type' => 'raw',    
            'value' => 'Apartment::getNameByType($data->type)',    
            'filter' => array(0 => 'Все', 1 => 'Продажа', 2 => 'Аренда'),  
        ),          
        ...    
    )    
));
</code>

Добавить в модели метода search
<code php>
if ($this->type)  
    $criteria->compare('type', $this->type);  
</code>

===== Форма с шагами =====

  * [[:yii_multi_page_form_wizard|Форма из нескольких шагов]]

===== Одна форма с данными из двух и более моделей =====

<code php>
public function actionCreate() {  
    $category = new Category;  
    $subCategory = new SubCategory;  
      
    if(isset($_POST['Category'], $_POST['SubCategory'])) {  
        // собираем входные данные  
        $model->attributes = $_POST['Category'];  
        $sub->attributes = $_POST['SubCategory'];  
   
        // валидация  
        $valid = $model->validate();  
        $valid = $sub->validate() && $valid;  
   
        if($valid) {  
            // сохраняем данные без валидации (параметр false), так как уже валидация была выше.  
            if($model->save(false)) {  
                $sub->category_id = $model->id;  
                $sub->save(false);  
                $this->redirect(array('view','id'=>$model->id));  
            }             
        }  
    }  
   
    $this->render('create', array(  
        'model'=>$model,  
        'sub'=>$sub,  
    ));  
}    
?>
</code>

Представление create модели Category имеет вид:
<code php>
<?php  
$this->breadcrumbs=array(  
    'Управление категориями' => array('admin'),  
    'Добавить категорию',  
);  
  
$this->renderPartial('_form',array(  
    'model'=>$model,  
    'sub' => $sub,  
));  
?>
</code>

И представление _form:
<code php>
<div class="form">  
    <?php $form=$this->beginWidget('CActiveForm', array(  
        'id'=>'category-form',  
        'enableAjaxValidation'=>false,  
    )); ?>  
  
    <p class="note">Поля, отмеченные <span class="required">*</span> являются обязательными.</p>  
  
    <?php echo $form->errorSummary(array($model, $sub)); ?>  
  
    <div class="row">  
        <?php echo $form->labelEx($model, 'model_field1'); ?>  
        <?php echo $form->textField($model, 'model_field1'); ?>  
        <?php echo $form->error($model, 'model_field1'); ?>  
    </div>  
   
    <div class="row">  
        <?php echo $form->labelEx($sub, 'sub_field1'); ?>  
        <?php echo $form->textField($sub, 'sub_field1'); ?>  
        <?php echo $form->error($sub, 'sub_field1'); ?>  
    </div>  
  
    <div class="row buttons">  
        <?php echo CHtml::submitButton($model->isNewRecord ? 'Добавить' : 'Сохранить'); ?>  
    </div>  
  
<?php $this->endWidget(); ?>  
</div> 
</code>

===== Кэширование запросов =====

  * [[yii:cache|Подробнее]]

**config/main.php**
<code php>
'cache'=>array(  
    'class'=>'system.caching.CFileCache',  
), 
</code>

Выставляем время кеша в 1000 секунд для DAO:
<code php>
$sql = 'SELECT field1, field2 FROM {{apartment_reference}}';  
$dependency = new CDbCacheDependency('SELECT MAX(date_updated) FROM apartment_reference');  
$apReference = Yii::app()->db->cache(1000, $dependency)->createCommand($sql)->queryAll();
</code>

То же самое для AR:
<code php>
$dependency = new CDbCacheDependency('SELECT MAX(date_updated) FROM apartment_reference');      
$apReference = apReference::model()->cache(1000, $dependency)->findAll();  
</code>

Для очистки всего кэша вызываем метод flush():
<code php>
Yii::app()->cache->flush();  
</code>

===== Отладка SQL запросов =====

**config/main.php**
<code php>
'db'=>array(  
    'charset' => 'utf8',  
    'enableParamLogging' => 0,  
    'enableProfiling' => true,  
    'schemaCachingDuration' => 0,   
),   
'log'=>array(  
    'class'=>'CLogRouter',  
    'routes'=>array(  
        array(  
            'class'=>'CWebLogRoute',  
            'levels'=>'trace',  
            'showInFireBug'=>true,  
        ),  
    ),  
), 
</code>

Запрос, созданный при помощи построителя запросов, можно посмотреть при помощи CDbCommand::getText().
<code php>
var_dump(Yii::app()->db->createCommand($sql)->text); 
</code>

===== Построитель запросов =====

  * [[yii:dao|Подробнее]]

**Удаление записи**
<code php>
Yii::app()->db->createCommand()->delete('{{apartment_reference}}', 'apartment_id=:id', array(':id'=>$this->id));
</code>

**Вставка записи**
<code php>
Yii::app()->db->createCommand()->insert('{{apartment_reference}}', array(  
    'field1'=>'valueField1',  
    'field2'=>'valueField2',  
));
</code>

**Изменение значения с строке**
<code php>
Yii::app()->db->createCommand()->update('{{apartment_reference}}', array(  
    'field1'=>'valueField1',  
), 'id=:id', array(':id'=>1));  
</code>

**Выборка данных**
<code php>
$apReference = Yii::app()->db->createCommand()  
  ->select('field1, field2')  
  ->from('{{apartment_reference}}')  
  ->where('id=:id', array(':id'=>1))  
  ->queryRow();  
</code>

===== Пример использования CArrayDataProvider =====

Контролер:
<code php>
public function actionViewGrid() {  
    $items = array(  
            array('id' => 1, 'title'=>'Title1', 'description' => 'Description1'),  
            array('id' => 2, 'title'=>'Title2', 'description' => 'Description2'),  
            array('id' => 3, 'title'=>'Title3', 'description' => 'Description3'),  
            array('id' => 4, 'title'=>'Title4', 'description' => 'Description4')  
    );  
      
    $itemsProvider = new CArrayDataProvider($items, array(  
        'pagination' => array(  
            'pageSize' => 2,  
        ),  
    ));  
      
    $this->render('viewgrid', array('itemsProvider' => $itemsProvider));  
}
</code>

Представление:
<code php>
$this->widget('zii.widgets.grid.CGridView', array(  
    'id' => 'itemGrid',  
    'dataProvider' => $itemsProvider,  
    'enablePagination' => true,  
    'columns' => array(  
        array(  
            'name' => 'ID',  
            'value' => '$data["id"]',  
            'sortable' => true,  
            'filter' => false,  
        ),  
        array(  
            'name' => 'Название',  
            'value' => '$data["title"]'  
        ),  
        array(  
            'name' => 'Описание',  
            'value' => '$data["description"]'  
        ),  
    )  
));
</code>

===== Для получения IP адреса пользователя используйте =====

<code php>
echo Yii::app()->request->userHostAddress;
</code>

===== CDbExpression =====

  * [[yii:cdbexpression|Подробнее]]

===== datePicker в CGridView =====

  * [[yii:widgets:cgridview|CGridView]]

===== Мета тэги =====

<code php>
Yii::app()->clientScript->registerMetaTag('описание сайта', 'description');  
Yii::app()->clientScript->registerMetaTag('ключевые слова сайта', 'keywords');  
</code>

===== Действия до валидации и после =====

**onBeforeValidate**
<code php>
public function onBeforeValidate($event) {  
    $scenarioArray = array(  
                    1 => 'scanario1',  
                    2 => 'scanario2',  
                    3 => 'scanario3',  
                    4 => 'scanario4',  
                    5 => 'scanario5',  
    );  
    $this->scenario = $scenarioArray[$this->fieldFromDB1];  
  
    if (!$this->field2 || !$this->field3)  
        $this->addError('phone', 'Заполните поля field2 и field3');  
    else   
        $this->fieldFromDB2 = $this->field2.$this->field3;  
}

public function onAfterValidate($event) {  
    // здесь некий код  
}

public function beforeValidate(){
}

public function AfterValidate(){
}
</code>

  * [[http://yiiframework.ru/doc/cookbook/ru/core.events|Отличия событий beforeValidate от onbeforeValidate]]

===== Добавляем индикатор загрузки при ajax запросах =====

Добавим дивчик на страницу:
<code php>
<div id="loading" style="display:none;">Загрузка ...</div>  
</code>

CSS:
<code css>
#loading{   
    position: fixed;   
    top: 40px;   
    leftleft: 0;   
    z-index: 5000;   
    background-color: #3C69AC;   
    font-size: 100%;  
    color: #FFFFFF;   
    padding: 5px;  
}
</code>

Отображение и скрытие дивчика:
<code php>
Yii::app()->clientScript->registerScript('loading', ' 
    $("#loading").bind("ajaxSend", function(){ 
        $(this).show(); 
    }).bind("ajaxComplete", function(){ 
        $(this).hide(); 
    }); 
', CClientScript::POS_READY);  
</code>

Установка путей загрузки картинки
<code php>
Yii::getPathOfAlias('webroot.uploads');  
</code>

===== Проверка запроса на наличие Ajax =====

<code php>
 if( Yii::app()->request->isAjaxRequest) {

    // do some thing….

}
</code>

===== Обновление грида через ajax =====

  * [[yii:widgets:cgridview|CGridView]]

===== Обновление представления через ajax =====

===== Постраничная разбивка =====

  * [[yii:pagination|Примеры постраничной навигации]]

===== Сессии session =====

<code php>
Yii::app()->session->add(‘product’, $productId);
Yii::app()->session->get(‘product’);
Yii::app()->session->remove(‘product’);
unset(Yii::app()->session[$variable][$key]);
</code>

===== Cookie =====

<code php>
$cookie=Yii::app()->request->cookies[$name];
$value=$cookie->value;

$cookie=new CHttpCookie($name,$value);
Yii::app()->request->cookies[$name]=$cookie;
</code>
