CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.3 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.3
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Acl
        • Auth
    • Core
    • Error
    • Event
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
      • Validator
    • Network
      • Email
      • Http
    • Routing
      • Filter
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • AclBehavior
  • ContainableBehavior
  • TranslateBehavior
  • TreeBehavior
  1: <?php
  2: /**
  3:  * Behavior for binding management.
  4:  *
  5:  * Behavior to simplify manipulating a model's bindings when doing a find operation
  6:  *
  7:  * PHP 5
  8:  *
  9:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 10:  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 11:  *
 12:  * Licensed under The MIT License
 13:  * For full copyright and license information, please see the LICENSE.txt
 14:  * Redistributions of files must retain the above copyright notice.
 15:  *
 16:  * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 17:  * @link          http://cakephp.org CakePHP(tm) Project
 18:  * @package       Cake.Model.Behavior
 19:  * @since         CakePHP(tm) v 1.2.0.5669
 20:  * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 21:  */
 22: 
 23: App::uses('ModelBehavior', 'Model');
 24: 
 25: /**
 26:  * Behavior to allow for dynamic and atomic manipulation of a Model's associations
 27:  * used for a find call. Most useful for limiting the amount of associations and
 28:  * data returned.
 29:  *
 30:  * @package       Cake.Model.Behavior
 31:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html
 32:  */
 33: class ContainableBehavior extends ModelBehavior {
 34: 
 35: /**
 36:  * Types of relationships available for models
 37:  *
 38:  * @var array
 39:  */
 40:     public $types = array('belongsTo', 'hasOne', 'hasMany', 'hasAndBelongsToMany');
 41: 
 42: /**
 43:  * Runtime configuration for this behavior
 44:  *
 45:  * @var array
 46:  */
 47:     public $runtime = array();
 48: 
 49: /**
 50:  * Initiate behavior for the model using specified settings.
 51:  *
 52:  * Available settings:
 53:  *
 54:  * - recursive: (boolean, optional) set to true to allow containable to automatically
 55:  *   determine the recursiveness level needed to fetch specified models,
 56:  *   and set the model recursiveness to this level. setting it to false
 57:  *   disables this feature. DEFAULTS TO: true
 58:  * - notices: (boolean, optional) issues E_NOTICES for bindings referenced in a
 59:  *   containable call that are not valid. DEFAULTS TO: true
 60:  * - autoFields: (boolean, optional) auto-add needed fields to fetch requested
 61:  *   bindings. DEFAULTS TO: true
 62:  *
 63:  * @param Model $Model Model using the behavior
 64:  * @param array $settings Settings to override for model.
 65:  * @return void
 66:  */
 67:     public function setup(Model $Model, $settings = array()) {
 68:         if (!isset($this->settings[$Model->alias])) {
 69:             $this->settings[$Model->alias] = array('recursive' => true, 'notices' => true, 'autoFields' => true);
 70:         }
 71:         $this->settings[$Model->alias] = array_merge($this->settings[$Model->alias], $settings);
 72:     }
 73: 
 74: /**
 75:  * Runs before a find() operation. Used to allow 'contain' setting
 76:  * as part of the find call, like this:
 77:  *
 78:  * `Model->find('all', array('contain' => array('Model1', 'Model2')));`
 79:  *
 80:  * {{{
 81:  * Model->find('all', array('contain' => array(
 82:  *  'Model1' => array('Model11', 'Model12'),
 83:  *  'Model2',
 84:  *  'Model3' => array(
 85:  *      'Model31' => 'Model311',
 86:  *      'Model32',
 87:  *      'Model33' => array('Model331', 'Model332')
 88:  * )));
 89:  * }}}
 90:  *
 91:  * @param Model $Model Model using the behavior
 92:  * @param array $query Query parameters as set by cake
 93:  * @return array
 94:  */
 95:     public function beforeFind(Model $Model, $query) {
 96:         $reset = (isset($query['reset']) ? $query['reset'] : true);
 97:         $noContain = false;
 98:         $contain = array();
 99: 
100:         if (isset($this->runtime[$Model->alias]['contain'])) {
101:             $noContain = empty($this->runtime[$Model->alias]['contain']);
102:             $contain = $this->runtime[$Model->alias]['contain'];
103:             unset($this->runtime[$Model->alias]['contain']);
104:         }
105: 
106:         if (isset($query['contain'])) {
107:             $noContain = $noContain || empty($query['contain']);
108:             if ($query['contain'] !== false) {
109:                 $contain = array_merge($contain, (array)$query['contain']);
110:             }
111:         }
112:         $noContain = $noContain && empty($contain);
113: 
114:         if ($noContain || empty($contain)) {
115:             if ($noContain) {
116:                 $query['recursive'] = -1;
117:             }
118:             return $query;
119:         }
120:         if ((isset($contain[0]) && is_bool($contain[0])) || is_bool(end($contain))) {
121:             $reset = is_bool(end($contain))
122:                 ? array_pop($contain)
123:                 : array_shift($contain);
124:         }
125:         $containments = $this->containments($Model, $contain);
126:         $map = $this->containmentsMap($containments);
127: 
128:         $mandatory = array();
129:         foreach ($containments['models'] as $model) {
130:             $instance = $model['instance'];
131:             $needed = $this->fieldDependencies($instance, $map, false);
132:             if (!empty($needed)) {
133:                 $mandatory = array_merge($mandatory, $needed);
134:             }
135:             if ($contain) {
136:                 $backupBindings = array();
137:                 foreach ($this->types as $relation) {
138:                     if (!empty($instance->__backAssociation[$relation])) {
139:                         $backupBindings[$relation] = $instance->__backAssociation[$relation];
140:                     } else {
141:                         $backupBindings[$relation] = $instance->{$relation};
142:                     }
143:                 }
144:                 foreach ($this->types as $type) {
145:                     $unbind = array();
146:                     foreach ($instance->{$type} as $assoc => $options) {
147:                         if (!isset($model['keep'][$assoc])) {
148:                             $unbind[] = $assoc;
149:                         }
150:                     }
151:                     if (!empty($unbind)) {
152:                         if (!$reset && empty($instance->__backOriginalAssociation)) {
153:                             $instance->__backOriginalAssociation = $backupBindings;
154:                         }
155:                         $instance->unbindModel(array($type => $unbind), $reset);
156:                     }
157:                     foreach ($instance->{$type} as $assoc => $options) {
158:                         if (isset($model['keep'][$assoc]) && !empty($model['keep'][$assoc])) {
159:                             if (isset($model['keep'][$assoc]['fields'])) {
160:                                 $model['keep'][$assoc]['fields'] = $this->fieldDependencies($containments['models'][$assoc]['instance'], $map, $model['keep'][$assoc]['fields']);
161:                             }
162:                             if (!$reset && empty($instance->__backOriginalAssociation)) {
163:                                 $instance->__backOriginalAssociation = $backupBindings;
164:                             } elseif ($reset) {
165:                                 $instance->__backAssociation[$type] = $backupBindings[$type];
166:                             }
167:                             $instance->{$type}[$assoc] = array_merge($instance->{$type}[$assoc], $model['keep'][$assoc]);
168:                         }
169:                         if (!$reset) {
170:                             $instance->__backInnerAssociation[] = $assoc;
171:                         }
172:                     }
173:                 }
174:             }
175:         }
176: 
177:         if ($this->settings[$Model->alias]['recursive']) {
178:             $query['recursive'] = (isset($query['recursive'])) ? max($query['recursive'], $containments['depth']) : $containments['depth'];
179:         }
180: 
181:         $autoFields = ($this->settings[$Model->alias]['autoFields']
182:                     && !in_array($Model->findQueryType, array('list', 'count'))
183:                     && !empty($query['fields']));
184: 
185:         if (!$autoFields) {
186:             return $query;
187:         }
188: 
189:         $query['fields'] = (array)$query['fields'];
190:         foreach (array('hasOne', 'belongsTo') as $type) {
191:             if (!empty($Model->{$type})) {
192:                 foreach ($Model->{$type} as $assoc => $data) {
193:                     if ($Model->useDbConfig == $Model->{$assoc}->useDbConfig && !empty($data['fields'])) {
194:                         foreach ((array)$data['fields'] as $field) {
195:                             $query['fields'][] = (strpos($field, '.') === false ? $assoc . '.' : '') . $field;
196:                         }
197:                     }
198:                 }
199:             }
200:         }
201: 
202:         if (!empty($mandatory[$Model->alias])) {
203:             foreach ($mandatory[$Model->alias] as $field) {
204:                 if ($field === '--primaryKey--') {
205:                     $field = $Model->primaryKey;
206:                 } elseif (preg_match('/^.+\.\-\-[^-]+\-\-$/', $field)) {
207:                     list($modelName, $field) = explode('.', $field);
208:                     if ($Model->useDbConfig == $Model->{$modelName}->useDbConfig) {
209:                         $field = $modelName . '.' . (
210:                             ($field === '--primaryKey--') ? $Model->$modelName->primaryKey : $field
211:                         );
212:                     } else {
213:                         $field = null;
214:                     }
215:                 }
216:                 if ($field !== null) {
217:                     $query['fields'][] = $field;
218:                 }
219:             }
220:         }
221:         $query['fields'] = array_unique($query['fields']);
222:         return $query;
223:     }
224: 
225: /**
226:  * Unbinds all relations from a model except the specified ones. Calling this function without
227:  * parameters unbinds all related models.
228:  *
229:  * @param Model $Model Model on which binding restriction is being applied
230:  * @return void
231:  * @link http://book.cakephp.org/2.0/en/core-libraries/behaviors/containable.html#using-containable
232:  */
233:     public function contain(Model $Model) {
234:         $args = func_get_args();
235:         $contain = call_user_func_array('am', array_slice($args, 1));
236:         $this->runtime[$Model->alias]['contain'] = $contain;
237:     }
238: 
239: /**
240:  * Permanently restore the original binding settings of given model, useful
241:  * for restoring the bindings after using 'reset' => false as part of the
242:  * contain call.
243:  *
244:  * @param Model $Model Model on which to reset bindings
245:  * @return void
246:  */
247:     public function resetBindings(Model $Model) {
248:         if (!empty($Model->__backOriginalAssociation)) {
249:             $Model->__backAssociation = $Model->__backOriginalAssociation;
250:             unset($Model->__backOriginalAssociation);
251:         }
252:         $Model->resetAssociations();
253:         if (!empty($Model->__backInnerAssociation)) {
254:             $assocs = $Model->__backInnerAssociation;
255:             $Model->__backInnerAssociation = array();
256:             foreach ($assocs as $currentModel) {
257:                 $this->resetBindings($Model->$currentModel);
258:             }
259:         }
260:     }
261: 
262: /**
263:  * Process containments for model.
264:  *
265:  * @param Model $Model Model on which binding restriction is being applied
266:  * @param array $contain Parameters to use for restricting this model
267:  * @param array $containments Current set of containments
268:  * @param boolean $throwErrors Whether non-existent bindings show throw errors
269:  * @return array Containments
270:  */
271:     public function containments(Model $Model, $contain, $containments = array(), $throwErrors = null) {
272:         $options = array('className', 'joinTable', 'with', 'foreignKey', 'associationForeignKey', 'conditions', 'fields', 'order', 'limit', 'offset', 'unique', 'finderQuery');
273:         $keep = array();
274:         if ($throwErrors === null) {
275:             $throwErrors = (empty($this->settings[$Model->alias]) ? true : $this->settings[$Model->alias]['notices']);
276:         }
277:         foreach ((array)$contain as $name => $children) {
278:             if (is_numeric($name)) {
279:                 $name = $children;
280:                 $children = array();
281:             }
282:             if (preg_match('/(?<!\.)\(/', $name)) {
283:                 $name = str_replace('(', '.(', $name);
284:             }
285:             if (strpos($name, '.') !== false) {
286:                 $chain = explode('.', $name);
287:                 $name = array_shift($chain);
288:                 $children = array(implode('.', $chain) => $children);
289:             }
290: 
291:             $children = (array)$children;
292:             foreach ($children as $key => $val) {
293:                 if (is_string($key) && is_string($val) && !in_array($key, $options, true)) {
294:                     $children[$key] = (array)$val;
295:                 }
296:             }
297: 
298:             $keys = array_keys($children);
299:             if ($keys && isset($children[0])) {
300:                 $keys = array_merge(array_values($children), $keys);
301:             }
302: 
303:             foreach ($keys as $i => $key) {
304:                 if (is_array($key)) {
305:                     continue;
306:                 }
307:                 $optionKey = in_array($key, $options, true);
308:                 if (!$optionKey && is_string($key) && preg_match('/^[a-z(]/', $key) && (!isset($Model->{$key}) || !is_object($Model->{$key}))) {
309:                     $option = 'fields';
310:                     $val = array($key);
311:                     if ($key{0} === '(') {
312:                         $val = preg_split('/\s*,\s*/', substr($key, 1, -1));
313:                     } elseif (preg_match('/ASC|DESC$/', $key)) {
314:                         $option = 'order';
315:                         $val = $Model->{$name}->alias . '.' . $key;
316:                     } elseif (preg_match('/[ =!]/', $key)) {
317:                         $option = 'conditions';
318:                         $val = $Model->{$name}->alias . '.' . $key;
319:                     }
320:                     $children[$option] = is_array($val) ? $val : array($val);
321:                     $newChildren = null;
322:                     if (!empty($name) && !empty($children[$key])) {
323:                         $newChildren = $children[$key];
324:                     }
325:                     unset($children[$key], $children[$i]);
326:                     $key = $option;
327:                     $optionKey = true;
328:                     if (!empty($newChildren)) {
329:                         $children = Hash::merge($children, $newChildren);
330:                     }
331:                 }
332:                 if ($optionKey && isset($children[$key])) {
333:                     if (!empty($keep[$name][$key]) && is_array($keep[$name][$key])) {
334:                         $keep[$name][$key] = array_merge((isset($keep[$name][$key]) ? $keep[$name][$key] : array()), (array)$children[$key]);
335:                     } else {
336:                         $keep[$name][$key] = $children[$key];
337:                     }
338:                     unset($children[$key]);
339:                 }
340:             }
341: 
342:             if (!isset($Model->{$name}) || !is_object($Model->{$name})) {
343:                 if ($throwErrors) {
344:                     trigger_error(__d('cake_dev', 'Model "%s" is not associated with model "%s"', $Model->alias, $name), E_USER_WARNING);
345:                 }
346:                 continue;
347:             }
348: 
349:             $containments = $this->containments($Model->{$name}, $children, $containments);
350:             $depths[] = $containments['depth'] + 1;
351:             if (!isset($keep[$name])) {
352:                 $keep[$name] = array();
353:             }
354:         }
355: 
356:         if (!isset($containments['models'][$Model->alias])) {
357:             $containments['models'][$Model->alias] = array('keep' => array(), 'instance' => &$Model);
358:         }
359: 
360:         $containments['models'][$Model->alias]['keep'] = array_merge($containments['models'][$Model->alias]['keep'], $keep);
361:         $containments['depth'] = empty($depths) ? 0 : max($depths);
362:         return $containments;
363:     }
364: 
365: /**
366:  * Calculate needed fields to fetch the required bindings for the given model.
367:  *
368:  * @param Model $Model Model
369:  * @param array $map Map of relations for given model
370:  * @param array|boolean $fields If array, fields to initially load, if false use $Model as primary model
371:  * @return array Fields
372:  */
373:     public function fieldDependencies(Model $Model, $map, $fields = array()) {
374:         if ($fields === false) {
375:             foreach ($map as $parent => $children) {
376:                 foreach ($children as $type => $bindings) {
377:                     foreach ($bindings as $dependency) {
378:                         if ($type === 'hasAndBelongsToMany') {
379:                             $fields[$parent][] = '--primaryKey--';
380:                         } elseif ($type === 'belongsTo') {
381:                             $fields[$parent][] = $dependency . '.--primaryKey--';
382:                         }
383:                     }
384:                 }
385:             }
386:             return $fields;
387:         }
388:         if (empty($map[$Model->alias])) {
389:             return $fields;
390:         }
391:         foreach ($map[$Model->alias] as $type => $bindings) {
392:             foreach ($bindings as $dependency) {
393:                 $innerFields = array();
394:                 switch ($type) {
395:                     case 'belongsTo':
396:                         $fields[] = $Model->{$type}[$dependency]['foreignKey'];
397:                         break;
398:                     case 'hasOne':
399:                     case 'hasMany':
400:                         $innerFields[] = $Model->$dependency->primaryKey;
401:                         $fields[] = $Model->primaryKey;
402:                         break;
403:                 }
404:                 if (!empty($innerFields) && !empty($Model->{$type}[$dependency]['fields'])) {
405:                     $Model->{$type}[$dependency]['fields'] = array_unique(array_merge($Model->{$type}[$dependency]['fields'], $innerFields));
406:                 }
407:             }
408:         }
409:         return array_unique($fields);
410:     }
411: 
412: /**
413:  * Build the map of containments
414:  *
415:  * @param array $containments Containments
416:  * @return array Built containments
417:  */
418:     public function containmentsMap($containments) {
419:         $map = array();
420:         foreach ($containments['models'] as $name => $model) {
421:             $instance = $model['instance'];
422:             foreach ($this->types as $type) {
423:                 foreach ($instance->{$type} as $assoc => $options) {
424:                     if (isset($model['keep'][$assoc])) {
425:                         $map[$name][$type] = isset($map[$name][$type]) ? array_merge($map[$name][$type], (array)$assoc) : (array)$assoc;
426:                     }
427:                 }
428:             }
429:         }
430:         return $map;
431:     }
432: 
433: }
434: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs