В свете моих начинаний на этом фреймворке и крайне положительных эмоциях по его использованию, делаю тему, дабы мы объединялись и решали насущные вопросы. Я конечно со всем помочь не смогу, но свою порцию геммороя я решил и даже уже запостил один баг репорт в баг трекер Тему прилепляю, флудеров и любителей похоливарить буду вычищать, не обижайтесь З.Ы. На опрос в тему не отвечать - созлдайте отдельный топик если вам интересно обсудить.
И так пошел 3-й месяц, как я серьёзно засел за Yii. Основной проект, который я на нём сделал и продолжаю сейчас делать - это админка и автоматизация обменя всяких электронных валют (это легко) и ваучеров (а вот это гемморой, т.к. API не существует в принципе, или гребёт грабительские проценты). На данный момент есть 3 части: * Админка со списками, гридами, попапами и прочей туфтой. Так же используется встроенная система прав (хватит для любой корпоративной системы любых размеров). * Легкий фронт-енд для пополнения Skype в автоматическом режиме. * Обработчик пополнения Skype в виде демона. Тоже от и до на Yii. Впечатления. Админка Задача по созданию грида для показывания всего этого дела с сортировкой и простейшими фильтрами с полным CRUD набором сводится к 2-м кликам в кодогенераторе Gii прямо из WEB панели. Дальше остаётся только настроить продвинутые фильтры (опции у массива) и прописать альтернативное отображение данных в ячейках если нужно (к примеру 2 поля показать в виде одного). Всё легко и просто, когда разберёшься в основах. Продвинутые вещи делаются быстро и легко, благо всё JS часть на jQuery, что радует. Конечно не без недоработок, но учитывая кол-во разработчиков (коих всего 6 или 7), то прогресс впечатляет. Пожелания помочь встречают на ура, баги чинят быстро (зарепортил один 3 недели назад, уже починили и релизнули). Им бы энергию наших жителей формума php.ru да на пользу, фреймворк бы взорвал умы PHP программистов уже через пол года Да, одна особенность - конфигурационные массивы у них ориентированы на замыкания, прямо как в jQuery. Делают это с помощью eval, но с 5.3 можно использовать замыкания, т.к. их поддержка уже давно сделана. Сейчас перебрался на 5.3 уже у себя локально, уже завтра опробую как оно с замыканиями, обещает быть невъебенно круто Фронтедн Простая форма в 6 полей (изначально было 4, потом добавилось ещё 2). Не совсем простое добавление в базу. Сделалось просто на ура, Layout подкоректировал и заверстал свой дизайн за 20 минут (для формы много не нужно, но то, как это сделалось легко удивило). Демон Как оказалось, Yii не просто позволяет писать консольные скрипты, но и целые приложения, которые в свою очередь могут запускать отдельные команды, записанные как самостоятельные скрипты. В общем на этой базе как раз и работает их консольный генератор yiic. При этом вам доступны все компоненты, модули, модели и консольные комманды, которые описаны в вашем приложении. Я пока особо не разогнался, но выглядет впечатляюще. Конфиги легко делаются для разных точек входа свои, просто пишем имя конфига прямо в точке входа - ну что может быть проще, не правда ли? Из минусов: Довольно оторванная от реальности дока. Впрочем над ней вечно не хватает человек, что бы доработать. О проблеме все знают, но желающих помочь почти нету. Разработчики не успевают. API хорошо документирован, но почти нет примеров. Иногда встречаются опции, которые вообще не описаны. Благо их названия говорят сами за себя (к примеру htmlOptions опция есть у всего, что генерирует html разметку, но нигде нету описания что же там за опции вообще поддерживаются или хотя бы намёка на то, что key => value пары трансформируются в attribute="value"). Сильно спасает гугл и офф. форум + русский форум на yiiframework.ru. В недра особо не лазил, но в основном куда залазил - хорошо написано. Излишней ООПешности пока не наблюдал, вроде как кратко и ясно. Стройная ООП модель позволяет переопределить любой класс в системе так, как нужно вам и через конфиг его подгрузить вместо системных. Самые базовые конечно вряд-ли, но всё что в серединке бутерброда уже легко поддаётся модификации через ООП. Сам фреймворк ставится как библиотека рядом с приложениями и так и юзается. Если приложению нужно что-то изменить в самом фреймворке - наследуем, переопределяем и готово. К фреймворку даже не прикасались. В основном это пока в общем всё. Пока ещё не настолько погрузился в него, но работа не стоит на месте и планируется более серьёзный функционал - посмотрим как Yii себя покажет.
Т.е. там есть средства генерирования админки по модели? Ну для банальных списков? А связи с другими моделями поддерживаются (имею ввиду в админке). Есть желание освоить какой-то фреймворк под задачи клепания сайтов дизайн-студии.. для тех, кого удалось отговорить от битрикса Вот размышляю, или свой допилить, который из симфони вырос или какой-то готовый взять. Работа с моделями интересна - выборка - поиск.
MiksIr Генерацию связей не тестил, т.к. я не определяю Foreign keys в MySQL. Но relations естественно есть и выглдят вот так: PHP: <?php /** * This is the model class for table "transactions". */ class Transactions extends CActiveRecord { // .... /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'skype' => array(self::HAS_ONE, 'SkypeTransactions', 'sky_id'), 'account' => array(self::BELONGS_TO, 'Accounts', 'trn_by_account'), 'vouchers' => array(self::HAS_MANY, 'Vouchers', 'trn_id'), 'countVouchers' => array(self::STAT, 'TransactionVouchers', 'tvc_trn_id'), ); } // ..... } Здесь задано 4 связи как видите. Связи задаются в моделях с обоих сторон. О какой админке идёт речь я не знаю, Yii это фреймворк, не CMS. Но позволяет собирать админки легко и быстро. Щас отдельным постом приведу примеры.
Вот вам модель (я опустил 4 метода по генерации отчётов, поскольку они всёравно были сделаны в лоб и в обход возможностей фреймворка. Да и занимают ещё 300 строк скучного кода): PHP: <?php /** * This is the model class for table "transactions". */ class Transactions extends CActiveRecord { /** * The followings are the available columns in table 'transactions': * @var integer $trn_id * @var string $trn_started_on * @var string $trn_finished_on * @var string $trn_total * @var string $trn_total_real * @var string $trn_status * @var string $trn_error_code * @var string $trn_email * @var integer $trn_by_account * @var string $trn_purse * @var string $trn_paysystem * @var string $trn_rate * @var string $trn_wm_id * @var string $trn_operator_status * @var string $trn_system_status */ public $trnvouchers; /** * Returns the static model of the specified AR class. * @return Transactions the static model class */ public static function model($className=__CLASS__) { return parent::model($className); } /** * @return string the associated database table name */ public function tableName() { return 'transactions'; } /** * @return array validation rules for model attributes. */ public function rules() { // NOTE: you should only define rules for those attributes that // will receive user inputs. return array( array('trn_started_on, trn_total, trn_email, trn_by_account, trn_purse, trn_paysystem, trn_rate, trn_system, trn_operator_status, trn_system_status', 'required'), array('trn_by_account', 'numerical', 'integerOnly'=>true), array('trn_total, trn_total_real, trn_rate', 'length', 'max'=>10), array('trn_status', 'length', 'max'=>13), array('trn_error_code', 'length', 'max'=>15), array('trn_email, trn_purse', 'length', 'max'=>100), array('trn_paysystem', 'length', 'max'=>4), array('trn_wm_id', 'length', 'max'=>20), array('trn_finished_on, trn_operator_status, trn_system_status', 'safe'), // The following rule is used by search(). // Please remove those attributes that should not be searched. array('trn_id, trn_started_on, trn_finished_on, trn_total, trn_total_real, trn_status, trn_error_code, trn_email, trn_by_account, trn_purse, trn_paysystem, trn_rate, trn_wm_id, trn_system, trn_ip', 'safe', 'on'=>'search'), ); } public function defaultScope() { return array( 'order'=>'trn_started_on DESC', ); } /** * @return array relational rules. */ public function relations() { // NOTE: you may need to adjust the relation name and the related // class name for the relations automatically generated below. return array( 'skype' => array(self::HAS_ONE, 'SkypeTransactions', 'sky_id'), 'account' => array(self::BELONGS_TO, 'Accounts', 'trn_by_account'), 'vouchers' => array(self::HAS_MANY, 'Vouchers', 'trn_id'), 'countVouchers' => array(self::STAT, 'TransactionVouchers', 'tvc_trn_id'), ); } /** * @return array customized attribute labels (name=>label) */ public function attributeLabels() { return array( 'trn_id' => 'ID', 'trn_started_on' => 'Start', 'trn_finished_on' => 'End', 'trn_total' => 'Total', 'trn_total_real' => 'Real total', 'trn_status' => 'Status', 'trn_error_code' => 'Error Code', 'trn_email' => 'Email', 'trn_by_account' => 'By Account', 'trn_rate' => 'Rate', 'trn_paysystem' => 'Money gateway', 'trn_purse' => 'Purse', 'trn_wm_id' => 'WM ID', 'trn_system' => 'System', 'trn_operator_status' => 'Operator status', 'trn_system_status' => 'System status', 'account.uac_login' => 'Processed by', 'trn_ip' => 'Customer IP', 'countVouchers' => 'Vouchers (search by code)', 'skype.sky_currency' => 'Skype account used', ); } /** * Retrieves a list of models based on the current search/filter conditions. * @return CActiveDataProvider the data provider that can return the models based on the search/filter conditions. */ public function search() { // Warning: Please modify the following code to remove attributes that // should not be searched. $criteria=new CDbCriteria; $criteria->with = array('account', 'countVouchers'); $criteria->compare('trn_id',$this->trn_id); $criteria->compare('trn_started_on',$this->trn_started_on,true); $criteria->compare('trn_finished_on',$this->trn_finished_on,true); $criteria->compare('trn_total',$this->trn_total,true); $criteria->compare('trn_total_real',$this->trn_total_real,true); $criteria->compare('trn_status',$this->trn_status,true); $criteria->compare('trn_error_code',$this->trn_error_code,true); $criteria->compare('trn_email',$this->trn_email,true); $criteria->compare('trn_by_account',$this->trn_by_account); $criteria->compare('trn_purse',$this->trn_purse,true); $criteria->compare('trn_paysystem',$this->trn_paysystem,true); $criteria->compare('trn_rate',$this->trn_rate,true); $criteria->compare('trn_wm_id',$this->trn_wm_id,true); $criteria->compare('trn_system', $this->trn_system, true); $criteria->compare('trn_ip', $this->trn_ip, true); $criteria->compare('trn_operator_status', $this->trn_operator_status, true); $criteria->compare('trn_system_status', $this->trn_system_status, true); // В данном случае тут таким образом обошел баг из 1.1.2, когда реляция работала для пейджера, но терялась для самого запроса почему-то. В 1.1.3 вроде починили, пока не проверил :) $criteria->join = 'LEFT JOIN '.TransactionVouchers::model()->tableName().' AS thevouchers ON t.trn_id = thevouchers.tvc_trn_id'; if (!empty($this->trnvouchers->tvc_code)) { $criteria->compare('tvc_code', $this->trnvouchers->tvc_code); $this->countVouchers = $this->trnvouchers->tvc_code; } if (!empty($this->account->uac_login)) { $criteria->compare('account.uac_login', $this->account->uac_login); } return new CActiveDataProvider(get_class($this), array( 'criteria'=>$criteria, 'pagination'=>array( 'pageSize' => 10, ), )); } } 70% кода сделал генератор, остальное я уже просто доправил до нужной кондиции и своей извращённой логике Самое приятное в генераторе, это то, что вы можете прямо из него перегенерировать модель после изменений в базе и она с помощью diff добавит то, что нужно, не трогая ваш код. При этом, когда вы работаете из WEB панели, любезно предоставит вам красивый diff для просмотра прямо в браузере по желанию. А вот к модели и контроллер (не обрезанный): PHP: <?php class TransactionsController extends SBaseController { /** * @var string the default layout for the views. Defaults to 'column2', meaning * using two-column layout. See 'protected/views/layouts/column2.php'. */ public $layout='column2'; /** * @var CActiveRecord the currently loaded data model instance. */ private $_model; /** * Displays a particular model. */ public function actionView() { $model = $this->loadModel(); $criteria=new CDbCriteria; $criteria->addColumnCondition(array('tvc_trn_id' => $model->trn_id)); $trn_vouchers = new CActiveDataProvider('TransactionVouchers', array( 'criteria'=> $criteria, )); $criteria=new CDbCriteria; $criteria->addColumnCondition(array('vch_trn_id' => $model->trn_id)); $result_vouchers = new CActiveDataProvider('Vouchers', array( 'criteria' => $criteria )); $ukash_online = Params::model()->findByAttributes(array('name' => 'Ukash automatics'))->value == "on" ? true : false; $this->renderPartial('view',array( 'model'=> $model, 'trn_vouchers' => $trn_vouchers, 'result_vouchers' => $result_vouchers, 'ukash_online' => $ukash_online )); } /** * Creates a new model. * If creation is successful, the browser will be redirected to the 'view' page. */ public function actionCreate() { $model=new Transactions; // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Transactions'])) { $model->attributes=$_POST['Transactions']; if($model->save()) $this->redirect(array('view','id'=>$model->trn_id)); } $this->render('create',array( 'model'=>$model, )); } /** * Updates a particular model. * If update is successful, the browser will be redirected to the 'view' page. */ public function actionUpdate() { $model=$this->loadModel(); // Uncomment the following line if AJAX validation is needed // $this->performAjaxValidation($model); if(isset($_POST['Transactions'])) { $model->attributes=$_POST['Transactions']; if($model->save()) { $this->redirect(array('view','id'=>$model->trn_id)); } } $this->render('update',array( 'model'=>$model, )); } /** * Lists all models. */ public function actionIndex() { $model=new Transactions('search'); $model->account = new Accounts(); $model->trnvouchers = new TransactionVouchers(); /** * @todo Remove when it is fixed in Yii 1.1.3 */ $model->setAttributes( array( 'trn_total_real'=>NULL, 'trn_status'=>NULL, 'trn_system' => NULL, 'trn_system_status' => NULL, 'trn_operator_status' => NULL, ) ); if(isset($_GET['Transactions'])) { $model->attributes=$_GET['Transactions']; if (isset($_GET['Transactions']['trnvouchers'])) { $model->trnvouchers->attributes = $_GET['Transactions']['trnvouchers']; } if (isset($_GET['Transactions']['account'])) { $model->account->attributes = $_GET['Transactions']['account']; } } $this->render('index',array( 'model'=>$model, )); } /** * Manages all models. */ public function actionAdmin() { $model=new Transactions('search'); /** * @todo Remove when it is fixed in Yii 1.1.3 */ $model->setAttributes( array( 'trn_total_real'=>NULL, 'trn_status'=>NULL, 'trn_system' => NULL, 'trn_system_status' => NULL, 'trn_operator_status' => NULL, ) ); if(isset($_GET['Transactions'])) $model->attributes=$_GET['Transactions']; $this->render('admin',array( 'model'=>$model, )); } public function actionStatus() { $model = $this->loadModel(); if (!in_array($model->trn_status, array('success', 'failed'))) { if(isset($_POST['Transactions'])) { $model->attributes=$_POST['Transactions']; $model->trn_finished_on = new CDbExpression('NOW()'); if ($model->trn_status == "success") { $model->trn_total_real = $this->trn_total; } if ($model->trn_system == "skype") { if ($model->save()) { $sky_trn = SkypeTransactions::model()->findbyPk($model->trn_id); $sky_trn->sky_status = $model->trn_status == "success" ? "success" : "error"; $sky_trn->sky_password = str_repeat('*', 8); $sky_trn->sky_finished = new CDbExpression('NOW()'); $sky_trn->save(); $this->redirect(array('view','id'=>$model->trn_id)); } } else { if ($model->trn_status == "success") { $sql = 'SELECT SUM(tvc_amount) FROM '.TransactionVouchers::model()->tableName().' WHERE tvc_trn_id = :id'; $db = $model->getDbConnection()->CreateCommand($sql); $model->trn_total_real = $db->queryScalar(array(':id' => $model->trn_id)); } if($model->save()) { if ($model->trn_status == 'success') { /* If save was a success then make a payment */ // Общая сумма реальная для выплаты в выбранной системе пользователя $total = number_format(round($model->trn_total_real * $model->trn_rate, 2), 2, '.', ''); //echo $total; // Кошелёк клиента $purse = $model->trn_purse; // Платёжная система $wm_id = $model->trn_wm_id; // Платёжная система $paysys = $model->trn_paysystem; // E-Mail клиента $email = $model->trn_email; // Система ваучеров $system = $model->trn_system; include('../confukash/autopay.inc'); } $this->redirect(array('view','id'=>$model->trn_id)); } } } $this->renderPartial('status',array( 'model'=>$model, )); } else { $this->redirect(array('view','id'=>$model->trn_id)); } } public function actionOpstatus() { $model = $this->loadModel(); if(isset($_POST['Transactions'])) { $model->attributes=$_POST['Transactions']; if($model->save()) { $this->redirect(array('view','id'=>$model->trn_id)); } } $this->renderPartial('opstatus',array( 'model'=>$model, )); } public function actionReports() { $transactions = new Transactions(); $transactionVouchers = new TransactionVouchers(); $this->render('reports', array( 'last24hrs' => $transactions->getLast24Reports(), 'vouchers_data_24hrs' => $transactionVouchers->getLast24Reports(), 'payout_totals_24hrs' => $transactions->getLast24PayoutTotals(), )); } public function actionReportsInterval() { $transactions = new Transactions(); $transactionVouchers = new TransactionVouchers(); if (!isset($_GET['start']) || empty($_GET['start'])) { $start = date('Y-m-d'); } else { $start = $_GET['start']; } if (!isset($_GET['end']) || empty($_GET['end'])) { $end = date('Y-m-d'); } else { $end = $_GET['end']; } $this->render('reports_period', array( 'data'=> $transactions->getTotalReports($start, $end), 'vouchers_data' => $transactionVouchers->getTotalReports($start, $end), 'payout_totals' => $transactions->getPayoutTotals($start, $end), 'start' => $start, 'end' => $end )); } /** * Returns the data model based on the primary key given in the GET variable. * If the data model is not found, an HTTP exception will be raised. */ public function loadModel() { if($this->_model===null) { if(isset($_GET['id'])) $this->_model=Transactions::model()->findbyPk($_GET['id']); if($this->_model===null) throw new CHttpException(404,'The requested page does not exist.'); } return $this->_model; } /** * Performs the AJAX validation. * @param CModel the model to be validated */ protected function performAjaxValidation($model) { if(isset($_POST['ajax']) && $_POST['ajax']==='transactions-form') { echo CActiveForm::validate($model); Yii::app()->end(); } } }
А, значит неверно понял. Я думал он уже подошел к стадии CMF и по модели генерит заготовки для админки - типа список - редактирование и т.д. В банальных сайтах это в общем рутинная и нудная задача =(
В этом плане кодогенератор у них сделан так, что в него проще парёной репы создать свои шаблоны и генерировать уже всё с них, избегая долгой и нудной рутины. Написал один раз и только пользуйся. Я до этого ещё не добрался, так что на вашей совести Вообще CRUD он генерирует на ура: список, администрирование (админский грид), добавление, удаление, редактирование - всё генерируется. Шаблоны только подправил под себя и готово (или сделал свои шаблоны).
А вот навёрнутый view для главного грида: PHP: <?php $this->breadcrumbs=array( 'Transactions', ); $this->menu=array( array('label'=>'Create Transactions', 'url'=>array('create')), array('label'=>'Manage Transactions', 'url'=>array('admin')), ); $data = Transactions::model(); ?> <?php echo CHtml::link('View reports for today', '/transactions/reports')?> | <?php echo CHtml::link('View reports', '/transactions/reportsInterval')?> <br /><br /> <h1>Transactions</h1> <?php $this->widget('zii.widgets.grid.CGridView', array( 'id'=>'transactions-grid', 'dataProvider'=>$model->search(), 'filter'=>$model, 'rowCssClassExpression' => '"rows ".$data->trn_status', 'selectableRows' => 1, 'selectionChanged' => 'transactionGridView', 'pager' => array( 'pageSize' => 50 ), 'columns'=>array( array( 'name' => 'trn_id', 'htmlOptions' => array('width' => '40'), ), array( 'name' => 'trn_system', 'filter' => array('ukash' => 'ukash', 'psc' => 'psc', 'cashu' => 'cashu', 'skype' => 'skype'), ), array( 'name' => 'trn_system_status', 'type' => 'html', 'filter' => array( 'new' => 'new', 'good' => 'good', 'bad' => 'bad', ), 'value' => '"<span class=\"".$data->trn_system_status."\">".$data->trn_system_status."</span>"', ), array( 'name' => 'trn_operator_status', 'type' => 'html', 'filter' => array( 'new' => 'new', 'good' => 'good', 'bad' => 'bad', ), 'value' => '"<span class=\"".$data->trn_operator_status."\">".$data->trn_operator_status."</span>"', ), array( 'name' => 'trn_total', 'type' => 'html', 'value' => '$data->trn_system == "skype" ? $data->trn_total." ".$data->skype->sky_currency : $data->trn_total." / <strong>".$data->trn_total_real."</strong>"' ), array( 'name' => 'trn_status', 'value' => '$data->trn_status.($data->trn_error_code != "" ? " (".$data->trn_error_code.")" : "")', ), 'trn_email', 'trn_started_on', 'trn_finished_on', array( 'name' => 'trn_paysystem', 'type'=>'html', 'value' => '($data->trn_status == "success") ? "<strong>".number_format($data->trn_total_real * $data->trn_rate, 2, ".", "")." ".$data->trn_paysystem."</strong>" : "<strong>".$data->trn_paysystem."</strong>"', 'filter' => array( 'WMZ' => 'WMZ', 'WME' => 'WME', 'STR' => 'STR', 'PMU' => 'PMU', 'LRU' => 'LRU', 'LIQ' => 'LIQ', 'GDP' => 'GDP', 'ECU' => 'ECU', 'PEC' => 'PEC', 'LRE' => 'LRE' ) ), 'trn_purse', array( 'name' => 'trn_rate', 'htmlOptions' => array('width' => '40'), ), 'trn_ip', array( 'name' => 'account.uac_login', 'filter' => '<input type="text" value="'.$model->account->uac_login.'" name="Transactions[account][uac_login]" />', ), array( 'name' => 'countVouchers', 'filter' => '<input type="text" value="'.($model->countVouchers == '0' ? '' : $model->countVouchers).'" name="Transactions[trnvouchers][tvc_code]" />', 'htmlOptions' => array('width' => '150'), ), array( 'class'=>'CButtonColumn', 'template' => '{setstatus}', 'buttons' => array( 'setstatus' => array( 'label' => 'Operator status', 'url' => 'Yii::app()->createUrl("/transactions/opstatus", array("id" => $data->trn_id))', 'click' => 'opstatus', ), ) ), ), ));?> <script type="text/javascript"> function transactionGridView(id) { $data = $.fn.yiiGridView.getSelection(id); if ($data.length == 1) { openWindow('/transactions/view/'+$data[0], 600, 700, 'trn'+$data[0], 'yes'); } return false; } function opstatus(id) { var url = $(this).attr('href'); return openWindow(url, 600, 700, 'opstatus', 'yes'); } /** * Open pop-up window * */ function openWindow(url, width, height, name, scrollbars) { if (typeof scrollbars == 'undefined') { scrollbars = 'no'; } var top = Math.ceil((screen.height - height)/2); var left = Math.ceil((screen.width - width)/2); if (typeof name == undefined || name == null){ name = Math.round(999999 * Math.random()); } var win = window.open(url,name ,'width='+width+',height='+height+',location=no,status=no,toolbar=no,menubar=no,resizable=no,scrollbars='+scrollbars+',top='+top+',left='+left); win.focus(); return false; } </script> Как я уже писал, замыкания тут прямо таки плачутся что бы их вставили. В ближайшее время шаблон соответственно преобразится
Что радует ещё, так это то, что если Active Record не справляется - не нужно опускаться до голого SQL. Есть так называемое DAO, на базе которого работает ActiveRecord. По сути это query builder, и сволочь удобный. Вот пример использования на примере валидатора модели: PHP: <?php class From extends CFormModel { public function checkSystem() { $sql = 'SELECT exr_rate, exr_code FROM exchange_rates WHERE exr_status = :status AND exr_id = :id AND FIND_IN_SET(:site, exr_site) AND exr_system = :system'; $params = array( ':id' => $this->system, ':system' => 'skype', ':site' => $this->getDomain(), ':status' => 'enabled', ); $this->rate_data = Yii::app()->getDb()->createCommand($sql)->queryRow(true, $params); if ($this->rate_data === false) { $this->addError('system', Yii::t('app', 'Invalid payment method selected!')); } } } На самом деле это можно было сделать и через ActiveRecord (даже FIND_IN_SET через CDbExpression подставился бы легко), но мне было лень на клиентском проекте добавлять целую модель ради простой валидации в одном месте А что там сложного то? На самом деле тут какраз всё до опупения просто, нужно всего-лишь ознакомится с азами Yii и посмотреть API по CGridView, моделям, контроллерам и views. Большая часть из этого есть в Yii Cookbook.
что то не видно тут билдера, но я думаю так писать удобно (билдеры считаю удобными только для базового ActiveRecord-а), сам почти так пишу, только плейсхолдеры типизирую. PHP: <?php $sql = 'SELECT exr_rate, exr_code FROM exchange_rates WHERE exr_status = ? AND exr_id = ?i AND FIND_IN_SET(?, exr_site) AND exr_system = ?'
Костян Ну я не стал извращаться с моделью, CDbCriteria (WHERE, JOIN's, ORDER BY, etc...) и CDbExpression. У меня пока небыло возможности хорошенько расковырять и сделать всё совсем правильно. Сроки поджимали. Щас будет чуть полегче, я многое переделаю the Yii way и тогда покажу. Считайте это быдлокодом для Yii, ибо по сути так оно и есть
Да как-то прсто привык немного к другим вывертам. Типа PHP: <?php class UserCarVinsController extends acController { protected $actions = array("display"=>array(), "results"=>array('params' => array(0 => 'vin') ) ); public function display() { } public function results() { $vin = trim($this->request->get('vin')); if (!$this->request->hasPost() || !$vin) { $this->redirect(array('action'=>'display')); return; } $car_vin = $this->dm->findFirst('CarVin', array('vin'=>$vin)); $res = !$car_vin->isNew(); $this->passToView('result', $res); } }
Вот есть у меня модель с кучей полей значения которые - списки. Причем, довольно статичные и пихать их в базу не хочется в том числе и из соображений производительности. Уносить во вью тоже не хочется - в нескольких местах используется. Хотелось бы определить модель, где будет массивом прописаны данные списка и слинковать ее с основной. Но я так понимаю, что не получится это ибо в relations указываем на эктив-рекорд модели только? CFormModel туда не подсунуть? Мож встречал реализации онного? Или эктив-рекорд использовать, только переопределить find на свой... в общем, примерчик бы
Или просто создать эти списки в виде статических arrayaccess классов, положить в extensions и вызывать их. Во view все же не дело все это помещать, ибо хочется еще и в валидаторе проверить...
MiksIr Возьми CModel за основу и реализуй хоть CActiveRecord аналог, который не будет данные тягать из базы Ну а дальше можно и relations задать. Разве что это немного через чур если данных таких не много. Или сделать extension/component класс и просто использовать их в модели как статичные или записывать в properties нужной модели.
Да вот я тоже думаю, что самому сейчас аналог CActiveRecord делать - это излишне будет. Т.е. в качестве развлекаловки то может пойти, но у меня тут живой проект, на котором решил попробовать Yii Другое дело, если где-то готовые решения проскакивали - по-этому и спросил. Пока пробую просто вспомогательные классы implements ArrayAccess.
MiksIr Заодно просмотри базовые классы Yii - там есть много полезных заготовок. API reference yii.base раздел.
Немного не IDE-friendly он... вот что. Куча всего не описано, что могло бы, плюс куча пересечений классов - пришлось разбираться что исключать из анализа IDE =(