1: <?php
2: /**
3: * Model behaviors base class.
4: *
5: * Adds methods and automagic functionality to Cake Models.
6: *
7: * PHP 5
8: *
9: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
10: * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
11: *
12: * Licensed under The MIT License
13: * Redistributions of files must retain the above copyright notice.
14: *
15: * @copyright Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
16: * @link http://cakephp.org CakePHP(tm) Project
17: * @package Cake.Model
18: * @since CakePHP(tm) v 1.2.0.0
19: * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
20: */
21:
22: /**
23: * Model behavior base class.
24: *
25: * Defines the Behavior interface, and contains common model interaction functionality. Behaviors
26: * allow you to simulate mixins, and create reusable blocks of application logic, that can be reused across
27: * several models. Behaviors also provide a way to hook into model callbacks and augment their behavior.
28: *
29: * ### Mixin methods
30: *
31: * Behaviors can provide mixin like features by declaring public methods. These methods should expect
32: * the model instance to be shifted onto the parameter list.
33: *
34: * {{{
35: * function doSomething($model, $arg1, $arg2) {
36: * //do something
37: * }
38: * }}}
39: *
40: * Would be called like `$this->Model->doSomething($arg1, $arg2);`.
41: *
42: * ### Mapped methods
43: *
44: * Behaviors can also define mapped methods. Mapped methods use pattern matching for method invocation. This
45: * allows you to create methods similar to Model::findAllByXXX methods on your behaviors. Mapped methods need to
46: * be declared in your behaviors `$mapMethods` array. The method signature for a mapped method is slightly different
47: * than a normal behavior mixin method.
48: *
49: * {{{
50: * public $mapMethods = array('/do(\w+)/' => 'doSomething');
51: *
52: * function doSomething($model, $method, $arg1, $arg2) {
53: * //do something
54: * }
55: * }}}
56: *
57: * The above will map every doXXX() method call to the behavior. As you can see, the model is
58: * still the first parameter, but the called method name will be the 2nd parameter. This allows
59: * you to munge the method name for additional information, much like Model::findAllByXX.
60: *
61: * @package Cake.Model
62: * @see Model::$actsAs
63: * @see BehaviorCollection::load()
64: */
65: class ModelBehavior extends Object {
66:
67: /**
68: * Contains configuration settings for use with individual model objects. This
69: * is used because if multiple models use this Behavior, each will use the same
70: * object instance. Individual model settings should be stored as an
71: * associative array, keyed off of the model name.
72: *
73: * @var array
74: * @see Model::$alias
75: */
76: public $settings = array();
77:
78: /**
79: * Allows the mapping of preg-compatible regular expressions to public or
80: * private methods in this class, where the array key is a /-delimited regular
81: * expression, and the value is a class method. Similar to the functionality of
82: * the findBy* / findAllBy* magic methods.
83: *
84: * @var array
85: */
86: public $mapMethods = array();
87:
88: /**
89: * Setup this behavior with the specified configuration settings.
90: *
91: * @param Model $model Model using this behavior
92: * @param array $config Configuration settings for $model
93: * @return void
94: */
95: public function setup($model, $config = array()) { }
96:
97: /**
98: * Clean up any initialization this behavior has done on a model. Called when a behavior is dynamically
99: * detached from a model using Model::detach().
100: *
101: * @param Model $model Model using this behavior
102: * @return void
103: * @see BehaviorCollection::detach()
104: */
105: public function cleanup($model) {
106: if (isset($this->settings[$model->alias])) {
107: unset($this->settings[$model->alias]);
108: }
109: }
110:
111: /**
112: * beforeFind can be used to cancel find operations, or modify the query that will be executed.
113: * By returning null/false you can abort a find. By returning an array you can modify/replace the query
114: * that is going to be run.
115: *
116: * @param Model $model Model using this behavior
117: * @param array $query Data used to execute this query, i.e. conditions, order, etc.
118: * @return boolean|array False or null will abort the operation. You can return an array to replace the
119: * $query that will be eventually run.
120: */
121: public function beforeFind($model, $query) {
122: return true;
123: }
124:
125: /**
126: * After find callback. Can be used to modify any results returned by find.
127: *
128: * @param Model $model Model using this behavior
129: * @param mixed $results The results of the find operation
130: * @param boolean $primary Whether this model is being queried directly (vs. being queried as an association)
131: * @return mixed An array value will replace the value of $results - any other value will be ignored.
132: */
133: public function afterFind($model, $results, $primary) { }
134:
135: /**
136: * beforeValidate is called before a model is validated, you can use this callback to
137: * add behavior validation rules into a models validate array. Returning false
138: * will allow you to make the validation fail.
139: *
140: * @param Model $model Model using this behavior
141: * @return mixed False or null will abort the operation. Any other result will continue.
142: */
143: public function beforeValidate($model) {
144: return true;
145: }
146:
147: /**
148: * beforeSave is called before a model is saved. Returning false from a beforeSave callback
149: * will abort the save operation.
150: *
151: * @param Model $model Model using this behavior
152: * @return mixed False if the operation should abort. Any other result will continue.
153: */
154: public function beforeSave($model) {
155: return true;
156: }
157:
158: /**
159: * afterSave is called after a model is saved.
160: *
161: * @param Model $model Model using this behavior
162: * @param boolean $created True if this save created a new record
163: * @return boolean
164: */
165: public function afterSave($model, $created) {
166: return true;
167: }
168:
169: /**
170: * Before delete is called before any delete occurs on the attached model, but after the model's
171: * beforeDelete is called. Returning false from a beforeDelete will abort the delete.
172: *
173: * @param Model $model Model using this behavior
174: * @param boolean $cascade If true records that depend on this record will also be deleted
175: * @return mixed False if the operation should abort. Any other result will continue.
176: */
177: public function beforeDelete($model, $cascade = true) {
178: return true;
179: }
180:
181: /**
182: * After delete is called after any delete occurs on the attached model.
183: *
184: * @param Model $model Model using this behavior
185: * @return void
186: */
187: public function afterDelete($model) { }
188:
189: /**
190: * DataSource error callback
191: *
192: * @param Model $model Model using this behavior
193: * @param string $error Error generated in DataSource
194: * @return void
195: */
196: public function onError($model, $error) { }
197:
198: /**
199: * If $model's whitelist property is non-empty, $field will be added to it.
200: * Note: this method should *only* be used in beforeValidate or beforeSave to ensure
201: * that it only modifies the whitelist for the current save operation. Also make sure
202: * you explicitly set the value of the field which you are allowing.
203: *
204: * @param Model $model Model using this behavior
205: * @param string $field Field to be added to $model's whitelist
206: * @return void
207: */
208: protected function _addToWhitelist($model, $field) {
209: if (is_array($field)) {
210: foreach ($field as $f) {
211: $this->_addToWhitelist($model, $f);
212: }
213: return;
214: }
215: if (!empty($model->whitelist) && !in_array($field, $model->whitelist)) {
216: $model->whitelist[] = $field;
217: }
218: }
219: }
220:
221: