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.
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.
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…
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();
}
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)
// 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.
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.
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.
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;
...
}
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.
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,
));
}
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.
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.