after issue deletion, it behaves weird.

greetings to everybody !

i am new to the yii forum and you bet i am a new yii learner, crawling on the trackstar example.

i have just finished the project and issue part of the example (till end of chapter 6).

one interesting thing is that when i delete one issue from the issue list, it redirects to issue/admin and gives this error:

Home » Error

Error 404

The requested project does not exist.

firstly, i don’t know why it redirects me to issue/admin. why not issue/index ?

secondly, i know issue/admin needs an project id to list the remaining issue items, but there is no view/deletion.php for me to edit to give him the pid.

can anybody help me out on this?

many thanks in advance.

partially perplexed ::) ,

XXX

Hey shgpan… I’ll give it a go (but I’m only learning too).

I just tried your scenario and got the same error.

The reason is that the method was changed a little earlier in the book to only show the issues belonging to the required project (around page 157).

So the menu link url needs to be changed to supply a pid value.

I’ve got no idea if this is right, but it works:

in IssueController.php




public function actionDelete($id)

{

   ...


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


   ...


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

      $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin','pid'=>$model->project->id));   


   ...

}



I guess if you wanted to redirect to issue/index you’d use:




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

   $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('index','pid'=>$model->project->id));



I presume that sort of thing is personal choice?

Hi Guys,

I’m also having problems with post deletion redirection but it’s not really specific to the delete action.

My problem is whenever I try to view the issue/admin&pid=1 (for example) page I get a CDbException. The error seems to show that I am attempting to run an SQL query without supplying a required $params array (From stack trace):




CDbCommand failed to execute the SQL statement: SQLSTATE[HY093]: Invalid parameter number: parameter was not defined. The SQL statement executed was: SELECT COUNT(*) FROM `tbl_issue` `t` WHERE project_id=:projectID 


C:\Users\Philip\xampp\yii\framework\db\CDbCommand.php(518)


506             return $result;

507         }

508         catch(Exception $e)

509         {

510             if($this->_connection->enableProfiling)

511                 Yii::endProfile('system.db.CDbCommand.query('.$this->getText().$par.')','system.db.CDbCommand.query');

512             $errorInfo = $e instanceof PDOException ? $e->errorInfo : null;

513             $message = $e->getMessage();

514             Yii::log(Yii::t('yii','CDbCommand::{method}() failed: {error}. The SQL statement executed was: {sql}.',

515                 array('{method}'=>$method, '{error}'=>$message, '{sql}'=>$this->getText().$par)),CLogger::LEVEL_ERROR,'system.db.CDbCommand');

516             if(YII_DEBUG)

517                 $message .= '. The SQL statement executed was: '.$this->getText().$par;

518             throw new CDbException(Yii::t('yii','CDbCommand failed to execute the SQL statement: {error}',

519                 array('{error}'=>$message)),(int)$e->getCode(),$errorInfo);

520         }

521     }

522 

523     /**

524      * Builds a SQL SELECT statement from the given query specification.

525      * @param array $query the query specification in name-value pairs. The following

526      * query options are supported: {@link select}, {@link distinct}, {@link from},

527      * {@link where}, {@link join}, {@link group}, {@link having}, {@link order},

528      * {@link limit}, {@link offset} and {@link union}.

529      * @return string the SQL statement

530      * @since 1.1.6







#0 	

–

 C:\Users\Philip\xampp\yii\framework\db\CDbCommand.php(413): CDbCommand->queryInternal("fetchColumn", 0, array())


408      * @return mixed the value of the first column in the first row of the query result. False is returned if there is no value.

409      * @throws CException execution failed

410      */

411     public function queryScalar($params=array())

412     {

413         $result=$this->queryInternal('fetchColumn',0,$params);

414         if(is_resource($result) && get_resource_type($result)==='stream')

415             return stream_get_contents($result);

416         else

417             return $result;

418     }



I won’t include the whole thing. If I navigate to project/admin&pid=1 I get the project admin page (grid view, searchable) but it doesn’t work for issue. I have clearly omitted something here and I’ve been trying to compare the project model, admin view and controller adminAction to the ones for issue. I really can’t see what I’m doing wrong and would really appreciate any thoughts on this so I can better understand what’s going on.

Here’s my actionAdmin function in the issue controller:


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,

		));

	}

Can someone start by telling me what the ‘search’ argument does when passed to the constructor of the issue model? Does it have something to do with the search() function in same? (I don’t understand this syntax basically).

Thanks for any help everyone. Please just shout if you need further details.

Ok, found the cause.

I had made a syntax error in the editing of the search() method within the Issue model class on page 144.

Cheers.

Still interested in hearing about the ‘search’ argument above. Want to “break down” the mysteries of Yii!

So your search works on your issues page? I was having issues with that to, see: ch-6-issue-search-issue and I can’t find my problem! I even compared to author’s source…

Hi murphaph,

When you supply a string as an argument to a new Model/ActiveRecord you are setting the scenario for that model. For example, these lines of code are equivalent.




$model=new Issue('search');


$model = new Issue();

$model->setScenario('search');



In your case, I don’t think the search scenario is doing anything. Just good practice to start using it.

Scenarios are really useful when it comes to validation. You can apply different rules to different scenarios.

Example:




    // User Controller:

    public function actionChangePassword()

    {

        // Load the current logged in user.

        $model = Yii::app()->user->getModel();

        $model->setScenario('changePassword');


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

        {

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


            if ($model->save())

            {

                Yii::app()->user->setFlash('success', 'Password updated successfully.');

                $this->redirect(array('project/index'));

            }

        }


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

            'model' => $model,

        ));

    }


    // User Model

    ...

    public function beforeSave()

    {

        if ($this->getScenario() == 'changePassword')

        {

            $this->password = md5($this->newPassword);

        }


        return parent::beforeSave();

    }



Cheers,

Matt

@scribbly

Please post your relevant Controller methods and Model methods.

If you look at the actionAdmin/actionIndex method, you’ll see they don’t expect any parameters. The parameters for action methods correspond to $_GET/$_POST values passed by URLs/Forms respectively.




public function actionAdmin() VS public function actionAdmin($id)



Therefore,




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

      $this->redirect(isset($_POST['returnUrl']) ? $_POST['returnUrl'] : array('admin','pid'=>$model->project->id)); 



Probably should be:




// 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'));



OR:




// 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('index'));



depending where you want to redirect to. But I need to see your Controller/Model to verify that.

Thanks,

Matt

No SCribbly, my search doesn’t work either. Didn’t think to check that tbh. Will take a look at it.

@Matt: Thanks for the info Matt. Appreciate it!

I’ve not used parameters in those methods. The issue is that the search function does not filter the records on the Issues/admin page (and I really have no idea on how to try and track down the problem, as my code is basically the same as the book’s source – but maybe I’m not looking in the right place??)

Appreciate some pointers…

From IssueController:


	/**

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

		));

	}



Which is detailed on page 143 of the book.

My Issue Model:


<?php


/**

 * This is the model class for table "tbl_issue".

 *

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

 * @property integer $id

 * @property string $name

 * @property string $description

 * @property integer $project_id

 * @property integer $type_id

 * @property integer $status_id

 * @property integer $owner_id

 * @property integer $requester_id

 * @property string $create_time

 * @property integer $create_user_id

 * @property string $update_time

 * @property integer $update_user_id

 *

 * The followings are the available model relations:

 * @property User $requester

 * @property User $owner

 * @property Project $project

 */

 


class Issue extends CActiveRecord

{

	 const TYPE_BUG=0;

	 const TYPE_FEATURE=1;

	 const TYPE_TASK=2;

	 

	 // Status Constants

	 const STATUS_NOTYETSTARTED=0;

	 const STATUS_STARTED=1;

	 const STATUS_FINISHED=2;

	 

	 	/**

	 * Returns the static model of the specified AR class.

	 * @return Issue 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 'tbl_issue';

	}

	

	public function gettypeOptions()

	{

		return array(

			self::TYPE_BUG=>'Bug',

			self::TYPE_FEATURE=>'Feature',

			self::TYPE_TASK=>'Task',

		);

	}

	public function getstatusOptions()

	{

		return array(

			self::STATUS_NOTYETSTARTED=>'Not Yet Started',

			self::STATUS_STARTED=>'Started',

			self::STATUS_FINISHED=>'Finished',

		);

	}

	/**

	* @return string the status text display for the current issue

	*/

    public function getStatusText()

    {

        $statusOptions = $this->statusOptions;

        return isset($statusOptions[$this->status_id]) ?

                $statusOptions[$this->status_id] :

                "unknown status ({$this->status_id})";

    }


	/**

	* @return string the type text display for the current issue

	*/


    public function getTypeText()

    {

        $typeOptions = $this->typeOptions;

        return isset($typeOptions[$this->type_id]) ?

                $typeOptions[$this->type_id] :

                "unknown type ({$this->type_id})";

    }




	/**

	 * @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('name', 'required'),

			array('project_id, type_id, status_id, owner_id, requester_id, create_user_id, update_user_id', 'numerical', 'integerOnly'=>true),

			array('name', 'length', 'max'=>256),

			array('description', 'length', 'max'=>2000),

			array('create_time, update_time', 'safe'),

			// The following rule is used by search().

			// Please remove those attributes that should not be searched.

			array('id, name, description, project_id, type_id, status_id, owner_id, requester_id, create_time, create_user_id, update_time, update_user_id', 'safe', 'on'=>'search'),

		);

	}


	/**

	 * @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(

			'requester' => array(self::BELONGS_TO, 'User', 'requester_id'),

			'owner' => array(self::BELONGS_TO, 'User', 'owner_id'),

			'project' => array(self::BELONGS_TO, 'Project', 'project_id'),

		);

	}


	/**

	 * @return array customized attribute labels (name=>label)

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'name' => 'Name',

			'description' => 'Description',

			'project_id' => 'Project',

			'type_id' => 'Type',

			'status_id' => 'Status',

			'owner_id' => 'Owner',

			'requester_id' => 'Requester',

			'create_time' => 'Create Time',

			'create_user_id' => 'Create User',

			'update_time' => 'Update Time',

			'update_user_id' => 'Update User',

		);

	}


	/**

	 * 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->compare('id',$this->id);

		$criteria->compare('name',$this->name,true);

		$criteria->compare('description',$this->description,true);

		//$criteria->compare('project_id',$this->project_id);

		$criteria->compare('type_id',$this->type_id);

		$criteria->compare('status_id',$this->status_id);

		$criteria->compare('owner_id',$this->owner_id);

		$criteria->compare('requester_id',$this->requester_id);

		$criteria->compare('create_time',$this->create_time,true);

		$criteria->compare('create_user_id',$this->create_user_id);

		$criteria->compare('update_time',$this->update_time,true);

		$criteria->compare('update_user_id',$this->update_user_id);

		$criteria->condition='project_id=:projectID';

		$criteria->params=array(':projectID'=>$this->project_id);


		return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

		));

	}

}

Ok. The 1st thing I would do is remove: $model->project_id = $this->_project->id;




        /**

         * Manages all models.

         */

        public function actionAdmin()

        {

                $model=new Issue('search');

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

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

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

                        

                // Remove this line.

                // I'm not sure where $this->_project->id is coming from.

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


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

                        'model'=>$model,

                ));

        }



You can uncomment //$criteria->compare(‘project_id’,$this->project_id); in the model’s search function since this can be drop down in the GridView and you can filter by this project that way.




        public function search()

        {

                // Warning: Please modify the following code to remove attributes that

                // should not be searched.


                $criteria=new CDbCriteria;


                $criteria->compare('id',$this->id);

                $criteria->compare('name',$this->name,true);

                $criteria->compare('description',$this->description,true);

                $criteria->compare('project_id',$this->project_id);

                $criteria->compare('type_id',$this->type_id);

                $criteria->compare('status_id',$this->status_id);

                $criteria->compare('owner_id',$this->owner_id);

                $criteria->compare('requester_id',$this->requester_id);

                $criteria->compare('create_time',$this->create_time,true);

                $criteria->compare('create_user_id',$this->create_user_id);

                $criteria->compare('update_time',$this->update_time,true);

                $criteria->compare('update_user_id',$this->update_user_id);

                $criteria->condition='project_id=:projectID';

                $criteria->params=array(':projectID'=>$this->project_id);


                return new CActiveDataProvider(get_class($this), array(

                        'criteria'=>$criteria,

                ));

        }



Look at what the search() function is returning: CActiveDataProvider. This is a list of Issues that match a certain criteria. You generally don’t need to touch the actionAdmin method since you view (CGridView) passes the filter criteria to actionAdmin, which passes it to the search method.

Make those two changes and let me know how the battle goes.

Matt

Please ignore my last comment. I just opened the book to see what’s going on and the actionAdmin looks right. I will take another look and get back to you.

Matt

Hey: thanks for the help but no change with those two changes.

BTW:

is added from page 143 of the book :blink:

and I commented


$criteria->compare('project_id',$this->project_id); 

because it was missing from the code on page 144 (and from the book’s source).

Like murphaph said above: I’d really like to understand why this is not working. Unfortunately, I didn’t test the search before the Chapter 6 changes, so am unsure if it worked originally: murphaph???

Can you verify $this->_project is being loaded in each method index, view, and admin? What does your URL look like when on the admin page? Does it have the project id in the url?

To test index.




public function actionIndex()

{

    echo $this->_project->id;

    ...

}



Matt

Yep… the id is written to the page corretly.

The URL is http://localhost/trackstar/index.php?r=issue/admin&pid=1

@scribbly,

As far as I can see, all looks correct. I have a few hours free; if you’d like, zip up your code so far (including the DB schema + data),send it to me, and I will take a look.

Cheers,

Matt

Hey,

I didn’t check the Issue search either before modifying it but when I comment out:


$criteria->condition = 'project_id=:projectID';

$criteria->params = array(':projectID' => $this->project_id);

from the search method in the Issue model, it works. Clearly this is not the right solution but might help us find it.

Ok - I think I got it. The book is wrong. Try this.




/**

* 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->compare('id',$this->id);

	$criteria->compare('name',$this->name,true);

	$criteria->compare('description',$this->description,true);

	$criteria->compare('type_id',$this->type_id);

	$criteria->compare('status_id',$this->status_id);

	$criteria->compare('owner_id',$this->owner_id);

	$criteria->compare('requester_id',$this->requester_id);

	$criteria->compare('create_time',$this->create_time,true);

	$criteria->compare('create_user_id',$this->create_user_id);

	$criteria->compare('update_time',$this->update_time,true);

	$criteria->compare('update_user_id',$this->update_user_id);

	// Add the following line

	// See http://www.yiiframework.com/doc/api/1.1/CDbCriteria#addSearchCondition-detail

	$criteria->addSearchCondition('project_id', $this->project_id);

	//$criteria->condition='project_id=:projectID';

	//$criteria->params=array(':projectID'=>$this->project_id);


	return new CActiveDataProvider(get_class($this), array(

			'criteria'=>$criteria,

	));

}



let me know.

Matt

Hi Matt,

I’ve tried your solution and it appears to work for me. Could you explain why you think the book does it the other way? The book is a little vague on this page. It just says to add that code without really explaining it.

Cheers.

If you add $criteria->condition=‘project_id=:projectID’ after the compare methods, it overwrites them. By placing it before the compare methods, like below, it works.




        public function search()

        {

			// Warning: Please modify the following code to remove attributes that

			// should not be searched.


			$criteria=new CDbCriteria;

			

			$criteria->condition='project_id=:projectID';

			$criteria->params=array(':projectID'=>$this->project_id);


			$criteria->compare('id',$this->id);

			$criteria->compare('name',$this->name,true);

			$criteria->compare('description',$this->description,true);

			$criteria->compare('type_id',$this->type_id);

			$criteria->compare('status_id',$this->status_id);

			$criteria->compare('owner_id',$this->owner_id);

			$criteria->compare('requester_id',$this->requester_id);

			$criteria->compare('create_time',$this->create_time,true);

			$criteria->compare('create_user_id',$this->create_user_id);

			$criteria->compare('update_time',$this->update_time,true);

			$criteria->compare('update_user_id',$this->update_user_id);


			return new CActiveDataProvider(get_class($this), array(

					'criteria'=>$criteria,

			));

        }



The two methods, addSearchCondition & compare, merge with the previous criteria. Try and work through the Compare Method, you’ll see it calls one of three methods, addInCondition, addSearchCondition (if you specify partial matches), or addCondition.

*Note, the code above is probably more efficient since addSearchCondition is using the LIKE operator whereas the $criteria->condition… is using the ‘WHERE pid = #’.

I’ve worked through most of the book myself and found it quite confusing in spots - it glosses over quite a few important details. If you get stuck, the best way to debug is to use Yii’s class reference and dig into the core code.

Matt

Thanks a mil Matt.

Another question if I may…

Can you please explain what the 2 project ID variables are in:


project_id=:projectID

Is this an actual SQL statement? I really don’t understand it.