diff --git a/.travis.yml b/.travis.yml index bc06b36..9a9fdd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,22 +12,31 @@ env: - REQUIRE="phpunit/phpunit:3.7.31" matrix: - - DB=mysql CAKE_VERSION=master + - DB=mysql CAKE_VERSION=2.7 matrix: include: - php: 5.3 env: - - CAKE_VERSION=master + - CAKE_VERSION=2.7 - php: 5.4 env: - - CAKE_VERSION=master + - CAKE_VERSION=2.7 - php: 5.5 env: - - CAKE_VERSION=master + - CAKE_VERSION=2.7 + - php: 5.3 + env: + - CAKE_VERSION=2.6 + - php: 5.4 + env: + - CAKE_VERSION=2.6 + - php: 5.5 + env: + - CAKE_VERSION=2.6 before_script: - - git clone https://github.com/burzum/travis.git --depth 1 ../travis + - git clone https://github.com/steinkel/travis.git --depth 1 ../travis - ../travis/before_script.sh - if [ "$PHPCS" != 1 ]; then echo " diff --git a/Config/Migration/001_initialize_tags_schema.php b/Config/Migration/001_initialize_tags_schema.php index f1f0d8b..5ca746a 100644 --- a/Config/Migration/001_initialize_tags_schema.php +++ b/Config/Migration/001_initialize_tags_schema.php @@ -42,13 +42,13 @@ class M49ac311a54844a9d87o822502jedc423 extends CakeMigration { 'up' => array( 'create_table' => array( 'tagged' => array( - 'id' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36, 'key' => 'primary'), - 'foreign_key' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36), - 'tag_id' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36), - 'model' => array('type' => 'string', 'null' => false, 'default' => NULL, 'key' => 'index'), - 'language' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 6), - 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), - 'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'key' => 'primary'), + 'foreign_key' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36), + 'tag_id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36), + 'model' => array('type' => 'string', 'null' => false, 'default' => null, 'key' => 'index'), + 'language' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 6), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'modified' => array('type' => 'datetime', 'null' => true, 'default' => null), 'indexes' => array( 'PRIMARY' => array('column' => 'id', 'unique' => 1), 'UNIQUE_TAGGING' => array('column' => array('model', 'foreign_key', 'tag_id', 'language'), 'unique' => 1), @@ -57,13 +57,13 @@ class M49ac311a54844a9d87o822502jedc423 extends CakeMigration { ) ), 'tags' => array( - 'id' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36, 'key' => 'primary'), - 'identifier' => array('type' => 'string', 'null' => true, 'default' => NULL, 'length' => 30, 'key' => 'index'), - 'name' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 30), - 'keyname' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 30), + 'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36, 'key' => 'primary'), + 'identifier' => array('type' => 'string', 'null' => true, 'default' => null, 'length' => 30, 'key' => 'index'), + 'name' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 30), + 'keyname' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 30), 'weight' => array('type' => 'integer', 'null' => false, 'default' => 0, 'length' => 2), - 'created' => array('type' => 'datetime', 'null' => true, 'default' => NULL), - 'modified' => array('type' => 'datetime', 'null' => true, 'default' => NULL), + 'created' => array('type' => 'datetime', 'null' => true, 'default' => null), + 'modified' => array('type' => 'datetime', 'null' => true, 'default' => null), 'indexes' => array( 'PRIMARY' => array('column' => 'id', 'unique' => 1), 'UNIQUE_TAG' => array('column' => array('identifier', 'keyname'), 'unique' => 1) @@ -76,27 +76,4 @@ class M49ac311a54844a9d87o822502jedc423 extends CakeMigration { ) ); -/** - * Before migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function before($direction) { - return true; - } - -/** - * After migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function after($direction) { - return true; - } - } -?> \ No newline at end of file diff --git a/Config/Migration/002_create_times_tagged_field.php b/Config/Migration/002_create_times_tagged_field.php index bf1914f..1bcf9c6 100644 --- a/Config/Migration/002_create_times_tagged_field.php +++ b/Config/Migration/002_create_times_tagged_field.php @@ -42,8 +42,8 @@ class M4c0d42bcd12c4db099c105f40e8f3d6d extends CakeMigration { 'create_field' => array( 'tagged' => array( 'times_tagged' => array('type' => 'integer', 'null' => false, 'default' => 1), - ) ), + ), ), 'down' => array( 'drop_field' => array( @@ -52,26 +52,4 @@ class M4c0d42bcd12c4db099c105f40e8f3d6d extends CakeMigration { ), ); -/** - * Before migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function before($direction) { - return true; - } - -/** - * After migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function after($direction) { - return true; - } } -?> \ No newline at end of file diff --git a/Config/Migration/003_occurrence_fields_for_tags.php b/Config/Migration/003_occurrence_fields_for_tags.php index 1b46878..a1e9a6e 100644 --- a/Config/Migration/003_occurrence_fields_for_tags.php +++ b/Config/Migration/003_occurrence_fields_for_tags.php @@ -42,8 +42,8 @@ class M8d01880f01c11e0be500800200c9a66 extends CakeMigration { 'create_field' => array( 'tags' => array( 'occurrence' => array('type' => 'integer', 'null' => false, 'default' => 0, 'length' => 8), - ) ), + ), 'drop_field' => array( 'tags' => array('weight') ), @@ -60,26 +60,4 @@ class M8d01880f01c11e0be500800200c9a66 extends CakeMigration { ), ); -/** - * Before migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function before($direction) { - return true; - } - -/** - * After migration callback - * - * @param string $direction, up or down direction of migration process - * @return boolean Should process continue - * @access public - */ - public function after($direction) { - return true; - } } -?> \ No newline at end of file diff --git a/Config/Migration/map.php b/Config/Migration/map.php index 1439cc8..f279118 100644 --- a/Config/Migration/map.php +++ b/Config/Migration/map.php @@ -17,10 +17,12 @@ $map = array( 1 => array( - '001_initialize_tags_schema' => 'M49ac311a54844a9d87o822502jedc423'), + '001_initialize_tags_schema' => 'M49ac311a54844a9d87o822502jedc423', + ), 2 => array( - '002_create_times_tagged_field' => 'M4c0d42bcd12c4db099c105f40e8f3d6d'), + '002_create_times_tagged_field' => 'M4c0d42bcd12c4db099c105f40e8f3d6d', + ), 3 => array( - '003_occurrence_fields_for_tags' => 'M8d01880f01c11e0be500800200c9a66'), + '003_occurrence_fields_for_tags' => 'M8d01880f01c11e0be500800200c9a66', + ), ); -?> \ No newline at end of file diff --git a/Config/Schema/schema.php b/Config/Schema/schema.php index 2d04602..88388ee 100644 --- a/Config/Schema/schema.php +++ b/Config/Schema/schema.php @@ -11,26 +11,6 @@ class TagSchema extends CakeSchema { -/** - * Before callback - * - * @param array Event - * @return boolean - */ - public function before($event = array()) { - return true; - } - -/** - * After callback - * - * @param array Event - * @return boolean - */ - public function after($event = array()) { - return true; - } - /** * Schema for taggeds table * diff --git a/Controller/TagsController.php b/Controller/TagsController.php index 4414043..2fafa1c 100644 --- a/Controller/TagsController.php +++ b/Controller/TagsController.php @@ -59,7 +59,7 @@ public function index() { /** * View * - * @param string + * @param string $keyName Tag key name * @return void */ public function view($keyName = null) { @@ -77,14 +77,14 @@ public function view($keyName = null) { * @return void */ public function admin_index() { - $this->{$this->modelClass}->recursive = 0; + $this->{$this->modelClass}->recursive = 0; $this->set('tags', $this->Paginator->paginate()); } /** * Views a single tag * - * @param string tag UUID + * @param string $keyName Tag key name * @return void */ public function admin_view($keyName) { @@ -113,7 +113,7 @@ public function admin_add() { /** * Edits a tag * - * @param string tag UUID + * @param string $tagId Tag UUID * @return void */ public function admin_edit($tagId = null) { @@ -138,11 +138,11 @@ public function admin_edit($tagId = null) { /** * Deletes a tag * - * @param string tag UUID + * @param string $tagId Tag UUID * @return void */ - public function admin_delete($id = null) { - if ($this->{$this->modelClass}->delete($id)) { + public function admin_delete($tagId = null) { + if ($this->{$this->modelClass}->delete($tagId)) { $this->Session->setFlash(__d('tags', 'Tag deleted.')); } else { $this->Session->setFlash(__d('tags', 'Invalid Tag.')); diff --git a/Docs/Documentation/Configuration.md b/Docs/Documentation/Configuration.md index 496f7c3..3868309 100644 --- a/Docs/Documentation/Configuration.md +++ b/Docs/Documentation/Configuration.md @@ -25,16 +25,20 @@ After adding the TaggableBehavior to your model, you will need to update your vi ```php public $actsAs = array( 'Tags.Taggable' => array( - 'separator' => '', - 'field' => 'tags', + 'separator' => ',', 'tagAlias' => 'Tag', 'tagClass' => 'Tags.Tag', + 'taggedAlias' => 'Tagged', 'taggedClass' => 'Tags.Tagged', + 'field' => 'tags', 'foreignKey' => 'foreign_key', 'associationForeignKey' => 'tag_id', + 'cacheOccurrence' => true, 'automaticTagging' => true, + 'taggedCounter' => false, 'unsetInAfterFind' => false, 'resetBinding' => false, + 'deleteTagsOnEmptyField' => false, ) ); ``` @@ -42,14 +46,18 @@ public $actsAs = array( The configuration above contains the default values for each setting. Here are some explanations: * **separator:** String used to separate tags in the Model.tags value. (Default: ```,```) -* **field:** Name of the Model field containing the tag list. (Default: ```tags```) * **tagAlias:** Alias for the HABTM relationship between your Model and Tag. (Default: ```Tag```) * **tagClass**: Name of the model representing Tags. (Default: ```Tags.Tag```) +* **taggedAlias:** Alias for the HABTM join model. (Default: ```Tagged```) * **taggedClass:** Name of the HABTM join model. (Default: ```Tags.Tagged```) +* **field:** Name of the Model field containing the tag list. (Default: ```tags```) * **foreignKey:** Name of the HABTM join model field containing the foreign key to the Tagable model. (Default: ```foreign_key```) * **associationForeignKey:** Name of the HABTM join model field containing the foreign key to the Tag model. (Default: ```tag_id```) +* **cacheOccurrence:** Name of the HABTM join model field containing the foreign key to the Tag model. (Default: ```true```) * **automaticTagging:** Whether or not the behavior will automatically call saveTags() after each save. (Default: ```true```) +* **taggedCounter:** True to update the number of times a particular tag was used for a specific record. * **unsetInAfterFind:** Whether or not the related Tag entries have to be unset after a find. If this value is true, the ```$data['Tag']``` array will be unset and tags will only be available using the ```$data['Model']['tags']``` value. (Default: false) * **resetBinding:** Value passed as the second param of to the ```bindModel()``` call when creating the HABTM association. If set to true, the binding will last only one query. (Default: false) +* **deleteTagsOnEmptyField:** Delete associated Tags if field is empty or empty string. Note that the ```tagClass```, ```taggedClass```, ```foreignKey``` and ```associationForeignKey``` values must not be changed if you use the plugin as it is shipped. Use these settings when you want to use your own models / tables structure. diff --git a/Docs/Documentation/Installation.md b/Docs/Documentation/Installation.md index b4b0017..345450a 100644 --- a/Docs/Documentation/Installation.md +++ b/Docs/Documentation/Installation.md @@ -3,6 +3,12 @@ Installation To install the plugin, place the files in a directory labelled "Tags/" in your "app/Plugin/" directory. +Then, include the following line in your `app/Config/bootstrap.php` to load the plugin in your application. + +``` +CakePlugin::load('Tags'); +``` + Git Submodule ------------- diff --git a/Docs/Documentation/The-Tag-Cloud-Helper.md b/Docs/Documentation/The-Tag-Cloud-Helper.md index eb5d668..38ca055 100644 --- a/Docs/Documentation/The-Tag-Cloud-Helper.md +++ b/Docs/Documentation/The-Tag-Cloud-Helper.md @@ -44,5 +44,6 @@ The second param for the ```display()``` method is an array of options. The avai * **after:** string to be displayed after each generated link. %size% will be replaced with tag size calculated from the weight. (Default: ```empty```) * **maxSize:** size of the heaviest tag. (Default: ```160```) * **minSize:** size of the lightest tag. (Default: ```80```) -* **url:** an array containing the default url. (Default: ```array('controller' => 'search'```)) -* **named**: the named parameter used to send the tag keyname. (Default: ```by```) \ No newline at end of file +* **url:** an array containing the default URL. (Default: ```array('controller' => 'search'```)) +* **named**: the named parameter used to send the tag keyname. (Default: ```by```) +* **paramType**: the type of URL parameters used (```named``` or ```querystring```). (Default: ```named```) diff --git a/Model/Behavior/TaggableBehavior.php b/Model/Behavior/TaggableBehavior.php index 23f39dd..c2fa759 100644 --- a/Model/Behavior/TaggableBehavior.php +++ b/Model/Behavior/TaggableBehavior.php @@ -28,18 +28,20 @@ class TaggableBehavior extends ModelBehavior { /** * Default settings * - * separator - separator used to enter a lot of tags, comma by default - * tagAlias - model alias for Tag model - * tagClass - class name of the table storing the tags - * taggedClass - class name of the HABTM association table between tags and models - * field - the fieldname that contains the raw tags as string - * foreignKey - foreignKey used in the HABTM association - * associationForeignKey - associationForeignKey used in the HABTM association - * automaticTagging - if set to true you don't need to use saveTags() manually - * language - only tags in a certain language, string or array - * taggedCounter - true to update the number of times a particular tag was used for a specific record - * unsetInAfterFind - unset 'Tag' results in afterFind - * deleteTagsOnEmptyField - delete associated Tags if field is empty. + * separator - separator used to enter a lot of tags, comma by default + * field - the fieldname that contains the raw tags as string + * tagAlias - model alias for Tag model + * tagClass - class name of the table storing the tags + * taggedAlias - model alias for the HABTM join model + * taggedClass - class name of the HABTM association table between tags and models + * foreignKey - foreignKey used in the HABTM association + * associationForeignKey - associationForeignKey used in the HABTM association + * cacheOccurrence - cache the weight or occurence of a tag in the tags table + * automaticTagging - if set to true you don't need to use saveTags() manually + * taggedCounter - true to update the number of times a particular tag was used for a specific record + * unsetInAfterFind - unset 'Tag' results in afterFind + * resetBinding - reset the bindModel() calls, default is false + * deleteTagsOnEmptyField - delete associated Tags if field is empty * * @var array */ @@ -54,19 +56,17 @@ class TaggableBehavior extends ModelBehavior { 'associationForeignKey' => 'tag_id', 'cacheOccurrence' => true, 'automaticTagging' => true, + 'taggedCounter' => false, 'unsetInAfterFind' => false, 'resetBinding' => false, - 'taggedCounter' => false, - 'deleteTagsOnEmptyField' => false + 'deleteTagsOnEmptyField' => false, ); /** * Setup * - * @param Model $model - * @param array $config - * - * @internal param array $settings + * @param Model $model Model instance that behavior is attached to + * @param array $config Configuration settings from model * @return void */ public function setup(Model $model, $config = array()) { @@ -80,14 +80,15 @@ public function setup(Model $model, $config = array()) { } /** - * bindTagAssociations + * Bind tag assocations * - * @param Model $model + * @param Model $model Model instance that behavior is attached to * @return void */ public function bindTagAssociations(Model $model) { extract($this->settings[$model->alias]); + list($plugin, $withClass) = pluginSplit($withModel, true); $model->bindModel(array( 'hasAndBelongsToMany' => array( $tagAlias => array( @@ -96,7 +97,8 @@ public function bindTagAssociations(Model $model) { 'associationForeignKey' => $associationForeignKey, 'unique' => true, 'conditions' => array( - 'Tagged.model' => $model->name), + $withClass . '.model' => $model->name + ), 'fields' => '', 'dependent' => true, 'with' => $withModel @@ -148,14 +150,14 @@ public function disassembleTags(Model $model, $string = '', $separator = ',') { /** * Saves a string of tags * - * @param Model $model + * @param Model $model Model instance that behavior is attached to * @param string $string comma separeted list of tags to be saved - * Tags can contain special tokens called `identifiers´ to namespace tags or classify them into catageories. - * A valid string is "foo, bar, cakephp:special". The token `cakephp´ will end up as the identifier or category for the tag `special´ + * Tags can contain special tokens called `identifiers´ to namespace tags or classify them into catageories. + * A valid string is "foo, bar, cakephp:special". The token `cakephp´ will end up as the identifier or category for the tag `special´ * @param mixed $foreignKey the identifier for the record to associate the tags with - * @param boolean $update true will remove tags that are not in the $string, false wont - * do this and just add new tags without removing existing tags associated to - * the current set foreign key + * @param bool $update True will remove tags that are not in the $string, false won't + * do this and just add new tags without removing existing tags associated to + * the current set foreign key * @return array */ public function saveTags(Model $model, $string = null, $foreignKey = null, $update = true) { @@ -210,8 +212,9 @@ public function saveTags(Model $model, $string = null, $foreignKey = null, $upda } foreach ($newTags as $key => $newTag) { $tagModel->create(); - $tagModel->save($newTag); - $newTagIds[] = $tagModel->getLastInsertId(); + if ($tagModel->save($newTag)) { + $newTagIds[] = $tagModel->getLastInsertId(); + } } if ($foreignKey !== false) { @@ -225,7 +228,7 @@ public function saveTags(Model $model, $string = null, $foreignKey = null, $upda $taggedAlias . '.foreign_key' => $foreignKey, $taggedAlias . '.language' => Configure::read('Config.language'), $taggedAlias . '.tag_id' => $existingTagIds), - 'fields' => 'Tagged.tag_id' + 'fields' => $taggedAlias . '.tag_id' )); $deleteAll = array( @@ -247,13 +250,16 @@ public function saveTags(Model $model, $string = null, $foreignKey = null, $upda $taggedAlias . '.model' => $model->name, $taggedAlias . '.foreign_key' => $foreignKey, $taggedAlias . '.language' => Configure::read('Config.language')), - 'fields' => 'Tagged.tag_id' + 'fields' => $taggedAlias . '.tag_id' )); - $oldTagIds = Set::extract($oldTagIds, '/Tagged/tag_id'); + $oldTagIds = Set::extract($oldTagIds, '/' . $taggedAlias . '/tag_id'); $tagModel->{$taggedAlias}->deleteAll($deleteAll, false); } elseif ($this->settings[$model->alias]['taggedCounter'] && !empty($alreadyTagged)) { - $tagModel->{$taggedAlias}->updateAll(array('times_tagged' => 'times_tagged + 1'), array('Tagged.tag_id' => $alreadyTagged)); + $tagModel->{$taggedAlias}->updateAll( + array('times_tagged' => 'times_tagged + 1'), + array($taggedAlias . '.tag_id' => $alreadyTagged) + ); } foreach ($existingTagIds as $tagId) { @@ -273,11 +279,11 @@ public function saveTags(Model $model, $string = null, $foreignKey = null, $upda $taggedAlias . '.model' => $model->name, $taggedAlias . '.foreign_key' => $foreignKey, $taggedAlias . '.language' => Configure::read('Config.language')), - 'fields' => 'Tagged.tag_id' + 'fields' => $taggedAlias . '.tag_id' )); if (!empty($newTagIds)) { - $newTagIds = Set::extract($newTagIds, '{n}.Tagged.tag_id'); + $newTagIds = Set::extract($newTagIds, '{n}.' . $taggedAlias . '.tag_id'); } $this->cacheOccurrence($model, array_merge($oldTagIds, $newTagIds)); @@ -292,8 +298,8 @@ public function saveTags(Model $model, $string = null, $foreignKey = null, $upda /** * Cache the weight or occurence of a tag in the tags table * - * @param Model $model instance of a model - * @param int|string|array $tagIds + * @param Model $model Model instance that behavior is attached to + * @param int|string|array $tagIds Tag ID or list of tag IDs * @return void */ public function cacheOccurrence(Model $model, $tagIds) { @@ -301,45 +307,49 @@ public function cacheOccurrence(Model $model, $tagIds) { $tagIds = array($tagIds); } + $tagAlias = $this->settings[$model->alias]['tagAlias']; + $taggedAlias = $this->settings[$model->alias]['taggedAlias']; + + $tagModel = $model->{$tagAlias}; + $taggedModel = $tagModel->{$taggedAlias}; + + $fieldName = Inflector::underscore($model->name) . '_occurrence'; foreach ($tagIds as $tagId) { - $fieldName = Inflector::underscore($model->name) . '_occurrence'; - $tagModel = $model->{$this->settings[$model->alias]['tagAlias']}; - $taggedModel = $tagModel->{$this->settings[$model->alias]['taggedAlias']}; $data = array($tagModel->primaryKey => $tagId); if ($tagModel->hasField($fieldName)) { $data[$fieldName] = $taggedModel->find('count', array( 'conditions' => array( - 'Tagged.tag_id' => $tagId, - 'Tagged.model' => $model->name + $taggedAlias . '.tag_id' => $tagId, + $taggedAlias . '.model' => $model->name ) )); } $data['occurrence'] = $taggedModel->find('count', array( 'conditions' => array( - 'Tagged.tag_id' => $tagId + $taggedAlias . '.tag_id' => $tagId ) )); $tagModel->save($data, array( 'validate' => false, - 'callbacks' => false) - ); + 'callbacks' => false, + )); } } /** * Creates a multibyte safe unique key * - * @param Model $model - * @param string Tag name string - * @returns string Multibyte safe key string + * @param Model $model Model instance that behavior is attached to + * @param string $string Tag name string + * @return string Multibyte safe key string */ public function multibyteKey(Model $model, $string = null) { $str = mb_strtolower($string); $str = preg_replace('/\xE3\x80\x80/', ' ', $str); $str = str_replace(array('_', '-'), '', $str); - $str = preg_replace( '#[:\#\*"()~$^{}`@+=;,<>!&%\.\]\/\'\\\\|\[]#', "\x20", $str ); + $str = preg_replace('#[:\#\*"()~$^{}`@+=;,<>!&%\.\]\/\'\\\\|\[]#', "\x20", $str); $str = str_replace('?', '', $str); $str = trim($str); $str = preg_replace('#\x20+#', '', $str); @@ -351,14 +361,15 @@ public function multibyteKey(Model $model, $string = null) { * initialization of data for text input * * Example usage (only 'Tag.name' field is needed inside of method): + * * * $this->Blog->hasAndBelongsToMany['Tag']['fields'] = array('name', 'keyname'); * $blog = $this->Blog->read(null, 123); * $blog['Blog']['tags'] = $this->Blog->Tag->tagArrayToString($blog['Tag']); * * - * @param Model $model - * @param array $data + * @param Model $model Model instance that behavior is attached to + * @param array $data Tag data array * @return string */ public function tagArrayToString(Model $model, $data = null) { @@ -368,7 +379,7 @@ public function tagArrayToString(Model $model, $data = null) { if (!empty($tag['identifier'])) { $tags[] = $tag['identifier'] . ':' . $tag['name']; } else { - $tags[] = $tag['name']; + $tags[] = $tag['name']; } } return join($this->settings[$model->alias]['separator'] . ' ', $tags); @@ -379,16 +390,20 @@ public function tagArrayToString(Model $model, $data = null) { /** * afterSave callback * - * @param Model $model - * @param array $created - * @param array $options + * @param Model $model Model instance that behavior is attached to + * @param bool $created True if this save created a new record + * @param array $options Options passed from Model::save() * @return void */ public function afterSave(Model $model, $created, $options = array()) { - $hasTags = !empty($model->data[$model->alias][$this->settings[$model->alias]['field']]); - if ($this->settings[$model->alias]['automaticTagging'] == true && $hasTags) { - $this->saveTags($model, $model->data[$model->alias][$this->settings[$model->alias]['field']], $model->id); - } else if (!$hasTags && $this->settings[$model->alias]['deleteTagsOnEmptyField']) { + if (!isset($model->data[$model->alias][$this->settings[$model->alias]['field']])) { + return; + } + $field = $model->data[$model->alias][$this->settings[$model->alias]['field']]; + $hasTags = !empty($field); + if ($this->settings[$model->alias]['automaticTagging'] === true && $hasTags) { + $this->saveTags($model, $field, $model->id); + } elseif (!$hasTags && $this->settings[$model->alias]['deleteTagsOnEmptyField']) { $this->deleteTagged($model); } } @@ -396,16 +411,20 @@ public function afterSave(Model $model, $created, $options = array()) { /** * Delete associated Tags if record has no tags and deleteTagsOnEmptyField is true * - * @param Model $model Model instance + * @param Model $model Model instance that behavior is attached to + * @param mixed $id Foreign key of the model, string for UUID or integer * @return void */ - public function deleteTagged(Model $model) { + public function deleteTagged(Model $model, $id = null) { extract($this->settings[$model->alias]); $tagModel = $model->{$tagAlias}; + if (is_null($id)) { + $id = $model->id; + } $tagModel->{$taggedAlias}->deleteAll( array( $taggedAlias . '.model' => $model->name, - $taggedAlias . '.foreign_key' => $model->id, + $taggedAlias . '.foreign_key' => $id, ) ); } @@ -413,9 +432,9 @@ public function deleteTagged(Model $model) { /** * afterFind Callback * - * @param Model $model - * @param array $results - * @param boolean $primary + * @param Model $model Model instance that behavior is attached to + * @param mixed $results The results of the find operation + * @param bool $primary Whether this model is being queried directly (vs. being queried as an association) * @return array */ public function afterFind(Model $model, $results, $primary = false) { diff --git a/Model/Tag.php b/Model/Tag.php index 80d9921..38baf5e 100644 --- a/Model/Tag.php +++ b/Model/Tag.php @@ -43,21 +43,40 @@ class Tag extends TagsAppModel { * @var array */ public $validate = array( - 'name' => array('rule' => 'notEmpty'), - 'keyname' => array('rule' => 'notEmpty') + 'name' => array('rule' => 'notBlank'), + 'keyname' => array('rule' => 'notBlank') ); +/** + * Custom validation for keeping BC to CakePHP version below 2.7 + * + * @param array $check + * @return bool + */ + public function notBlank($check) { + $value = array_values($check); + $value = $value[0]; + if (method_exists('Validation', 'notBlank')) { + return Validation::notBlank($value); + } else { + // below 2.7 + return Validation::notEmpty($value); + } + } + /** * Returns the data for a single tag * + * @param string $keyName Tag key name * @throws CakeException - * @param string keyname * @return array */ public function view($keyName = null) { $result = $this->find('first', array( 'conditions' => array( - $this->alias . '.keyname' => $keyName))); + $this->alias . '.keyname' => $keyName + ) + )); if (empty($result)) { throw new CakeException(__d('tags', 'Invalid Tag.')); @@ -68,8 +87,8 @@ public function view($keyName = null) { /** * Pre-populates the tag table with entered tags * - * @param array post data, should be Contoller->data - * @return boolean + * @param array $postData Post data, should be Contoller->data + * @return bool */ public function add($postData = null) { if (isset($postData[$this->alias]['tags'])) { @@ -88,16 +107,17 @@ public function add($postData = null) { /** * Edits an existing tag, allows only to modify upper/lowercased characters * + * @param string $tagId Tag UUID + * @param array $postData Controller post data usually $this->request->data * @throws CakeException - * @param string tag uuid - * @param array controller post data usually $this->request->data * @return mixed True on successfully save else post data as array */ public function edit($tagId = null, $postData = null) { $tag = $this->find('first', array( 'contain' => array(), 'conditions' => array( - $this->alias . '.' . $this->primaryKey => $tagId) + $this->alias . '.' . $this->primaryKey => $tagId + ) )); $this->set($tag); diff --git a/Model/Tagged.php b/Model/Tagged.php index 50f2b2d..4f0ae38 100644 --- a/Model/Tagged.php +++ b/Model/Tagged.php @@ -50,14 +50,14 @@ class Tagged extends TagsAppModel { * Returns a tag cloud * * The result contains a "weight" field which has a normalized size of the tag - * occurrence set. The min and max size can be set by passing 'minSize" and - * 'maxSize' to the query. This value can be used in the view to controll the + * occurrence set. The min and max size can be set by passing 'minSize' and + * 'maxSize' to the query. This value can be used in the view to control the * size of the tag font. * + * @param string $state State string ('before' or 'after') + * @param array $query Query array + * @param array $results Result set for 'after' state * @todo Ideas to improve this are welcome - * @param string $state - * @param array $query - * @param array $results * @return array * @link https://github.com/CakeDC/tags/issues/10 */ @@ -135,16 +135,18 @@ public function _findCloud($state, $query, $results = array()) { * Find all the Model entries tagged with a given tag * * The query must contain a Model name, and can contain a 'by' key with the Tag keyname to filter the results + * * * $this->Article->Tagged->find('tagged', array( * 'by' => 'cakephp', * 'model' => 'Article')); - * * - * @TODO Find a way to populate the "magic" field Article.tags - * @param string $state - * @param array $query - * @param array $results + * @param string $state State string ('before' or 'after') + * @param array $query Query array + * @param array $results Result set for 'after' state + * @todo Find a way to populate the "magic" field Article.tags + * @throws InvalidArgumentException if recursive setting is -1 * @return mixed Query array if state is before, array of results or integer (count) if state is after */ public function _findTagged($state, $query, $results = array()) { @@ -157,7 +159,11 @@ public function _findTagged($state, $query, $results = array()) { 'foreignKey' => 'foreign_key', 'type' => 'INNER', 'conditions' => array( - $this->alias . '.model' => $Model->alias)))), false); + $this->alias . '.model' => $Model->alias + ) + ) + ) + ), false); if (!isset($query['recursive'])) { $query['recursive'] = 0; diff --git a/Model/TagsAppModel.php b/Model/TagsAppModel.php index 2c1a738..783b6e6 100644 --- a/Model/TagsAppModel.php +++ b/Model/TagsAppModel.php @@ -23,14 +23,15 @@ class TagsAppModel extends AppModel { * @var array */ public $actsAs = array( - 'Containable'); + 'Containable' + ); /** * Customized paginateCount method * - * @param array - * @param integer - * @param array + * @param array $conditions Query conditions + * @param int $recursive Recursive setting + * @param array $extra Extra settings * @return array */ public function paginateCount($conditions = array(), $recursive = 0, $extra = array()) { diff --git a/README.md b/README.md index dd6b8e1..c3ba3c5 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ Tags Plugin for CakePHP ======================= -The **Tags** plugin includes the TaggableBehavior that allows you to simply tag everything. - -It saves all tags in a single tags table and connects any kind of records to them through the tagged table. - [![Bake Status](https://secure.travis-ci.org/CakeDC/tags.png?branch=master)](http://travis-ci.org/CakeDC/tags) [![Downloads](https://poser.pugx.org/CakeDC/tags/d/total.png)](https://packagist.org/packages/CakeDC/tags) [![Latest Version](https://poser.pugx.org/CakeDC/tags/v/stable.png)](https://packagist.org/packages/CakeDC/tags) +The **Tags** plugin includes the TaggableBehavior that allows you to simply tag everything. + +It saves all tags in a single tags table and connects any kind of records to them through the tagged table. + Requirements ------------ diff --git a/Test/Case/Controller/TagsControllerTest.php b/Test/Case/Controller/TagsControllerTest.php index 117bb55..d3ab2d2 100644 --- a/Test/Case/Controller/TagsControllerTest.php +++ b/Test/Case/Controller/TagsControllerTest.php @@ -23,7 +23,7 @@ class TestTagsController extends TagsController { /** * Auto render * - * @var boolean + * @var bool */ public $autoRender = false; @@ -37,6 +37,9 @@ class TestTagsController extends TagsController { /** * Override controller method for testing * + * @param string|array $url URL to redirect to (copied to `$this->redirectUrl`) + * @param int $status Optional HTTP status code (eg: 404) + * @param bool $exit If true, exit() will be called after the redirect * @return void */ public function redirect($url, $status = null, $exit = true) { @@ -46,11 +49,14 @@ public function redirect($url, $status = null, $exit = true) { /** * Override controller method for testing * + * @param string $view View to use for rendering + * @param string $layout Layout to use * @return void */ - public function render($action = null, $layout = null, $file = null) { - $this->renderedView = $action; + public function render($view = null, $layout = null) { + $this->renderedView = $view; } + } /** @@ -68,7 +74,8 @@ class TagsControllerTest extends CakeTestCase { */ public $fixtures = array( 'plugin.tags.tagged', - 'plugin.tags.tag'); + 'plugin.tags.tag' + ); /** * Tags Controller Instance @@ -87,7 +94,8 @@ public function setUp() { $this->Tags = new TestTagsController(new CakeRequest(null, false)); $this->Tags->params = array( 'named' => array(), - 'url' => array()); + 'url' => array() + ); $this->Tags->constructClasses(); $this->Tags->Session = $this->getMock('SessionComponent', array(), array(), '', false); } @@ -175,7 +183,6 @@ public function testAdminDelete() { ->with($this->equalTo(__d('tags', 'Tag deleted.'))) ->will($this->returnValue(true)); - $this->Tags->admin_delete('WRONG-ID!!!'); $this->assertEquals($this->Tags->redirectUrl, array('action' => 'index')); @@ -191,14 +198,18 @@ public function testAdminDelete() { public function testAdminAdd() { $this->Tags->data = array( 'Tag' => array( - 'tags' => 'tag1, tag2, tag3')); + 'tags' => 'tag1, tag2, tag3' + ) + ); $this->Tags->admin_add(); $this->assertEquals($this->Tags->redirectUrl, array('action' => 'index')); // adding same tags again. $this->Tags->data = array( 'Tag' => array( - 'tags' => 'tag1, tag2, tag3')); + 'tags' => 'tag1, tag2, tag3' + ) + ); $this->Tags->admin_add(); $this->assertEquals($this->Tags->redirectUrl, array('action' => 'index')); } @@ -219,14 +230,18 @@ public function testAdminEdit() { 'occurrence' => 1, 'article_occurrence' => 1, 'created' => '2008-06-02 18:18:11', - 'modified' => '2008-06-02 18:18:37')); + 'modified' => '2008-06-02 18:18:37' + ) + ); $this->assertEquals($this->Tags->data, $tag); $this->Tags->data = array( 'Tag' => array( 'id' => 'tag-1', - 'name' => 'CAKEPHP')); + 'name' => 'CAKEPHP' + ) + ); $this->Tags->admin_edit('tag-1'); $this->assertEquals($this->Tags->redirectUrl, array('action' => 'index')); diff --git a/Test/Case/Model/Behavior/TaggableBehaviorTest.php b/Test/Case/Model/Behavior/TaggableBehaviorTest.php index a3bdf07..1cf72df 100644 --- a/Test/Case/Model/Behavior/TaggableBehaviorTest.php +++ b/Test/Case/Model/Behavior/TaggableBehaviorTest.php @@ -60,6 +60,40 @@ class Article extends CakeTestModel { public $actsAs = array('Tags.Taggable'); } +/** + * Custom tag model + * + * @package tags + * @subpackage tags.tests.cases.behaviors + */ +class CustomTag extends CakeTestModel { + +/** + * Use table + * + * @var string + */ + public $useTable = 'tags'; + +} + +/** + * Custom tagged model + * + * @package tags + * @subpackage tags.tests.cases.behaviors + */ +class CustomTagged extends CakeTestModel { + +/** + * Use table + * + * @var string + */ + public $useTable = 'tagged'; + +} + /** * TaggableTest * @@ -91,7 +125,42 @@ class TaggableBehaviorTest extends CakeTestCase { public $fixtures = array( 'plugin.tags.tagged', 'plugin.tags.tag', - 'plugin.tags.article'); + 'plugin.tags.article' + ); + +/** + * Behavior settings. + * + * @var array + */ + public $behaviorSettings = array( + 'default' => array(), + 'noPluginPrefix' => array( + 'tagClass' => 'Tag', + 'taggedClass' => 'Tagged', + ), + 'customAliasAndClass' => array( + 'tagAlias' => 'CustomTagAlias', + 'taggedAlias' => 'CustomTaggedAlias', + 'tagClass' => 'Tags.CustomTag', + 'taggedClass' => 'Tags.CustomTagged', + ), + ); + +/** + * Run test cases multiple times with different behavior settings + * + * @param PHPUnit_Framework_TestResult $result The test result object + * @return PHPUnit_Framework_TestResult + */ + public function run(PHPUnit_Framework_TestResult $result = null) { + foreach ($this->behaviorSettings as $behaviorSettings) { + $this->behaviorSettings = $behaviorSettings; + $result = parent::run($result); + } + + return $result; + } /** * Method executed before each test @@ -102,7 +171,7 @@ public function setUp() { parent::setUp(); $this->Article = ClassRegistry::init('Article'); Configure::write('Config.language', 'eng'); - $this->Article->Behaviors->attach('Tags.Taggable', array()); + $this->Article->Behaviors->attach('Tags.Taggable', $this->behaviorSettings); } /** @@ -122,31 +191,39 @@ public function tearDown() { * @return void */ public function testOccurrenceCache() { - $resultBefore = $this->Article->Tag->find('first', array( + $tagAlias = $this->Article->Behaviors->Taggable->settings['Article']['tagAlias']; + + $resultBefore = $this->Article->{$tagAlias}->find('first', array( 'contain' => array(), 'conditions' => array( - 'Tag.keyname' => 'cakephp'))); + $tagAlias . '.keyname' => 'cakephp' + ) + )); // adding a new record with the cakephp tag to increase the occurrence $data = array('title' => 'Test Article', 'tags' => 'cakephp, php'); $this->Article->create(); $this->Article->save($data, false); - $resultAfter = $this->Article->Tag->find('first', array( + $resultAfter = $this->Article->{$tagAlias}->find('first', array( 'contain' => array(), 'conditions' => array( - 'Tag.keyname' => 'cakephp'))); + $tagAlias . '.keyname' => 'cakephp' + ) + )); - $this->assertEquals($resultAfter['Tag']['occurrence'] - $resultBefore['Tag']['occurrence'], 1); + $this->assertEquals($resultAfter[$tagAlias]['occurrence'] - $resultBefore[$tagAlias]['occurrence'], 1); // updating the record to not have the cakephp tag anymore, decreases the occurrence $data = array('id' => $this->Article->id, 'title' => 'Test Article', 'tags' => 'php, something, else'); $this->Article->save($data, false); - $resultAfter = $this->Article->Tag->find('first', array( + $resultAfter = $this->Article->{$tagAlias}->find('first', array( 'contain' => array(), 'conditions' => array( - 'Tag.keyname' => 'cakephp'))); - $this->assertEquals($resultAfter['Tag']['occurrence'], 1); + $tagAlias . '.keyname' => 'cakephp' + ) + )); + $this->assertEquals($resultAfter[$tagAlias]['occurrence'], 1); } /** @@ -160,7 +237,9 @@ public function testTagSaving() { $this->Article->save($data, false); $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1'))); + 'id' => 'article-1' + ) + )); $this->assertTrue(!empty($result['Article']['tags'])); $data['tags'] = 'foo, developer, developer, php'; @@ -168,23 +247,27 @@ public function testTagSaving() { $result = $this->Article->find('first', array( 'contain' => array('Tag'), 'conditions' => array( - 'id' => 'article-1'))); + 'id' => 'article-1' + ) + )); $this->assertTrue(!empty($result['Article']['tags'])); $this->assertEquals(3, count($result['Tag'])); - $data['tags'] = 'cakephp:foo, developer, cakephp:developer, cakephp:php'; $this->Article->save($data, false); $result = $this->Article->Tag->find('all', array( 'recursive' => -1, 'order' => 'Tag.identifier DESC, Tag.name ASC', 'conditions' => array( - 'Tag.identifier' => 'cakephp'))); + 'Tag.identifier' => 'cakephp' + ) + )); $result = Set::extract($result, '{n}.Tag.keyname'); $this->assertEquals($result, array( - 'developer', 'foo', 'php')); + 'developer', 'foo', 'php' + )); $this->assertFalse($this->Article->saveTags('foo, bar', null)); $this->assertFalse($this->Article->saveTags(array('foo', 'bar'), 'something')); @@ -226,7 +309,9 @@ public function testTagArrayToString() { $this->Article->save($data, false); $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1'))); + 'id' => 'article-1' + ) + )); $result = $this->Article->tagArrayToString($result['Tag']); $this->assertTrue(!empty($result)); $this->assertInternalType('string', $result); @@ -240,7 +325,9 @@ public function testTagArrayToString() { $this->Article->save($data, false); $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1'))); + 'id' => 'article-1' + ) + )); $result = $this->Article->tagArrayToString($result['Tag']); $this->assertTrue(!empty($result)); @@ -267,20 +354,26 @@ public function testMultibyteKey() { * @return void */ public function testAfterFind() { + $tagAlias = $this->Article->Behaviors->Taggable->settings['Article']['tagAlias']; + $data['id'] = 'article-1'; $data['tags'] = 'foo, bar, test'; $this->Article->save($data, false); $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1'))); - $this->assertTrue(isset($result['Tag'])); + 'id' => 'article-1' + ) + )); + $this->assertTrue(isset($result[$tagAlias])); $this->Article->Behaviors->Taggable->settings['Article']['unsetInAfterFind'] = true; $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1'))); - $this->assertTrue(!isset($result['Tag'])); + 'id' => 'article-1' + ) + )); + $this->assertTrue(!isset($result[$tagAlias])); } /** @@ -292,7 +385,8 @@ public function testAfterFindFields() { $this->Article->Behaviors->detach('Taggable'); $results = $this->Article->find('first', array( 'recursive' => -1, - 'fields' => array('id'))); + 'fields' => array('id') + )); $expected = array($this->Article->alias => array('id' => 'article-1')); $this->assertIdentical($results, $expected); } @@ -361,7 +455,8 @@ public function testSavingTagsDoesNotCreateEmptyRecords() { $this->Article->save($data, false); $result = $this->Article->find('first', array( 'conditions' => array( - 'id' => 'article-1') + 'id' => 'article-1' + ) )); $count = $this->Article->Tag->find('count', array( @@ -384,6 +479,60 @@ public function testSavingTagsWithDifferentIdentifier() { $this->Article->save($data); $data = $this->Article->findById('article-1'); $this->assertEquals('bar:cakephp, foo:cakephp', $data['Article']['tags']); + } +/** + * testDeletingMoreThanOneTagAtATime + * + * @link https://github.com/CakeDC/tags/issues/86 + * @return void + */ + public function testDeletingMoreThanOneTagAtATime() { + // Adding five tags for testing + $data = array( + 'Article' => array( + 'id' => 'article-test-delete-tags', + 'tags' => 'foo, bar, test, second, third', + ) + ); + $this->Article->create(); + $this->Article->save($data, false); + $result = $this->Article->find('first', array( + 'conditions' => array( + 'id' => 'article-test-delete-tags' + ) + )); + $this->assertEquals($result['Article']['tags'], 'third, second, test, bar, foo'); + // Removing three of the five previously added tags + $result['Article']['tags'] = 'third, second'; + $this->Article->save($result, false); + $result = $this->Article->find('first', array( + 'conditions' => array( + 'id' => 'article-test-delete-tags' + ) + )); + $this->assertEquals($result['Article']['tags'], 'second, third'); + // Removing all tags, empty string - WON'T work as expected because of deleteTagsOnEmptyField + $result['Article']['tags'] = ''; + $this->Article->save($result, false); + $result = $this->Article->find('first', array( + 'conditions' => array( + 'id' => 'article-test-delete-tags' + ) + )); + $this->assertEquals($result['Article']['tags'], 'third, second'); + // Now with deleteTagsOnEmptyField + $this->Article->Behaviors->load('Tags.Taggable', array( + 'deleteTagsOnEmptyField' => true + )); + $result['Article']['tags'] = ''; + $this->Article->save($result, false); + $result = $this->Article->find('first', array( + 'conditions' => array( + 'id' => 'article-test-delete-tags' + ) + )); + $this->assertEquals($result['Article']['tags'], ''); } + } diff --git a/Test/Case/Model/TagTest.php b/Test/Case/Model/TagTest.php index 042c4e3..43ca44f 100644 --- a/Test/Case/Model/TagTest.php +++ b/Test/Case/Model/TagTest.php @@ -76,14 +76,16 @@ public function testTagFind() { $expected = array( 'Tag' => array( - 'id' => 'tag-1', - 'identifier' => null, - 'name' => 'CakePHP', - 'keyname' => 'cakephp', + 'id' => 'tag-1', + 'identifier' => null, + 'name' => 'CakePHP', + 'keyname' => 'cakephp', 'occurrence' => 1, 'article_occurrence' => 1, - 'created' => '2008-06-02 18:18:11', - 'modified' => '2008-06-02 18:18:37')); + 'created' => '2008-06-02 18:18:11', + 'modified' => '2008-06-02 18:18:37' + ) + ); $this->assertEquals($results, $expected); } @@ -107,23 +109,29 @@ public function testView() { * @return void */ public function testAdd() { - $result = $this->Tag->add( - array('Tag' => array( - 'tags' => 'tag1, tag2, tag3'))); + $result = $this->Tag->add(array( + 'Tag' => array( + 'tags' => 'tag1, tag2, tag3' + ) + )); $this->assertTrue($result); $result = $this->Tag->find('all', array( 'recursive' => -1, 'fields' => array( - 'Tag.name'))); + 'Tag.name' + ) + )); $result = Set::extract($result, '{n}.Tag.name'); $this->assertTrue(in_array('tag1', $result)); $this->assertTrue(in_array('tag2', $result)); $this->assertTrue(in_array('tag3', $result)); // adding same tags again. - $result = $this->Tag->add( - array('Tag' => array( - 'tags' => 'tag1, tag2, tag3'))); + $result = $this->Tag->add(array( + 'Tag' => array( + 'tags' => 'tag1, tag2, tag3' + ) + )); $this->assertTrue($result); } @@ -139,20 +147,26 @@ public function testEdit() { $data = array( 'Tag' => array( 'id' => 'tag-1', - 'name' => 'CAKEPHP')); + 'name' => 'CAKEPHP' + ) + ); $this->assertTrue($this->Tag->edit('tag-1', $data)); $data = array( 'Tag' => array( 'id' => 'tag-1', - 'name' => 'CAKEPHP111')); + 'name' => 'CAKEPHP111' + ) + ); $this->assertFalse($this->Tag->edit('tag-1', $data)); $data = array( 'Tag' => array( 'id' => 'tag-1', 'name' => 'CAKEPHP', - 'keyname' => '')); + 'keyname' => '' + ) + ); $this->assertEquals($this->Tag->edit('tag-1', $data), $data); $this->expectException('CakeException'); diff --git a/Test/Case/Model/TaggedTest.php b/Test/Case/Model/TaggedTest.php index b2cae29..b8c35f3 100644 --- a/Test/Case/Model/TaggedTest.php +++ b/Test/Case/Model/TaggedTest.php @@ -105,7 +105,9 @@ public function testTaggedFind() { 'language' => 'eng', 'times_tagged' => 1, 'created' => '2008-12-02 12:32:31', - 'modified' => '2008-12-02 12:32:31')); + 'modified' => '2008-12-02 12:32:31' + ) + ); $this->assertEquals($result, $expected); } @@ -117,7 +119,8 @@ public function testTaggedFind() { */ public function testFindCloud() { $result = $this->Tagged->find('cloud', array( - 'model' => 'Article')); + 'model' => 'Article' + )); $this->assertEquals(count($result), 3); $this->assertTrue(isset($result[0][0]['occurrence'])); @@ -127,7 +130,8 @@ public function testFindCloud() { $this->assertTrue(is_array($result) && !empty($result)); $result = $this->Tagged->find('cloud', array( - 'limit' => 1)); + 'limit' => 1 + )); $this->assertEquals(count($result), 1); } @@ -140,17 +144,20 @@ public function testFindTagged() { $this->Tagged->recursive = -1; $result = $this->Tagged->find('tagged', array( 'by' => 'cakephp', - 'model' => 'Article')); + 'model' => 'Article' + )); $this->assertEquals(count($result), 1); $this->assertEquals($result[0]['Article']['id'], 'article-1'); $result = $this->Tagged->find('tagged', array( - 'model' => 'Article')); + 'model' => 'Article' + )); $this->assertEquals(count($result), 2); // Test call to paginateCount by Controller::pagination() $result = $this->Tagged->paginateCount(array(), 1, array( 'model' => 'Article', - 'type' => 'tagged')); + 'type' => 'tagged' + )); $this->assertEquals($result, 2); } @@ -164,13 +171,15 @@ public function testFindTaggedWithConditions() { $result = $this->Tagged->find('tagged', array( 'by' => 'cakephp', 'model' => 'Article', - 'conditions' => array('Article.title LIKE' => 'Second %'))); + 'conditions' => array('Article.title LIKE' => 'Second %') + )); $this->assertEquals(count($result), 0); $result = $this->Tagged->find('tagged', array( 'by' => 'cakephp', 'model' => 'Article', - 'conditions' => array('Article.title LIKE' => 'First %'))); + 'conditions' => array('Article.title LIKE' => 'First %') + )); $this->assertEquals(count($result), 1); $this->assertEquals($result[0]['Article']['id'], 'article-1'); } @@ -186,16 +195,24 @@ public function testDeepAssociationsHasOne() { 'belongsTo' => array( 'Article' => array( 'className' => 'TaggedArticle', - 'foreignKey' => 'foreign_key')))); + 'foreignKey' => 'foreign_key' + ) + ) + )); $this->Tagged->Article->bindModel(array( 'hasOne' => array( - 'User' => array()))); + 'User' => array() + ) + )); $result = $this->Tagged->find('all', array( 'contain' => array( 'Article' => array( - 'User')))); + 'User' + ) + ) + )); $this->assertEquals($result[0]['Article']['User']['name'], 'CakePHP'); } @@ -211,16 +228,24 @@ public function testDeepAssociationsBelongsTo() { 'belongsTo' => array( 'Article' => array( 'className' => 'TaggedArticle', - 'foreignKey' => 'foreign_key')))); + 'foreignKey' => 'foreign_key' + ) + ) + )); $this->Tagged->Article->bindModel(array( 'belongsTo' => array( - 'User' => array()))); + 'User' => array() + ) + )); $result = $this->Tagged->find('all', array( 'contain' => array( 'Article' => array( - 'User')))); + 'User' + ) + ) + )); $this->assertEquals($result[0]['Article']['User']['name'], 'CakePHP'); } diff --git a/Test/Case/View/Helper/TagCloudHelperTest.php b/Test/Case/View/Helper/TagCloudHelperTest.php index 7786c16..bd8e73d 100644 --- a/Test/Case/View/Helper/TagCloudHelperTest.php +++ b/Test/Case/View/Helper/TagCloudHelperTest.php @@ -35,7 +35,9 @@ class TagCloudHelperTest extends CakeTestCase { 'keyname' => 'cakephp', 'weight' => 2, 'created' => '2008-06-02 18:18:11', - 'modified' => '2008-06-02 18:18:37')), + 'modified' => '2008-06-02 18:18:37' + ) + ), array( 'Tag' => array( 'id' => 2, @@ -44,7 +46,9 @@ class TagCloudHelperTest extends CakeTestCase { 'keyname' => 'cakedc', 'weight' => 2, 'created' => '2008-06-01 18:18:15', - 'modified' => '2008-06-01 18:18:15')), + 'modified' => '2008-06-01 18:18:15' + ) + ), ); /** @@ -55,8 +59,9 @@ class TagCloudHelperTest extends CakeTestCase { public $TagCloud; /** - * (non-PHPdoc) - * @see cake/tests/lib/CakeTestCase#setUp($method) + * Setup the test case + * + * @return void */ public function setUp() { parent::setUp(); @@ -76,7 +81,8 @@ public function testDisplay() { // Test tags shuffling $options = array( - 'shuffle' => true); + 'shuffle' => true + ); $expected = 'CakePHP CakeDC '; $i = 100; do { @@ -87,7 +93,8 @@ public function testDisplay() { // Test normal display $options = array( - 'shuffle' => false); + 'shuffle' => false + ); $result = $this->TagCloud->display($this->sampleTags, $options); $this->assertEquals($result, $expected); @@ -101,18 +108,23 @@ public function testDisplay() { 'named' => 'query' )); $result = $this->TagCloud->display($this->sampleTags, $options); - $expected = 'CakePHP '. + $expected = 'CakePHP ' . 'CakeDC '; $this->assertEquals($result, $expected); $tags = $this->sampleTags; $tags[1]['Tag']['weight'] = 1; $result = $this->TagCloud->display($tags, $options); - $expected = 'CakePHP '. + $expected = 'CakePHP ' . 'CakeDC '; $this->assertEquals($result, $expected); } +/** + * Test that display method defines correct size when using custom weight fields + * + * @return void + */ public function testDisplayShouldDefineCorrectSizeWhenCustomWeightField() { $tags = $this->sampleTags; $tags[0]['Tag']['custom_weight'] = 6; @@ -125,14 +137,37 @@ public function testDisplayShouldDefineCorrectSizeWhenCustomWeightField() { ); $result = $this->TagCloud->display($tags, $options); - $expected = 'CakePHP '. + $expected = 'CakePHP ' . 'CakeDC '; $this->assertEquals($result, $expected); } /** - * (non-PHPdoc) - * @see cake/tests/lib/CakeTestCase#tearDown($method) + * Test query string param type + * + * @return void + */ + public function testQueryStringUrlParams() { + $tags = $this->sampleTags; + $tags[0]['Tag']['custom_weight'] = 6; + $tags[1]['Tag']['custom_weight'] = 3; + + $options = array( + 'shuffle' => false, + 'extract' => '{n}.Tag.custom_weight', + 'paramType' => 'querystring' + ); + + $result = $this->TagCloud->display($tags, $options); + $expected = 'CakePHP ' . + 'CakeDC '; + $this->assertEquals($result, $expected); + } + +/** + * Tear down the test case + * + * @return void */ public function tearDown() { parent::tearDown(); diff --git a/Test/Fixture/ArticleFixture.php b/Test/Fixture/ArticleFixture.php index 2edfe6d..c7a094d 100644 --- a/Test/Fixture/ArticleFixture.php +++ b/Test/Fixture/ArticleFixture.php @@ -37,11 +37,13 @@ class ArticleFixture extends CakeTestFixture { array( 'id' => 'article-1', 'title' => 'First Article', - 'user_id' => 'user-1'), + 'user_id' => 'user-1' + ), array( 'id' => 'article-2', 'title' => 'Second Article', - 'user_id' => 'user-2'), + 'user_id' => 'user-2' + ), array( 'id' => 'article-3', 'title' => 'Third Article', diff --git a/Test/Fixture/TagFixture.php b/Test/Fixture/TagFixture.php index 75159b4..132154f 100644 --- a/Test/Fixture/TagFixture.php +++ b/Test/Fixture/TagFixture.php @@ -58,7 +58,8 @@ class TagFixture extends CakeTestFixture { 'occurrence' => 1, 'article_occurrence' => 1, 'created' => '2008-06-02 18:18:11', - 'modified' => '2008-06-02 18:18:37'), + 'modified' => '2008-06-02 18:18:37' + ), array( 'id' => 'tag-2', 'identifier' => null, @@ -67,7 +68,8 @@ class TagFixture extends CakeTestFixture { 'occurrence' => 1, 'article_occurrence' => 1, 'created' => '2008-06-01 18:18:15', - 'modified' => '2008-06-01 18:18:15'), + 'modified' => '2008-06-01 18:18:15' + ), array( 'id' => 'tag-3', 'identifier' => null, diff --git a/Test/Fixture/UserFixture.php b/Test/Fixture/UserFixture.php index 386c26b..f225186 100644 --- a/Test/Fixture/UserFixture.php +++ b/Test/Fixture/UserFixture.php @@ -23,9 +23,10 @@ class UserFixture extends CakeTestFixture { * @var array */ public $fields = array( - 'id' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36), + 'id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36), 'name' => array('type' => 'string', 'null' => false), - 'article_id' => array('type' => 'string', 'null' => false, 'default' => NULL, 'length' => 36)); + 'article_id' => array('type' => 'string', 'null' => false, 'default' => null, 'length' => 36) + ); /** * records property @@ -36,14 +37,18 @@ class UserFixture extends CakeTestFixture { array( 'id' => 'user-1', 'name' => 'CakePHP', - 'article_id' => 'article-1'), + 'article_id' => 'article-1' + ), array( 'id' => 'user-2', 'name' => 'Second User', - 'article_id' => 'article-2'), + 'article_id' => 'article-2' + ), array( 'id' => 'user-3', 'name' => 'Third User', - 'article_id' => 'article-3')); + 'article_id' => 'article-3' + ) + ); } diff --git a/View/Helper/TagCloudHelper.php b/View/Helper/TagCloudHelper.php index 5af8daa..c5caf04 100644 --- a/View/Helper/TagCloudHelper.php +++ b/View/Helper/TagCloudHelper.php @@ -29,7 +29,7 @@ class TagCloudHelper extends AppHelper { /** * Method to output a tag-cloud formatted based on the weight of the tags * - * @param array $tags + * @param array $tags Tag data array * @param array $options Display options. Valid keys are: * - shuffle: true to shuffle the tag list, false to display them in the same order than passed [default: true] * - extract: Set::extract() compatible format string. Path to extract weight values from the $tags array [default: {n}.Tag.weight] @@ -37,8 +37,9 @@ class TagCloudHelper extends AppHelper { * - after: string to be displayed after each generated link. "%size%" will be replaced with tag size calculated from the weight [default: empty] * - maxSize: size of the heaviest tag [default: 160] * - minSize: size of the lightest tag [default: 80] - * - url: an array containing the default url + * - url: an array containing the default URL * - named: the named parameter used to send the tag [default: by] + * - paramType: the type of URL parameters used (named or querystring) [default: named] * @return string */ public function display($tags = null, $options = array()) { @@ -56,7 +57,8 @@ public function display($tags = null, $options = array()) { 'url' => array( 'controller' => 'search' ), - 'named' => 'by' + 'named' => 'by', + 'paramType' => 'named' ); $options = array_merge($defaults, $options); @@ -93,20 +95,25 @@ public function display($tags = null, $options = array()) { /** * Generates the URL for a tag * - * @param array - * @param array + * @param array $tag Tag data array + * @param array $options Display options * @return array|string */ protected function _tagUrl($tag, $options) { - $options['url'][$options['named']] = $tag[$options['tagModel']]['keyname']; + if ($options['paramType'] === 'named') { + $options['url'][$options['named']] = $tag[$options['tagModel']]['keyname']; + } else { + $options['url']['?'][$options['named']] = $tag[$options['tagModel']]['keyname']; + } + return $options['url']; } /** * Replaces %size% in strings with the calculated "size" of the tag * - * @param string - * @param float + * @param string $string Before or after string + * @param float $size Calculated size * @return string */ protected function _replace($string, $size) {