[TYPO3-mvc] Multiple Checkboxes for an object in a form

Christian Zenker christian.zenker at 599media.de
Tue Nov 9 15:38:28 CET 2010


> I'm going to test it and then post my solution here if it works.

Ok, it seems to work now:

The Tx_CzDmRegister_Domain_Model_Address is the heart of the model. It  
holds contact information of subscribers based on tt_address. It also has  
an M-M-relation to the table sys_dmail_category (reperesented by  
Tx_CzDmRegister_Domain_Model_MailCategory). This is a vital snippet in the  
Tx_CzDmRegister_Domain_Model_Address


/**
  * the property moduleSysDmailCategory
  *
  * @var  
Tx_Extbase_Persistence_ObjectStorage<Tx_CzDmRegister_Domain_Model_MailCategory>  
moduleSysDmailCategory
  */
protected $moduleSysDmailCategory;

/**
  * getter for moduleSysDmailCategory
  *
  * @return  
Tx_Extbase_Persistence_ObjectStorage<Tx_CzDmRegister_Domain_Model_MailCategory>
  */
public function getMailCategories() {
	return $this->moduleSysDmailCategory;
}

/**
  * setter for moduleSysDmailCategory
  *
  * @param  
Tx_Extbase_Persistence_ObjectStorage<Tx_CzDmRegister_Domain_Model_MailCategory>  
$moduleSysDmailCategory
  * @return Tx_CzDmRegister_Domain_Model_Address
  */
public function setMailCategories($moduleSysDmailCategory) {
	$this->moduleSysDmailCategory = $moduleSysDmailCategory;
	return $this;
}

public function getMailCategoriesAsArray() {
	$return = array();
	if(!is_null($this->moduleSysDmailCategory)) {
		foreach($this->moduleSysDmailCategory as $cat) {
			$return[] = $cat->getUid();
		}
	}
	return $return;
}

/**
  * @param array $categories
  */
public function setMailCategoriesAsArray($categories) {
	/**
	 * the ids of the currently selected categories
	 * @var array<integer>
	 */
	$current = array();
	if(empty($this->moduleSysDmailCategory)) {
		$this->moduleSysDmailCategory =  
t3lib_div::makeInstance('Tx_Extbase_Persistence_ObjectStorage');
	}
	foreach($this->moduleSysDmailCategory as $cat) {
		$current[] = $cat->getUid();
	}
	// clean the input data
	foreach($categories as &$cat) {
		$cat = intval($cat);
	}
	
	/**
	 * uids of records that need to be linked
	 * @var array<integer>
	 */
	$link = array_diff($categories, $current);
	/**
	 * uids of records that need to be unlinked
	 * @var array<integer>
	 */
	$unlink = array_diff($current, $categories);
	
	if($link) {
		// if there are records that need to be linked
		foreach(t3lib_div::makeInstance('Tx_CzDmRegister_Domain_Repository_MailCategoryRepository')->findByUids($link)  
as $cat) {
			$this->moduleSysDmailCategory->attach($cat);
		}
	}
	
	if($unlink) {
		// if there are records that need to be unlinked
		
		/* we will first select all records to remove and then unlink them in a  
batch
		 *
		 * this is done because unsetting objects while using the iterator  
interface will
		 * usually skip some records
		 */
		$unlinkRecords = array();
		foreach($this->moduleSysDmailCategory as $cat) {
			if(in_array($cat->getUid(), $unlink)) {
				$unlinkRecords[] = $cat;
			}
		}
		foreach($unlinkRecords as $cat) {
			$this->moduleSysDmailCategory->offsetUnset($cat);
		}
	}
}

/**
  * just a dummy for the property mapper
  * it seems the property mapper does not recognize a property even if a  
setter exists
  *
  * @deprecated This is only needed for the property mapper and should  
never be actually used
  * @var array
  */
protected $mailCategoriesAsArray;

Note the ...AsArray() methods. They are used to work together with the  
Form_CheckboxViewHelper. Franz, I guess this is basically what you have  
done in your VH.

The template gets the Tx_CzDmRegister_Domain_Model_Address as 'address'  
and a list of all available categories  
(Tx_CzDmRegister_Domain_Model_MailCategory) as 'categories'.

The template looks like that:

<f:form name="address" method="post" controller="Address" action="create"  
object="{address}">
<!-- the other stuff -->
	<label for="categories">Categories</label>
	<f:for each="{categories}" as="category">
		<label for="category-{category.uid}">{category.category}</label>
		<f:form.checkbox id="category-{category.uid}"  
property="mailCategoriesAsArray" value="{category.uid}"/>
	</f:for>
<!-- submit -->
</f:form>

and the createAction has just this signature:

public function createAction(Tx_CzDmRegister_Domain_Form_Address $address)  
{
	//...
}

And that's it.

Just never, *never*, NEVER(!!!) try to set the "checked" argument in the  
Form_CheckboxViewHelper! It will cost you hours and drive you almost  
insane. Believe me - I know what I am talking about. ;) For some reason  
the VH is hardcoded to basically ignore the "property" argument if the  
"checked" argument is set. I would call this a bug, but I guess there is a  
reason why this was done...? Otherwise this works like a charm.

Christian.


More information about the TYPO3-project-typo3v4mvc mailing list