tags:

views:

28

answers:

2

I have a added the cakePHP tags functionality to my applications. I have a table called projects that can have many tags associated. So I have a tags table with an id and a tag varchar field. A projects table with a tags varchar field, and a link table projects_tags that has an id, tag_id, and project_id field. When I add the tags input to my view the tags that exist get populated in a dropdown box for some reason. Can anyone figure out what it is I've done wrong here?

Here is my tags model:

<?php
class ProjectTag extends AppModel {
var $name = 'ProjectTag';


var $hasAndBelongsToMany = array('Tag' => 
                           array('className'    => 'Tag', 
                                 'joinTable'    => 'tags', 
                                 'foreignKey'   => 'tag_id', 
                                 'conditions'   => '', 
                                 'order'        => '', 
                                 'limit'        => '', 
                                 'unique'       => true, 
                                 'finderQuery'  => '', 
                                 'deleteQuery'  => '', 
                           ),
                           array('className'    => 'Project', 
                                 'joinTable'    => 'projects', 
                                 'foreignKey'   => 'project_id', 
                                 'conditions'   => '', 
                                 'order'        => '', 
                                 'limit'        => '', 
                                 'unique'       => true, 
                                 'finderQuery'  => '', 
                                 'deleteQuery'  => '', 
                           )

                           );

}

?>

Here is my add project view add.ctp:

<?php
echo $form->create('Project');
echo $form->input('title', array('label' => 'Title'));
echo $form->input('website', array('label' => 'Website'));
echo $form->input('description', array('label' => 'Description'));
echo $form->input('language_id', array('label' => 'Language'));
echo $form->input('tags');
echo $form->end('Post project');
?>

Here is the tag behavior model:

<?php

class TagBehavior extends ModelBehavior {
/**
 * Initiate behaviour for the model using specified settings.
 *
 * @param object $model    Model using the behaviour
 * @param array $settings    Settings to override for model.
 *
 * @access public
 */
function setup(&$model, $settings = array()) {


    $default = array( 'table_label' => 'tags', 'tag_label' => 'tag', 'separator' => ',');

    if (!isset($this->settings[$model->name])) {
        $this->settings[$model->name] = $default;
    }

$this->settings[$model->name] = array_merge($this->settings[$model->name], ife(is_array($settings), $settings, array()));

}

/**
 * Run before a model is saved, used to set up tag for model.
 *
 * @param object $model    Model about to be saved.
 *
 * @access public
 * @since 1.0
 */
function beforeSave(&$model) {
// Define the new tag model
$Tag =& new Tag;
    if ($model->hasField($this->settings[$model->name]['table_label']) 
    && $Tag->hasField($this->settings[$model->name]['tag_label'])) {


    // Parse out all of the 
    $tag_list = $this->_parseTag($model->data[$model->name][$this->settings[$model->name]['table_label']], $this->settings[$model->name]);
    $tag_info = array(); // New tag array to store tag id and names from db
    foreach($tag_list as $t) {
        if ($res = $Tag->find($this->settings[$model->name]['tag_label'] . " LIKE '" . $t . "'")) {
            $tag_info[] = $res['Tag']['id'];
        } else {
            $Tag->save(array('id'=>'',$this->settings[$model->name]['tag_label']=>$t));
            $tag_info[] = sprintf($Tag->getLastInsertID());
        }
        unset($res);
    }

    // This prepares the linking table data...
    $model->data['Tag']['Tag'] = $tag_info;
    // This formats the tags field before save...
    $model->data[$model->name][$this->settings[$model->name]['table_label']] = implode(', ', $tag_list);
}
return true;
}


/**
 * Parse the tag string and return a properly formatted array
 *
 * @param string $string    String.
 * @param array $settings    Settings to use (looks for 'separator' and 'length')
 *
 * @return string    Tag for given string.
 *
 * @access private
 */
function _parseTag($string, $settings) {
    $string = strtolower($string);

    $string = preg_replace('/[^a-z0-9' . $settings['separator'] . ' ]/i', '', $string);
    $string = preg_replace('/' . $settings['separator'] . '[' . $settings['separator'] . ']*/', $settings['separator'], $string);

$string_array = preg_split('/' . $settings['separator'] . '/', $string);
$return_array = array();

foreach($string_array as $t) {
    $t = ucwords(trim($t));
    if (strlen($t)>0) {
        $return_array[] = $t;
    }
}

    return $return_array;
}
}

?>

Here is my project_controller add:

function add() {


    $this->set("Languages", $this->Project->Language->find("list"));


    if (!empty($this->data)) {
        if ($this->Project->save($this->data)) {
            $this->Session->setFlash('Project added.');
            $this->redirect(array('action' => 'index'));
        }
    }
}
+1  A: 

It's because of the relationship in the model. CakePHP will automatically associate the tags and create a drop down for you. That is the purpose of having an associated field. It is basically saying, "This tag needs to be something that exists in this 'tag' table." Therefore it pre-populates it with the data from tags.

Take out the HABTM relationship and it will go back to a test box.

cdburgess
that seems to have worked! thanks! Now that I've removed the relationship how does it tie tags to projects?
iamjonesy
You can create the relationship in a different model. But what you need to ask yourself is more around design. How do you want to use this field? If you can explain what it is you are trying to accomplish, then it would be easier to explain how the relationships need to be configured.
cdburgess
+1  A: 

cdburgess is right about the reason the dropdown is appearing, but you can override that reasonable default to meet your own needs by tweaking the Cake "magic" field:

echo $form->input('tags', array( 'type' => 'text' ) );

I won't swear by the syntax, but this will create the tags input as a textbox.

IMO, this is a much better option than dropping an association that has so much power behind it in other places.

Rob Wilkerson
Thanks Rob this worked a treat. I had to keep my relationships as I was using multiple models here and using link tables... thanks
iamjonesy
Glad it worked out. Associations are so key to the magic of Cake, Rails, etc. that removing them should be a very last resort in almost all circumstances.
Rob Wilkerson