euromark 11 years ago
parent
commit
c3a074ee84

+ 104 - 0
docs/Behavior/Jsonable.md

@@ -0,0 +1,104 @@
+# Jsonable Behavior
+
+A CakePHP behavior to automatically store nested data as JSON string and return the array on read again.
+- Data can be of type array, params or list - or kept in JSON format
+- Additional sanitize functionality with "clean", "sort" and "unique
+
+## Configs
+- 'fields' => array(), // Fields to convert
+- 'input' => 'array', // json, array, param, list (param/list only works with specific fields)
+- 'output' => 'array', // json, array, param, list (param/list only works with specific fields)
+- 'separator' => '|', // only for param or list
+- 'keyValueSeparator' => ':', // only for param
+- 'leftBound' => '{', // only for list
+- 'rightBound' => '}', // only for list
+- 'clean' => true, // only for param or list (autoclean values on insert)
+- 'sort' => false, // only for list
+- 'unique' => true, // only for list (autoclean values on insert),
+- 'map' => array(), // map on a different DB field
+- 'encodeParams' // params for json_encode
+- 'decodeParams' // params for json_decode
+
+## Usage
+Attach it to your models in `initialze()` like so:
+```php
+$this->addBehavior('Tools.Jsonable', $config);
+```
+In my first scenario where I used it, I had a geocoder behavior attached to the model which returned an array.
+I wanted to save all the returned values, though, for debugging purposes in a field "debug".
+By using the following snippet I was able to do exactly that with a single line of config.
+The rest is CakePHP automagic :)
+
+```php
+$this->addBehavior('Tools.Jsonable',
+	array('fields' => array('debug'), 'map' => array('geocoder_result'));
+```
+I could access the array in the view as any other array since the behavior re-translates it back into an array on find().
+
+Note: The mapping option is useful if you want to rename certain fields.
+In my case the geocoder puts its data into the field "geocoder_result".
+I might need to access this array later on in the model. So I "jsonable" it in the "debug" field for DB input
+and leave the source field untouched for any later usage.
+The same goes for the output: It will map the JSON content of "debug" back to the field "geocoder_result" as array, so
+I have both types available then.
+
+## Examples
+
+### Params
+What if needed something more frontend suitable.
+I want to be able to use a textarea field where I can put all kinds of params
+which will then also be available as array afterwards (as long as you are not in edit mode, of course).
+
+We can switch to param style here globally for the entity:
+
+```php
+$this->addBehavior('Tools.Jsonable',
+	array('fields' => 'details', 'input' => 'param', 'output' => 'array'));
+```
+
+Only for the add/edit action we need to also make "output" "param" at runtime:
+```php
+$this->Table->behaviors()->Jsonable->options(
+	array('fields' => 'details', 'input' => 'param', 'output' => 'param'));
+```
+
+The form contains a "details" textarea field. We can insert:
+```php
+param1:value1|param2:value2
+```
+
+In our views we get our data now as array:
+```php
+debug($entity->get('details'));
+// Prints:
+// array('param1' => 'value1', 'param2' => 'value2')));
+```
+
+
+### Enums
+we can also simulate an ENUM by using
+```php
+$this->addBehavior('Tools.Jsonable',
+	array('fields' => 'tags', 'sort' => true, 'input' => 'list', 'output' => 'array'));
+```
+Dont' forget to use `'output' => 'list'` for add/edit actions.
+
+In our textarea we can now type:
+```
+dog, cat, cat, fish
+```
+
+In our views we would result in:
+```php
+debug($entity->get('tags'));
+// Prints:
+// array('cat', 'dog', 'fish');
+```
+
+Note: The cleanup automation you can additionally turn on/off. There are more things to explore. Dig into the source code for that.
+
+Yes – you could make a new table/relation for this in the first place.
+But sometimes it’s just quicker to create such an enumeration field.
+
+Bear in mind: It then cannot be sorted/searched by those values, though.
+For a more static solution take a look at my [Static Enums](http://www.dereuromark.de/2010/06/24/static-enums-or-semihardcoded-attributes/).

docs/Passwordable.md → docs/Behavior/Passwordable.md


+ 76 - 0
docs/Behavior/Slugged.md

@@ -0,0 +1,76 @@
+# Slugged Behavior
+
+A CakePHP behavior to automatically create and store slugs.
+- Input data can consist of one or many fields
+- Slugs can be unique and persistent, ideal for lookups by slug
+- Multibyte aware, umlauts etc will be properly replaced
+
+## Configs
+- label:
+	set to the name of a field to use for the slug, an array of fields to use as slugs or leave as null to rely
+	on the format returned by find('list') to determine the string to use for slugs
+- field: The slug field name
+- overwriteField: The boolean field to trigger overwriting if "overwrite" is false
+- mode: has the following values
+	ascii - retuns an ascii slug generated using the core Inflector::slug() function
+	display - a dummy mode which returns a slug legal for display - removes illegal (not unprintable) characters
+	url - returns a slug appropriate to put in a URL
+	class - a dummy mode which returns a slug appropriate to put in a html class (there are no restrictions)
+	id - retuns a slug appropriate to use in a html id
+- separator: The separator to use
+- length:
+ Set to 0 for no length. Will be auto-detected if possible via schema.
+- overwrite: has 2 values
+	false - once the slug has been saved, do not change it (use if you are doing lookups based on slugs)
+	true - if the label field values change, regenerate the slug (use if you are the slug is just window-dressing)
+- unique: has 2 values
+	false - will not enforce a unique slug, whatever the label is is direclty slugged without checking for duplicates
+	true - use if you are doing lookups based on slugs (see overwrite)
+- case: has the following values
+	null - don't change the case of the slug
+	low - force lower case. E.g. "this-is-the-slug"
+	up - force upper case E.g. "THIS-IS-THE-SLUG"
+	title - force title case. E.g. "This-Is-The-Slug"
+	camel - force CamelCase. E.g. "ThisIsTheSlug"
+- replace: custom replacements as array
+- on: beforeSave or beforeValidate
+- scope: certain conditions to use as scope
+- tidy: If cleanup should be run on slugging
+
+## Usage
+Attach it to your models in `initialze()` like so:
+```php
+$this->addBehavior('Tools.Slugged', $config);
+```
+
+## Examples
+
+### Persistent slugs
+We want to store categories and we need a slug for nice SEO URLs like `/category/[slugname]/`.
+
+```php
+$this->addBehavior('Tools.Jsonable',
+	array('label' => 'name', 'unique' => true, 'mode' => 'ascii'));
+```
+
+Upon creating and storing a new record it will look for content in "name" and create a slug in "slug" field.
+
+With the above config on "edit" the slug will not be modified if you alter the name. That is important to know.
+You cannot just change the slug, as the URL is most likely indexed by search engines now.
+
+If you want to do that, you would also need a .htaccess rewrite rule to 301 redirect from the old to the new slug.
+So if that is the case, you could add an "overwrite field" to your form.
+```html
+echo $this->Form->field('overwrite_slug', ['type' => 'checkbox']);
+```
+Once that boolean checkbox is clicked it will then perform the slug update on save.
+
+### Non persistent slugs
+If we just append the slug to the URL, such as `/category/123-[slugname]`, then we don't need to persist the slug.
+```php
+$this->addBehavior('Tools.Jsonable',
+	array('label' => 'name', 'overwrite' => true, 'mode' => 'ascii'));
+```
+Note that we don't need "unique" either then.
+
+Each save now re-triggers the slug generation.

+ 3 - 1
docs/README.md

@@ -12,7 +12,9 @@ This cake3 branch only works for **CakePHP3.x** - please use the master branch f
 * [Upgrade guide from 2.x to 3.x](Upgrade.md)
 
 ## Detailed Documentation - Quicklinks
-* [Passwordable](Passwordable.md)
+* [Behavior/Passwordable](Behavior/Passwordable.md)
+* [Behavior/Slugged](Behavior/Slugged.md)
+* [Behavior/Jsonable](Behavior/Jsonable.md)
 * ...
 
 ## Basic enhancements of the core

+ 11 - 2
docs/Upgrade.md

@@ -21,5 +21,14 @@
 
 ## Behavior
 - `run`/`before` config options for callback decisions have been unified to `on` and complete callback/event name, e.g. `'on' => 'beforeValidate'`.
-- model names are now table names, and plural.
-- Slugged option "slugField" is now "field", "multiSlug" has been removed for now as well as currencies.
+
+### SluggedBehavior
+- Model names are now table names, and plural.
+- Slugged option "slugField" is now "field", "multiSlug" has been removed for now as well as currencies.
+
+### PasswordableBehavior
+- You can/should now specify a "validator", defaluts to "default".
+
+### JsonableBehavior
+- No auto-detect anymore, fields need to be specified manually
+

+ 18 - 15
src/Model/Behavior/SluggedBehavior.php

@@ -26,29 +26,36 @@ class SluggedBehavior extends Behavior {
 	/**
 	 * Default config
 	 *
-	 * - length
-	 *  Set to 0 for no length. Will be auto-detected if possible via schema.
-	 * - label
+	 * - label:
 	 * 	set to the name of a field to use for the slug, an array of fields to use as slugs or leave as null to rely
 	 * 	on the format returned by find('list') to determine the string to use for slugs
-	 * - overwrite has 2 values
-	 * 	false - once the slug has been saved, do not change it (use if you are doing lookups based on slugs)
-	 * 	true - if the label field values change, regenerate the slug (use if you are the slug is just window-dressing)
-	 * - unique has 2 values
-	 * 	false - will not enforce a unique slug, whatever the label is is direclty slugged without checking for duplicates
-	 * 	true - use if you are doing lookups based on slugs (see overwrite)
-	 * - mode has the following values
+	 * - field: The slug field name
+	 * - overwriteField: The boolean field to trigger overwriting if "overwrite" is false
+	 * - mode: has the following values
 	 * 	ascii - retuns an ascii slug generated using the core Inflector::slug() function
 	 * 	display - a dummy mode which returns a slug legal for display - removes illegal (not unprintable) characters
 	 * 	url - returns a slug appropriate to put in a URL
 	 * 	class - a dummy mode which returns a slug appropriate to put in a html class (there are no restrictions)
 	 * 	id - retuns a slug appropriate to use in a html id
-	 * - case has the following values
+	 * - separator: The separator to use
+	 * - length:
+	 *  Set to 0 for no length. Will be auto-detected if possible via schema.
+	 * - overwrite: has 2 values
+	 * 	false - once the slug has been saved, do not change it (use if you are doing lookups based on slugs)
+	 * 	true - if the label field values change, regenerate the slug (use if you are the slug is just window-dressing)
+	 * - unique: has 2 values
+	 * 	false - will not enforce a unique slug, whatever the label is is direclty slugged without checking for duplicates
+	 * 	true - use if you are doing lookups based on slugs (see overwrite)
+	 * - case: has the following values
 	 * 	null - don't change the case of the slug
 	 * 	low - force lower case. E.g. "this-is-the-slug"
 	 * 	up - force upper case E.g. "THIS-IS-THE-SLUG"
 	 * 	title - force title case. E.g. "This-Is-The-Slug"
 	 * 	camel - force CamelCase. E.g. "ThisIsTheSlug"
+	 * - replace: custom replacements as array
+	 * - on: beforeSave or beforeValidate
+	 * - scope: certain conditions to use as scope
+	 * - tidy: If cleanup should be run on slugging
 	 *
 	 * @var array
 	 */
@@ -58,7 +65,6 @@ class SluggedBehavior extends Behavior {
 		'overwriteField' => 'overwrite_slug',
 		'mode' => 'url',
 		'separator' => '-',
-		'defaultSuffix' => null,
 		'length' => null,
 		'overwrite' => false,
 		'unique' => false,
@@ -70,8 +76,6 @@ class SluggedBehavior extends Behavior {
 			'#' => 'hash',
 		),
 		'on' => 'beforeValidate',
-		'language' => null,
-		'encoding' => null,
 		'scope' => array(),
 		'tidy' => true,
 		'implementedFinders' => ['slugged' => 'findSlugged'],
@@ -471,7 +475,6 @@ class SluggedBehavior extends Behavior {
 	 * @param mixed $pattern
 	 * @param mixed $replace
 	 * @param mixed $string
-	 * @param string $encoding
 	 * @return void
 	 */
 	protected function _pregReplace($pattern, $replace, $string) {