ArrayContext.php 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since CakePHP(tm) v 3.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View\Form;
  16. use Cake\Network\Request;
  17. use Cake\Utility\Hash;
  18. /**
  19. * Provides a basic array based context provider for FormHelper.
  20. *
  21. * This adapter is useful in testing or when you have forms backed by
  22. * simple array data structures.
  23. *
  24. * Important keys:
  25. *
  26. * - `defaults` The default values for fields. These values
  27. * will be used when there is no request data set. Data should be nested following
  28. * the dot separated paths you access your fields with.
  29. * - `required` A nested array of fields, relationships and boolean
  30. * flags to indicate a field is required.
  31. * - `schema` An array of data that emulate the column structures that
  32. * Cake\Database\Schema\Table uses. This array allows you to control
  33. * the inferred type for fields and allows auto generation of attributes
  34. * like maxlength, step and other HTML attributes. If you want
  35. * primary key/id detection to work. Make sure you have provided a `_constraints`
  36. * array that contains `primary`. See below for an exmaple.
  37. * - `errors` An array of validation errors. Errors should be nested following
  38. * the dot separated paths you access your fields with.
  39. *
  40. * ### Example
  41. *
  42. * {{{
  43. * $data = [
  44. * 'schema' => [
  45. * 'id' => ['type' => 'integer'],
  46. * 'title' => ['type' => 'string', 'length' => 255],
  47. * '_constraints' => [
  48. * 'primary' => ['type' => 'primary', 'columns' => ['id']]
  49. * ]
  50. * ],
  51. * 'defaults' => [
  52. * 'id' => 1,
  53. * 'title' => 'First post!',
  54. * ]
  55. * ];
  56. * }}}
  57. */
  58. class ArrayContext implements ContextInterface {
  59. /**
  60. * The request object.
  61. *
  62. * @var \Cake\Network\Request
  63. */
  64. protected $_request;
  65. /**
  66. * Context data for this object.
  67. *
  68. * @var array
  69. */
  70. protected $_context;
  71. /**
  72. * Constructor.
  73. *
  74. * @param \Cake\Network\Request
  75. * @param array
  76. */
  77. public function __construct(Request $request, array $context) {
  78. $this->_request = $request;
  79. $context += [
  80. 'schema' => [],
  81. 'required' => [],
  82. 'defaults' => [],
  83. 'errors' => [],
  84. ];
  85. $this->_context = $context;
  86. }
  87. /**
  88. * Get the fields used in the context as a primary key.
  89. *
  90. * @return array
  91. */
  92. public function primaryKey() {
  93. if (
  94. empty($this->_context['schema']['_constraints']) ||
  95. !is_array($this->_context['schema']['_constraints'])
  96. ) {
  97. return [];
  98. }
  99. foreach ($this->_context['schema']['_constraints'] as $data) {
  100. if (isset($data['type']) && $data['type'] === 'primary') {
  101. return isset($data['columns']) ? (array)$data['columns'] : [];
  102. }
  103. }
  104. return [];
  105. }
  106. /**
  107. * {@inheritDoc}
  108. */
  109. public function isPrimaryKey($field) {
  110. $primaryKey = $this->primaryKey();
  111. return in_array($field, $primaryKey);
  112. }
  113. /**
  114. * Returns whether or not this form is for a create operation.
  115. *
  116. * For this method to return true, both the primary key constraint
  117. * must be defined in the 'schema' data, and the 'defaults' data must
  118. * contain a value for all fields in the key.
  119. *
  120. * @return boolean
  121. */
  122. public function isCreate() {
  123. $primary = $this->primaryKey();
  124. foreach ($primary as $column) {
  125. if (!empty($this->_context['defaults'][$column])) {
  126. return false;
  127. }
  128. }
  129. return true;
  130. }
  131. /**
  132. * Get the current value for a given field.
  133. *
  134. * This method will coalesce the current request data and the 'defaults'
  135. * array.
  136. *
  137. * @param string $field A dot separated path to the field a value
  138. * is needed for.
  139. * @return mixed
  140. */
  141. public function val($field) {
  142. $val = $this->_request->data($field);
  143. if ($val !== null) {
  144. return $val;
  145. }
  146. if (empty($this->_context['defaults']) || !is_array($this->_context['defaults'])) {
  147. return null;
  148. }
  149. return Hash::get($this->_context['defaults'], $field);
  150. }
  151. /**
  152. * Check if a given field is 'required'.
  153. *
  154. * In this context class, this is simply defined by the 'required' array.
  155. *
  156. * @param string $field A dot separated path to check required-ness for.
  157. * @return boolean
  158. */
  159. public function isRequired($field) {
  160. if (!is_array($this->_context['required'])) {
  161. return false;
  162. }
  163. $required = Hash::get($this->_context['required'], $field);
  164. return (bool)$required;
  165. }
  166. /**
  167. * {@inheritDoc}
  168. */
  169. public function fieldNames() {
  170. $schema = $this->_context['schema'];
  171. unset($schema['_constraints'], $schema['_indexes']);
  172. return array_keys($schema);
  173. }
  174. /**
  175. * Get the abstract field type for a given field name.
  176. *
  177. * @param string $field A dot separated path to get a schema type for.
  178. * @return null|string An abstract data type or null.
  179. * @see \Cake\Database\Type
  180. */
  181. public function type($field) {
  182. if (!is_array($this->_context['schema'])) {
  183. return null;
  184. }
  185. $schema = Hash::get($this->_context['schema'], $field);
  186. return isset($schema['type']) ? $schema['type'] : null;
  187. }
  188. /**
  189. * Get an associative array of other attributes for a field name.
  190. *
  191. * @param string $field A dot separated path to get additional data on.
  192. * @return array An array of data describing the additional attributes on a field.
  193. */
  194. public function attributes($field) {
  195. if (!is_array($this->_context['schema'])) {
  196. return [];
  197. }
  198. $schema = (array)Hash::get($this->_context['schema'], $field);
  199. $whitelist = ['length' => null, 'precision' => null];
  200. return array_intersect_key($schema, $whitelist);
  201. }
  202. /**
  203. * Check whether or not a field has an error attached to it
  204. *
  205. * @param string $field A dot separated path to check errors on.
  206. * @return boolean Returns true if the errors for the field are not empty.
  207. */
  208. public function hasError($field) {
  209. if (empty($this->_context['errors'])) {
  210. return false;
  211. }
  212. return (bool)Hash::check($this->_context['errors'], $field);
  213. }
  214. /**
  215. * Get the errors for a given field
  216. *
  217. * @param string $field A dot separated path to check errors on.
  218. * @return array An array of errors, an empty array will be returned when the
  219. * context has no errors.
  220. */
  221. public function error($field) {
  222. if (empty($this->_context['errors'])) {
  223. return [];
  224. }
  225. return Hash::get($this->_context['errors'], $field);
  226. }
  227. }