Stuck on displaying "lazy relational query"

I tried copying the code for the demo blog as how it pulls the “comments” and displays it when you’re about to post but no good for me.

I’m trying to display data from another table. So I have table “group” with (id, title). And then I have “member” with "firstname, lastname, membersince, groupid)

I want to display the “firstname, lastname & membersince” on my group view ALONG with the title. I already have the title displaying, easy. :lol:

But how do I add firstname, lastname and membersince? This has really gotten me stuck for the past 2 days. I can’t figure it out.

I know it has something to do with relations() but whenever I add some code like this


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(

	       			'member' => array(self::BELONGS_TO, 'Member', 'member_id'),

		);

	}

And then try to call it, it doesn’t go through. I just get error “Undefined variable: Member

First: You have to use ‘member’ instead of Member as the lower case one is your relation

Then you should have the following relations:

  1. In group: a HAS_MANY members relation called ‘members’

  2. In member: a BELONGS_TO relation called ‘group’

Now if you are in a group view you can call all members who belong to this group with $model->members and you’ll receive an array. If you call $model->group in a member view you will receive a single group model.

Group view:




echo 'Title: '.$model->title;

echo 'Members:';

foreach($model->members as $member)

    echo $member->firstname



Member view:




echo 'Name: '.$member->firstname;

echo 'Group: '. !is_null($member->group) ? $member->group->title : null;



EDIT: I just saw that you could mean that a group really belong to a single user (like a group created on facebook for example). If that’s the case then your relation should be ok and the group view would look like




echo 'Group title: '.$model->title;

echo 'Creator: '. !is_null($model->member ? $model->member->firstname : null;



Thank you for your reply I see what you are saying and I have added the code however I am getting an error "[font="Verdana"][size="2"]Undefined variable: model"[/size][/font]

[font="Verdana"][size="2"]Somewhere it is messing up. Do I need to add attributeLabels for it to go through?[/size]

[/font]

[font="Verdana"][size="2"]EDIT: I added the code in my _view file

[/size][/font]

[font="Verdana"][size="2"]


<div class="view">[/size]

[size="2"]

[/size]

[size="2"]	<b><?php echo CHtml::encode($data->getAttributeLabel('id')); ?>:</b>[/size]

[size="2"]	<?php echo CHtml::link(CHtml::encode($data->id), array('view', 'id'=>$data->id)); ?>[/size]

[size="2"]	<br />[/size]

[size="2"]

[/size]

[size="2"]	<b><?php echo CHtml::encode($data->getAttributeLabel('title')); ?>:</b>[/size]

[size="2"]	<?php echo CHtml::encode($data->title); ?>[/size]

[size="2"]	<br />[/size]

[size="2"]

[/size]

[size="2"]	//Haesnel's help here[/size]

[size="2"]

[/size]


[size="2"]<?php echo 'Title: '.$model->title;[/size]

[size="2"]echo 'Members:';[/size]

[size="2"]foreach($model->members as $member)[/size]

[size="2"]    echo $member->firstname[/size]

[size="2"]

[/size]

[size="2"]

[/size]

[size="2"]</div>[/size]

[size="2"]

[/size][size="2"]

</div>

[/size]

[/font]

[font=“Verdana”][size=“2”]I don’t know why the code snippet is messing up in post here is raw:[/size][/font]

[font="Verdana"]

[size="2"]<div class="view">[/size]

[size="2"]

[/size]

[size=“2”] <b><?php echo CHtml::encode($data->getAttributeLabel(‘id’)); ?>:</b>[/size]

[size=“2”] <?php echo CHtml::link(CHtml::encode($data->id), array(‘view’, ‘id’=>$data->id)); ?>[/size]

[size="2"] <br />[/size]

[size="2"]

[/size]

[size=“2”] <b><?php echo CHtml::encode($data->getAttributeLabel(‘title’)); ?>:</b>[/size]

[size="2"] <?php echo CHtml::encode($data->title); ?>[/size]

[size="2"] <br />[/size]

[size=“2”]<?php echo 'Title: '.$model->title;[/size]

[size=“2”]echo ‘Members:’;[/size]

[size="2"]foreach($model->members as $member)[/size]

[size="2"] echo $member->firstname[/size]

[size="2"]

[/size]

[size="2"]

[/size]

[size="2"]</div>[/size][/font]

I am creating the "Group" with (title) and then adding multiple "firstname, lastname, membersince" to that group.

I load "Group view" and want to display each group (easy) but also "firstname, lastname, membersince" (not easy… for me)

Ok, I assume you are using CListView or CDetailView so in this case $data should contain your model. Just rename $model with $data in my example and try again

Yes you are correct! (CDetailView) I am not getting any errors now, but “firstname” is not showing up anywhere. I’ll keep looking. Thanks again!

Then $data->members doesn’t return any members I think. Are you sure this group has some members assigned? :)

Well I’m getting Property “Group.members” is not defined Now.

Yes the database tables have entries. I appreciate your help.

If the relations() function in your first post is the one you use in your GROUP model than you told Yii that a a group belongs to a single user (only ONE user). Is this correct? If so than you can show the attributes of this single member in your group view using




echo 'Group title: '.$data->title;

echo 'The member this group belongs to: '. !is_null($data->member ? $data->member->firstname : null;



If a group should have many members (and this is true in 99% of all cases) than you have a wrong schema design. I know that some people think that

‘member’=>array(self::BELONGS_TO ,‘Member’,‘member_id’)

translates to "A member belongs to a group" but this is wrong. It says "This group belongs to a member".

My group controller:


 <?php


class GroupController 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';


	/**

	 * @return array action filters

	 */

	public function filters()

	{

		return array(

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

		);

	}


	/**

	 * 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','view2'),

				'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()

{

    Yii::import('ext.multimodelform.MultiModelForm');

 

    $model = new Group;

 

    $member = new Member;

    $validatedMembers = array();  //ensure an empty array

 

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

    {

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

 

        if( //validate detail before saving the master

            MultiModelForm::validate($member,$validatedMembers,$deleteItems) &&

            $model->save()

           )

           {

             //the value for the foreign key 'groupid'

             $masterValues = array ('groupid'=>$model->id);

             if (MultiModelForm::save($member,$validatedMembers,$deleteMembers,$masterValues))

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

            }

    }

 

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

        'model'=>$model,

        //submit the member and validatedItems to the widget in the edit form

        'member'=>$member,

        'validatedMembers' => $validatedMembers,

    ));

}


	/**

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

{

    Yii::import('ext.multimodelform.MultiModelForm');

 

    $model=$this->loadModel($id); //the Group model

 

    $member = new Member;

    $validatedMembers = array(); //ensure an empty array

 

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

    {

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

 

        //the value for the foreign key 'groupid'

        $masterValues = array ('groupid'=>$model->id);

 

        if( //Save the master model after saving valid members

            MultiModelForm::save($member,$validatedMembers,$deleteMembers,$masterValues) &&

            $model->save()

           )

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

    }

 

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

        'model'=>$model,

        //submit the member and validatedItems to the widget in the edit form

        'member'=>$member,

        'validatedMembers' => $validatedMembers,

    ));

}


	/**

	 * Deletes a particular model.

	 * If deletion is successful, the browser will be redirected to the 'admin' 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('Group');

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

			'dataProvider'=>$dataProvider,

		));

	}


	/**

	 * Manages all models.

	 */

	public function actionAdmin()

	{

		$model=new Group('search');

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

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

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


		$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=Group::model()->findByPk($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']==='group-form')

		{

			echo CActiveForm::validate($model);

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

		}

	}

}



Group model:


<?php


/**

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

 *

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

 * @property integer $id

 * @property string $title

 */

class Group extends CActiveRecord

{

	/**

	 * Returns the static model of the specified AR class.

	 * @return Group 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 'group';

	}


	/**

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

			array('title', 'length', 'max'=>30),

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

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

			array('id, title', '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(

		   			'member' => array(self::HAS_MANY, 'member', 'member_id',),

		);

	}


	/**

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

	 */

	public function attributeLabels()

	{

		return array(

			'id' => 'ID',

			'title' => 'Title',

			'firstname' => 'First Name',

		);

	}


	/**

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


		return new CActiveDataProvider($this, array(

			'criteria'=>$criteria,

		));

	}

}

And my view:


 <?php

$this->breadcrumbs=array(

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

	$model->title,

);


$this->menu=array(

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

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

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

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

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

);

?>


<h1>View Group #<?php echo $model->id; ?></h1>


<?php $this->widget('zii.widgets.CDetailView', array(

	'data'=>$model,

	'attributes'=>array(

		'id',

		'title',

		'firstname',

		

	),

)); ?>



Just so I can get that outta’ the way.

Yes there are multiple users. This is my scheme:


CREATE TABLE IF NOT EXISTS `group` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `title` varchar(30) NOT NULL,

  PRIMARY KEY (`id`),

  KEY `id` (`id`)

) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;


--

-- Dumping data for table `group`

--


INSERT INTO `group` (`id`, `title`) VALUES

(1, 'test'),

(2, 'test');


-- --------------------------------------------------------


--

-- Table structure for table `member`

--


CREATE TABLE IF NOT EXISTS `member` (

  `id` int(11) NOT NULL AUTO_INCREMENT,

  `groupid` int(11) NOT NULL,

  `firstname` varchar(25) NOT NULL,

  `lastname` varchar(25) NOT NULL,

  `membersince` date NOT NULL,

  PRIMARY KEY (`id`)

) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;


--

-- Dumping data for table `member`

--


INSERT INTO `member` (`id`, `groupid`, `firstname`, `lastname`, `membersince`) VALUES

(1, 1, 'test', 'test', '0000-00-00'),

(2, 2, 'test', 'test', '0000-00-00');

What should I change here? I thought it was pretty basic myself.

Ok, your database design seems to be right (on first sight). The problem is your relations() function. As I said




//this now is in your group model

'member'=>array(self::BELONGS_TO ,'Member','member_id')



literally translates to "This groupmodel belongs to a single member". But you should do




'members'=>array(self::HAS_MANY ,'Member','group_id')



which says: "A groupmodel has many members associated with it which are models of class Member and can be found by searching for this group id in the groupid field in the member table"

Ok I understand. I am still getting the error "Property "Group.firstname" is not defined." after applying the correct code.

After using the following code though:


<?php $this->widget('zii.widgets.CDetailView', array(

	'data'=>$model,

	'attributes'=>array(

		'id',

		'title',

		'firstname',

		

	),

)); ?>



Is there any other way to display the "firstname, lastname" in my view.php ?

A group doesn’t have a first name but you are telling CDetailView to show it, of course you’ll get an error. And there isn’t only one firstname as you have several members and each member has a firstname so this statements doesn’t make sense at all.

If you want to use CDetailView you’ll need something like this:




<?php $memberNames=array()

foreach($model->members as $member)

    $memberNames[]=$member->firstname;

?>

<?php $this->widget('zii.widgets.CDetailView', array(

        'data'=>$model,

        'attributes'=>array(

                'id',

                'title',

                array(

                    'label'=>'Members'

                    'type'=>'text',

                    'value'=>implode(', ',$memberNames)

                )

        ),

)); ?>



or you make an extra section below your CDetailView showing all the members




foreach($model->members as $member)

    $this->renderPartial('_yourmemberview','model'=>$member)



Trying to figure out where the syntax error is coming from in the code.

first line is missing a semicolon at the end and the last line in the second example is missing one too. I am only using this forum text editor for coding

Ok. So fixed 1 problem and now it says "T_CONSTANT_ENCAPSED_STRING, expecting ‘)’ on line 31.

So I’m taking a break for now. This is upsetting I can’t get something so simple like this. How much is a job like this worth for payment?

EDIT: Fixed the syntax and now getting CDbException

“CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘members.group_id’ in ‘where clause’. The SQL statement executed was: SELECT members.id AS t1_c0, members.groupid AS t1_c1, members.firstname AS t1_c2, members.lastname AS t1_c3, members.membersince AS t1_c4 FROM member members WHERE (members.group_id=:ypl0)”

Arg!

Always look at what the exception is telling you: it is not group_id but groupid ;)

Do you accept bitcoin for donation? You have solved my problem!

No it is alright, I am happy we solved this issue. Just give me a +vote on one of my posts. That’s enough ;)