Yii Framework Forum: Search for MANY_MANY value in CGridView - Yii Framework Forum

Jump to content

  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

Search for MANY_MANY value in CGridView Rate Topic: ***** 7 Votes

#1 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 20 December 2010 - 09:27 PM

Hi All,

I've searched extensively on the topic of working with MANY_MANY relations, and still haven't found an elegant answer to this problem, which should be very simple.

I'm making a MANY_MANY relation to allow a company to be listed in multiple categories for a phone book type directory. Here's the basic table structure:

      +-------+      +----------------+     +--------+
      |company|      |company-category|     |category|
      +-------+      +----------------+     +--------+
      |id     |-----<|company_id      |  +--|id      |
      |name   |      |category_id     |>-+  |name    |
      +-------+      +----------------+     +--------+


In CGridView (admin.php), I'd like to filter the display of companies by a category they are associated with. It seems there should be a way to word the $criteria->compare() statement to do this, but I can't get it to work. Is this not supported with MANY_MANY relations? I've tried the addCondition() method, but it doesn't do anything either.

Here's what I've got so far (which isn't working for the search):

From the model:
	public function relations()
	{
		return array(
			'categories'=>array(self::MANY_MANY, 'Category', 'category-company(company_id, category_id)'),
		);
	}

	public function getRelatedCategoryNames () // IS THERE A MORE EFFICIENT WAY OF DOING THIS?
	{
		foreach ($this->categories as $k) {
			$out[] = "$k->name";
		}
		return implode(',<br />', $out);
	
	}

	public function attributeLabels()
	{
		return array(
			...
			'assignedCategories' => 'Categories',
			...
		);
	}


	public function search()
	{
		$criteria=new CDbCriteria;
		...
                //first attempt
		$criteria->compare('categories.id',$this->assignedCategories,false); // doesn't work
		...
                //second attempt (not done simultaneously with first attempt!)
		if ($this->assignedCategories) {
			$criteria->addColumnCondition(array('category_listing.id' => 
                           $this->assignedCategories));
		}
	}


From the View (admin.php) file:

<?php $this->widget('zii.widgets.grid.CGridView', array(
	'id'=>'eld-listing-grid',
	'dataProvider'=>$model->search(),
	'filter'=>$model,
	'columns'=>array(
		...
		array(
			'name' => 'assignedCategories',
			'filter'=> CHtml::listData(Category::model()->findAll(array('order'=>'name ASC')),'id','name'),
			'value' => '$data->RelatedCategoryNames',
		),
		...


Can anyone help? This is the simplest of the MANY_MANY things I need to do, and I feel dense for not being able to make it work. It would be great to have a cookbook of different scenarios. The official Class Reference doesn't really supply scenarios, and Agile Web Application Development with YII1.1 and PHP5 only cursorily discusses MANY_MANY relations. Does anyone know of a better source?

Thanks so much in advance,

Bill
0

#2 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 21 December 2010 - 05:44 AM

*
POPULAR

As the MANY_MANY relation return an array of objects... getRelatedCategoryName() can be like this

public function getRelatedCategoryNames ()
{
   $out=CHtml::listData($this->categories,'id','name');
   return implode(',<br />', $out);
}


You haven't posted all your model... so make sure that you have declared $assignedCategories as a public attribute and that it's in the on=>'search' rule

search function:
	public function search()
	{
		$criteria=new CDbCriteria;

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

		$criteria->compare('category_id',$this->assignedCategories);
		$criteria->with=array('categories');
		$criteria->together=true;

		return new CActiveDataProvider(get_class($this), array(
			'criteria'=>$criteria,
		));
	}


in the view the column is like this:
...
array(
   'name'=>'assignedCategories',
   'filter'=>CHtml::listData(Category::model()->findAll(array('order'=>'name ASC')),'id','name'),
   'type'=>'html',
   'value'=>'$data->relatedCategoryNames',
),
...

Find more about me.... btw. Do you know your WAN IP?
9

#3 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 21 December 2010 - 02:51 PM

Thank you so much, mdomba! That totally worked. I had declared assignedCategories as a public attribute, but I hadn't put it in the on=>'search' rule. And I like the way you made the assignment more efficient with listData.

You wouldn't happen to have a more elegant solution to working with this scenario would you? I'd like to retrieve the is_highlighted value from the connecting table of the MANY_MANY relation. And I'd also like to be able to set it when it's created. What's the best way to access this from the Model and save it?

      +-------+      +----------------+     +--------+
      |company|      |company-category|     |category|
      +-------+      +----------------+     +--------+
      |id     |-----<|company_id      |  +--|id      |
      |name   |      |category_id     |>-+  |name    |
      |etc.   |      |is_highlighted  |     |etc.    |
      |       |      |description     |     |        |
      +-------+      +----------------+     +--------+


I've seen this talked about in other posts, where they recommend building a separate HAS_MANY relationship between company and company-category. But this has major draw backs.
0

#4 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 21 December 2010 - 03:56 PM

I don't know in what situation you need the is_highlighted to be displayed... in CGridView or somewhere else?...
And how do you add a category to a company?... becasue there the is_highlighted should be set...

Anyway you can have both relations... one MANY_MANY and one HAS_MANY and in any situation you can choose whitch one to use.
Find more about me.... btw. Do you know your WAN IP?
2

#5 User is offline   fouss 

  • Advanced Member
  • PipPipPip
  • Yii
  • Group: Members
  • Posts: 385
  • Joined: 05-October 10
  • Location:Bamako Mali

Posted 22 December 2010 - 08:16 AM

You'll also need http://www.yiiframew...1/en/form.table
Posted Image
1

#6 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 22 December 2010 - 07:35 PM

View Postmdomba, on 21 December 2010 - 03:56 PM, said:

I don't know in what situation you need the is_highlighted to be displayed... in CGridView or somewhere else?...
And how do you add a category to a company?... becasue there the is_highlighted should be set...

Anyway you can have both relations... one MANY_MANY and one HAS_MANY and in any situation you can choose whitch one to use.


I'll have to get back to you on this.

Now I have the issue that in the results on the admin page, it's only showing the subset of the relation I selected in the MANY_MANY field. How do I get it to show all the possible MANY_MANY values?

In other words, the admin screen starts by showing a list of food companies. In the assignedCategories field, it shows the categories they are connected to (for example, 'produce','honey','dairy'). When I filter by one of these entries (let's say I filter for 'produce') using the technique you helped me with above, it shows all the companies that have been MANY_MANY assigned to 'produce' — but the assignedCategories field now shows *only* 'produce' — 'honey' and 'dairy' have been dropped.

I assume this is because my 'categories' relation has been used to filter this result, so that when I call getRelatedCategoryNames() the list has been reduced to the one I'm searching for. How should I go about having it list all the associated categories in the filtered result? Set up another relation? Or call the list from the 'companies' MANY_MANY relation in the Category model (like, Category::model()->companies)?

Thanks for taking the time to work with a Yii OOP newbie.
0

#7 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 23 December 2010 - 02:58 AM

*
POPULAR

View PostOcean Wind, on 22 December 2010 - 07:35 PM, said:

...
I assume this is because my 'categories' relation has been used to filter this result, so that when I call getRelatedCategoryNames() the list has been reduced to the one I'm searching for.
...


That's right... it's becasue in the model->search() we are limiting the output with the condition "companies.category_id == selected category" and that's why it gives only that records out...

If you want to show all categories...

1. Make a model for the categorycompany table...

2. make a new relation HAS_MANY in the company model so that you have both relation
'categories'=>array(self::MANY_MANY, 'Category', 'companycategory(company_id, category_id)'),
'companycategory'=>array(self::HAS_MANY,'Companycategory','company_id'),


3. In the model->serach() method change the line
$criteria->with=array('categories');

to
$criteria->with=array('companycategory');


so that the search is performed by the HAS_MANY relation

This way the getRelatedCategoryNames() that uses the MANY_MANY relation will not be filtered and will always give you all the categories...
Find more about me.... btw. Do you know your WAN IP?
5

#8 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 23 December 2010 - 04:33 PM

View Postmdomba, on 23 December 2010 - 02:58 AM, said:

That's right... it's becasue in the model->search() we are limiting the output with the condition "companies.category_id == selected category" and that's why it gives only that records out...

If you want to show all categories...

1. Make a model for the categorycompany table...

2. make a new relation HAS_MANY in the company model so that you have both relation
'categories'=>array(self::MANY_MANY, 'Category', 'companycategory(company_id, category_id)'),
'companycategory'=>array(self::HAS_MANY,'Companycategory','company_id'),


3. In the model->serach() method change the line
$criteria->with=array('categories');

to
$criteria->with=array('companycategory');


so that the search is performed by the HAS_MANY relation

This way the getRelatedCategoryNames() that uses the MANY_MANY relation will not be filtered and will always give you all the categories...


Simple and elegant. I like it!

Thank you. You've been a lot of help.
0

#9 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 24 December 2010 - 04:39 PM

Could you help me with the other side of the equation — collecting the data? I'm trying to create a form that manages data for the main model/table and saves data in the joining model/table for the MANY_MANY relation. Once again here's the basic MANY_MANY relationship:

      +-------+      +----------------+     +--------+
      |company|      |company-category|     |category|
      +-------+      +----------------+     +--------+
      |id     |-----<|company_id      |  +--|id      |
      |name   |      |category_id     |>-+  |name    |
      +-------+      +----------------+     +--------+


I want to have a checkboxlist in the form (at this point using the activeform widget), where the user can select (or deselect) the associated categories for the company.

Using Qiang's article as a guide (http://tinyurl.com/2aqerpf) I'm instantiating 2 models in the relevant controller actions: 1 for company and 1 for company-category. Looking at the Create action:

	public function actionCreate()
	{
		$a=new Company;
		$b=new CompanyCategory;

		if(isset($_POST['Company'],$_POST['CompanyCategory']))
		{
			$a->attributes=$_POST['Company'];
			$a->modified_by = Yii::app()->user->name;
			$valid=$a->validate();
			if ($valid) {
				if($a->save(false)) {  
					foreach ($_POST['CompanyCategory'] as $category_id) {
						$b->attributes = array('company_id' => $a->id,'category_id'=>$category_id,'modified_by'=>Yii::app()->user->name);
				       	 	$b->save(false);
					}
			        $this->redirect(array('view','id'=>$a->id));
			    } 
			}
		}

		$this->render('create',array(
			'a'=>$a,
			'b'=>$b,
		));
	}



As before, the model has:

	public function relations()
	{
		return array(
			'companyCategory' => array(self::HAS_MANY, 'CompanyCategory', 'company_id'),
			'categories'=>array(self::MANY_MANY, 'Category', 'company-category(company_id, category_id)'),
		);
	}
...
	public function attributeLabels()
	{
		return array(
			...
			'assignedCategories' => 'Categories',
			...
		);
	}



create.php invokes $_form.php like this:

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


* note that this should read ...,'b'=>$b (lowercase). For some reason the text editing widget keeps capping $B.

and $_form.php contains this to render the checkbox:

	<div class="row">
		<?php echo $form->label($a,'assignedCategories'); ?>
		<?php echo $form->checkBoxList($b,'category_id',CHtml::listData(Category::model()->findAll(array('order'=>'name ASC')), 'id', 'name')) ?>
		<?php echo $form->error($b,'category_id'); ?>

	</div>


When I run this, it throws a CDbException:
CDbCommand failed to execute the SQL statement: SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails. (`my_db/company-category`, CONSTRAINT `fk_company-category_category1` FOREIGN KEY (`category_id`) REFERENCES `category` (`id`) ON DELETE NO )


These are Mysql tables formatted as InnoDB with the relations defined using MySQL Workbench.

So, am I going about this the right way? Or is there a better way? I have a feeling there's a more Yii way to save the company-category records that would get rid of the CDbException.

Thanks in advance...
0

#10 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 25 December 2010 - 07:04 AM

I'm just checking the forum (have not much time)...

On the first read... as it says that it's something with the category_id... check the value of $category_id after the

foreach ($_POST['CompanyCategory'] as $category_id)


I thing that $category_id is here the CompanyCategory model... and you cannot do 'category_id'=>$category_id but 'category_id'=>$category_id->category_id
Find more about me.... btw. Do you know your WAN IP?
1

#11 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 25 December 2010 - 06:06 PM

View Postmdomba, on 25 December 2010 - 07:04 AM, said:

I'm just checking the forum (have not much time)...

On the first read... as it says that it's something with the category_id... check the value of $category_id after the

foreach ($_POST['CompanyCategory'] as $category_id)


I thing that $category_id is here the CompanyCategory model... and you cannot do 'category_id'=>$category_id but 'category_id'=>$category_id->category_id


Yes, that was one of the problems. I got the Create action working (yay!).

I'm having a problem getting the Update action to display, since it's sending an array of models to the form. Here's what I have in the controller's Update action:

	public function actionUpdate($id)
	{
		//THESE ARE ACTUALLY IN SEPARATE FUNCTIONS, BUT TO MAKE THIS SHORT HERE ARE THE CALLS 
		$a=Company::model()->findByPk((int)$id);
		$b=CategoryCompany::model()->findAllByAttributes(array('company_id' => $id));

		if(isset($_POST['Company'],$_POST['CategoryCompany']))
		{
			... PROCESSING HAPPENS HERE - NOT WORRIED ABOUT THIS NOW ...
		}
		
		//HERE'S WHERE I'M SENDING THE MODELS TO update.php TO DISPLAY THE FORM BEFORE PROCESSING
		$this->render('update',array(
			'a'=>$a,
			'b'=>$b, //THIS IS FAILING BECAUSE IT'S PASSING AN ARRAY OF OBJECTS
		));
	}



How should I be passing the MANY_MANY related models to the form?

Thanks again, mdomba. I'm hoping this thread will be useful to others as well.

Have a great Christmas season!
0

#12 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 26 December 2010 - 08:48 AM

Thank you... I too hope that others will benefit from this thread...

Why do you think 'b'=>$b is failing ... it should work... all depends how you use it in the view... can you post your view code here?
Find more about me.... btw. Do you know your WAN IP?
0

#13 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 27 December 2010 - 01:55 PM

View Postmdomba, on 26 December 2010 - 08:48 AM, said:

Than you... I too hope that others will benefit from this thread...

Why do you think 'b'=>$b is failing ... it should work... all depends how you use it in the view... can you post your view code here?


Here's the relevant portion of update.php:
<h1>Update <strong><?php echo $model->name; ?></strong></h1>

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


And here's _form.php:

<?php $form=$this->beginWidget('CActiveForm', array(
	'id'=>'eld-listing-form',
	'enableAjaxValidation'=>false,
)); ?>

	<p class="note">Fields with <span class="required">*</span> are required.</p>

	<?php echo $form->errorSummary($a); ?>

	... fields from $a ...

	<div class="row">

		//THE LABEL IS DEFINED IN THE $a MODEL
		<?php echo $form->label($a,'assignedCategories'); ?>

		//HERE, THE MODEL IS $b AND THE DEFINING FIELD IS 'category_id'. I WANT TO DISPLAY
		//ALL CATEGORIES, SO I'M GRABBING THEM STRAIGHT FROM THE Category MODEL. HOWEVER, I DON'T
		//UNDERSTAND HOW Yii WILL KNOW TO CHECK THE ONES THAT HAVE BEEN RELATED
		<?php echo $form->checkBoxList($b,'category_id',CHtml::listData(Category::model()->findAll(array('order'=>'name ASC')), 'id', 'name')) ?>

		//I ASSUME ERRORS SHOULD BE ASSOCIATED WITH THE MODEL USED IN THE FIELD DISPLAY
		<?php echo $form->error($b,'category_id'); ?>

	</div>


When I run this, I get this error:

PHP Error
Description

get_class() expects parameter 1 to be object, array given
Source File

/WWW/t/transitioncolorado.org/framework/web/helpers/CHtml.php(1978)

01966:                 $sub=substr($attribute,0,$pos+1);
01967:                 $attribute=substr($attribute,$pos+1);
01968:                 return get_class($model).$sub.'['.$attribute.']';
01969:             }
01970:             if(preg_match('/\](\w+\[.*)$/',$attribute,$matches))
01971:             {
01972:                 $name=get_class($model).'['.str_replace(']','][',trim(strtr($attribute,array(']['=>']','['=>']')),']')).']';
01973:                 $attribute=$matches[1];
01974:                 return $name;
01975:             }
01976:         }
01977:         else
01978: return get_class($model).'['.$attribute.']';
01979:     }
01980: 
01981:     /**
01982:      * Evaluates the attribute value of the model.
01983:      * This method can recognize the attribute name written in array format.
01984:      * For example, if the attribute name is 'name[a][b]', the value "$model->name['a']['b']" will be returned.
01985:      * @param CModel $model the data model
01986:      * @param string $attribute the attribute name
01987:      * @return mixed the attribute value
01988:      * @since 1.1.3
01989:      */
01990:     public static function resolveValue($model,$attribute)

Stack Trace

#0 /WWW/t/transitioncolorado.org/framework/web/helpers/CHtml.php(1978): get_class()
#1 /WWW/t/transitioncolorado.org/framework/web/helpers/CHtml.php(1130): resolveName()
#2 /WWW/t/transitioncolorado.org/framework/web/widgets/CActiveForm.php(449): activeLabel()
#3 /WWW/t/transitioncolorado.org/sm_protected/views/eatlocaldirectory/company/_form.php(20): CActiveForm->label()
#4 /WWW/t/transitioncolorado.org/framework/web/CBaseController.php(119): require()
#5 /WWW/t/transitioncolorado.org/framework/web/CBaseController.php(88): CompanyController->renderInternal()
#6 /WWW/t/transitioncolorado.org/framework/web/CController.php(833): CompanyController->renderFile()
#7 /WWW/t/transitioncolorado.org/sm_protected/views/eatlocaldirectory/company/update.php(18): CompanyController->renderPartial()
#8 /WWW/t/transitioncolorado.org/framework/web/CBaseController.php(119): require()
#9 /WWW/t/transitioncolorado.org/framework/web/CBaseController.php(88): CompanyController->renderInternal()
#10 /WWW/t/transitioncolorado.org/framework/web/CController.php(833): CompanyController->renderFile()
#11 /WWW/t/transitioncolorado.org/framework/web/CController.php(746): CompanyController->renderPartial()
#12 /WWW/t/transitioncolorado.org/sm_protected/controllers/eatlocaldirectory/CompanyController.php(124): CompanyController->render()
#13 unknown(0): CompanyController->actionUpdate()
#14 /WWW/t/transitioncolorado.org/framework/web/actions/CInlineAction.php(54): ReflectionMethod->invokeArgs()
#15 /WWW/t/transitioncolorado.org/framework/web/CController.php(300): CInlineAction->run()
#16 /WWW/t/transitioncolorado.org/framework/web/filters/CFilterChain.php(133): CompanyController->runAction()
#17 /WWW/t/transitioncolorado.org/framework/web/filters/CFilter.php(41): CFilterChain->run()
#18 /WWW/t/transitioncolorado.org/framework/web/CController.php(1084): CAccessControlFilter->filter()
#19 /WWW/t/transitioncolorado.org/framework/web/filters/CInlineFilter.php(59): CompanyController->filterAccessControl()
#20 /WWW/t/transitioncolorado.org/framework/web/filters/CFilterChain.php(130): CInlineFilter->filter()
#21 /WWW/t/transitioncolorado.org/framework/web/CController.php(283): CFilterChain->run()
#22 /WWW/t/transitioncolorado.org/framework/web/CController.php(257): CompanyController->runActionWithFilters()
#23 /WWW/t/transitioncolorado.org/framework/web/CWebApplication.php(324): CompanyController->run()
#24 /WWW/t/transitioncolorado.org/framework/web/CWebApplication.php(121): CWebApplication->runController()
#25 /WWW/t/transitioncolorado.org/framework/base/CApplication.php(135): CWebApplication->processRequest()
#26 /WWW/t/transitioncolorado.org/html/site_manager/index.php(14): CWebApplication->run()


Since the tables are related, I'm wondering whether I shouldn't be trying to accomplish this using an eager-loading with() command in the Update controller instead of two separate models, such as:
$a=Company::model()->with('categories')->findByPk((int)$id);

'categories' is the name of the MANY_MANY relation. I'm not sure how I would work with this in the update form, though.
0

#14 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 27 December 2010 - 09:02 PM

I think I've found the solution. I caved and installed the CSaveRelationsBehavior component. Made life a lot easier.

Also, I realized I was trying to get array step-through automation where Yii had none. Unfortunately, this means I had to give up on the CActiveForm functions to do my work for me. That's okay, at least it works.

Here's what I ended up with.

The controller:
	public function actionCreate()
	{
		$model=new Company;

		if(isset($_POST['Company']))
		{
			$model->attributes=$_POST['Company'];
			$model->modified_by = Yii::app()->user->name;
			$valid=$model->validate();
			if ($valid) {
				$model->setRelationRecords('categories',$_POST['category_id']); //IS THERE A WAY TO VALIDATE THIS?
				$model->save();
		        $this->redirect(array('view','id'=>$model->id));
			}
		}

		$this->render('create',array(
			'model'=>$model,
		));
	}

	public function actionUpdate($id)
	{
		$model=$this->loadModel($id);

		if(isset($_POST['Company']))
		{
			$model->attributes=$_POST['Company'];
			$model->modified_by = Yii::app()->user->name;
			$model->setRelationRecords('categories',$_POST['category_id']);
			$valid=$model->validate(); // I DON'T THINK THIS IS VALIDATING THE RELATED ENTRIES
			if ($valid) {
				$model->save(false);
				$this->redirect(array('view','id'=>$model->id));
			}
		}

		$this->render('update',array(
			'model'=>$model,
		));
	}
		

	public function actionDelete($id)
	{
		if(Yii::app()->request->isPostRequest)
		{
			// THIS DELETES ALL SUBTABLES BECAUSE OF CASCADING FOREIGN KEY CONSTRAINTS IN MYSQL(INNODB)
			$model = $this->loadModel($id); 
			$model->delete();
		}
		else
			throw new CHttpException(400,'Invalid request. Please do not repeat this request again.');
	}




And here are key pieces of the model:

	public function relations()
	{
		return array(
			'categoryCompany' => array(self::HAS_MANY, 'CategoryCompany', 'company_id'),
			'categories'=>array(self::MANY_MANY, 'Category', 'category-company(company_id, category_id)'),
		);
	}

	public function getRelatedCategories ()
	{
		$out = CHtml::listData($this->categories,'id','name');
		return $out;
	}
	
	public function isRelated ($category_id)
	{
		if (isset($this->id)) {
			//we only want this to evaluate when doing an update
			$categories = array_keys($this->getRelatedCategories());
			$out = in_array($category_id,$categories) ? true : false;
		} else {
			//otherwise we must be doing a create. But you could define defaults here, too...
			$out = false;
		}
		return $out;
	}
	
	public function getAllCategories ()
	{
		$out = CHtml::listData(EldCategory::model()->findAll(array('order'=>'name ASC')), 'id', 'name');
		return $out;
	}


And here's the _form row with the related checkboxes:
	<div class="row">
		<?php foreach ($model->allCategories as $category_id => $category_name) { ?>
		<label><input type="checkbox" name="category_id[]" value="<?php echo $category_id ?>" <?php echo $model->isRelated($category_id) ? 'checked="checked"' : '' ?> /> <?php echo $category_name ?></label><br />
		<?php } ?>
	</div>


Although this works, I'm a little bummed that I can't use the CActiveForm elements to create the form. Is there a better way to do this?
0

#15 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 28 December 2010 - 07:42 AM

Solution with CActiveForm...

You don't need to pass two models... instead of the second model you can use the $assignedCategories that we already have...

actionCreate
 public function actionCreate()
 {
   $model=new Company;

   iif(isset($_POST['Company']))
   {
  	$model->attributes=$_POST['Company'];
  	if($model->save())
     	foreach ($model->assignedCategories as $category_id) {
	    	$cc=new Companycategory;
	    	$cc->company_id = $model->id;
	    	$cc->category_id = $category_id;
	    	$cc->save(false);
     	}
  	$this->redirect(array('admin'));
   }
   $this->render('create',array(
  	'model'=>$model,
   ));
}


In the _form you use this like
  <div class="row">
     	<?php echo $form->label($model,'assignedCategories'); ?>
     	<?php echo $form->checkBoxList($model,'assignedCategories',CHtml::listData(Category::model()->findAll(array('order'=>'name ASC')),'id','name')) ?>
     	<?php echo $form->error($model,'assignedCategories'); ?>
  </div>


For the update there is a "small" problem... a user can uncheck/check any category... so that for that table we would need to check if a previous record existed (was checked) and now it's unchecked that record should be deleted... and if previous record does not exist (was unchecked) and now is checked.. .we need to create that record...

So to make it easyer I just delete all records (categoryes) for that company and insert only the checked...
public function actionUpdate($id)
{
   $model=$this->loadModel($id);
   //.. use the relation companycategory to get current categoryes
   $model->assignedCategories = CHtml::listData($model->companycategory,'category_id','category_id');

   if(isset($_POST['Company']))
   {
   	$model->attributes=$_POST['Company'];
   	if($model->save())
       	//.. delete all company categoryes
   		Companycategory::model()->deleteAll('company_id=:company_id',array(':company_id'=>$model->id));
       	//.. add checked categoryes to the company
   		foreach ($model->assignedCategories as $category_id) {
   			$cc=new Companycategory;
   			$cc->company_id = $model->id;
   			$cc->category_id = $category_id;
   			$cc->save(false);
   		}
   		$this->redirect(array('admin'));
   }
	$this->render('update',array(
   	'model'=>$model,
	));
	}

Find more about me.... btw. Do you know your WAN IP?
2

#16 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 28 December 2010 - 03:25 PM

View Postmdomba, on 28 December 2010 - 07:42 AM, said:

Solution with CActiveForm...

You don't need to pass two models... instead of the second model you can use the $assignedCategories that we already have...

actionCreate
 public function actionCreate()
 {
   $model=new Company;

   iif(isset($_POST['Company']))
   {
  	$model->attributes=$_POST['Company'];
  	if($model->save())
     	foreach ($model->assignedCategories as $category_id) {
	    	$cc=new Companycategory;
	    	$cc->company_id = $model->id;
	    	$cc->category_id = $category_id;
	    	$cc->save(false);
     	}
  	$this->redirect(array('admin'));
   }
   $this->render('create',array(
  	'model'=>$model,
   ));
}



Beautiful. That's the way I thought Yii should work.

For some reason I'm not able to get assignedCategories to stick in the model; therefore, $model->assignedCategories is empty. I've declared public $assignedCategories; as a property in the model and have it listed as safe in the rules:

array('address, default_description, default_distribution_location, assignedCategories', 'safe'),


I've also tried directly assigning it:

$model->attributes=$_POST['EldListing'];
$model->assignedCategories = $_POST['EldListing']['assignedCategories'];


But a print_r of $model->attributes reveals:
Array
(
    [primary_category_id] => 3
    [name] => Bill's Dairy Farm
    [address] => 
    [city] => 
    [state] => 
    [zip] => 
    [phone] => 
    [contact_person] => 
    [email] => 
    [skype] => 
    [twitter] => 
    [url] => 
    [default_description] => 
    [default_distribution_location] => 
    [modified_by] => OceanWind
    [id] => 
    [id_organization] => 
    [modified_on] => 
)


While a print_r of $_POST reveals...
Array
(
    [Company] => Array
        (
            [primary_category_id] => 3
            [assignedCategories] => Array
                (
                    [0] => 3
                    [1] => 8
                    [2] => 10
                )

            [name] => Bill's Dairy Farm
            [address] => 
            [city] => 
            [state] => 
            [zip] => 
            [phone] => 
            [contact_person] => 
            [email] => 
            [skype] => 
            [twitter] => 
            [url] => 
            [default_description] => 
            [default_distribution_location] => 
        )

    [yt0] => Create
)


Is there something else I need to do to get the model to recognize the $assignedCategories property?
0

#17 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 28 December 2010 - 03:39 PM

View PostOcean Wind, on 28 December 2010 - 03:25 PM, said:

For some reason I'm not able to get assignedCategories to stick in the model

...

Is there something else I need to do to get the model to recognize the $assignedCategories property?


What's strange is that in actionUpdate, I can get the variables to bind just fine:

	public function actionUpdate($id)
	{
		$model=$this->loadModel($id);
		$model->assignedCategories = CHtml::listData($model->companycategory,'category_id','category_id');
		print_r($model->assignedCategories); exit;


reveals...

Array
(
    [3] => 3
    [8] => 8
    [10] => 10
)


I don't have any assignments going on otherwise in the model (such as public function getAssignedCategories())
0

#18 User is offline   Maurizio Domba Cerin 

  • Yii - Yesss It Is !!!
  • Yii
  • Group: Yii Dev Team
  • Posts: 4,353
  • Joined: 12-October 09
  • Location:Croatia

Posted 28 December 2010 - 04:39 PM

Nothing else is needed... only to specify it as a safe attribute... for the massive assignment to work...

Anyway the line
$model->assignedCategories = $_POST['EldListing']['assignedCategories'];

should work even without the rules...

Find more about me.... btw. Do you know your WAN IP?
0

#19 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 28 December 2010 - 05:20 PM

View PostOcean Wind, on 28 December 2010 - 03:39 PM, said:

What's strange is that in actionUpdate, I can get the variables to bind just fine:

...


Actually, it works only when I pull in the variables from the model. When I pull them in from the $_POST variables, assignedCategories doesn't come through:

	public function actionUpdate($id)
	{
		$model=$this->loadModel($id);
		$model->assignedCategories = CHtml::listData($model->companycategory,'category_id','category_id'); //THIS WORKS

		if(isset($_POST['Company']))
		{
			$model->attributes = $_POST['Company']; // NO assignedCategories HERE

0

#20 User is offline   Ocean Wind 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 63
  • Joined: 14-September 10
  • Location:Denver, Colorado, USA

Posted 28 December 2010 - 05:46 PM

View Postmdomba, on 28 December 2010 - 04:39 PM, said:

Nothing else is needed... only to specify it as a safe attribute... for the massive assignment to work...

Anyway the line
$model->assignedCategories = $_POST['Company']['assignedCategories'];

should work even without the rules...


I can't imagine why it's not working. It's only when I try to assign it from $_POST, and I've verified that assignedCategories exists in the $_POST array.

Is it a bug?

For the time being, I can do everything I want using $_POST['Company']['assignedCategories'] directly.
0

Share this topic:


  • (2 Pages)
  • +
  • 1
  • 2
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users