2

I have a model for 'AddOns', which has a bunch of data in. I also have tags, which work in the usual manner, just like on SO, allowing a maximum of 5 tags from a comma-separated list.

The behaviour I am trying to create is as follows:

  • User creates add on
  • Add on is created
  • System splits tags up into an array and loops through them
  • System looks up tag, if it exists we'll use that ID
  • If tag doesn't exist we will create it and use that ID
  • Create a link between the tag and the add on

I can do this very easily using a manual query, but I'm not sure that is the best method or how I should approach this. Here is my code:

if ($this->request->is('post')) {
$this->AddOn->create();
$this->AddOn->set('user_id', $this->Auth->user('id'));
if ($this->AddOn->save($this->request->data)) {

    // Get the ID of the addon
    $addon_id = $this->AddOn->getInsertID();
    $tagsarr = explode(',', $this->request->data['Tag']['Tag']);
    foreach($tagsarr as $tag){
        $tagdb = $this->Tags->findByTagName(trim($tag));
        if(!empty($tagdb)){
            // HELP! We have a tag, but how do we add the link?
        } else {
            // Add the tag, but then how do we link it?
        }
        unset($tagdb);
    }

    $this->Session->setFlash(
        __('The %s has been saved', __('add on'))           
    );
    $this->redirect(array('action' => 'index'));
} else {
    $this->Session->setFlash(
        __('The %s could not be saved. Please, try again.', __('add on')),
    );
}
}

Edit: Added some pseudo code below of what I'm trying to achieve.

$AddOn->create();
$AddOn->save(); // Create AddOn
$AddOnID = $AddOn->insertId(); // Newly inserted AddOn's ID

$tagsArr = explode(',', $post->tags); // Make an array of tags [this,is,a,tag,list]
foreach($tagsArr as $tag){ // Loop through them
  $tagFromDb = $Tags->findTagByName($tag); // Try and find the tag from the tags table
  if(!$tagFromDb){ // Have we found it? No
    $tagId = $Tags->newTag($tag); // Add it and reference
  } else { // Have we found it? Yes
    $tagId = $tagFromDb->id; // Reference it
  }
  $AddOnTagsLink->addAddOnTagsLink($AddOnID, $tagId); // Add the link
}
Mike
  • 8,767
  • 8
  • 49
  • 103
  • Have you read the section in the book that shows how to save HABTM data?: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#hasandbelongstomany-habtm If you have, what have you tried? – Dave May 13 '13 at 13:34
  • I'm not sure that is what I need. I'm looking to create the link after adding the main record ('addon'), referencing both that and the tag ID found in the tags table (assuming there is one). I'll update the question with some pseudo code of what I'm trying to achieve – Mike May 13 '13 at 17:34
  • @Dave - I've added the pseudo code, probably better at explaining what I'm trying to achieve than anything. – Mike May 13 '13 at 17:41
  • Maybe you want to test your schema on http://cakeapp.com ? – powtac May 14 '13 at 00:19

1 Answers1

1

The logic for implementing this should be in a model, rather than a controller. Using the before save callback allows us to add a "tags" field to the form and save normally.

public function beforeSave($options = array()) {
    if (isset($this->data['AddOn']['tags'])) {
        $this->data['Tag'] = array();
        $submitted = array_map('trim', explode(',', $this->data['AddOn']['tags']));
        $existing = $this->Tag->find('list', array(
            'fields' => array('id', 'name'),
            'conditions' => array('name' => $submitted),
            'recursive' => -1
        ));
        foreach ($submitted as $tag) {
            if(!empty($tag)) {
                $id = array_search($tag, $existing);
                if (!$id) {
                    $this->Tag->save(array('name' => $tag));
                    $id = $this->Tag->id;
                }
                $this->data['Tag'][] = array('tag_id' => $id);
            }
        }
    }
    return true;
}

That code could be improved by saving all the new tags with one query (using saveAll) which would limit the number of queries to three (get existing, create missing, save) instead of one per new tag, but that requires some extra work for handling returned IDs. If your users will be creating a lot of new tags often you should do that.

Community
  • 1
  • 1
Pádraig Galvin
  • 1,065
  • 8
  • 20
  • 1
    This was absolutely invaluable. It wasn't quite right, and I had to make some amendments - probably more my poorly described question than your answer though - but it certainly helped, and I now fully understand the issue (rather than a copy&paste solution). Thanks – Mike May 15 '13 at 12:52
  • You're welcome! I had a similar problem once myself so I made some assumptions. Now that you mention it I see your form must be different since the submitted data isn't quite the same. – Pádraig Galvin May 15 '13 at 13:01