Chapter 6

I have followed the book up until this point without too much problem, but this problem I can’t figure out. I am at the end of Chapter 6. When I click on Update Issue, it seems the site would render fine but when it gets to Owner, it gives me the PHP error:


Fatal error: Call to a member function getUserOptions() on a non-object in C:\wamp\www\trackstar\protected\views\issue\_form.php

The form worked fine for creating issue, I don’t know why it fails for update. Since I access the link to update from view, here is my menu from views/issue/update.php:


$this->menu=array(

	array('label'=>'List Issue', 'url'=>array('index','pid'=>$model->project->id)),

	array('label'=>'Create Issue', 'url'=>array('create','pid'=>$model->project->id)),

	array('label'=>'Update Issue', 'url'=>array('update', 'id'=>$model->id)),

	array('label'=>'Delete Issue', 'url'=>'#', 'linkOptions'=>array('submit'=>array('delete','id'=>$model->id),'confirm'=>'Are you sure you want to delete this item?')),

	array('label'=>'Manage Issue', 'url'=>array('admin','pid'=>$model->project->id)),

);



This is views/issue/update.php:


<?php

$this->breadcrumbs=array(

	'Issues'=>array('index'),

	$model->name=>array('view','id'=>$model->id),

	'Update',

);


$this->menu=array(

	array('label'=>'List Issue', 'url'=>array('index')),

	array('label'=>'Create Issue', 'url'=>array('create')),

	array('label'=>'View Issue', 'url'=>array('view', 'id'=>$model->id)),

	array('label'=>'Manage Issue', 'url'=>array('admin')),

);

?>


<h1>Update Issue <?php echo $model->id; ?></h1>


<?php echo $this->renderPartial('_form', array('model'=>$model)); ?>

Here is part of my views/issue/_form.php:


<div class="row">

	<?php echo $form->labelEx($model,'owner_id'); ?>

	<?php echo $form->dropDownList($model,'owner_id',$this->getProject()->getUserOptions()); ?>

	<?php echo $form->error($model,'owner_id'); ?>

</div>


<div class="row">

	<?php echo $form->labelEx($model,'requester_id'); ?>

	<?php echo $form->dropDownList($model,'requester_id',$this->getProject()->getUserOptions()); ?>

	<?php echo $form->error($model,'requester_id'); ?>

</div>

Here is my IssueController class:


<?php


class IssueController extends Controller

{

	/**

	 * @var string the default layout for the views. Defaults to '//layouts/column2', meaning

	 * using two-column layout. See 'protected/views/layouts/column2.php'.

	 */

	public $layout='//layouts/column2';


        /**

        * @var private property containing the associated Project model instance.

        */

        private $_project = null;


        /**

        * Protected method to load the associated Project model class

        * @project_id the primary identifier of the associated Project

        * @return object the Project data model based on the primary key

        */

        protected function loadProject($project_id)

        {

            //if the project property is null, create it based on input id

            if($this->_project===null)

            {

                  $this->_project=Project::model()->findbyPk($project_id);

                  if($this->_project===null)

                  {

                    throw new CHttpException(404,'The requested project does not exist.');

                  }

            }

            return $this->_project;

        }


	/**

	 * @return array action filters

	 */

	public function filters()

        {

		return array(

                    'accessControl', // perform access control for CRUD operations

                    'projectContext + create index admin' // check to ensure valid project context

		);

	}


	/**

	 * Specifies the access control rules.

	 * This method is used by the 'accessControl' filter.

	 * @return array access control rules

	 */

	public function accessRules()

	{

		return array(

			array('allow',  // allow all users to perform 'index' and 'view' actions

				'actions'=>array('index','view'),

				'users'=>array('*'),

			),

			array('allow', // allow authenticated user to perform 'create' and 'update' actions

				'actions'=>array('create','update'),

				'users'=>array('@'),

			),

			array('allow', // allow admin user to perform 'admin' and 'delete' actions

				'actions'=>array('admin','delete'),

				'users'=>array('admin'),

			),

			array('deny',  // deny all users

				'users'=>array('*'),

			),

		);

	}


	/**

	 * Displays a particular model.

	 * @param integer $id the ID of the model to be displayed

	 */

	public function actionView($id)

	{

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

			'model'=>$this->loadModel($id),

		));

	}


	/**

	 * Creates a new model.

	 * If creation is successful, the browser will be redirected to the 'view' page.

	 */

	public function actionCreate()

	{

		$model=new Issue;

                $model->project_id = $this->_project->id;

		// Uncomment the following line if AJAX validation is needed

		// $this->performAjaxValidation($model);


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

		{

			$model->attributes=$_POST['Issue'];

			if($model->save())

				$this->redirect(array('view','id'=>$model->id));

		}


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

			'model'=>$model,

		));

	}


	/**

	 * Updates a particular model.

	 * If update is successful, the browser will be redirected to the 'view' page.

	 * @param integer $id the ID of the model to be updated

	 */

	public function actionUpdate($id)

	{

		$model=$this->loadModel($id);


		// Uncomment the following line if AJAX validation is needed

		// $this->performAjaxValidation($model);


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

		{

			$model->attributes=$_POST['Issue'];

			if($model->save())

				$this->redirect(array('view','id'=>$model->id));

		}


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

			'model'=>$model,

		));

	}


	/**

	 * Deletes a particular model.

	 * If deletion is successful, the browser will be redirected to the 'index' page.

	 * @param integer $id the ID of the model to be deleted

	 */

	public function actionDelete($id)

	{

		if(Yii::app()->request->isPostRequest)

		{

			// we only allow deletion via POST request

			$this->loadModel($id)->delete();


			// if AJAX request (triggered by deletion via admin grid view),

                        // we should not redirect the browser

			if(!isset($_GET['ajax']))

				$this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin'));

		}

		else

			throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');

	}


	/**

	 * Lists all models.

	 */

	public function actionIndex()

	{


		$dataProvider=new CActiveDataProvider('Issue', array(

                    'criteria'=>array(

                        'condition'=>'project_id=:projectId',

                        'params'=>array(':projectId'=>$this->_project->id),

                    )

                ));

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

			'dataProvider'=>$dataProvider,

		));

	}


	/**

	 * Manages all models.

	 */

	public function actionAdmin()

	{

		$model=new Issue('search');

		$model->unsetAttributes();  // clear any default values

		if(isset($_GET['Issue']))

			$model->attributes=$_GET['Issue'];

                

                $model->project_id = $this->_project->id;


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

			'model'=>$model,

		));

	}


	/**

	 * 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.

	 * @param integer the ID of the model to be loaded

	 */

	public function loadModel($id)

	{

		$model=Issue::model()->findByPk((int)$id);

		if($model===null)

			throw new CHttpException(404,'The requested page does not exist.');

		return $model;

	}


	/**

	 * Performs the AJAX validation.

	 * @param CModel the model to be validated

	 */

	protected function performAjaxValidation($model)

	{

		if(isset($_POST['ajax']) && $_POST['ajax']==='issue-form')

		{

			echo CActiveForm::validate($model);

			Yii::app()->end();

		}

	}


        /**

        * In-class defined filter method, configured for use in the above filters() method

        * It is called before the actionCreate() action method is run in order

         * to ensure a proper project context

        */


        public function filterProjectContext($filterChain)

        {

             //set the project identifier based on either the GET or POST input

             //request variables, since we allow both types for our actions

             $projectId = null;

             if(isset($_GET['pid']))

                  $projectId = $_GET['pid'];

             else

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

                        $projectId = $_POST['pid'];

             $this->loadProject($projectId);

             //complete the running of other filters and execute the requested action

             $filterChain->run();

        }


        public function getProject(){

            return $this->_project;

        }

}

Thank You in advance!

When you update the issue, do you have an owner selected?

Yes I have both owner and requester selected when I first create the issue.

Do you still have them when you update it?

I don’t know if I still have them in update, because when I go to the update page, the page fails at the point where owner is supposed to show. I guess one thing i’m unclear about is how can _form call getProject() while it’s not passing a ‘pid’ in the menu (like for ‘create issue’ or ‘list issue’, see the original post for code)

Unfortunately, in order to be able to cover many topics in a limited number of pages, not every detail of the functionality could be fully implemented in the book. Updating an issue was not fully covered (Refer to the NOTE section in the book at the top of page 145 at the end of chapter 6).

For this issue, you can follow the same approach we did for the creation and explicitly add a pid querystring param to the url as well as add the ‘update’ action method to the list of methods we need to ensure project context (i.e. add update to the filter: ‘projectContext + create index admin update’)

OR

since the issue model is loaded in the actionUpdate method, and through relational AR, the issue knows its parent project, you can just set the property direclty in the actionUpdate() method right after the main issue model is loaded. So, in IssueController::actionUpdate()


 /**

     * Updates a particular model.

     * If update is successful, the browser will be redirected to the 'view' page.

     */

	public function actionUpdate()

	{

		$model=$this->loadModel();

		

		$this->_project = $model->project;


   	...

Hope this helps.

Wow. Thank you so much. That solved the problem.

Originally I traced back the problem to adding pid in the update link, but completely forgot to add ‘update’ to the filter.

I got the same problem but I checked the URL in view.php. I seems URL for the delete button is not right. So I finally fixed with this to pass the format of delete&id=x&pid=y

public function actionUpdate($id)

{


	&#036;model=&#036;this-&gt;loadModel(&#036;id);


            //get current project id


            //


            &#036;model-&gt;project_id=&#036;this-&gt;_project-&gt;id;

in view.php:

array(‘label’=>‘Update Issue’, ‘url’=>array(‘update’, ‘id’=>$model->id,‘pid’=>$model->project->id)),

Then everything is OK. Hope it helps.

I had the same exact problem in 1.1.7 and this thread [SOLVED]it.

that’s a nice relation in your chapter but in my case it didnt solve my hiccup ! i have 3 tables: agent (id, user_id, agency_id) user (id, person_id) person (id, name, surname). i want the user_id field in agent table to display the name and surname from the person table in a drop down… any idea ?

change your code with this code

public function actionUpdate($id)

{


            


	&#036;model=&#036;this-&gt;loadModel(&#036;id);


            &#036;this-&gt;loadProject(&#036;model-&gt;project_id);





	// Uncomment the following line if AJAX validation is needed


	// &#036;this-&gt;performAjaxValidation(&#036;model);





	if(isset(&#036;_POST['Issue']))


	{


		&#036;model-&gt;attributes=&#036;_POST['Issue'];


		if(&#036;model-&gt;save())


		&#036;this-&gt;redirect(array('view','id'=&gt;&#036;model-&gt;id));


	}





	&#036;this-&gt;render('update',array(


		'model'=&gt;&#036;model,


	));


}