Get One Record From Cactiverecord's Array

Hi there.

I’m new in Yii, doing my first project (some kind of personal calendar, or organizer).

The solution I use is apparently not optimal, if you know how to do it better, please write me about it.

Situation:

I need to change the value of some CActiveRecord model with particular field I don’t know until user choose it in view.

How can I filter (select from) CActiveRecord’s array that I pass to the view from my controller without using all the variety of find…() methods?

I think they will query database, but it’s unnecessary for me, because I already pass all needed information to the view.

I use CActiveRecord instead of DataProvider in order to save changes, which user will do.

Here are the fields of model I need to change (change only field Name).




 * The followings are the available columns in table 'tbl_category':

 * @property string $ID

 * @property string $OwnerID

 * @property string $Name



So for example, user checks the name of Category from DropDownList he wants to change and then he writes a new name for this Category, push the button and saves new name of Category in database.

[list=1][][size=2]Create a ManageCategoryForm model ([/size]CFormModel[size=2]).[/size][][size=2]Wrap the drop down and textfield in a form with a submit button. [/size][*][size=2]Then in your controller you can access the categoryId and the new value.[/size][/list]

Matt

Thanks for attention.

I’m acquainted with the process of creating web application itself. But I wonder how to work with the data (get, choose one of, change and save changes in DB) I get from DB with as less queries to DB as possible. And in your answer I can’t find some advices how to do that. If it will be possible, I would like to see some code solution for my situation.

And as I understand the purpose of using CFormModel… It will be better from documentation:

Would you please clarify how CFormModel can work with data from DB and than save it back to DB.

Do you mean to create CForm Model only for taking data from client and than get it in controller, find data in DB according to received data and change it manually?

Hi Alexander, welcome to the forum

PHP is a stateless language, so that it has to re-construct the necessary model objects from the scratch every time it responds to a request from the client browser. We don’t have a direct mechanism to pass model objects across requests (or between the server and the client).

So, “querying all the time” is a normal state for a PHP web app, and almost always it’s the best solution for us.

Thanks

I.e. is it normal to call database from view-file?

I still can’t solve this problem, what would you recommend?

If I create view class extending CFormModel and get data from user, will the view retrieve the data back to controller?

No, the view never receives data from the user. All the input of the user will come to the server as a new request. Displaying the form and receiving the data belong to 2 different life cycles of the app.

http://www.yiiframework.com/doc/guide/1.1/en/basics.mvc#a-typical-workflow

I’m sorry. I said wrong. I mean getting attributes with using $_POST array, of course.

I code an individual CFormModel, as waterloomatt suggests:




class ManageCategoryForm extends CFormModel

{

	public $categoryID; // integer

	public $newName; // string


	/**

	 * Declares the validation rules.

	 */

	public function rules()

	{

		return array(

			array('categoryID, newName', 'required'),

            array('newName', 'length', 'max'=>48, 'min'=>3),

		);

	}

}



Here is code of my action:




   	/**

	 * Updates a name of particular category model.

	 * ID of the model is gets from user's request

	 */

	public function actionUpdateCategory()

	{

        $model=TblTask::model()->thisUser()->findAll(); //thisUser - scope

        $model2=TblCategory::model()->thisUser()->findAll();

        $model3=ManageCategoryForm;


		if(isset($_POST['ManageCategoryForm']))

		{

			$model3->attributes=$_POST['ManageCategoryForm'];

			if($model3->save())

			{

                Yii::app()->user->setFlash('updateCategory','Изменения сохранены.');

				$this->refresh();

            }

        }


		$this->render('updateCategory',array(

			'model'=>$model, /* all the user's tasks */

                        'model2'=>$model2, /* all the user's categories */

                        'model3'=>$model3, /* form for selecting categoryName to change */

		));

	}



and this is my view _formCategoryUpdate, which renders in updateCategory.php




<?php

/* @var $this TaskController */

/* @var $form CActiveForm */

?>


<div class="form">


<?php $form=$this->beginWidget('CActiveForm', array(

	'id'=>'tbl-category-form',

	'enableAjaxValidation'=>false,

    'clientOptions'=>array(

		'validateOnSubmit'=>true,                            

	),

)); ?>


<?php if(Yii::app()->user->hasFlash('_formCategoryUpdate')): ?>


<div class="flash-success">

	<?php echo Yii::app()->user->getFlash('_formCategoryUpdate'); ?>

</div>


<?php else: ?>


	<p class="note">Поля, отмеченные <span class="required">*</span> являются обязательными.</p>


	<div class="row">

    Выберите категорию для изменения: <br />

		<?php echo $form->labelEx($model3,'categoryID'); ?>

		<?php echo $form->dropDownList($model3,'categoryID', CHtml::listData($model2, 'ID', 'Name')); ?>

		<?php echo $form->error($model3,'categoryID'); ?>

	</div>

    

	<div class="row">

    Введите новое имя категории: <br />

		<?php echo $form->labelEx($model3,'newName'); ?>

		<?php echo $form->textField($model3,'newName'); ?>

		<?php echo $form->error($model3,'newName'); ?>

	</div>

    

	<div class="row buttons">

		<?php echo CHtml::submitButton($model->isNewRecord ? 'Создать категорию' : 'Сохранить изменения'); ?>

	</div>


<?php $this->endWidget(); ?>


</div><!-- form -->


<?php endif; ?>



I get PHP warning because of discrepancy of what gets in $form->labelEx, $form->dropDownList, $form->error.

In this view I want user to select Category he wants to change name of, and newName for this Category

I see.

CFormModel doesn’t have an ability to access a database table by itself. We have to use CActiveRecord model to do that.




...

	if(isset($_POST['ManageCategoryForm']))

	{

		$model3->attributes=$_POST['ManageCategoryForm'];

		if($model3->validate())

                {

                      $model4 = TblCategory::model()->findByPk($model3->categoryID);

                      if ($model4 !== null)

                      {

                             $model4->name = $model3->name;

                             if($model4->save())

		             {

		                  ...



I think you can use TblCategory model instead of ManageCategoryForm model here, but you will have to load a model instance from the table before you update it, anyway.




...

if(isset($_POST['TblCategory']))

{

	$model3->attributes=$_POST['TblCategory'];

	if($model3->validate())

        {

               $model4 = TblCategory::model()->findByPk($model3->ID);

               if ($model4 !== null)

               {

                       $model4->name = $model3->name;

                       if($model4->save())

	               {

	                         ...



Note: In your view code, <?php if: ?> … <?php else: ?> … <?php endif; ?> will create a <form> opening tag without the corresponding </form> ending tag when there’s a flash message.

Thanks for your note, I was in hurry while writing those piece of code.

In your advice, $model3->attributes=$_POST[‘TblCategory’];, how will I get info from user without form (like ManageCategoryForm)? Without it these are just models, user willn’t see anything, and without it I can’t determine, what new name will be

CActiveRecord and CFormModel share the same base class, CModel. We can use any of them as a data container. CActiveRecord can do everything that CFormModel can do.

Please don’t get me wrong, I don’t mean to offend you, but I’m not quite sure yet whether you have a clear notion what a web application is like … probably you are from the world of desktop app …

So, this is a kind of normal work flow when we use CActiveRecord to display and update the data in the db.

  1. Server side: Displays a list of data.



1-1. Fetches data into an array of CActiveRecords from the db.

1-2. Passes the array to the view.

1-3. The view creates HTML output by reading the array.

     Every list item has the link to update it, holding the ID of the data.

1-4. Sends the HTML to the user's browser.

1-5. Dies.



This is usually done in "acitonAdmin" method of the controller.

  1. Client side: Selects one from the list.



2-1. User clicks on the link of an item.

2-2. Browser requests an URL to update the item.

     The URL contains the ID of the data.



  1. Server side: Displays a form of a data.



3-1. Fetches the data with the given ID into a CActiveRecord from the db,

     using findByPk() or something like that.

3-2. Passes the model to the form view.

3-3. The view creates HTML form by reading the model.

3-4. Sends the HTML to the user's browser.

3.5. Dies.



  1. Client side: Edits the fields in the form and posts it.



4-1. User edits data in the fields of the form.

4-2. Submits.

4-3. The edited data gets sent to the server using post method.



  1. Server side: Receives the post and update the data.



5-1. Fetches the data with the given ID into a CActiveRecord from the db,

     using findByPk() or something like that.

5-2. Modifies the values of the model with the user inputs that are given in $_POST.

5-3. Validates the values.

5-4-A. If validated ...

5-4-A-1. Writes it back to the db.

5-4-A-2. Redirects.

5-4-B. If not validated ...

5-4-B-1. Passes the modified model to the form view.

5-4-B-2. The view creates HTML form by reading the model, with some error messages.

5-4-B-3. Sends the HTML to the user's browser.

5-5. Dies.



In the server side, 3. and 5. share the same controller action method.

Once again, please don’t be offended. All of the above might have been quite clear to you, and I might have failed to understand what you are concerned about.

Thanks again to all who trying to help me, especially softark. I solve my problem (with first variant).

And at the end of topic may I hear some recommendations about the topic of my post - how to work with array of CActiveRecords? I have problems with every time I need to process array of find results. (For example, when I need to print Category Name instead of it’s ID when use CGridView widget. I add my own column in widget and I need to past the name of task’s category).