mirror of
https://github.com/kamilwylegala/cakephp2-php8.git
synced 2025-01-18 18:46:17 +00:00
Merge branch '2.0-pagination' into 2.0
This commit is contained in:
commit
a9a1994a7e
18 changed files with 1623 additions and 737 deletions
|
@ -18,21 +18,71 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* PaginatorComponent
|
||||
* This component is used to handle automatic model data pagination. The primary way to use this
|
||||
* component is to call the paginate() method. There is a convience wrapper on Controller as well.
|
||||
*
|
||||
* This component is used to handle automatic model data pagination
|
||||
* ### Configuring pagination
|
||||
*
|
||||
* You configure pagination using the PaginatorComponent::$settings. This allows you to configure
|
||||
* the default pagination behavior in general or for a specific model. General settings are used when there
|
||||
* are no specific model configuration, or the model you are paginating does not have specific settings.
|
||||
*
|
||||
* {{{
|
||||
* $this->Paginator->settings = array(
|
||||
* 'limit' => 20,
|
||||
* 'maxLimit' => 100
|
||||
* );
|
||||
* }}}
|
||||
*
|
||||
* The above settings will be used to paginate any model. You can configure model specific settings by
|
||||
* keying the settings with the model name.
|
||||
*
|
||||
* {{{
|
||||
* $this->Paginator->settings = array(
|
||||
* 'Post' => array(
|
||||
* 'limit' => 20,
|
||||
* 'maxLimit' => 100
|
||||
* ),
|
||||
* 'Comment' => array( ... )
|
||||
* );
|
||||
* }}}
|
||||
*
|
||||
* This would allow you to have different pagination settings for `Comment` and `Post` models.
|
||||
*
|
||||
* @package cake.libs.controller.components
|
||||
*
|
||||
*/
|
||||
class PaginatorComponent extends Component {
|
||||
|
||||
/**
|
||||
* Pagination settings
|
||||
* Pagination settings. These settings control pagination at a general level.
|
||||
* You can also define sub arrays for pagination settings for specific models.
|
||||
*
|
||||
* - `maxLimit` The maximum limit users can choose to view. Defaults to 100
|
||||
* - `limit` The initial number of items per page. Defaults to 20.
|
||||
* - `page` The starting page, defaults to 1.
|
||||
* - `paramType` What type of parameters you want pagination to use?
|
||||
* - `named` Use named parameters / routed parameters.
|
||||
* - `querystring` Use query string parameters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $settings = array();
|
||||
public $settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'named'
|
||||
);
|
||||
|
||||
/**
|
||||
* A list of parameters users are allowed to set using request parameters. Modifying
|
||||
* this list will allow users to have more influence over pagination,
|
||||
* be careful with what you permit.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $whitelist = array(
|
||||
'limit', 'sort', 'page', 'direction'
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -41,7 +91,7 @@ class PaginatorComponent extends Component {
|
|||
* @param array $settings Array of configuration settings.
|
||||
*/
|
||||
public function __construct(ComponentCollection $collection, $settings = array()) {
|
||||
$settings = array_merge(array('page' => 1, 'limit' => 20), (array)$settings);
|
||||
$settings = array_merge($this->settings, (array)$settings);
|
||||
$this->Controller = $collection->getController();
|
||||
parent::__construct($collection, $settings);
|
||||
}
|
||||
|
@ -50,8 +100,9 @@ class PaginatorComponent extends Component {
|
|||
* Handles automatic pagination of model records.
|
||||
*
|
||||
* @param mixed $object Model to paginate (e.g: model instance, or 'Model', or 'Model.InnerModel')
|
||||
* @param mixed $scope Conditions to use while paginating
|
||||
* @param array $whitelist List of allowed options for paging
|
||||
* @param mixed $scope Additional find conditions to use while paginating
|
||||
* @param array $whitelist List of allowed fields for ordering. This allows you to prevent ordering
|
||||
* on non-indexed, or undesirable columns.
|
||||
* @return array Model query results
|
||||
*/
|
||||
public function paginate($object = null, $scope = array(), $whitelist = array()) {
|
||||
|
@ -60,8 +111,116 @@ class PaginatorComponent extends Component {
|
|||
$scope = $object;
|
||||
$object = null;
|
||||
}
|
||||
$assoc = null;
|
||||
|
||||
$object = $this->_getObject($object);
|
||||
|
||||
if (!is_object($object)) {
|
||||
throw new MissingModelException($object);
|
||||
}
|
||||
|
||||
$options = $this->mergeOptions($object->alias);
|
||||
$options = $this->validateSort($object, $options, $whitelist);
|
||||
$options = $this->checkLimit($options);
|
||||
|
||||
$conditions = $fields = $order = $limit = $page = $recursive = null;
|
||||
|
||||
if (!isset($options['conditions'])) {
|
||||
$options['conditions'] = array();
|
||||
}
|
||||
|
||||
$type = 'all';
|
||||
|
||||
if (isset($options[0])) {
|
||||
$type = $options[0];
|
||||
unset($options[0]);
|
||||
}
|
||||
|
||||
extract($options);
|
||||
|
||||
if (is_array($scope) && !empty($scope)) {
|
||||
$conditions = array_merge($conditions, $scope);
|
||||
} elseif (is_string($scope)) {
|
||||
$conditions = array($conditions, $scope);
|
||||
}
|
||||
if ($recursive === null) {
|
||||
$recursive = $object->recursive;
|
||||
}
|
||||
|
||||
$extra = array_diff_key($options, compact(
|
||||
'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
|
||||
));
|
||||
if ($type !== 'all') {
|
||||
$extra['type'] = $type;
|
||||
}
|
||||
|
||||
if ($object->hasMethod('paginateCount')) {
|
||||
$count = $object->paginateCount($conditions, $recursive, $extra);
|
||||
} else {
|
||||
$parameters = compact('conditions');
|
||||
if ($recursive != $object->recursive) {
|
||||
$parameters['recursive'] = $recursive;
|
||||
}
|
||||
$count = $object->find('count', array_merge($parameters, $extra));
|
||||
}
|
||||
$pageCount = intval(ceil($count / $limit));
|
||||
|
||||
if ($page === 'last' || $page >= $pageCount) {
|
||||
$options['page'] = $page = $pageCount;
|
||||
} elseif (intval($page) < 1) {
|
||||
$options['page'] = $page = 1;
|
||||
}
|
||||
$page = $options['page'] = (int)$page;
|
||||
|
||||
if ($object->hasMethod('paginate')) {
|
||||
$results = $object->paginate(
|
||||
$conditions, $fields, $order, $limit, $page, $recursive, $extra
|
||||
);
|
||||
} else {
|
||||
$parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
|
||||
if ($recursive != $object->recursive) {
|
||||
$parameters['recursive'] = $recursive;
|
||||
}
|
||||
$results = $object->find($type, array_merge($parameters, $extra));
|
||||
}
|
||||
$defaults = $this->getDefaults($object->alias);
|
||||
unset($defaults[0]);
|
||||
|
||||
$paging = array(
|
||||
'page' => $page,
|
||||
'current' => count($results),
|
||||
'count' => $count,
|
||||
'prevPage' => ($page > 1),
|
||||
'nextPage' => ($count > ($page * $limit)),
|
||||
'pageCount' => $pageCount,
|
||||
'order' => $order,
|
||||
'limit' => $limit,
|
||||
'options' => Set::diff($options, $defaults),
|
||||
'paramType' => $options['paramType']
|
||||
);
|
||||
if (!isset($this->Controller->request['paging'])) {
|
||||
$this->Controller->request['paging'] = array();
|
||||
}
|
||||
$this->Controller->request['paging'] = array_merge(
|
||||
(array)$this->Controller->request['paging'],
|
||||
array($object->alias => $paging)
|
||||
);
|
||||
|
||||
if (
|
||||
!in_array('Paginator', $this->Controller->helpers) &&
|
||||
!array_key_exists('Paginator', $this->Controller->helpers)
|
||||
) {
|
||||
$this->Controller->helpers[] = 'Paginator';
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object pagination will occur on.
|
||||
*
|
||||
* @param mixed $object The object you are looking for.
|
||||
* @return mixed The model object to paginate on.
|
||||
*/
|
||||
protected function _getObject($object) {
|
||||
if (is_string($object)) {
|
||||
$assoc = null;
|
||||
if (strpos($object, '.') !== false) {
|
||||
|
@ -98,26 +257,71 @@ class PaginatorComponent extends Component {
|
|||
}
|
||||
}
|
||||
}
|
||||
return $object;
|
||||
}
|
||||
|
||||
if (!is_object($object)) {
|
||||
throw new MissingModelException($object);
|
||||
/**
|
||||
* Merges the various options that Pagination uses.
|
||||
* Pulls settings together from the following places:
|
||||
*
|
||||
* - General pagination settings
|
||||
* - Model specific settings.
|
||||
* - Request parameters
|
||||
*
|
||||
* The result of this method is the aggregate of all the option sets combined together. You can change
|
||||
* PaginatorComponent::$whitelist to modify which options/values can be set using request parameters.
|
||||
*
|
||||
* @param string $alias Model alias being paginated, if the general settings has a key with this value
|
||||
* that key's settings will be used for pagination instead of the general ones.
|
||||
* @return array Array of merged options.
|
||||
*/
|
||||
public function mergeOptions($alias) {
|
||||
$defaults = $this->getDefaults($alias);
|
||||
switch ($defaults['paramType']) {
|
||||
case 'named':
|
||||
$request = $this->Controller->request->params['named'];
|
||||
break;
|
||||
case 'querystring':
|
||||
$request = $this->Controller->request->query;
|
||||
break;
|
||||
}
|
||||
$options = array_merge(
|
||||
$this->Controller->request->params,
|
||||
$this->Controller->request->query,
|
||||
$this->Controller->passedArgs
|
||||
);
|
||||
$request = array_intersect_key($request, array_flip($this->whitelist));
|
||||
return array_merge($defaults, $request);
|
||||
}
|
||||
|
||||
if (isset($this->settings[$object->alias])) {
|
||||
$defaults = $this->settings[$object->alias];
|
||||
/**
|
||||
* Get the default settings for a $model. If there are no settings for a specific model, the general settings
|
||||
* will be used.
|
||||
*
|
||||
* @param string $alias Model name to get default settings for.
|
||||
* @return array An array of pagination defaults for a model, or the general settings.
|
||||
*/
|
||||
public function getDefaults($alias) {
|
||||
if (isset($this->settings[$alias])) {
|
||||
$defaults = $this->settings[$alias];
|
||||
} else {
|
||||
$defaults = $this->settings;
|
||||
}
|
||||
|
||||
if (isset($options['show'])) {
|
||||
$options['limit'] = $options['show'];
|
||||
}
|
||||
return array_merge(
|
||||
array('page' => 1, 'limit' => 20, 'maxLimit' => 100, 'paramType' => 'named'),
|
||||
$defaults
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the desired sorting can be performed on the $object. Only fields or
|
||||
* virtualFields can be sorted on. The direction param will also be sanitized. Lastly
|
||||
* sort + direction keys will be converted into the model friendly order key.
|
||||
*
|
||||
* You can use the whitelist parameter to control which columns/fields are available for sorting.
|
||||
* This helps prevent users from ordering large result sets on un-indexed values.
|
||||
*
|
||||
* @param Model $object The model being paginated.
|
||||
* @param array $options The pagination options being used for this request.
|
||||
* @param array $whitelist The list of columns that can be used for sorting. If empty all keys are allowed.
|
||||
* @return array An array of options with sort + direction removed and replaced with order if possible.
|
||||
*/
|
||||
public function validateSort($object, $options, $whitelist = array()) {
|
||||
if (isset($options['sort'])) {
|
||||
$direction = null;
|
||||
if (isset($options['direction'])) {
|
||||
|
@ -128,7 +332,14 @@ class PaginatorComponent extends Component {
|
|||
}
|
||||
$options['order'] = array($options['sort'] => $direction);
|
||||
}
|
||||
|
||||
|
||||
if (!empty($whitelist)) {
|
||||
$field = key($options['order']);
|
||||
if (!in_array($field, $whitelist)) {
|
||||
$options['order'] = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($options['order']) && is_array($options['order'])) {
|
||||
$alias = $object->alias ;
|
||||
$key = $field = key($options['order']);
|
||||
|
@ -147,107 +358,22 @@ class PaginatorComponent extends Component {
|
|||
$options['order'][$alias . '.' . $field] = $value;
|
||||
}
|
||||
}
|
||||
$vars = array('fields', 'order', 'limit', 'page', 'recursive');
|
||||
$keys = array_keys($options);
|
||||
$count = count($keys);
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (!in_array($keys[$i], $vars, true)) {
|
||||
unset($options[$keys[$i]]);
|
||||
}
|
||||
if (empty($whitelist) && ($keys[$i] === 'fields' || $keys[$i] === 'recursive')) {
|
||||
unset($options[$keys[$i]]);
|
||||
} elseif (!empty($whitelist) && !in_array($keys[$i], $whitelist)) {
|
||||
unset($options[$keys[$i]]);
|
||||
}
|
||||
}
|
||||
$conditions = $fields = $order = $limit = $page = $recursive = null;
|
||||
|
||||
if (!isset($defaults['conditions'])) {
|
||||
$defaults['conditions'] = array();
|
||||
}
|
||||
|
||||
$type = 'all';
|
||||
|
||||
if (isset($defaults[0])) {
|
||||
$type = $defaults[0];
|
||||
unset($defaults[0]);
|
||||
}
|
||||
|
||||
$options = array_merge(array('page' => 1, 'limit' => 20), $defaults, $options);
|
||||
/**
|
||||
* Check the limit parameter and ensure its within the maxLimit bounds.
|
||||
*
|
||||
* @param array $options An array of options with a limit key to be checked.
|
||||
* @return array An array of options for pagination
|
||||
*/
|
||||
public function checkLimit($options) {
|
||||
$options['limit'] = (int) $options['limit'];
|
||||
if (empty($options['limit']) || $options['limit'] < 1) {
|
||||
$options['limit'] = 1;
|
||||
}
|
||||
|
||||
extract($options);
|
||||
|
||||
if (is_array($scope) && !empty($scope)) {
|
||||
$conditions = array_merge($conditions, $scope);
|
||||
} elseif (is_string($scope)) {
|
||||
$conditions = array($conditions, $scope);
|
||||
}
|
||||
if ($recursive === null) {
|
||||
$recursive = $object->recursive;
|
||||
}
|
||||
|
||||
$extra = array_diff_key($defaults, compact(
|
||||
'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
|
||||
));
|
||||
if ($type !== 'all') {
|
||||
$extra['type'] = $type;
|
||||
}
|
||||
|
||||
if (method_exists($object, 'paginateCount')) {
|
||||
$count = $object->paginateCount($conditions, $recursive, $extra);
|
||||
} else {
|
||||
$parameters = compact('conditions');
|
||||
if ($recursive != $object->recursive) {
|
||||
$parameters['recursive'] = $recursive;
|
||||
}
|
||||
$count = $object->find('count', array_merge($parameters, $extra));
|
||||
}
|
||||
$pageCount = intval(ceil($count / $limit));
|
||||
|
||||
if ($page === 'last' || $page >= $pageCount) {
|
||||
$options['page'] = $page = $pageCount;
|
||||
} elseif (intval($page) < 1) {
|
||||
$options['page'] = $page = 1;
|
||||
}
|
||||
$page = $options['page'] = (integer)$page;
|
||||
|
||||
if (method_exists($object, 'paginate')) {
|
||||
$results = $object->paginate(
|
||||
$conditions, $fields, $order, $limit, $page, $recursive, $extra
|
||||
);
|
||||
} else {
|
||||
$parameters = compact('conditions', 'fields', 'order', 'limit', 'page');
|
||||
if ($recursive != $object->recursive) {
|
||||
$parameters['recursive'] = $recursive;
|
||||
}
|
||||
$results = $object->find($type, array_merge($parameters, $extra));
|
||||
}
|
||||
$paging = array(
|
||||
'page' => $page,
|
||||
'current' => count($results),
|
||||
'count' => $count,
|
||||
'prevPage' => ($page > 1),
|
||||
'nextPage' => ($count > ($page * $limit)),
|
||||
'pageCount' => $pageCount,
|
||||
'defaults' => array_merge(array('limit' => 20, 'step' => 1), $defaults),
|
||||
'options' => $options
|
||||
);
|
||||
if (!isset($this->Controller->request['paging'])) {
|
||||
$this->Controller->request['paging'] = array();
|
||||
}
|
||||
$this->Controller->request['paging'] = array_merge(
|
||||
(array)$this->Controller->request['paging'],
|
||||
array($object->alias => $paging)
|
||||
);
|
||||
|
||||
if (!in_array('Paginator', $this->Controller->helpers) && !array_key_exists('Paginator', $this->Controller->helpers)) {
|
||||
$this->Controller->helpers[] = 'Paginator';
|
||||
}
|
||||
return $results;
|
||||
$options['limit'] = min((int)$options['limit'], $options['maxLimit']);
|
||||
return $options;
|
||||
}
|
||||
}
|
|
@ -537,7 +537,7 @@ class HttpSocket extends CakeSocket {
|
|||
if (!method_exists($authClass, 'authentication')) {
|
||||
throw new SocketException(sprintf(__('The %s do not support authentication.'), $authClass));
|
||||
}
|
||||
call_user_func("$authClass::authentication", $this, $this->_auth[$method]);
|
||||
call_user_func_array("$authClass::authentication", array($this, &$this->_auth[$method]));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -563,7 +563,7 @@ class HttpSocket extends CakeSocket {
|
|||
if (!method_exists($authClass, 'proxyAuthentication')) {
|
||||
throw new SocketException(sprintf(__('The %s do not support proxy authentication.'), $authClass));
|
||||
}
|
||||
call_user_func("$authClass::proxyAuthentication", $this, $this->_proxy);
|
||||
call_user_func_array("$authClass::proxyAuthentication", array($this, &$this->_proxy));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,14 +42,14 @@ class BehaviorCollection extends ObjectCollection {
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
private $__methods = array();
|
||||
protected $_methods = array();
|
||||
|
||||
/**
|
||||
* Keeps a list of all methods which have been mapped with regular expressions
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $__mappedMethods = array();
|
||||
protected $_mappedMethods = array();
|
||||
|
||||
/**
|
||||
* Attaches a model object and loads a list of behaviors
|
||||
|
@ -128,7 +128,7 @@ class BehaviorCollection extends ObjectCollection {
|
|||
$this->_loaded[$name]->setup(ClassRegistry::getObject($this->modelName), $config);
|
||||
|
||||
foreach ($this->_loaded[$name]->mapMethods as $method => $alias) {
|
||||
$this->__mappedMethods[$method] = array($alias, $name);
|
||||
$this->_mappedMethods[$method] = array($name, $alias);
|
||||
}
|
||||
$methods = get_class_methods($this->_loaded[$name]);
|
||||
$parentMethods = array_flip(get_class_methods('ModelBehavior'));
|
||||
|
@ -140,11 +140,11 @@ class BehaviorCollection extends ObjectCollection {
|
|||
foreach ($methods as $m) {
|
||||
if (!isset($parentMethods[$m])) {
|
||||
$methodAllowed = (
|
||||
$m[0] != '_' && !array_key_exists($m, $this->__methods) &&
|
||||
$m[0] != '_' && !array_key_exists($m, $this->_methods) &&
|
||||
!in_array($m, $callbacks)
|
||||
);
|
||||
if ($methodAllowed) {
|
||||
$this->__methods[$m] = array($m, $name);
|
||||
$this->_methods[$m] = array($name, $m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -170,9 +170,9 @@ class BehaviorCollection extends ObjectCollection {
|
|||
$this->_loaded[$name]->cleanup(ClassRegistry::getObject($this->modelName));
|
||||
unset($this->_loaded[$name]);
|
||||
}
|
||||
foreach ($this->__methods as $m => $callback) {
|
||||
if (is_array($callback) && $callback[1] == $name) {
|
||||
unset($this->__methods[$m]);
|
||||
foreach ($this->_methods as $m => $callback) {
|
||||
if (is_array($callback) && $callback[0] == $name) {
|
||||
unset($this->_methods[$m]);
|
||||
}
|
||||
}
|
||||
$this->_enabled = array_values(array_diff($this->_enabled, (array)$name));
|
||||
|
@ -190,51 +190,70 @@ class BehaviorCollection extends ObjectCollection {
|
|||
}
|
||||
|
||||
/**
|
||||
* Dispatches a behavior method
|
||||
* Dispatches a behavior method. Will call either normal methods or mapped methods.
|
||||
*
|
||||
* If a method is not handeled by the BehaviorCollection, and $strict is false, a
|
||||
* special return of `array('unhandled')` will be returned to signal the method was not found.
|
||||
*
|
||||
* @param Model $model The model the method was originally called on.
|
||||
* @param string $method The method called.
|
||||
* @param array $params Parameters for the called method.
|
||||
* @param boolean $strict If methods are not found, trigger an error.
|
||||
* @return array All methods for all behaviors attached to this object
|
||||
*/
|
||||
public function dispatchMethod(&$model, $method, $params = array(), $strict = false) {
|
||||
$methods = array_keys($this->__methods);
|
||||
$check = array_flip($methods);
|
||||
$found = isset($check[$method]);
|
||||
$call = null;
|
||||
|
||||
if ($strict && !$found) {
|
||||
public function dispatchMethod($model, $method, $params = array(), $strict = false) {
|
||||
$method = $this->hasMethod($method, true);
|
||||
|
||||
if ($strict && empty($method)) {
|
||||
trigger_error(__("BehaviorCollection::dispatchMethod() - Method %s not found in any attached behavior", $method), E_USER_WARNING);
|
||||
return null;
|
||||
} elseif ($found) {
|
||||
$methods = array_combine($methods, array_values($this->__methods));
|
||||
$call = $methods[$method];
|
||||
} else {
|
||||
$count = count($this->__mappedMethods);
|
||||
$mapped = array_keys($this->__mappedMethods);
|
||||
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
if (preg_match($mapped[$i] . 'i', $method)) {
|
||||
$call = $this->__mappedMethods[$mapped[$i]];
|
||||
array_unshift($params, $method);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($call)) {
|
||||
return call_user_func_array(
|
||||
array(&$this->_loaded[$call[1]], $call[0]),
|
||||
array_merge(array(&$model), $params)
|
||||
);
|
||||
if (empty($method)) {
|
||||
return array('unhandled');
|
||||
}
|
||||
return array('unhandled');
|
||||
if (count($method) === 3) {
|
||||
array_unshift($params, $method[2]);
|
||||
unset($method[2]);
|
||||
}
|
||||
return call_user_func_array(
|
||||
array($this->_loaded[$method[0]], $method[1]),
|
||||
array_merge(array(&$model), $params)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the method list for attached behaviors, i.e. all public, non-callback methods
|
||||
* Gets the method list for attached behaviors, i.e. all public, non-callback methods.
|
||||
* This does not include mappedMethods.
|
||||
*
|
||||
* @return array All public methods for all behaviors attached to this collection
|
||||
*/
|
||||
public function methods() {
|
||||
return $this->__methods;
|
||||
return $this->_methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if a behavior in this collection implements the provided method. Will
|
||||
* also check mappedMethods.
|
||||
*
|
||||
* @param string $method The method to find.
|
||||
* @param boolean $callback Return the callback for the method.
|
||||
* @return mixed If $callback is false, a boolean will be returnned, if its true, an array
|
||||
* containing callback information will be returnned. For mapped methods the array will have 3 elements.
|
||||
*/
|
||||
public function hasMethod($method, $callback = false) {
|
||||
if (isset($this->_methods[$method])) {
|
||||
return $callback ? $this->_methods[$method] : true;
|
||||
}
|
||||
foreach ($this->_mappedMethods as $pattern => $target) {
|
||||
if (preg_match($pattern . 'i', $method)) {
|
||||
if ($callback) {
|
||||
$target[] = $method;
|
||||
return $target;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1066,6 +1066,23 @@ class Model extends Object {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that a method is callable on a model. This will check both the model's own methods, its
|
||||
* inherited methods and methods that could be callable through behaviors.
|
||||
*
|
||||
* @param string $method The method to be called.
|
||||
* @return boolean True on method being callable.
|
||||
*/
|
||||
public function hasMethod($method) {
|
||||
if (method_exists($this, $method)) {
|
||||
return true;
|
||||
}
|
||||
if ($this->Behaviors->hasMethod($method)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the supplied field is a model Virtual Field
|
||||
*
|
||||
|
|
|
@ -39,6 +39,25 @@
|
|||
*
|
||||
* Would be called like `$this->Model->doSomething($arg1, $arg2);`.
|
||||
*
|
||||
* ### Mapped methods
|
||||
*
|
||||
* Behaviors can also define mapped methods. Mapped methods use pattern matching for method invocation. This
|
||||
* allows you to create methods similar to Model::findAllByXXX methods on your behaviors. Mapped methods need to
|
||||
* be declared in your behaviors `$mapMethods` array. The method signature for a mapped method is slightly different
|
||||
* than a normal behavior mixin method.
|
||||
*
|
||||
* {{{
|
||||
* var $mapMethods = array('/do(\w+)/' => 'doSomething');
|
||||
*
|
||||
* function doSomething($model, $method, $arg1, $arg2) {
|
||||
* //do something
|
||||
* }
|
||||
* }}}
|
||||
*
|
||||
* The above will map every doXXX() method call to the behavior. As you can see, the model is
|
||||
* still the first parameter, but the called method name will be the 2nd parameter. This allows
|
||||
* you to munge the method name for additional information, much like Model::findAllByXX.
|
||||
*
|
||||
* @package cake.libs.model
|
||||
* @see Model::$actsAs
|
||||
* @see BehaviorCollection::load()
|
||||
|
|
|
@ -164,6 +164,11 @@ class CakeRoute {
|
|||
$parsed = str_replace(array_keys($routeParams), array_values($routeParams), $parsed);
|
||||
$this->_compiledRoute = '#^' . $parsed . '[/]*$#';
|
||||
$this->keys = $names;
|
||||
|
||||
//remove defaults that are also keys. They can cause match failures
|
||||
foreach ($this->keys as $key) {
|
||||
unset($this->defaults[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -259,53 +264,62 @@ class CakeRoute {
|
|||
|
||||
//check that all the key names are in the url
|
||||
$keyNames = array_flip($this->keys);
|
||||
if (array_intersect_key($keyNames, $url) != $keyNames) {
|
||||
if (array_intersect_key($keyNames, $url) !== $keyNames) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$diffUnfiltered = Set::diff($url, $defaults);
|
||||
$diff = array();
|
||||
// Missing defaults is a fail.
|
||||
if (array_diff_key($defaults, $url) !== array()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($diffUnfiltered as $key => $var) {
|
||||
if ($var === 0 || $var === '0' || !empty($var)) {
|
||||
$diff[$key] = $var;
|
||||
$greedyNamed = Router::$named['greedy'];
|
||||
$allowedNamedParams = Router::$named['rules'];
|
||||
|
||||
$named = $pass = $_query = array();
|
||||
|
||||
foreach ($url as $key => $value) {
|
||||
|
||||
// keys that exist in the defaults and have different values is a match failure.
|
||||
$defaultExists = array_key_exists($key, $defaults);
|
||||
if ($defaultExists && $defaults[$key] != $value) {
|
||||
return false;
|
||||
} elseif ($defaultExists) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the key is a routed key, its not different yet.
|
||||
if (array_key_exists($key, $keyNames)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// pull out passed args
|
||||
$numeric = is_numeric($key);
|
||||
if ($numeric && isset($defaults[$key]) && $defaults[$key] == $value) {
|
||||
continue;
|
||||
} elseif ($numeric) {
|
||||
$pass[] = $value;
|
||||
unset($url[$key]);
|
||||
continue;
|
||||
}
|
||||
|
||||
// pull out named params if named params are greedy or a rule exists.
|
||||
if (
|
||||
($greedyNamed || isset($allowedNamedParams[$key])) &&
|
||||
($value !== false && $value !== null)
|
||||
) {
|
||||
$named[$key] = $value;
|
||||
continue;
|
||||
}
|
||||
|
||||
// keys that don't exist are different.
|
||||
if (!$defaultExists && !empty($value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//if a not a greedy route, no extra params are allowed.
|
||||
if (!$this->_greedy && array_diff_key($diff, $keyNames) != array()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//remove defaults that are also keys. They can cause match failures
|
||||
foreach ($this->keys as $key) {
|
||||
unset($defaults[$key]);
|
||||
}
|
||||
$filteredDefaults = array_filter($defaults);
|
||||
|
||||
//if the difference between the url diff and defaults contains keys from defaults its not a match
|
||||
if (array_intersect_key($filteredDefaults, $diffUnfiltered) !== array()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$passedArgsAndParams = array_diff_key($diff, $filteredDefaults, $keyNames);
|
||||
list($named, $params) = Router::getNamedElements($passedArgsAndParams, $url['controller'], $url['action']);
|
||||
|
||||
//remove any pass params, they have numeric indexes, skip any params that are in the defaults
|
||||
$pass = array();
|
||||
$i = 0;
|
||||
while (isset($url[$i])) {
|
||||
if (!isset($diff[$i])) {
|
||||
$i++;
|
||||
continue;
|
||||
}
|
||||
$pass[] = $url[$i];
|
||||
unset($url[$i], $params[$i]);
|
||||
$i++;
|
||||
}
|
||||
|
||||
//still some left over parameters that weren't named or passed args, bail.
|
||||
if (!empty($params)) {
|
||||
if (!$this->_greedy && (!empty($pass) || !empty($named))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -317,7 +331,7 @@ class CakeRoute {
|
|||
}
|
||||
}
|
||||
}
|
||||
return $this->_writeUrl(array_merge($url, compact('pass', 'named')));
|
||||
return $this->_writeUrl(array_merge($url, compact('pass', 'named', '_query')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -373,4 +387,21 @@ class CakeRoute {
|
|||
$out = str_replace('//', '/', $out);
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a well-formed querystring from $q
|
||||
*
|
||||
* Will compose an array or nested array into a proper querystring.
|
||||
*
|
||||
* @param mixed $q An array of parameters to compose into a query string.
|
||||
* @param bool $escape Whether or not to use escaped &
|
||||
* @return string
|
||||
*/
|
||||
public function queryString($q, $escape = false) {
|
||||
$join = '&';
|
||||
if ($escape === true) {
|
||||
$join = '&';
|
||||
}
|
||||
return '?' . http_build_query($q, null, $join);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,9 @@
|
|||
App::import('Core', 'CakeResponse');
|
||||
App::import('Core', 'route/CakeRoute');
|
||||
/**
|
||||
* Redirect route will perform an immediate redirect
|
||||
* Redirect route will perform an immediate redirect. Redirect routes
|
||||
* are useful when you want to have Routing layer redirects occur in your
|
||||
* application, for when URLs move.
|
||||
*
|
||||
* PHP5
|
||||
*
|
||||
|
@ -27,6 +29,25 @@ class RedirectRoute extends CakeRoute {
|
|||
*/
|
||||
public $response = null;
|
||||
|
||||
/**
|
||||
* The location to redirect to. Either a string or a cake array url.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $redirect;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param string $template Template string with parameter placeholders
|
||||
* @param array $defaults Array of defaults for the route.
|
||||
* @param string $params Array of parameters and additional options for the Route
|
||||
*/
|
||||
public function __construct($template, $defaults = array(), $options = array()) {
|
||||
parent::__construct($template, $defaults, $options);
|
||||
$this->redirect = (array)$defaults;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string url into an array. Parsed urls will result in an automatic
|
||||
* redirection
|
||||
|
@ -42,22 +63,21 @@ class RedirectRoute extends CakeRoute {
|
|||
if (!$this->response) {
|
||||
$this->response = new CakeResponse();
|
||||
}
|
||||
$redirect = $this->defaults;
|
||||
if (count($this->defaults) == 1 && !isset($this->defaults['controller'])) {
|
||||
$redirect = $this->defaults[0];
|
||||
$redirect = $this->redirect;
|
||||
if (count($this->redirect) == 1 && !isset($this->redirect['controller'])) {
|
||||
$redirect = $this->redirect[0];
|
||||
}
|
||||
if (isset($this->options['persist']) && is_array($redirect)) {
|
||||
$argOptions['context'] = array('action' => $redirect['action'], 'controller' => $redirect['controller']);
|
||||
$args = Router::getArgs($params['_args_'], $argOptions);
|
||||
$redirect += $args['pass'];
|
||||
$redirect += $args['named'];
|
||||
}
|
||||
$redirect += Router::getArgs($params['_args_'], $argOptions) + array('url' => array());
|
||||
$redirect = Router::reverse($redirect);
|
||||
}
|
||||
$status = 301;
|
||||
if (isset($this->options['status']) && ($this->options['status'] >= 300 && $this->options['status'] < 400)) {
|
||||
$status = $this->options['status'];
|
||||
}
|
||||
$this->response->header(array('Location' => Router::url($redirect, true)));
|
||||
$this->response->statusCode($status);
|
||||
$this->response->statusCode($status);
|
||||
$this->response->send();
|
||||
}
|
||||
|
||||
|
|
|
@ -281,7 +281,8 @@ class Router {
|
|||
*
|
||||
* `Router::redirect('/home/*', array('controller' => 'posts', 'action' => 'view', array('persist' => true));`
|
||||
*
|
||||
* Redirects /home/* to /posts/view and passes the parameters to /posts/view
|
||||
* Redirects /home/* to /posts/view and passes the parameters to /posts/view. Using an array as the
|
||||
* redirect destination allows you to use other routes to define where a url string should be redirected ot.
|
||||
*
|
||||
* `Router::redirect('/posts/*', 'http://google.com', array('status' => 302));`
|
||||
*
|
||||
|
@ -290,7 +291,8 @@ class Router {
|
|||
* ### Options:
|
||||
*
|
||||
* - `status` Sets the HTTP status (default 301)
|
||||
* - `persist` Passes the params to the redirected route, if it can
|
||||
* - `persist` Passes the params to the redirected route, if it can. This is useful with greedy routes,
|
||||
* routes that end in `*` are greedy. As you can remap urls and not loose any passed/named args.
|
||||
*
|
||||
* @param string $route A string describing the template of the route
|
||||
* @param array $url A url to redirect to. Can be a string or a Cake array-based url
|
||||
|
@ -310,7 +312,9 @@ class Router {
|
|||
}
|
||||
|
||||
/**
|
||||
* Specifies what named parameters CakePHP should be parsing. The most common setups are:
|
||||
* Specifies what named parameters CakePHP should be parsing out of incoming urls. By default
|
||||
* CakePHP will parse every named parameter out of incoming URLs. However, if you want to take more
|
||||
* control over how named parameters are parsed you can use one of the following setups:
|
||||
*
|
||||
* Do not parse any named parameters:
|
||||
*
|
||||
|
@ -324,7 +328,7 @@ class Router {
|
|||
*
|
||||
* {{{ Router::connectNamed(array('page' => '[\d]+'), array('default' => false, 'greedy' => false)); }}}
|
||||
*
|
||||
* Parse only the page parameter no mater what.
|
||||
* Parse only the page parameter no matter what.
|
||||
*
|
||||
* {{{ Router::connectNamed(array('page'), array('default' => false, 'greedy' => false)); }}}
|
||||
*
|
||||
|
@ -346,15 +350,23 @@ class Router {
|
|||
* );
|
||||
* }}}
|
||||
*
|
||||
* ### Options
|
||||
*
|
||||
* - `greedy` Setting this to true will make Router parse all named params. Setting it to false will
|
||||
* parse only the connected named params.
|
||||
* - `default` Set this to true to merge in the default set of named parameters.
|
||||
* - `reset` Set to true to clear existing rules and start fresh.
|
||||
* - `separator` Change the string used to separate the key & value in a named parameter. Defaults to `:`
|
||||
*
|
||||
* @param array $named A list of named parameters. Key value pairs are accepted where values are
|
||||
* either regex strings to match, or arrays as seen above.
|
||||
* @param array $options Allows to control all settings: separator, greedy, reset, default
|
||||
* @return array
|
||||
*/
|
||||
public static function connectNamed($named, $options = array()) {
|
||||
if (isset($options['argSeparator'])) {
|
||||
self::$named['separator'] = $options['argSeparator'];
|
||||
unset($options['argSeparator']);
|
||||
if (isset($options['separator'])) {
|
||||
self::$named['separator'] = $options['separator'];
|
||||
unset($options['separator']);
|
||||
}
|
||||
|
||||
if ($named === true || $named === false) {
|
||||
|
@ -901,7 +913,7 @@ class Router {
|
|||
* @see Router::url()
|
||||
*/
|
||||
protected static function _handleNoRoute($url) {
|
||||
$named = $args = array();
|
||||
$named = $args = $query = array();
|
||||
$skip = array_merge(
|
||||
array('bare', 'action', 'controller', 'plugin', 'prefix'),
|
||||
self::$_prefixes
|
||||
|
@ -912,10 +924,11 @@ class Router {
|
|||
|
||||
// Remove this once parsed URL parameters can be inserted into 'pass'
|
||||
for ($i = 0; $i < $count; $i++) {
|
||||
$key = $keys[$i];
|
||||
if (is_numeric($keys[$i])) {
|
||||
$args[] = $url[$keys[$i]];
|
||||
$args[] = $url[$key];
|
||||
} else {
|
||||
$named[$keys[$i]] = $url[$keys[$i]];
|
||||
$named[$key] = $url[$key];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -927,7 +940,7 @@ class Router {
|
|||
}
|
||||
}
|
||||
|
||||
if (empty($named) && empty($args) && (!isset($url['action']) || $url['action'] === 'index')) {
|
||||
if (empty($named) && empty($args) && empty($query) && (!isset($url['action']) || $url['action'] === 'index')) {
|
||||
$url['action'] = null;
|
||||
}
|
||||
|
||||
|
@ -950,7 +963,7 @@ class Router {
|
|||
}
|
||||
|
||||
if (!empty($named)) {
|
||||
foreach ($named as $name => $value) {
|
||||
foreach ($named as $name => $value) {
|
||||
if (is_array($value)) {
|
||||
$flattend = Set::flatten($value, '][');
|
||||
foreach ($flattend as $namedKey => $namedValue) {
|
||||
|
@ -961,6 +974,9 @@ class Router {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!empty($query)) {
|
||||
$output .= Router::queryString($query);
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
|
@ -979,7 +995,7 @@ class Router {
|
|||
if (isset(self::$named['rules'][$param])) {
|
||||
$rule = self::$named['rules'][$param];
|
||||
if (Router::matchNamed($param, $val, $rule, compact('controller', 'action'))) {
|
||||
$named[$param] = $val;
|
||||
$named[substr($param, 1)] = $val;
|
||||
unset($params[$param]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -137,7 +137,6 @@ class Helper extends Object {
|
|||
*/
|
||||
public function __construct(View $View, $settings = array()) {
|
||||
$this->_View = $View;
|
||||
$this->params = $View->params;
|
||||
$this->request = $View->request;
|
||||
if (!empty($this->helpers)) {
|
||||
$this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
|
||||
|
|
|
@ -42,7 +42,8 @@ class PaginatorHelper extends AppHelper {
|
|||
private $__defaultModel = null;
|
||||
|
||||
/**
|
||||
* The class used for 'Ajax' pagination links.
|
||||
* The class used for 'Ajax' pagination links. Defaults to JsHelper. You should make sure
|
||||
* that JsHelper is defined as a helper before PaginatorHelper, if you want to customize the JsHelper.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
|
@ -53,26 +54,30 @@ class PaginatorHelper extends AppHelper {
|
|||
*
|
||||
* The values that may be specified are:
|
||||
*
|
||||
* - `$options['format']` Format of the counter. Supported formats are 'range' and 'pages'
|
||||
* - `format` Format of the counter. Supported formats are 'range' and 'pages'
|
||||
* and custom (default). In the default mode the supplied string is parsed and constants are replaced
|
||||
* by their actual values.
|
||||
* Constants: %page%, %pages%, %current%, %count%, %start%, %end% .
|
||||
* - `$options['separator']` The separator of the actual page and number of pages (default: ' of ').
|
||||
* - `$options['url']` Url of the action. See Router::url()
|
||||
* - `$options['url']['sort']` the key that the recordset is sorted.
|
||||
* - `$options['url']['direction']` Direction of the sorting (default: 'asc').
|
||||
* - `$options['url']['page']` Page # to display.
|
||||
* - `$options['model']` The name of the model.
|
||||
* - `$options['escape']` Defines if the title field for the link should be escaped (default: true).
|
||||
* - `$options['update']` DOM id of the element updated with the results of the AJAX call.
|
||||
* placeholders: %page%, %pages%, %current%, %count%, %start%, %end% .
|
||||
* - `separator` The separator of the actual page and number of pages (default: ' of ').
|
||||
* - `url` Url of the action. See Router::url()
|
||||
* - `url['sort']` the key that the recordset is sorted.
|
||||
* - `url['direction']` Direction of the sorting (default: 'asc').
|
||||
* - `url['page']` Page number to use in links.
|
||||
* - `model` The name of the model.
|
||||
* - `escape` Defines if the title field for the link should be escaped (default: true).
|
||||
* - `update` DOM id of the element updated with the results of the AJAX call.
|
||||
* If this key isn't specified Paginator will use plain HTML links.
|
||||
* - `$options['indicator']` DOM id of the element that will be shown when doing AJAX requests. **Only supported by
|
||||
* AjaxHelper**
|
||||
* - `paging['paramType']` The type of parameters to use when creating links. Valid options are
|
||||
* 'querystring', 'named', and 'route'. See PaginatorComponent::$settings for more information.
|
||||
* - `convertKeys` - A list of keys in url arrays that should be converted to querysting params
|
||||
* if paramType == 'querystring'.
|
||||
*
|
||||
* @var array
|
||||
* @access public
|
||||
*/
|
||||
public $options = array();
|
||||
public $options = array(
|
||||
'convertKeys' => array('page', 'limit', 'sort', 'direction')
|
||||
);
|
||||
|
||||
/**
|
||||
* Constructor for the helper. Sets up the helper that is used for creating 'AJAX' links.
|
||||
|
@ -110,7 +115,6 @@ class PaginatorHelper extends AppHelper {
|
|||
*/
|
||||
public function beforeRender($viewFile) {
|
||||
$this->options['url'] = array_merge($this->request->params['pass'], $this->request->params['named']);
|
||||
|
||||
parent::beforeRender($viewFile);
|
||||
}
|
||||
|
||||
|
@ -160,6 +164,9 @@ class PaginatorHelper extends AppHelper {
|
|||
);
|
||||
unset($options[$model]);
|
||||
}
|
||||
if (!empty($options['convertKeys'])) {
|
||||
$options['convertKeys'] = array_merge($this->options['convertKeys'], $options['convertKeys']);
|
||||
}
|
||||
$this->options = array_filter(array_merge($this->options, $options));
|
||||
}
|
||||
|
||||
|
@ -189,7 +196,7 @@ class PaginatorHelper extends AppHelper {
|
|||
public function sortKey($model = null, $options = array()) {
|
||||
if (empty($options)) {
|
||||
$params = $this->params($model);
|
||||
$options = array_merge($params['defaults'], $params['options']);
|
||||
$options = $params['options'];
|
||||
}
|
||||
|
||||
if (isset($options['sort']) && !empty($options['sort'])) {
|
||||
|
@ -215,7 +222,7 @@ class PaginatorHelper extends AppHelper {
|
|||
|
||||
if (empty($options)) {
|
||||
$params = $this->params($model);
|
||||
$options = array_merge($params['defaults'], $params['options']);
|
||||
$options = $params['options'];
|
||||
}
|
||||
|
||||
if (isset($options['direction'])) {
|
||||
|
@ -246,6 +253,10 @@ class PaginatorHelper extends AppHelper {
|
|||
* @return string A "previous" link or $disabledTitle text if the link is disabled.
|
||||
*/
|
||||
public function prev($title = '<< Previous', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
|
||||
$defaults = array(
|
||||
'rel' => 'prev'
|
||||
);
|
||||
$options = array_merge($defaults, (array)$options);
|
||||
return $this->__pagingLink('Prev', $title, $options, $disabledTitle, $disabledOptions);
|
||||
}
|
||||
|
||||
|
@ -265,6 +276,10 @@ class PaginatorHelper extends AppHelper {
|
|||
* @return string A "next" link or or $disabledTitle text if the link is disabled.
|
||||
*/
|
||||
public function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
|
||||
$defaults = array(
|
||||
'rel' => 'next'
|
||||
);
|
||||
$options = array_merge($defaults, (array)$options);
|
||||
return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions);
|
||||
}
|
||||
|
||||
|
@ -348,11 +363,11 @@ class PaginatorHelper extends AppHelper {
|
|||
$url = array_merge((array)$options['url'], (array)$url);
|
||||
unset($options['url']);
|
||||
}
|
||||
unset($options['convertKeys']);
|
||||
|
||||
$url = $this->url($url, true, $model);
|
||||
|
||||
$obj = isset($options['update']) ? $this->_ajaxHelperClass : 'Html';
|
||||
$url = array_merge(array('page' => $this->current($model)), $url);
|
||||
$url = array_merge(Set::filter($url, true), array_intersect_key($url, array('plugin' => true)));
|
||||
return $this->{$obj}->link($title, $url, $options);
|
||||
}
|
||||
|
||||
|
@ -366,9 +381,7 @@ class PaginatorHelper extends AppHelper {
|
|||
*/
|
||||
public function url($options = array(), $asArray = false, $model = null) {
|
||||
$paging = $this->params($model);
|
||||
$url = array_merge(array_filter(Set::diff(array_merge(
|
||||
$paging['defaults'], $paging['options']), $paging['defaults'])), $options
|
||||
);
|
||||
$url = array_merge(array_filter($paging['options']), $options);
|
||||
|
||||
if (isset($url['order'])) {
|
||||
$sort = $direction = null;
|
||||
|
@ -378,6 +391,7 @@ class PaginatorHelper extends AppHelper {
|
|||
unset($url['order']);
|
||||
$url = array_merge($url, compact('sort', 'direction'));
|
||||
}
|
||||
$url = $this->_convertUrlKeys($url, $paging['paramType']);
|
||||
|
||||
if ($asArray) {
|
||||
return $url;
|
||||
|
@ -385,6 +399,28 @@ class PaginatorHelper extends AppHelper {
|
|||
return parent::url($url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the keys being used into the format set by options.paramType
|
||||
*
|
||||
* @param array $url Array of url params to convert
|
||||
* @return converted url params.
|
||||
*/
|
||||
protected function _convertUrlKeys($url, $type) {
|
||||
if ($type == 'named') {
|
||||
return $url;
|
||||
}
|
||||
if (!isset($url['?'])) {
|
||||
$url['?'] = array();
|
||||
}
|
||||
foreach ($this->options['convertKeys'] as $key) {
|
||||
if (isset($url[$key])) {
|
||||
$url['?'][$key] = $url[$key];
|
||||
unset($url[$key]);
|
||||
}
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Protected method for generating prev/next links
|
||||
*
|
||||
|
@ -419,6 +455,7 @@ class PaginatorHelper extends AppHelper {
|
|||
if ($this->{$check}($model)) {
|
||||
return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'class'))));
|
||||
} else {
|
||||
unset($options['rel']);
|
||||
return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));
|
||||
}
|
||||
}
|
||||
|
@ -526,9 +563,9 @@ class PaginatorHelper extends AppHelper {
|
|||
}
|
||||
$start = 0;
|
||||
if ($paging['count'] >= 1) {
|
||||
$start = (($paging['page'] - 1) * $paging['options']['limit']) + 1;
|
||||
$start = (($paging['page'] - 1) * $paging['limit']) + 1;
|
||||
}
|
||||
$end = $start + $paging['options']['limit'] - 1;
|
||||
$end = $start + $paging['limit'] - 1;
|
||||
if ($paging['count'] < $end) {
|
||||
$end = $paging['count'];
|
||||
}
|
||||
|
@ -566,7 +603,12 @@ class PaginatorHelper extends AppHelper {
|
|||
|
||||
/**
|
||||
* Returns a set of numbers for the paged result set
|
||||
* uses a modulus to decide how many numbers to show on each side of the current page (default: 8)
|
||||
* uses a modulus to decide how many numbers to show on each side of the current page (default: 8).
|
||||
*
|
||||
* `$this->Paginator->numbers(array('first' => 2, 'last' => 2));`
|
||||
*
|
||||
* Using the first and last options you can create links to the beginning and end of the page set.
|
||||
*
|
||||
*
|
||||
* ### Options
|
||||
*
|
||||
|
@ -577,9 +619,9 @@ class PaginatorHelper extends AppHelper {
|
|||
* - `separator` Separator content defaults to ' | '
|
||||
* - `tag` The tag to wrap links in, defaults to 'span'
|
||||
* - `first` Whether you want first links generated, set to an integer to define the number of 'first'
|
||||
* links to generate
|
||||
* links to generate.
|
||||
* - `last` Whether you want last links generated, set to an integer to define the number of 'last'
|
||||
* links to generate
|
||||
* links to generate.
|
||||
* - `ellipsis` Ellipsis content, defaults to '...'
|
||||
*
|
||||
* @param mixed $options Options for the numbers, (before, after, model, modulus, separator)
|
||||
|
@ -689,32 +731,42 @@ class PaginatorHelper extends AppHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a first or set of numbers for the first pages
|
||||
* Returns a first or set of numbers for the first pages.
|
||||
*
|
||||
* `echo $this->Paginator->first('< first');`
|
||||
*
|
||||
* Creates a single link for the first page. Will output nothing if you are on the first page.
|
||||
*
|
||||
* `echo $this->Paginator->first(3);`
|
||||
*
|
||||
* Will create links for the first 3 pages, once you get to the third or greater page. Prior to that
|
||||
* nothing will be output.
|
||||
*
|
||||
* ### Options:
|
||||
*
|
||||
* - `tag` The tag wrapping tag you want to use, defaults to 'span'
|
||||
* - `before` Content to insert before the link/tag
|
||||
* - `after` Content to insert after the link/tag
|
||||
* - `model` The model to use defaults to PaginatorHelper::defaultModel()
|
||||
* - `separator` Content between the generated links, defaults to ' | '
|
||||
* - `ellipsis` Content for ellipsis, defaults to '...'
|
||||
*
|
||||
* @param mixed $first if string use as label for the link, if numeric print page numbers
|
||||
* @param mixed $options
|
||||
* @param mixed $first if string use as label for the link. If numeric, the number of page links
|
||||
* you want at the beginning of the range.
|
||||
* @param mixed $options An array of options.
|
||||
* @return string numbers string.
|
||||
*/
|
||||
public function first($first = '<< first', $options = array()) {
|
||||
$options = array_merge(
|
||||
array(
|
||||
'tag' => 'span',
|
||||
'after'=> null,
|
||||
'after' => null,
|
||||
'model' => $this->defaultModel(),
|
||||
'separator' => ' | ',
|
||||
'ellipsis' => '...',
|
||||
'ellipsis' => '...'
|
||||
),
|
||||
(array)$options);
|
||||
|
||||
$params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
|
||||
$params = array_merge(array('page' => 1), (array)$this->params($options['model']));
|
||||
unset($options['model']);
|
||||
|
||||
if ($params['pageCount'] <= 1) {
|
||||
|
@ -725,7 +777,7 @@ class PaginatorHelper extends AppHelper {
|
|||
|
||||
$out = '';
|
||||
|
||||
if (is_int($first) && $params['page'] > $first) {
|
||||
if (is_int($first) && $params['page'] >= $first) {
|
||||
if ($after === null) {
|
||||
$after = $ellipsis;
|
||||
}
|
||||
|
@ -736,7 +788,8 @@ class PaginatorHelper extends AppHelper {
|
|||
}
|
||||
}
|
||||
$out .= $after;
|
||||
} elseif ($params['page'] > 1) {
|
||||
} elseif ($params['page'] > 1 && is_string($first)) {
|
||||
$options += array('rel' => 'first');
|
||||
$out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options))
|
||||
. $after;
|
||||
}
|
||||
|
@ -744,7 +797,15 @@ class PaginatorHelper extends AppHelper {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a last or set of numbers for the last pages
|
||||
* Returns a last or set of numbers for the last pages.
|
||||
*
|
||||
* `echo $this->Paginator->last('last >');`
|
||||
*
|
||||
* Creates a single link for the last page. Will output nothing if you are on the last page.
|
||||
*
|
||||
* `echo $this->Paginator->last(3);`
|
||||
*
|
||||
* Will create links for the last 3 pages. Once you enter the page range, no output will be created.
|
||||
*
|
||||
* ### Options:
|
||||
*
|
||||
|
@ -782,7 +843,7 @@ class PaginatorHelper extends AppHelper {
|
|||
$out = '';
|
||||
$lower = $params['pageCount'] - $last + 1;
|
||||
|
||||
if (is_int($last) && $params['page'] < $lower) {
|
||||
if (is_int($last) && $params['page'] <= $lower) {
|
||||
if ($before === null) {
|
||||
$before = $ellipsis;
|
||||
}
|
||||
|
@ -793,7 +854,8 @@ class PaginatorHelper extends AppHelper {
|
|||
}
|
||||
}
|
||||
$out = $before . $out;
|
||||
} elseif ($params['page'] < $params['pageCount']) {
|
||||
} elseif ($params['page'] < $params['pageCount'] && is_string($last)) {
|
||||
$options += array('rel' => 'last');
|
||||
$out = $before . $this->Html->tag(
|
||||
$tag, $this->link($last, array('page' => $params['pageCount']), $options
|
||||
));
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
* @license MIT License (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
App::import('Controller', 'Controller', false);
|
||||
App::import('Component', 'Paginator');
|
||||
App::import('Core', array('CakeRequest', 'CakeResponse'));
|
||||
|
||||
/**
|
||||
|
@ -205,6 +206,22 @@ class PaginatorTest extends CakeTestCase {
|
|||
*/
|
||||
public $fixtures = array('core.post', 'core.comment');
|
||||
|
||||
/**
|
||||
* setup
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function setUp() {
|
||||
parent::setUp();
|
||||
$this->request = new CakeRequest('controller_posts/index');
|
||||
$this->request->params['pass'] = $this->request->params['named'] = array();
|
||||
$this->Controller = new Controller($this->request);
|
||||
$this->Paginator = new PaginatorComponent($this->getMock('ComponentCollection'), array());
|
||||
$this->Paginator->Controller = $this->Controller;
|
||||
$this->Controller->Post = $this->getMock('Model');
|
||||
$this->Controller->Post->alias = 'Post';
|
||||
}
|
||||
|
||||
/**
|
||||
* testPaginate method
|
||||
*
|
||||
|
@ -212,13 +229,10 @@ class PaginatorTest extends CakeTestCase {
|
|||
* @return void
|
||||
*/
|
||||
function testPaginate() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
|
||||
$Controller->passedArgs[] = '1';
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->request->params['pass'] = array('1');
|
||||
$Controller->request->query = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
|
@ -233,67 +247,95 @@ class PaginatorTest extends CakeTestCase {
|
|||
$results = Set::extract($Controller->Paginator->paginate(), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($results, array(1, 2, 3));
|
||||
|
||||
$Controller->passedArgs = array('page' => '-1');
|
||||
$Controller->request->params['named'] = array('page' => '-1');
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual($results, array(1, 2, 3));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'asc');
|
||||
$Controller->request->params['named'] = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'asc');
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual($results, array(1, 2, 3));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'desc');
|
||||
$Controller->request->params['named'] = array('sort' => 'PaginatorControllerPost.id', 'direction' => 'desc');
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual($results, array(3, 2, 1));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'id', 'direction' => 'desc');
|
||||
$Controller->request->params['named'] = array('sort' => 'id', 'direction' => 'desc');
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual($results, array(3, 2, 1));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'NotExisting.field', 'direction' => 'desc');
|
||||
$Controller->request->params['named'] = array('sort' => 'NotExisting.field', 'direction' => 'desc');
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1, 'Invalid field in query %s');
|
||||
$this->assertEqual($results, array(1, 2, 3));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'PaginatorControllerPost.author_id', 'direction' => 'allYourBase');
|
||||
$Controller->request->params['named'] = array(
|
||||
'sort' => 'PaginatorControllerPost.author_id', 'direction' => 'allYourBase'
|
||||
);
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->PaginatorControllerPost->lastQuery['order'][0], array('PaginatorControllerPost.author_id' => 'asc'));
|
||||
$this->assertEqual($results, array(1, 3, 2));
|
||||
|
||||
$Controller->passedArgs = array('page' => '1 " onclick="alert(\'xss\');">');
|
||||
$Controller->Paginator->settings = array('limit' => 1);
|
||||
$Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['page'], 1, 'XSS exploit opened %s');
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['options']['page'], 1, 'XSS exploit opened %s');
|
||||
|
||||
$Controller->passedArgs = array();
|
||||
$Controller->Paginator->settings = array('limit' => 0);
|
||||
$Controller->request->params['named'] = array();
|
||||
$Controller->Paginator->settings = array('limit' => 0, 'maxLimit' => 10, 'paramType' => 'named');
|
||||
$Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
|
||||
|
||||
$Controller->passedArgs = array();
|
||||
$Controller->Paginator->settings = array('limit' => 'garbage!');
|
||||
$Controller->request->params['named'] = array();
|
||||
$Controller->Paginator->settings = array('limit' => 'garbage!', 'maxLimit' => 10, 'paramType' => 'named');
|
||||
$Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
|
||||
|
||||
$Controller->passedArgs = array();
|
||||
$Controller->Paginator->settings = array('limit' => '-1');
|
||||
$Controller->request->params['named'] = array();
|
||||
$Controller->Paginator->settings = array('limit' => '-1', 'maxLimit' => 10, 'paramType' => 'named');
|
||||
$Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['limit'], 1);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['pageCount'], 3);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['prevPage'], false);
|
||||
$this->assertIdentical($Controller->params['paging']['PaginatorControllerPost']['nextPage'], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that non-numeric values are rejected for page, and limit
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testPageParamCasting() {
|
||||
$this->Controller->Post->expects($this->at(0))
|
||||
->method('hasMethod')
|
||||
->with('paginateCount')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->Controller->Post->expects($this->at(1))
|
||||
->method('find')
|
||||
->will($this->returnValue(2));
|
||||
|
||||
$this->Controller->Post->expects($this->at(2))
|
||||
->method('hasMethod')
|
||||
->with('paginate')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->Controller->Post->expects($this->at(3))
|
||||
->method('find')
|
||||
->will($this->returnValue(array('stuff')));
|
||||
|
||||
$this->request->params['named'] = array('page' => '1 " onclick="alert(\'xss\');">');
|
||||
$this->Paginator->settings = array('limit' => 1, 'maxLimit' => 10, 'paramType' => 'named');
|
||||
$this->Paginator->paginate('Post');
|
||||
$this->assertSame(1, $this->request->params['paging']['Post']['page'], 'XSS exploit opened');
|
||||
}
|
||||
|
||||
/**
|
||||
* testPaginateExtraParams method
|
||||
*
|
||||
|
@ -301,120 +343,118 @@ class PaginatorTest extends CakeTestCase {
|
|||
* @return void
|
||||
*/
|
||||
function testPaginateExtraParams() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
|
||||
$Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
|
||||
$Controller->passedArgs[] = '1';
|
||||
$Controller->request->params['pass'] = array('1');
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$Controller->passedArgs = array('page' => '-1', 'contain' => array('PaginatorControllerComment'));
|
||||
$Controller->request->params['named'] = array('page' => '-1', 'contain' => array('PaginatorControllerComment'));
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.id'), array(1, 2, 3));
|
||||
$this->assertTrue(!isset($Controller->PaginatorControllerPost->lastQuery['contain']));
|
||||
|
||||
$Controller->passedArgs = array('page' => '-1');
|
||||
$Controller->Paginator->settings = array('PaginatorControllerPost' => array('contain' => array('PaginatorControllerComment')));
|
||||
$Controller->request->params['named'] = array('page' => '-1');
|
||||
$Controller->Paginator->settings = array(
|
||||
'PaginatorControllerPost' => array(
|
||||
'contain' => array('PaginatorControllerComment'),
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
),
|
||||
);
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['page'], 1);
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.id'), array(1, 2, 3));
|
||||
$this->assertTrue(isset($Controller->PaginatorControllerPost->lastQuery['contain']));
|
||||
|
||||
$Controller->Paginator->settings = array('PaginatorControllerPost' => array('popular', 'fields' => array('id', 'title')));
|
||||
$Controller->Paginator->settings = array(
|
||||
'PaginatorControllerPost' => array(
|
||||
'popular', 'fields' => array('id', 'title'), 'maxLimit' => 10, 'paramType' => 'named'
|
||||
),
|
||||
);
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.id'), array(2, 3));
|
||||
$this->assertEqual($Controller->PaginatorControllerPost->lastQuery['conditions'], array('PaginatorControllerPost.id > ' => '1'));
|
||||
|
||||
$Controller->passedArgs = array('limit' => 12);
|
||||
$Controller->Paginator->settings = array('limit' => 30);
|
||||
$Controller->request->params['named'] = array('limit' => 12);
|
||||
$Controller->Paginator->settings = array('limit' => 30, 'maxLimit' => 100, 'paramType' => 'named');
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$paging = $Controller->params['paging']['PaginatorControllerPost'];
|
||||
|
||||
$this->assertEqual($Controller->PaginatorControllerPost->lastQuery['limit'], 12);
|
||||
$this->assertEqual($paging['options']['limit'], 12);
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->uses = array('ControllerPaginateModel');
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->request->query = array();
|
||||
$Controller->constructClasses();
|
||||
$Controller->Paginator->settings = array(
|
||||
'ControllerPaginateModel' => array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id')
|
||||
'ControllerPaginateModel' => array(
|
||||
'contain' => array('ControllerPaginateModel'),
|
||||
'group' => 'Comment.author_id',
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
)
|
||||
);
|
||||
$result = $Controller->Paginator->paginate('ControllerPaginateModel');
|
||||
$expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id');
|
||||
$expected = array(
|
||||
'contain' => array('ControllerPaginateModel'),
|
||||
'group' => 'Comment.author_id',
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
);
|
||||
$this->assertEqual($Controller->ControllerPaginateModel->extra, $expected);
|
||||
$this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected);
|
||||
|
||||
$Controller->Paginator->settings = array(
|
||||
'ControllerPaginateModel' => array('foo', 'contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id')
|
||||
'ControllerPaginateModel' => array(
|
||||
'foo', 'contain' => array('ControllerPaginateModel'),
|
||||
'group' => 'Comment.author_id',
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
)
|
||||
);
|
||||
$Controller->Paginator->paginate('ControllerPaginateModel');
|
||||
$expected = array('contain' => array('ControllerPaginateModel'), 'group' => 'Comment.author_id', 'type' => 'foo');
|
||||
$expected = array(
|
||||
'contain' => array('ControllerPaginateModel'),
|
||||
'group' => 'Comment.author_id',
|
||||
'type' => 'foo',
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
);
|
||||
$this->assertEqual($Controller->ControllerPaginateModel->extra, $expected);
|
||||
$this->assertEqual($Controller->ControllerPaginateModel->extraCount, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* testPaginatePassedArgs method
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function testPaginatePassedArgs() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller->uses = array('PaginatorControllerPost');
|
||||
$Controller->passedArgs[] = array('1', '2', '3');
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$Controller->Paginator->settings = array(
|
||||
'fields' => array(),
|
||||
'order' => '',
|
||||
'limit' => 5,
|
||||
'page' => 1,
|
||||
'recursive' => -1
|
||||
);
|
||||
$conditions = array();
|
||||
$Controller->Paginator->paginate('PaginatorControllerPost',$conditions);
|
||||
|
||||
$expected = array(
|
||||
'fields' => array(),
|
||||
'order' => '',
|
||||
'limit' => 5,
|
||||
'page' => 1,
|
||||
'recursive' => -1,
|
||||
'conditions' => array()
|
||||
);
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options'],$expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that special paginate types are called and that the type param doesn't leak out into defaults or options.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testPaginateSpecialType() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
|
||||
$Controller->passedArgs[] = '1';
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$Controller->Paginator->settings = array('PaginatorControllerPost' => array('popular', 'fields' => array('id', 'title')));
|
||||
$Controller->Paginator->settings = array(
|
||||
'PaginatorControllerPost' => array(
|
||||
'popular',
|
||||
'fields' => array('id', 'title'),
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
)
|
||||
);
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.id'), array(2, 3));
|
||||
$this->assertEqual($Controller->PaginatorControllerPost->lastQuery['conditions'], array('PaginatorControllerPost.id > ' => '1'));
|
||||
$this->assertFalse(isset($Controller->params['paging']['PaginatorControllerPost']['defaults'][0]));
|
||||
$this->assertEqual(
|
||||
$Controller->PaginatorControllerPost->lastQuery['conditions'],
|
||||
array('PaginatorControllerPost.id > ' => '1')
|
||||
);
|
||||
$this->assertFalse(isset($Controller->params['paging']['PaginatorControllerPost']['options'][0]));
|
||||
}
|
||||
|
||||
|
@ -425,17 +465,17 @@ class PaginatorTest extends CakeTestCase {
|
|||
* @return void
|
||||
*/
|
||||
function testDefaultPaginateParams() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->modelClass = 'PaginatorControllerPost';
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
$Controller->Paginator->settings = array('order' => 'PaginatorControllerPost.id DESC');
|
||||
$Controller->Paginator->settings = array(
|
||||
'order' => 'PaginatorControllerPost.id DESC',
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
);
|
||||
$results = Set::extract($Controller->Paginator->paginate('PaginatorControllerPost'), '{n}.PaginatorControllerPost.id');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['defaults']['order'], 'PaginatorControllerPost.id DESC');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['order'], 'PaginatorControllerPost.id DESC');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['order'], 'PaginatorControllerPost.id DESC');
|
||||
$this->assertEqual($results, array(3, 2, 1));
|
||||
}
|
||||
|
||||
|
@ -445,10 +485,7 @@ class PaginatorTest extends CakeTestCase {
|
|||
* @return void
|
||||
*/
|
||||
function testPaginateOrderVirtualField() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->uses = array('PaginatorControllerPost', 'PaginatorControllerComment');
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
@ -458,12 +495,14 @@ class PaginatorTest extends CakeTestCase {
|
|||
|
||||
$Controller->Paginator->settings = array(
|
||||
'fields' => array('id', 'title', 'offset_test'),
|
||||
'order' => array('offset_test' => 'DESC')
|
||||
'order' => array('offset_test' => 'DESC'),
|
||||
'maxLimit' => 10,
|
||||
'paramType' => 'named'
|
||||
);
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.offset_test'), array(4, 3, 2));
|
||||
|
||||
$Controller->passedArgs = array('sort' => 'offset_test', 'direction' => 'asc');
|
||||
$Controller->request->params['named'] = array('sort' => 'offset_test', 'direction' => 'asc');
|
||||
$result = $Controller->Paginator->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual(Set::extract($result, '{n}.PaginatorControllerPost.offset_test'), array(2, 3, 4));
|
||||
}
|
||||
|
@ -474,11 +513,252 @@ class PaginatorTest extends CakeTestCase {
|
|||
* @expectedException MissingModelException
|
||||
*/
|
||||
function testPaginateMissingModel() {
|
||||
$request = new CakeRequest('controller_posts/index');
|
||||
$request->params['pass'] = $request->params['named'] = array();
|
||||
|
||||
$Controller = new PaginatorTestController($request);
|
||||
$Controller = new PaginatorTestController($this->request);
|
||||
$Controller->constructClasses();
|
||||
$Controller->Paginator->paginate('MissingModel');
|
||||
$Controller->Paginator->paginate('MissingModel');
|
||||
}
|
||||
|
||||
/**
|
||||
* test that option merging prefers specific models
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMergeOptionsModelSpecific() {
|
||||
$this->Paginator->settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'named',
|
||||
'Post' => array(
|
||||
'page' => 1,
|
||||
'limit' => 10,
|
||||
'maxLimit' => 50,
|
||||
'paramType' => 'named',
|
||||
)
|
||||
);
|
||||
$result = $this->Paginator->mergeOptions('Silly');
|
||||
$this->assertEquals($this->Paginator->settings, $result);
|
||||
|
||||
$result = $this->Paginator->mergeOptions('Post');
|
||||
$expected = array('page' => 1, 'limit' => 10, 'paramType' => 'named', 'maxLimit' => 50);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test mergeOptions with named params.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMergeOptionsNamedParams() {
|
||||
$this->request->params['named'] = array(
|
||||
'page' => 10,
|
||||
'limit' => 10
|
||||
);
|
||||
$this->Paginator->settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'named',
|
||||
);
|
||||
$result = $this->Paginator->mergeOptions('Post');
|
||||
$expected = array('page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named');
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test merging options from the querystring.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMergeOptionsQueryString() {
|
||||
$this->request->params['named'] = array(
|
||||
'page' => 10,
|
||||
'limit' => 10
|
||||
);
|
||||
$this->request->query = array(
|
||||
'page' => 99,
|
||||
'limit' => 75
|
||||
);
|
||||
$this->Paginator->settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'querystring',
|
||||
);
|
||||
$result = $this->Paginator->mergeOptions('Post');
|
||||
$expected = array('page' => 99, 'limit' => 75, 'maxLimit' => 100, 'paramType' => 'querystring');
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that the default whitelist doesn't let people screw with things they should not be allowed to.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMergeOptionsDefaultWhiteList() {
|
||||
$this->request->params['named'] = array(
|
||||
'page' => 10,
|
||||
'limit' => 10,
|
||||
'fields' => array('bad.stuff'),
|
||||
'recursive' => 1000,
|
||||
'conditions' => array('bad.stuff'),
|
||||
'contain' => array('bad')
|
||||
);
|
||||
$this->Paginator->settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'named',
|
||||
);
|
||||
$result = $this->Paginator->mergeOptions('Post');
|
||||
$expected = array('page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named');
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that modifying the whitelist works.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMergeOptionsExtraWhitelist() {
|
||||
$this->request->params['named'] = array(
|
||||
'page' => 10,
|
||||
'limit' => 10,
|
||||
'fields' => array('bad.stuff'),
|
||||
'recursive' => 1000,
|
||||
'conditions' => array('bad.stuff'),
|
||||
'contain' => array('bad')
|
||||
);
|
||||
$this->Paginator->settings = array(
|
||||
'page' => 1,
|
||||
'limit' => 20,
|
||||
'maxLimit' => 100,
|
||||
'paramType' => 'named',
|
||||
);
|
||||
$this->Paginator->whitelist[] = 'fields';
|
||||
$result = $this->Paginator->mergeOptions('Post');
|
||||
$expected = array(
|
||||
'page' => 10, 'limit' => 10, 'maxLimit' => 100, 'paramType' => 'named', 'fields' => array('bad.stuff')
|
||||
);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that invalid directions are ignored.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testValidateSortInvalidDirection() {
|
||||
$model = $this->getMock('Model');
|
||||
$model->alias = 'model';
|
||||
$model->expects($this->any())->method('hasField')->will($this->returnValue(true));
|
||||
|
||||
$options = array('sort' => 'something', 'direction' => 'boogers');
|
||||
$result = $this->Paginator->validateSort($model, $options);
|
||||
|
||||
$this->assertEquals('asc', $result['order']['model.something']);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that fields not in whitelist won't be part of order conditions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testValidateSortWhitelistFailure() {
|
||||
$model = $this->getMock('Model');
|
||||
$model->alias = 'model';
|
||||
$model->expects($this->any())->method('hasField')->will($this->returnValue(true));
|
||||
|
||||
$options = array('sort' => 'body', 'direction' => 'asc');
|
||||
$result = $this->Paginator->validateSort($model, $options, array('title', 'id'));
|
||||
|
||||
$this->assertNull($result['order']);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that virtual fields work.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testValidateSortVirtualField() {
|
||||
$model = $this->getMock('Model');
|
||||
$model->alias = 'model';
|
||||
|
||||
$model->expects($this->at(0))
|
||||
->method('hasField')
|
||||
->with('something')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$model->expects($this->at(1))
|
||||
->method('hasField')
|
||||
->with('something', true)
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$options = array('sort' => 'something', 'direction' => 'desc');
|
||||
$result = $this->Paginator->validateSort($model, $options);
|
||||
|
||||
$this->assertEquals('desc', $result['order']['something']);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that maxLimit is respected
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testCheckLimit() {
|
||||
$result = $this->Paginator->checkLimit(array('limit' => 1000000, 'maxLimit' => 100));
|
||||
$this->assertEquals(100, $result['limit']);
|
||||
|
||||
$result = $this->Paginator->checkLimit(array('limit' => 'sheep!', 'maxLimit' => 100));
|
||||
$this->assertEquals(1, $result['limit']);
|
||||
|
||||
$result = $this->Paginator->checkLimit(array('limit' => '-1', 'maxLimit' => 100));
|
||||
$this->assertEquals(1, $result['limit']);
|
||||
|
||||
$result = $this->Paginator->checkLimit(array('limit' => null, 'maxLimit' => 100));
|
||||
$this->assertEquals(1, $result['limit']);
|
||||
|
||||
$result = $this->Paginator->checkLimit(array('limit' => 0, 'maxLimit' => 100));
|
||||
$this->assertEquals(1, $result['limit']);
|
||||
}
|
||||
|
||||
/**
|
||||
* testPaginateMaxLimit
|
||||
*
|
||||
* @return void
|
||||
* @access public
|
||||
*/
|
||||
function testPaginateMaxLimit() {
|
||||
$Controller = new Controller($this->request);
|
||||
|
||||
$Controller->uses = array('PaginatorControllerPost', 'ControllerComment');
|
||||
$Controller->passedArgs[] = '1';
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$Controller->request->params['named'] = array(
|
||||
'contain' => array('ControllerComment'), 'limit' => '1000'
|
||||
);
|
||||
$result = $Controller->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['limit'], 100);
|
||||
|
||||
$Controller->request->params['named'] = array(
|
||||
'contain' => array('ControllerComment'), 'limit' => '1000', 'maxLimit' => 1000
|
||||
);
|
||||
$result = $Controller->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['limit'], 100);
|
||||
|
||||
$Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '10');
|
||||
$result = $Controller->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['limit'], 10);
|
||||
|
||||
$Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '1000');
|
||||
$Controller->paginate = array('maxLimit' => 2000, 'paramType' => 'named');
|
||||
$result = $Controller->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['limit'], 1000);
|
||||
|
||||
$Controller->request->params['named'] = array('contain' => array('ControllerComment'), 'limit' => '5000');
|
||||
$result = $Controller->paginate('PaginatorControllerPost');
|
||||
$this->assertEqual($Controller->params['paging']['PaginatorControllerPost']['options']['limit'], 2000);
|
||||
}
|
||||
}
|
|
@ -1255,7 +1255,8 @@ class ControllerTest extends CakeTestCase {
|
|||
$Controller->passedArgs[] = '1';
|
||||
$Controller->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
$this->assertEqual($Controller->paginate, array('page' => 1, 'limit' => 20));
|
||||
$expected = array('page' => 1, 'limit' => 20, 'maxLimit' => 100, 'paramType' => 'named');
|
||||
$this->assertEqual($Controller->paginate, $expected);
|
||||
|
||||
$results = Set::extract($Controller->paginate('ControllerPost'), '{n}.ControllerPost.id');
|
||||
$this->assertEqual($results, array(1, 2, 3));
|
||||
|
|
|
@ -340,7 +340,16 @@ class TestBehavior extends ModelBehavior {
|
|||
*
|
||||
* @package cake.tests.cases.libs.model
|
||||
*/
|
||||
class Test2Behavior extends TestBehavior{
|
||||
class Test2Behavior extends TestBehavior {
|
||||
public $mapMethods = array('/mappingRobot(\w+)/' => 'mapped');
|
||||
|
||||
function resolveMethod($model, $stuff) {
|
||||
|
||||
}
|
||||
|
||||
function mapped($model, $method, $query) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1026,34 +1035,6 @@ class BehaviorCollectionTest extends CakeTestCase {
|
|||
$this->assertTrue($Apple->testData('one', 'two', 'three', 'four', 'five', 'six'));
|
||||
}
|
||||
|
||||
/**
|
||||
* testBehaviorTrigger method
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function testBehaviorTrigger() {
|
||||
$Apple = new Apple();
|
||||
$Apple->Behaviors->attach('Test');
|
||||
$Apple->Behaviors->attach('Test2');
|
||||
$Apple->Behaviors->attach('Test3');
|
||||
|
||||
$Apple->beforeTestResult = array();
|
||||
$Apple->Behaviors->trigger('beforeTest', array(&$Apple));
|
||||
$expected = array('testbehavior', 'test2behavior', 'test3behavior');
|
||||
$this->assertIdentical($Apple->beforeTestResult, $expected);
|
||||
|
||||
$Apple->beforeTestResult = array();
|
||||
$Apple->Behaviors->trigger('beforeTest', array(&$Apple), array('break' => true, 'breakOn' => 'test2behavior'));
|
||||
$expected = array('testbehavior', 'test2behavior');
|
||||
$this->assertIdentical($Apple->beforeTestResult, $expected);
|
||||
|
||||
$Apple->beforeTestResult = array();
|
||||
$Apple->Behaviors->trigger('beforeTest', array($Apple), array('break' => true, 'breakOn' => array('test2behavior', 'test3behavior')));
|
||||
$expected = array('testbehavior', 'test2behavior');
|
||||
$this->assertIdentical($Apple->beforeTestResult, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* undocumented function
|
||||
*
|
||||
|
@ -1116,4 +1097,59 @@ class BehaviorCollectionTest extends CakeTestCase {
|
|||
|
||||
$Sample->Behaviors->trigger('beforeTest', array(&$Sample));
|
||||
}
|
||||
|
||||
/**
|
||||
* test that hasMethod works with basic functions.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testHasMethodBasic() {
|
||||
$Sample = new Sample();
|
||||
$Collection = new BehaviorCollection();
|
||||
$Collection->init('Sample', array('Test', 'Test2'));
|
||||
|
||||
$this->assertTrue($Collection->hasMethod('testMethod'));
|
||||
$this->assertTrue($Collection->hasMethod('resolveMethod'));
|
||||
|
||||
$this->assertFalse($Collection->hasMethod('No method'));
|
||||
}
|
||||
|
||||
/**
|
||||
* test that hasMethod works with mapped methods.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testHasMethodMappedMethods() {
|
||||
$Sample = new Sample();
|
||||
$Collection = new BehaviorCollection();
|
||||
$Collection->init('Sample', array('Test', 'Test2'));
|
||||
|
||||
$this->assertTrue($Collection->hasMethod('look for the remote in the couch'));
|
||||
$this->assertTrue($Collection->hasMethod('mappingRobotOnTheRoof'));
|
||||
}
|
||||
|
||||
/**
|
||||
* test hasMethod returrning a 'callback'
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testHasMethodAsCallback() {
|
||||
$Sample = new Sample();
|
||||
$Collection = new BehaviorCollection();
|
||||
$Collection->init('Sample', array('Test', 'Test2'));
|
||||
|
||||
$result = $Collection->hasMethod('testMethod', true);
|
||||
$expected = array('Test', 'testMethod');
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$result = $Collection->hasMethod('resolveMethod', true);
|
||||
$expected = array('Test2', 'resolveMethod');
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$result = $Collection->hasMethod('mappingRobotOnTheRoof', true);
|
||||
$expected = array('Test2', 'mapped', 'mappingRobotOnTheRoof');
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -3189,112 +3189,6 @@ class ContainableBehaviorTest extends CakeTestCase {
|
|||
$this->assertEqual($result, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* testPaginate method
|
||||
*
|
||||
* @access public
|
||||
* @return void
|
||||
*/
|
||||
function testPaginate() {
|
||||
App::import('Core', 'Controller');
|
||||
$Controller = new Controller($this->getMock('CakeRequest'));
|
||||
$Controller->uses = array('Article');
|
||||
$Controller->passedArgs[] = '1';
|
||||
$Controller->request->params['url'] = array();
|
||||
$Controller->constructClasses();
|
||||
|
||||
$Controller->paginate = array('Article' => array('fields' => array('title'), 'contain' => array('User(user)')));
|
||||
$result = $Controller->paginate('Article');
|
||||
$expected = array(
|
||||
array('Article' => array('title' => 'First Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
|
||||
array('Article' => array('title' => 'Second Article'), 'User' => array('user' => 'larry', 'id' => 3)),
|
||||
array('Article' => array('title' => 'Third Article'), 'User' => array('user' => 'mariano', 'id' => 1)),
|
||||
);
|
||||
$this->assertEqual($result, $expected);
|
||||
|
||||
$r = $Controller->Article->find('all');
|
||||
$this->assertTrue(Set::matches('/Article[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/User[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/Tag[id=1]', $r));
|
||||
|
||||
$Controller->paginate = array('Article' => array('contain' => array('Comment(comment)' => 'User(user)'), 'fields' => array('title')));
|
||||
$result = $Controller->paginate('Article');
|
||||
$expected = array(
|
||||
array(
|
||||
'Article' => array('title' => 'First Article', 'id' => 1),
|
||||
'Comment' => array(
|
||||
array(
|
||||
'comment' => 'First Comment for First Article',
|
||||
'user_id' => 2,
|
||||
'article_id' => 1,
|
||||
'User' => array('user' => 'nate')
|
||||
),
|
||||
array(
|
||||
'comment' => 'Second Comment for First Article',
|
||||
'user_id' => 4,
|
||||
'article_id' => 1,
|
||||
'User' => array('user' => 'garrett')
|
||||
),
|
||||
array(
|
||||
'comment' => 'Third Comment for First Article',
|
||||
'user_id' => 1,
|
||||
'article_id' => 1,
|
||||
'User' => array('user' => 'mariano')
|
||||
),
|
||||
array(
|
||||
'comment' => 'Fourth Comment for First Article',
|
||||
'user_id' => 1,
|
||||
'article_id' => 1,
|
||||
'User' => array('user' => 'mariano')
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'Article' => array('title' => 'Second Article', 'id' => 2),
|
||||
'Comment' => array(
|
||||
array(
|
||||
'comment' => 'First Comment for Second Article',
|
||||
'user_id' => 1,
|
||||
'article_id' => 2,
|
||||
'User' => array('user' => 'mariano')
|
||||
),
|
||||
array(
|
||||
'comment' => 'Second Comment for Second Article',
|
||||
'user_id' => 2,
|
||||
'article_id' => 2,
|
||||
'User' => array('user' => 'nate')
|
||||
)
|
||||
)
|
||||
),
|
||||
array(
|
||||
'Article' => array('title' => 'Third Article', 'id' => 3),
|
||||
'Comment' => array()
|
||||
),
|
||||
);
|
||||
$this->assertEqual($result, $expected);
|
||||
|
||||
$r = $Controller->Article->find('all');
|
||||
$this->assertTrue(Set::matches('/Article[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/User[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/Tag[id=1]', $r));
|
||||
|
||||
$Controller->Article->unbindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User'), 'hasAndBelongsToMany' => array('Tag')), false);
|
||||
$Controller->Article->bindModel(array('hasMany' => array('Comment'), 'belongsTo' => array('User')), false);
|
||||
|
||||
$Controller->paginate = array('Article' => array('contain' => array('Comment(comment)', 'User(user)'), 'fields' => array('title')));
|
||||
$r = $Controller->paginate('Article');
|
||||
$this->assertTrue(Set::matches('/Article[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/User[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
|
||||
$this->assertFalse(Set::matches('/Comment[id=1]', $r));
|
||||
|
||||
$r = $this->Article->find('all');
|
||||
$this->assertTrue(Set::matches('/Article[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/User[id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/Comment[article_id=1]', $r));
|
||||
$this->assertTrue(Set::matches('/Comment[id=1]', $r));
|
||||
}
|
||||
|
||||
/**
|
||||
* testOriginalAssociations method
|
||||
*
|
||||
|
|
|
@ -2008,4 +2008,27 @@ class ModelIntegrationTest extends BaseModelTest {
|
|||
$expected = $db->name('Domain.DomainHandle');
|
||||
$this->assertEqual($result, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that model->hasMethod checks self and behaviors.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testHasMethod() {
|
||||
$Article = new Article();
|
||||
$Article->Behaviors = $this->getMock('BehaviorCollection');
|
||||
|
||||
$Article->Behaviors->expects($this->at(0))
|
||||
->method('hasMethod')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$Article->Behaviors->expects($this->at(1))
|
||||
->method('hasMethod')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$this->assertTrue($Article->hasMethod('find'));
|
||||
|
||||
$this->assertTrue($Article->hasMethod('pass'));
|
||||
$this->assertFalse($Article->hasMethod('fail'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -191,8 +191,7 @@ class CakeRouteTestCase extends CakeTestCase {
|
|||
$this->assertEqual($route->options, array('extra' => '[a-z1-9_]*', 'slug' => '[a-z1-9_]+', 'action' => 'view'));
|
||||
$expected = array(
|
||||
'controller' => 'pages',
|
||||
'action' => 'view',
|
||||
'extra' => null,
|
||||
'action' => 'view'
|
||||
);
|
||||
$this->assertEqual($route->defaults, $expected);
|
||||
|
||||
|
@ -289,6 +288,45 @@ class CakeRouteTestCase extends CakeTestCase {
|
|||
$this->assertEqual($result, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that non-greedy routes fail with extra passed args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testGreedyRouteFailurePassedArg() {
|
||||
$route = new CakeRoute('/:controller/:action', array('plugin' => null));
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', '0'));
|
||||
$this->assertFalse($result);
|
||||
|
||||
$route = new CakeRoute('/:controller/:action', array('plugin' => null));
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'test'));
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that non-greedy routes fail with extra passed args
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testGreedyRouteFailureNamedParam() {
|
||||
$route = new CakeRoute('/:controller/:action', array('plugin' => null));
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'page' => 1));
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that falsey values do not interrupt a match.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testMatchWithFalseyValues() {
|
||||
$route = new CakeRoute('/:controller/:action/*', array('plugin' => null));
|
||||
$result = $route->match(array(
|
||||
'controller' => 'posts', 'action' => 'index', 'plugin' => null, 'admin' => false
|
||||
));
|
||||
$this->assertEqual($result, '/posts/index/');
|
||||
}
|
||||
|
||||
/**
|
||||
* test match() with greedy routes, named parameters and passed args.
|
||||
*
|
||||
|
@ -304,6 +342,12 @@ class CakeRouteTestCase extends CakeTestCase {
|
|||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5));
|
||||
$this->assertEqual($result, '/posts/view/5');
|
||||
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 0));
|
||||
$this->assertEqual($result, '/posts/view/0');
|
||||
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, '0'));
|
||||
$this->assertEqual($result, '/posts/view/0');
|
||||
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'view', 'plugin' => null, 5, 'page' => 1, 'limit' => 20, 'order' => 'title'));
|
||||
$this->assertEqual($result, '/posts/view/5/page:1/limit:20/order:title');
|
||||
|
||||
|
@ -319,6 +363,17 @@ class CakeRouteTestCase extends CakeTestCase {
|
|||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that named params with null/false are excluded
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
function testNamedParamsWithNullFalse() {
|
||||
$route = new CakeRoute('/:controller/:action/*');
|
||||
$result = $route->match(array('controller' => 'posts', 'action' => 'index', 'page' => null, 'sort' => false));
|
||||
$this->assertEquals('/posts/index/', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* test that match with patterns works.
|
||||
*
|
||||
|
@ -417,4 +472,5 @@ class CakeRouteTestCase extends CakeTestCase {
|
|||
$result = $route->parse('/blog/foobar');
|
||||
$this->assertFalse($result);
|
||||
}
|
||||
|
||||
}
|
|
@ -648,7 +648,7 @@ class RouterTest extends CakeTestCase {
|
|||
|
||||
$result = Router::url(array('page' => 3));
|
||||
$expected = '/magazine/admin/subscriptions/index/page:3';
|
||||
$this->assertEqual($result, $expected);
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
Router::reload();
|
||||
Router::connect('/admin/subscriptions/:action/*', array('controller' => 'subscribe', 'admin' => true, 'prefix' => 'admin'));
|
||||
|
@ -1333,14 +1333,14 @@ class RouterTest extends CakeTestCase {
|
|||
|
||||
Router::reload();
|
||||
Router::connect('/foo/*', array('controller' => 'bar', 'action' => 'fubar'));
|
||||
Router::connectNamed(array(), array('argSeparator' => '='));
|
||||
Router::connectNamed(array(), array('separator' => '='));
|
||||
$result = Router::parse('/foo/param1=value1/param2=value2');
|
||||
$expected = array('pass' => array(), 'named' => array('param1' => 'value1', 'param2' => 'value2'), 'controller' => 'bar', 'action' => 'fubar', 'plugin' => null);
|
||||
$this->assertEqual($result, $expected);
|
||||
|
||||
Router::reload();
|
||||
Router::connect('/controller/action/*', array('controller' => 'controller', 'action' => 'action'), array('named' => array('param1' => 'value[\d]')));
|
||||
Router::connectNamed(array(), array('greedy' => false, 'argSeparator' => '='));
|
||||
Router::connectNamed(array(), array('greedy' => false, 'separator' => '='));
|
||||
$result = Router::parse('/controller/action/param1=value1/param2=value2');
|
||||
$expected = array('pass' => array('param2=value2'), 'named' => array('param1' => 'value1'), 'controller' => 'controller', 'action' => 'action', 'plugin' => null);
|
||||
$this->assertEqual($result, $expected);
|
||||
|
@ -1348,7 +1348,7 @@ class RouterTest extends CakeTestCase {
|
|||
Router::reload();
|
||||
Router::connect('/:controller/:action/*');
|
||||
Router::connectNamed(array('page'), array('default' => false, 'greedy' => false));
|
||||
$result = Router::parse('/categories/index?limit=5');
|
||||
$result = Router::parse('/categories/index/limit=5');
|
||||
$this->assertTrue(empty($result['named']));
|
||||
}
|
||||
|
||||
|
@ -1514,7 +1514,7 @@ class RouterTest extends CakeTestCase {
|
|||
|
||||
$result = Router::url(array('controller' => 'images', 'action' => 'add'));
|
||||
$expected = '/images/add';
|
||||
$this->assertEqual($result, $expected);
|
||||
$this->assertEquals($expected, $result);
|
||||
|
||||
$result = Router::url(array('controller' => 'images', 'action' => 'add', 'protected' => true));
|
||||
$expected = '/protected/images/add';
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue