I would like to document several speedy ways I have of working with HABTM data.
The code includes: HABTM Select, HABTM Checkbox, HABTM Text Add, HABTM TextArea Edit.
The Models
Before we get started, lets setup the models.
models/post.phpvar $name = 'Post';
var $hasAndBelongsToMany = array(
'Tag' => array(
'className' => 'Tag',
'joinTable' => 'posts_to_tags',
'foreignKey' => 'post_id',
'associationForeignKey' => 'tag_id',
'with' => 'PostToTag',
'unique' => false, // important to set this so cake does not auto-delete records on update
),
);
}
var $name = 'Tag';
}
HABTM Select
This is the default CakePHP way of handling HABTM forms.
It will allow you to add or remove HABTM data using a multiple select box (by holding CTRL).
views/posts/form.ctpvar $name = 'Posts';
function form($id = null) {
if (!empty($this->data)) {
// form sends data as:
// $this->data['Post']['Tag']['Tag'][1] = 1;
// $this->data['Post']['Tag']['Tag'][3] = 3;
// save the data (auto-handles habtm save)
$this->Post->create();
if ($this->Post->save($this->data)) {
$this->_flash(__('The Post has been saved.', true),'success');
$this->redirect(array('action'=>'view',$this->Post->id));
}
else {
$this->_flash(__('The Post could not be saved. Please, try again.', true),'warning');
}
}
if (empty($this->data)) {
$this->data = $this->Post->read(null, $id);
}
}
}
HABTM Checkbox
If you have too many options to list in a select box, or if the labels in the select options do not suit your needs then you may want to try HABTM Checkbox.
It will allow you to add or remove HABTM data using a checkbox for each HABTM item.
views/posts/form.ctp<fieldset>
<legend><?php __('Post Details');?></legend>
<?php
echo $form->input('id');
echo $form->input('name');
?>
</fieldset>
<fieldset>
<legend><?php __('Tags');?></legend>
<?php
$checked = $form->value('Tag.Tag');
foreach ($tags as $id=>$label) {
echo $form->input("Tag.checkbox.$id", array(
'label'=>$label,
'type'=>'checkbox',
'checked'=>(isset($checked[$id])?'checked':false),
));
}
?>
</fieldset>
<?php echo $form->end('Submit');?>
var $name = 'Posts';
function form($id = null) {
if (!empty($this->data)) {
// get the tags from the checkbox data
$this->data['Tag']['Tag'] = array();
foreach($this->data['Tag']['checkbox'] as $k=>$v) {
if ($v) $this->data['Tag']['Tag'][] = $k;
}
// save the data
$this->Post->create();
if ($this->Post->save($this->data)) {
$this->_flash(__('The Post has been saved.', true),'success');
$this->redirect(array('action'=>'view',$this->Post->id));
}
else {
$this->_flash(__('The Post could not be saved. Please, try again.', true),'warning');
}
}
if (empty($this->data)) {
$this->data = $this->Post->read(null, $id);
}
}
}
HABTM Text Add
If you want to allow users to enter tags that are added to the current tags.
It will allow you to add but not remove HABTM data using a text input with comma seperated values.
views/posts/form.ctp<fieldset>
<legend><?php __('Post Details');?></legend>
<?php
echo $form->input('id');
echo $form->input('name');
?>
</fieldset>
<fieldset>
<legend><?php __('Tags');?></legend>
<?php
// display current tags
$links = array();
foreach($post['Tag'] as $k=>$row)
$links[] = $html->link(__($row['name'],true),array('controller'=>'postss','action'=>'index','tag_id'=>$row['id']));
echo '<div style="margin:10px 5px;font-weight:bold;font-size:12px;">'.implode(', ',$links).'</div>';
// display tag add form
echo $form->create('Post', array('url'=>array('action' => 'view', $post['Post']['id'])));
echo $form->input('Tag.tags',array('label'=>__('Add Tags',true),'after'=>__('Seperate each tag with a comma. Eg: agility, training, ball control',true)));
echo $form->end('Save');
?>
</fieldset>
<?php echo $form->end('Submit');?>
var $name = 'Posts';
function form($id = null) {
if (!empty($this->data)) {
// get the tags from the text data
if ($this->data['Tag.tags']) {
$this->data['Post']['id'] = $id;
$tags = explode(',',$this->data['Tag']['tags']);
foreach($tags as $_tag) {
$_tag = strtolower(trim($_tag));
// check if the tag exists
$this->Post->Tag->recursive = -1;
$tag = $this->Post->Tag->findByName($_tag);
if (!$tag) {
// create new tag
$this->Post->Tag->create();
$tag = $this->Post->Tag->save(array('name'=>$_tag));
$tag['Tag']['id'] = $this->Post->Tag->id;
if (!$tag) {
$this->_flash(__(sprintf('The Tag %s could not be saved.',$_tag), true),'success');
}
}
if ($tag) {
// use current tag
$this->data['Tag']['Tag'][$tag['Tag']['id']] = $tag['Tag']['id'];
}
}
}
// save the data
$this->Post->create();
if ($this->Post->save($this->data)) {
$this->_flash(__('The Post has been saved.', true),'success');
$this->redirect(array('action'=>'view',$this->Post->id));
}
else {
$this->_flash(__('The Post could not be saved. Please, try again.', true),'warning');
}
}
if (empty($this->data)) {
$this->data = $this->Post->read(null, $id);
}
// get the posts current tags
$post = $id ? $this->Post->find(array('Post.id'=>$id)) : false;
$this->set(compact('post'));
}
}
HABTM TextArea Edit
Finally, your admin may ask you if they can just edit all of the data as a comma seperated list.
It will allow you to add and remove HABTM data using a textarea input with comma seperated values.
views/posts/form.ctp<fieldset>
<legend><?php __('Post Details');?></legend>
<?php
echo $form->input('id');
echo $form->input('name');
?>
</fieldset>
<fieldset>
<legend><?php __('Tags');?></legend>
<?php
// display current tags
$links = array();
foreach($post['Tag'] as $k=>$row)
$links[] = $html->link(__($row['name'],true),array('controller'=>'postss','action'=>'index','tag_id'=>$row['id']));
echo '<div style="margin:10px 5px;font-weight:bold;font-size:12px;">'.implode(', ',$links).'</div>';
// display tag add form
echo $form->create('Post', array('url'=>array('action' => 'view', $post['Post']['id'])));
echo $form->input('Tag.tags',array('label'=>__('Add Tags',true),'after'=>__('Seperate each tag with a comma. Eg: agility, training, ball control',true)));
echo $form->end('Save');
?>
</fieldset>
<?php echo $form->end('Submit');?>
var $name = 'Posts';
function form($id = null) {
if (!empty($this->data)) {
// get the tags from the textarea data
$tags = explode(',',$this->data['Post']['tags']);
foreach($tags as $_tag) {
$_tag = strtolower(trim($_tag));
$this->Post->Tag->recursive = -1;
$tag = $this->Post->Tag->findByName($_tag);
if (!$tag) {
$this->Post->Tag->create();
$tag = $this->Post->Tag->save(array('name'=>$_tag));
$tag['Tag']['id'] = $this->Post->Tag->id;
if (!$tag) {
$this->_flash(__(sprintf('The Tag %s could not be saved.',$_tag), true),'success');
}
}
if ($tag) {
$this->data['Tag']['Tag'][$tag['Tag']['id']] = $tag['Tag']['id'];
}
}
// delete old tags
$this->Post->PostToTag->deleteAll(array('post_id'=>$this->data['Post']['id']));
// save the data
$this->Post->create();
if ($this->Post->save($this->data)) {
$this->_flash(__('The Post has been saved.', true),'success');
$this->redirect(array('action'=>'view',$this->Post->id));
}
else {
$this->_flash(__('The Post could not be saved. Please, try again.', true),'warning');
}
}
if (empty($this->data)) {
$this->data = $this->Post->read(null, $id);
// load the habtm data for the textarea
$tags = array();
if (isset($this->data['Tag']) && !empty($this->data['Tag'])) {
foreach($this->data['Tag'] as $tag) {
$tags[] = $tag['name'];
}
$this->data['Post']['tags'] = implode(', ',$tags);
}
}
// get the posts current tags
$post = $id ? $this->Post->find(array('Post.id'=>$id)) : false;
$this->set(compact('post'));
}
}
In Closing
There are many great ways to work with HABTM data, these are just a few examples of what can be done. Enjoy your CakePHP.










Post new comment