Si, è una versione modificata di un analogo behavior che ho trovato su CakeForge (o su il Bakery, non ricordo).
Il funzionamento è semplice:
- Nel form del modello a cui associare i tags si inserisce un campo per scrivere una sequenza di tags distanziate da un separatore (impostabile).
- Il behavior si occupa di fare il "parsing" della stringa, di ottenere i singoli tags e di inserirli nell'apposita tabella (se non sono già presenti). Infine di associarli al modello che utilizza il behavior.
- Offre anche alcune funzioni per avere il processo inverso in modo da riottenere automaticamente la stringa delle tags pronta, da usare ad esempio per la visualizzazione.
Ecco il codice:
<?php
/**
* Taggable Behavior class file.
*
* Model Behavior to support tags.
*
* @author Emanuele 'Zuck' Bertoldi
* @filesource
* @package app
* @subpackage models.behaviors
*/
class TaggableBehavior 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' => 'name', '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 (isset($model->data[$model->name][$this->settings[$model->name]['table_label']]) &&
$tag->hasField($this->settings[$model->name]['tag_label'])) {
// Parse out all of the tags.
$tag_list = $this->_parseTags($model->data[$model->name][$this->settings[$model->name]['table_label']], $this->settings[$model->name]);
$tag_data = 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_data[] = $res['Tag']['id'];
} else {
$tag->save(array('Tag' => array($this->settings[$model->name]['tag_label'] => $t)));
$tag_data[] = sprintf($tag->getLastInsertID());
}
unset($res);
}
// This prepares the linking table data...
$model->data['Tag']['Tag'] = $tag_data;
}
return true;
}
/**
* Run after a model is deleted, used to clear unreferenced tags.
*
* @param object $model Model about to be deleted.
*
* @access public
* @since 1.0
*/
function afterDelete(&$model) {
// TODO:.... Garbage collector and reference counting.
}
/**
* Run after a model is found, used to fill model tag string.
*
* @param object $model Model about to be found.
* @param mixed $results Model rows found.
*
* @access public
* @since 1.0
*/
function afterFind(&$model, &$results) {
if (!empty($results)) {
foreach ($results as &$m) {
$m[$model->name][$this->settings[$model->name]['table_label']] = $this->_tagString($m, $this->settings[$model->name]);
}
}
}
/**
* 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 array Tags for given string.
*
* @access private
*/
function _parseTags($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) {
if (strlen($t) > 0) {
$return_array[] = $t;
}
}
return $return_array;
}
/**
* Parse the tag array and return a properly formatted string
*
* @param mixed &$model Model.
* @param array $settings Settings to use (looks for 'separator' and 'length')
*
* @return string Tag for given string.
*
* @access private
*/
function _tagString($data, $settings) {
$string = '';
$tag_array = $data['Tag'];
foreach($tag_array as $t) {
$word = $t[$settings['tag_label']];
if (strlen($word) > 0) {
$string .= $word.$settings['separator'];
}
}
return $string;
}
}
?>
A questo punto basta impostare il modello così (esempio):
<?php
class Page extends AppModel
{
var $name = 'Page';
var $actsAs = 'Taggable';
/**
* Associazione "molti-a-molti" con le tags.
*/
var $hasAndBelongsToMany = 'Tag';
}
?>
E il gioco è fatto, impostando un form del genere:
<?php echo $form->create('Page', array('url' => ...)); ?>
<?php echo $form->error('title'); ?>
<?php echo $form->label('title','Titolo (*):'); ?>
<?php echo $form->text('title', array('class' => 'text')); ?>
<?php echo $form->error('body'); ?>
<?php echo $form->label('body','Corpo della pagina (*):'); ?>
<?php echo $form->textarea('body'); ?>
// Separate ad esempio da uno spazio, da ', ' o altro... (impostabile)
<?php echo $form->label('tags','Parole chiave:'); ?>
<?php echo $form->text('tags', array('class' => 'text')); ?>
<?php echo $form->submit('Pulisci', array('div' => false, 'name' => 'previous')); ?>
<?php echo $form->submit('Invia', array('div' => false, 'name' => 'next')); ?>
<?php echo $form->end(); ?>
Sicuramente si può migliorare: ad esempio è possibile spostare l'HABTM dentro al behavior? In questo modo sarebbe ancora più automatico: colleghi, imposti e via...Un altro perfezionamento riguarda i parametri impostabili: per ora si può scegliere il nome del campo nel modello 'Tag' che ne rappresenta il valore (il nome) e il nome del campo "ausiliario" nel form che viene passato contenente la stringa da "parsare".
Nella versione originale la stringa veniva memorizzata come attributo del modello che utilizzava il comportamento, ma questo creava ridondanze e un campo in più assolutamente brutto e inutile. Nella mia versione la stringa viene parsata e ricreata a richiesta e non c'e' bisogno di conservarla memorizzata nel DB in quanto bastano già le singole righe nella tabella "Tag" e relative associazioni.
Last edited by zuck (07-02-2008 11:53:39)