From 3272ec09bc7ef2a6b38b5ded02b6a40956cc40ae Mon Sep 17 00:00:00 2001 From: euromark Date: Wed, 30 Apr 2014 19:49:17 +0200 Subject: [PATCH 001/292] Backport overwrite from 3.0 into 2.6 --- en/appendices/2-6-migration-guide.rst | 14 ++++++++++++++ en/console-and-shells.rst | 12 ++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 en/appendices/2-6-migration-guide.rst diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst new file mode 100644 index 0000000000..03b639c322 --- /dev/null +++ b/en/appendices/2-6-migration-guide.rst @@ -0,0 +1,14 @@ +2.6 Migration Guide +################### + +CakePHP 2.6 is a fully API compatible upgrade from 2.5. This page outlines +the changes and improvements made in 2.6. + +Console +======= + +Shell +----- + +- ``overwrite()`` has been added to allow generating progress bars or to avoid outputting + too many lines by replacing text that has been already outputted to the screen. diff --git a/en/console-and-shells.rst b/en/console-and-shells.rst index 1ba04b59f7..de561d442d 100644 --- a/en/console-and-shells.rst +++ b/en/console-and-shells.rst @@ -950,6 +950,18 @@ Shell API On windows systems, plain output is the default unless the ``ANSICON`` environment variable is present. +.. php:method:: overwrite($message = null, $newlines = 1, $size = null) + + :param string $method: The message to print. + :param integer $newlines: The number of newlines to follow the message. + :param integer $size: The number of bytes to overwrite + + A useful method to generate progress bars or to avoid outputting too many lines. + + Warning: You cannot overwrite text that contains newlines. + + .. versionadded:: 2.6 + .. php:method:: runCommand($command, $argv) Runs the Shell with the provided argv. From a72fa6a6a17710bac5397900804f0e7bdc7fae22 Mon Sep 17 00:00:00 2001 From: euromark Date: Wed, 30 Apr 2014 19:50:15 +0200 Subject: [PATCH 002/292] correct indentation --- en/console-and-shells.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/en/console-and-shells.rst b/en/console-and-shells.rst index de561d442d..adfeac7f04 100644 --- a/en/console-and-shells.rst +++ b/en/console-and-shells.rst @@ -958,9 +958,9 @@ Shell API A useful method to generate progress bars or to avoid outputting too many lines. - Warning: You cannot overwrite text that contains newlines. + Warning: You cannot overwrite text that contains newlines. - .. versionadded:: 2.6 + .. versionadded:: 2.6 .. php:method:: runCommand($command, $argv) From 503a1ea9b3ece6875fe07c729055136ac62dcf76 Mon Sep 17 00:00:00 2001 From: euromark Date: Tue, 13 May 2014 23:48:45 +0200 Subject: [PATCH 003/292] Note about parentNode() update. --- en/appendices/2-6-migration-guide.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 03b639c322..5372053977 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -12,3 +12,11 @@ Shell - ``overwrite()`` has been added to allow generating progress bars or to avoid outputting too many lines by replacing text that has been already outputted to the screen. + +Behavior +======== + +AclBehavior +----------- + +- ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. \ No newline at end of file From 4616c40362a85e51ce038200a8b773cbb975b2bb Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 24 May 2014 16:51:33 -0400 Subject: [PATCH 004/292] Update docs for cakephp/cakephp#3478 Validation::between() is now Validation::lengthBetween() --- en/appendices/2-6-migration-guide.rst | 11 ++++++++++- en/models/data-validation.rst | 10 +++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 5372053977..6735e0f65f 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -19,4 +19,13 @@ Behavior AclBehavior ----------- -- ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. \ No newline at end of file +- ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. + +Utility +======= + +Validation +---------- + +- ``Validation::between`` has been deprecated, you should use + :php:meth:`Validation::lengthBetween` instead. diff --git a/en/models/data-validation.rst b/en/models/data-validation.rst index ebd6e1d7af..f0d0c2022f 100644 --- a/en/models/data-validation.rst +++ b/en/models/data-validation.rst @@ -61,7 +61,7 @@ some of these built-in validation rules:: 'message' => 'Alphabets and numbers only' ), 'between' => array( - 'rule' => array('between', 5, 15), + 'rule' => array('lengthBetween', 5, 15), 'message' => 'Between 5 to 15 characters' ) ), @@ -497,7 +497,7 @@ multiple calls to add to create as many rules as you like:: 'required' => 'create' )) ->add('password', 'size', array( - 'rule' => array('between', 8, 20), + 'rule' => array('lengthBetween', 8, 20), 'message' => 'Password should be at least 8 chars long' )); @@ -509,7 +509,7 @@ It is also possible to add multiple rules at once for a single field:: 'required' => 'create' ), 'size' => array( - 'rule' => array('between', 8, 20), + 'rule' => array('lengthBetween', 8, 20), 'message' => 'Password should be at least 8 chars long' ) )); @@ -622,7 +622,7 @@ with usage examples. ) ); -.. php:staticmethod:: between(string $check, integer $min, integer $max) +.. php:staticmethod:: lengthBetween(string $check, integer $min, integer $max) The length of the data for the field must fall within the specified numeric range. Both minimum and maximum values must be supplied. @@ -630,7 +630,7 @@ with usage examples. public $validate = array( 'password' => array( - 'rule' => array('between', 5, 15), + 'rule' => array('lengthBetween', 5, 15), 'message' => 'Passwords must be between 5 and 15 characters long.' ) ); From c7c7ae0d893a57cfa44cdce069c7d19b93d4c198 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 29 May 2014 21:50:48 -0400 Subject: [PATCH 005/292] Update docs for cakephp/cakephp#3548 --- en/appendices/2-6-migration-guide.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 6735e0f65f..5e5dea0c7d 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -21,6 +21,16 @@ AclBehavior - ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. +Network +======= + +CakeRequest +----------- + +- ``CakeRequest::param()`` can now read values using :ref:`hash-path-syntax` + like ``data()``. + + Utility ======= From 5e989e772089919612393051d3b5313ab8f2388a Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 5 Jun 2014 08:36:01 -0400 Subject: [PATCH 006/292] Update docs for cakephp/cakephp#3604 --- en/appendices/2-6-migration-guide.rst | 8 ++++++++ en/models/saving-your-data.rst | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 5e5dea0c7d..cef08de153 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -21,6 +21,14 @@ AclBehavior - ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. +Model +===== + +Model +----- + +- ``Model::save()`` had the ``atomic`` option back-ported from 3.0. + Network ======= diff --git a/en/models/saving-your-data.rst b/en/models/saving-your-data.rst index 3eded0b917..2989260447 100644 --- a/en/models/saving-your-data.rst +++ b/en/models/saving-your-data.rst @@ -115,11 +115,12 @@ as keys: * ``callbacks`` Set to false to disable callbacks. Using 'before' or 'after' will enable only those callbacks. * ``counterCache`` (since 2.4) Boolean to control updating of counter caches (if any) +* ``atomic`` (since 2.6) Boolean to indicate you want records saved in + a transaction. More information about model callbacks is available :doc:`here ` - .. tip:: If you don't want the ``modified`` field to be automatically updated when saving some From a58f7cfbd0c0c91f8b5747c7759c2cda5da188a7 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Fri, 6 Jun 2014 08:05:42 -0400 Subject: [PATCH 007/292] Update docs for cakephp/cakephp#3653 --- en/appendices/2-6-migration-guide.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index cef08de153..5617e8a2d7 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -47,3 +47,15 @@ Validation - ``Validation::between`` has been deprecated, you should use :php:meth:`Validation::lengthBetween` instead. + + +View +==== + +HtmlHelper +---------- + +- :php:meth:`HtmlHelper::css()` had the ``once`` option added. It works the same + as the ``once`` option for ``HtmlHelper::script()``. The default value is + ``false`` to maintain backwards compatibility. + From 4a80af06a766a07c01dde1c7d7d932dbc889bfed Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 25 Jun 2014 09:20:36 -0400 Subject: [PATCH 008/292] Update docs for cakephp/cakephp#3754 --- en/appendices/2-6-migration-guide.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 5617e8a2d7..8686e1d83c 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -42,6 +42,11 @@ CakeRequest Utility ======= +Hash +---- + +- ``Hash::get()`` now raises an exception when the path argument is invalid. + Validation ---------- From 23688fc41a7341fdb39e8f7574a3f53dcb6bece4 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 25 Jun 2014 23:12:30 -0400 Subject: [PATCH 009/292] Add migration notes for cakephp/cakephp#3596 --- en/appendices/2-6-migration-guide.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 8686e1d83c..b100046456 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -21,6 +21,17 @@ AclBehavior - ``Model::parentNode()`` now gets the type (Aro, Aco) passed as first argument: ``$model->parentNode($type)``. +Datasource +========== + +Mysql +----- + +- Schema migrations with MySQL now support an ``after`` key when adding + a column. This key allows you to specify which column the new one should be + added after. + + Model ===== From fe15653e5bedcff8dfc5aee010f857c11008b7bd Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 29 Jun 2014 23:01:30 -0400 Subject: [PATCH 010/292] Add note about CakeRequest::setInput() Refs cakephp/cakephp#3764 --- en/appendices/2-6-migration-guide.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index b100046456..029441a2fe 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -48,6 +48,7 @@ CakeRequest - ``CakeRequest::param()`` can now read values using :ref:`hash-path-syntax` like ``data()``. +- ``CakeRequest:setInput()`` Utility From 127ab3e791c2b65094896bb44af668fe44b01b6a Mon Sep 17 00:00:00 2001 From: euromark Date: Thu, 10 Jul 2014 14:34:17 +0200 Subject: [PATCH 011/292] stackTrace() --- en/appendices/2-6-migration-guide.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 029441a2fe..a72e92e9e0 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -4,6 +4,12 @@ CakePHP 2.6 is a fully API compatible upgrade from 2.5. This page outlines the changes and improvements made in 2.6. +Basics.php +========== + +- ``stackTrace()`` has been added as a convenience wrapper function for ```Debugger::trace()``. + It directly echos just as ``debug()`` does. + Console ======= From cdf67e5637246829ab11eb5e49c50f89c2571490 Mon Sep 17 00:00:00 2001 From: euromark Date: Thu, 10 Jul 2014 14:37:16 +0200 Subject: [PATCH 012/292] add docs --- en/appendices/2-6-migration-guide.rst | 2 +- en/core-libraries/global-constants-and-functions.rst | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index a72e92e9e0..5a382ded54 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -8,7 +8,7 @@ Basics.php ========== - ``stackTrace()`` has been added as a convenience wrapper function for ```Debugger::trace()``. - It directly echos just as ``debug()`` does. + It directly echos just as ``debug()`` does. But only if debug level is on. Console ======= diff --git a/en/core-libraries/global-constants-and-functions.rst b/en/core-libraries/global-constants-and-functions.rst index ef05f20c88..86c08ed035 100644 --- a/en/core-libraries/global-constants-and-functions.rst +++ b/en/core-libraries/global-constants-and-functions.rst @@ -129,6 +129,10 @@ such as debugging and translating content. which it was called. Also see :doc:`/development/debugging` +.. php:function:: stackTrace(array $options = array()) + + If the application's DEBUG level is non-zero, the stack trace is printed out. + .. php:function:: env(string $key) Gets an environment variable from available sources. Used as a From cc133439fbe6a057484a844834ac6c827296e060 Mon Sep 17 00:00:00 2001 From: ADmad Date: Thu, 10 Jul 2014 23:41:26 +0530 Subject: [PATCH 013/292] Add deprecation info for $confirmMessage argument. --- en/appendices/2-6-migration-guide.rst | 9 +++++++++ en/core-libraries/helpers/form.rst | 16 ++++++++++------ en/core-libraries/helpers/html.rst | 13 ++++++++----- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 5a382ded54..b2d36979af 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -81,4 +81,13 @@ HtmlHelper - :php:meth:`HtmlHelper::css()` had the ``once`` option added. It works the same as the ``once`` option for ``HtmlHelper::script()``. The default value is ``false`` to maintain backwards compatibility. +- The ``$confirmMessage`` argument of :php:meth:`HtmlHelper::link()` has been + deprecated. You should instead use key ``confirm`` in ``$options`` to specify + the message. +FormHelper +---------- + +- The ``$confirmMessage`` argument of :php:meth:`FormHelper::postLink()` has been + deprecated. You should instead use key ``confirm`` in ``$options`` to specify + the message. diff --git a/en/core-libraries/helpers/form.rst b/en/core-libraries/helpers/form.rst index ba2a074a50..08f938434a 100644 --- a/en/core-libraries/helpers/form.rst +++ b/en/core-libraries/helpers/form.rst @@ -961,7 +961,7 @@ Form Element-Specific Methods All elements are created under a form for the ``User`` model as in the examples above. For this reason, the HTML code generated will contain attributes that reference to the User model. Ex: name=data[User][username], id=UserUsername - + .. php:method:: label(string $fieldName, string $text, array $options) Create a label element. ``$fieldName`` is used for generating the @@ -1032,14 +1032,14 @@ Ex: name=data[User][username], id=UserUsername .. code-block:: html - + If the form is edited (that is, the array ``$this->request->data`` will contain the information saved for the ``User`` model), the value corresponding to ``id`` field will automatically be added to the HTML generated. Example for data[User][id] = 10: - + .. code-block:: html - + .. versionchanged:: 2.0 @@ -1061,7 +1061,7 @@ Ex: name=data[User][username], id=UserUsername If the form is edited (that is, the array ``$this->request->data`` will contain the information saved for the ``User`` model), the value - corresponding to ``notes`` field will automatically be added to the HTML + corresponding to ``notes`` field will automatically be added to the HTML generated. Example: .. code-block:: html @@ -1077,7 +1077,7 @@ Ex: name=data[User][username], id=UserUsername textarea should be escaped. Defaults to ``true``. :: - + echo $this->Form->textarea('notes', array('escape' => false); // OR.... echo $this->Form->input( @@ -1531,6 +1531,10 @@ Creating buttons and submit elements .. versionchanged:: 2.3 The ``method`` option was added. + .. versionchanged:: 2.6 + The argument ``$confirmMessage`` was deprecated. Use ``confirm`` key + in ``$options`` instead. + Creating date and time inputs ============================= diff --git a/en/core-libraries/helpers/html.rst b/en/core-libraries/helpers/html.rst index 781ef426ef..997e091089 100644 --- a/en/core-libraries/helpers/html.rst +++ b/en/core-libraries/helpers/html.rst @@ -115,7 +115,7 @@ methods of the HtmlHelper and how to use them. If you want to include a CSS file which shares a name with a loaded plugin you can do the following. For example if you had a ``Blog`` plugin, and also wanted to include ``app/webroot/css/Blog.common.css``, you would:: - + .. versionchanged:: 2.4 echo $this->Html->css('Blog.common.css', array('plugin' => false)); @@ -373,14 +373,13 @@ methods of the HtmlHelper and how to use them. Dashboard - Specify ``$confirmMessage`` to display a JavaScript ``confirm()`` + Specify ``confirm`` key in options to display a JavaScript ``confirm()`` dialog:: echo $this->Html->link( 'Delete', array('controller' => 'recipes', 'action' => 'delete', 6), - array(), - "Are you sure you wish to delete this recipe?" + array('confirm' => 'Are you sure you wish to delete this recipe?'), ); Will output: @@ -475,6 +474,10 @@ methods of the HtmlHelper and how to use them. .. versionchanged:: 2.4 The ``escapeTitle`` option was added. + .. versionchanged:: 2.6 + The argument ``$confirmMessage`` was deprecated. Use ``confirm`` key + in ``$options`` instead. + Also check :php:meth:`HtmlHelper::url` method for more examples of different types of URLs. @@ -1102,7 +1105,7 @@ Creating breadcrumb trails with HtmlHelper .. versionchanged:: 2.3 The 'separator', 'firstClass' and 'lastClass' options were added. - + .. versionchanged:: 2.5 The 'escape' option was added. From 045e495b631ad2b2f724bce0728b7990b6d417a7 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Thu, 17 Jul 2014 09:28:37 -0400 Subject: [PATCH 014/292] Update docs for cakephp/cakephp#3906 --- en/appendices/2-6-migration-guide.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index b2d36979af..7449e76d02 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -64,6 +64,9 @@ Hash ---- - ``Hash::get()`` now raises an exception when the path argument is invalid. +- ``Hash::nest()`` now raises an exception when the nesting operation results in + no data. + Validation ---------- From 363dc421eaecb62901f3685353e3ce4949643567 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sun, 20 Jul 2014 22:01:37 -0400 Subject: [PATCH 015/292] Update docs for cakephp/cakephp#3706 --- en/appendices/2-6-migration-guide.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 7449e76d02..35b523ff39 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -56,6 +56,13 @@ CakeRequest like ``data()``. - ``CakeRequest:setInput()`` +HttpSocket +---------- + +- ``HttpSocket::head()`` was added. +- You can now use the ``protocol`` option to override the specific protocol to + use when making a request. + Utility ======= From 76a8740733bc6b8485c32c91d51658b881a4702d Mon Sep 17 00:00:00 2001 From: Mark Story Date: Mon, 21 Jul 2014 23:59:10 -0400 Subject: [PATCH 016/292] Update migration guide for cakephp/cakephp#4039 --- en/appendices/2-6-migration-guide.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 35b523ff39..b8955e208e 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -13,6 +13,11 @@ Basics.php Console ======= +ConsoleOptionParser +------------------- + +- ``ConsoleOptionParser::removeSubcommand()`` was added. + Shell ----- From 9de0267e7c3bdecbabd2370c943066d13feaa844 Mon Sep 17 00:00:00 2001 From: ExtraBlind Date: Wed, 30 Jul 2014 13:56:19 +0200 Subject: [PATCH 017/292] Update events.rst Part translation in french --- fr/core-libraries/events.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/fr/core-libraries/events.rst b/fr/core-libraries/events.rst index 63f5aa978f..fa41795ce8 100644 --- a/fr/core-libraries/events.rst +++ b/fr/core-libraries/events.rst @@ -42,13 +42,13 @@ vous êtes déjà quelque part familier avec les events dans CakePHP. Example event usage =================== - -Let's suppose you are building a Cart plugin, and you'd like to focus on just -handling order logic. You don't really want to include shipping logic, emailing -the user or decrementing the item from the stock, but these are important tasks -to the people using your plugin. If you were not using events, you may try to -implement this by attaching behaviors to models, or adding components to your -controllers. Doing so represents a challenge most of the time, since you +Supposons que vous codez un Plugin de gestion de panier, et que vous vouliez +vous focaliser sur la logique de lors de la commande. Vous ne voulez pas à ce +moment là inclure la logique pour l'expédition, l'email ou la décrémentation +du produit dans le stock, mais ce sont des tâches importantes pour les personnes +utilisant votre plugin. Si vous n'utilisiez pas les évènements vous auriez pu +implémenter cela en attachant des behaviors à vos modèles ou en ajoutant des +composant à votre controller. Doing so represents a challenge most of the time, since you would have to come up with the code for externally loading those behaviors or attaching hooks to your plugin controllers. From 09709188d57b1613986aa8b2814a3cb7e6a6bc34 Mon Sep 17 00:00:00 2001 From: euromark Date: Fri, 1 Aug 2014 20:25:57 +0200 Subject: [PATCH 018/292] Deprecate ssn() --- en/appendices/2-6-migration-guide.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index b8955e208e..959517503d 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -85,6 +85,8 @@ Validation - ``Validation::between`` has been deprecated, you should use :php:meth:`Validation::lengthBetween` instead. +- ``Validation::ssn`` has been deprecated, you should use + :php:meth:`Validation::personId` instead. View From ec801b62f094dff2943854eebd6fb89965b5665c Mon Sep 17 00:00:00 2001 From: euromark Date: Mon, 4 Aug 2014 16:43:46 +0200 Subject: [PATCH 019/292] Correct 2.6 migration around ssn() --- en/appendices/2-6-migration-guide.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 959517503d..87f5baeed7 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -85,8 +85,7 @@ Validation - ``Validation::between`` has been deprecated, you should use :php:meth:`Validation::lengthBetween` instead. -- ``Validation::ssn`` has been deprecated, you should use - :php:meth:`Validation::personId` instead. +- ``Validation::ssn`` has been deprecated and can be provided as standalone/plugin solution. View From b6aab98b1c6c9f2adad2dfa123b23b18f675d9ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 22:30:45 +0200 Subject: [PATCH 020/292] Improved wording --- en/contributing/cakephp-coding-conventions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 292587270a..81987a7bd3 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -241,7 +241,7 @@ Commenting Code All comments should be written in English, and should in a clear way describe the commented block of code. -Comments can include the following `phpDocumentor `_ +Comments may only include the following `phpDocumentor `_ tags: * `@author `_ From 65d7ad55d302941d62eb977bce0dbfdf6ba59911 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 22:42:05 +0200 Subject: [PATCH 021/292] Removed ``@author`` from list of allowed annotations --- en/contributing/cakephp-coding-conventions.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 81987a7bd3..aa85d4f8fe 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -244,7 +244,6 @@ describe the commented block of code. Comments may only include the following `phpDocumentor `_ tags: -* `@author `_ * `@copyright `_ * `@deprecated `_ Using the ``@version `` format, where ``version`` and ``description`` are mandatory. From 7d02392d0089b9cf02b406c44b45442427b7c7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 22:42:59 +0200 Subject: [PATCH 022/292] Removed ``@example`` from list of allowed annotations --- en/contributing/cakephp-coding-conventions.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index aa85d4f8fe..991f93be20 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -247,7 +247,6 @@ tags: * `@copyright `_ * `@deprecated `_ Using the ``@version `` format, where ``version`` and ``description`` are mandatory. -* `@example `_ * `@ignore `_ * `@internal `_ * `@link `_ From 8ff77d371d4acb8686703c2b108d98d15f0b9ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 23:10:59 +0200 Subject: [PATCH 023/292] Removed ``@version`` from list of allowed annotations Not used anymore after https://github.com/cakephp/cakephp/pull/4561 --- en/contributing/cakephp-coding-conventions.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 991f93be20..3dccb7e305 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -252,7 +252,6 @@ tags: * `@link `_ * `@see `_ * `@since `_ -* `@version `_ PhpDoc tags are very much like JavaDoc tags in Java. Tags are only processed if they are the first thing in a DocBlock line, for example:: From 6613632fc5a41ff663553006e5593caf595e9431 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 23:27:35 +0200 Subject: [PATCH 024/292] Moved note about comments in English to new "Language" section --- en/contributing/cakephp-coding-conventions.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 3dccb7e305..5ada63d9d3 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -10,6 +10,11 @@ You can use the `CakePHP Code Sniffer `_ to check that your code follows required standards. +Language +======== + +All code and comments should be written in English. + Adding New Features =================== @@ -238,9 +243,6 @@ indented with one tab:: Commenting Code =============== -All comments should be written in English, and should in a clear way -describe the commented block of code. - Comments may only include the following `phpDocumentor `_ tags: From 94c8de2748a017e1e0070521c1a892e9545a4925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 9 Sep 2014 23:30:25 +0200 Subject: [PATCH 025/292] Removed example that makes no sense anymore --- en/contributing/cakephp-coding-conventions.rst | 9 --------- 1 file changed, 9 deletions(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 5ada63d9d3..94590bd32e 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -258,15 +258,6 @@ tags: PhpDoc tags are very much like JavaDoc tags in Java. Tags are only processed if they are the first thing in a DocBlock line, for example:: - /** - * Tag example. - * - * @author this tag is parsed, but this @version is ignored - * @version 1.0 this tag is also parsed - */ - -:: - /** * Example of inline phpDoc tags. * From 66c6b2c23c9a3ff1e2013d207038faaa03703fca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Wed, 10 Sep 2014 00:22:13 +0200 Subject: [PATCH 026/292] Rewritten Commenting Code section of the coding conventions --- .../cakephp-coding-conventions.rst | 125 +++++++++++++++--- 1 file changed, 105 insertions(+), 20 deletions(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index 94590bd32e..c38bfcfd81 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -240,44 +240,129 @@ indented with one tab:: ->subject('A great message') ->send(); -Commenting Code -=============== +DocBlocks +========= + +All comment blocks, with the exception of the first block in a file, should +always be preceded by a newline. -Comments may only include the following `phpDocumentor `_ -tags: +File Header DocBlock +-------------------- + +All php files should contain a file header DocBlock, +which should look like this:: + + `_ tags are: * `@copyright `_ +* `@link `_ +* `@since `_ +* `@license `_ + +Class DocBlocks +--------------- + +Class DocBlocks should look like this:: + + /** + * Short description of the class. + * + * Long description of class. + * Can use multiple lines. + * + * @deprecated 3.0.0 Removed as of 2.6.0. Will be removed in 3.0.0. Use Bar instead. + * @see Bar + * @link http://book.cakephp.org/2.0/en/foo.html + */ + class Foo { + + } + +Class DocBlocks may contain the following `phpDocumentor `_ tags: + * `@deprecated `_ Using the ``@version `` format, where ``version`` and ``description`` are mandatory. -* `@ignore `_ * `@internal `_ * `@link `_ +* `@property `_ * `@see `_ * `@since `_ +* `@uses `_ + +Property DocBlocks +------------------ -PhpDoc tags are very much like JavaDoc tags in Java. Tags are only -processed if they are the first thing in a DocBlock line, for example:: +Property DocBlocks should look like this:: /** - * Example of inline phpDoc tags. - * - * This function works hard with foo() to rule the world. + * @var string|null Description of property. * - * @return void + * @deprecated 3.0.0 Deprecated as of 2.5.0. Will be removed in 3.0.0. Use $_bla instead. + * @see Bar::$_bla + * @link http://book.cakephp.org/2.0/en/foo.html#properties */ - function bar() { - } + protected $_bar = null; + +Property DocBlocks may contain the following `phpDocumentor `_ tags: + +* `@deprecated `_ + Using the ``@version `` format, where ``version`` and ``description`` are mandatory. +* `@internal `_ +* `@link `_ +* `@see `_ +* `@since `_ +* `@var `_ + +Method/Function DocBlocks +------------------------- + +Method and functions DocBlocks should look like this:: /** - * Foo function. + * Short description of the method. * - * @return void - */ - function foo() { - } + * Long description of method. + * Can use multiple lines. + * + * @param string $param2 first parameter. + * @param array|null $param2 Second parameter. + * @return array An array of cakes. + * @throws Exception If something goes wrong. + * + * @link http://book.cakephp.org/2.0/en/foo.html#bar + * @deprecated 3.0.0 Deprecated as of 2.5.0. Will be removed in 3.0.0. Use Bar::baz instead. + * @see Bar::baz + /* + public function bar($param1, $param2 = null) { + } -Comment blocks, with the exception of the first block in a file, should -always be preceded by a newline. +Method and function DocBLocks may contain the following `phpDocumentor `_ tags: + +* `@deprecated `_ + Using the ``@version `` format, where ``version`` and ``description`` are mandatory. +* `@internal `_ +* `@link `_ +* `@param `_ +* `@return `_ +* `@throws `_ +* `@see `_ +* `@since `_ +* `@uses `_ Variable Types -------------- From b8bb48b7196a7174a7357dff93ab09d4be68ab16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Wed, 10 Sep 2014 00:38:42 +0200 Subject: [PATCH 027/292] Added fixes mentioned by @dereuromark --- en/contributing/cakephp-coding-conventions.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/en/contributing/cakephp-coding-conventions.rst b/en/contributing/cakephp-coding-conventions.rst index c38bfcfd81..f71096af39 100644 --- a/en/contributing/cakephp-coding-conventions.rst +++ b/en/contributing/cakephp-coding-conventions.rst @@ -249,10 +249,10 @@ always be preceded by a newline. File Header DocBlock -------------------- -All php files should contain a file header DocBlock, +All PHP files should contain a file header DocBlock, which should look like this:: - Date: Sat, 13 Sep 2014 04:15:56 +0200 Subject: [PATCH 028/292] maxlength info. --- en/appendices/2-6-migration-guide.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 87f5baeed7..d2126f8c6f 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -107,3 +107,5 @@ FormHelper - The ``$confirmMessage`` argument of :php:meth:`FormHelper::postLink()` has been deprecated. You should instead use key ``confirm`` in ``$options`` to specify the message. +- The ``maxlength`` attribute will now also be applied to textareas, when the corresponding + DB field is of type varchar, as per HTML specs. From b2ea385915a27b982ba6876f32c8a4995e613122 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Mon, 22 Sep 2014 20:59:31 -0400 Subject: [PATCH 029/292] Update docs for cakephp/cakephp#4550 --- en/appendices/2-6-migration-guide.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index d2126f8c6f..bd3abe08f6 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -72,6 +72,12 @@ HttpSocket Utility ======= +CakeTime +-------- + +- ``CakeTime::timeAgoInWords()`` now supports ``strftime()`` compatible absolute + date formats. This helps make localizing formatted times easier. + Hash ---- From d3524c000f9d23f90163b3c46bc6be5dc822acad Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 4 Oct 2014 22:56:42 -0400 Subject: [PATCH 030/292] Add missing things to migration guide. --- en/appendices/2-6-migration-guide.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index bd3abe08f6..2619ff6b78 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -7,8 +7,13 @@ the changes and improvements made in 2.6. Basics.php ========== -- ``stackTrace()`` has been added as a convenience wrapper function for ```Debugger::trace()``. +- ``stackTrace()`` has been added as a convenience wrapper function for ``Debugger::trace()``. It directly echos just as ``debug()`` does. But only if debug level is on. +- New i18n functions have been added. The new functions allow you to include + message context which allows you disambiguate possibly confusing message + strings. For example 'read' can mean multiple things in english depending on + the context. The new ``__x``, ``__xn``, ``__dx``, ``__dxn``, ``__dxc``, + ``__dxcn``, and ``__xc`` functions provide access to the new features. Console ======= @@ -50,6 +55,10 @@ Model ----- - ``Model::save()`` had the ``atomic`` option back-ported from 3.0. +- ``Model::afterFind()`` now always uses a consistent format for afterFind(). + When ``$primary`` is false, the results will always be located under + ``$data[0]['ModelName']``. You can set the ``useConsistentAfterFind`` property + to false on your models to restore the original behavior. Network ======= @@ -59,7 +68,7 @@ CakeRequest - ``CakeRequest::param()`` can now read values using :ref:`hash-path-syntax` like ``data()``. -- ``CakeRequest:setInput()`` +- ``CakeRequest:setInput()`` was added. HttpSocket ---------- From 4b6c38391b79303e369ad993377fbf7e70ed02f1 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Sat, 11 Oct 2014 07:24:09 +0200 Subject: [PATCH 031/292] [fr] correction backported from 3.0 --- fr/contributing/code.rst | 2 +- fr/controllers.rst | 4 ++-- fr/core-libraries/caching.rst | 2 +- fr/deployment.rst | 2 +- fr/views.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/fr/contributing/code.rst b/fr/contributing/code.rst index 475ec17614..5c31be6f68 100644 --- a/fr/contributing/code.rst +++ b/fr/contributing/code.rst @@ -3,7 +3,7 @@ Code Les correctifs et les pull requests sont les meilleures façons de contribuer au code de CakePHP. Les pull requests peuvent être créés sur Github, et sont -préférés aux correctifs attachés au tickets. +préférés aux correctifs attachés aux tickets. Configuration initiale ====================== diff --git a/fr/controllers.rst b/fr/controllers.rst index 5bac22bd60..639d2487ef 100644 --- a/fr/controllers.rst +++ b/fr/controllers.rst @@ -4,7 +4,7 @@ Controllers (Contrôleurs) Les Controllers sont le 'C' dans MVC. Après que le routage a été appliqué et que le bon controller a été trouvé, l'action de votre controller est appelée. Votre controller devra gérer l'interprétation des données requêtées, -s'assurer que les bon models sont appelés, et que la bonne réponse ou vue est +s'assurer que les bons models sont appelés et que la bonne réponse ou vue est rendue. Les controllers peuvent être imaginés comme un homme au milieu entre le Model et la Vue. Le mieux est de garder des controllers peu chargés, et des models plus fournis. Cela vous aidera à réutiliser plus facilement votre @@ -14,7 +14,7 @@ Habituellement, les controllers sont utilisés pour gérer la logique autour d'un seul model. Par exemple, si vous construisez un site pour gérer une boulangerie en-ligne, vous aurez sans doute un RecettesController qui gère vos recettes et un IngredientsController qui gère vos ingrédients. Cependant, -il est aussi possible d'avoir des controllers qui fonctionnent avec plus qu'un +il est aussi possible d'avoir des controllers qui fonctionnent avec plus d'un model. Dans CakePHP, un controller est nommé d'après le model principal qu'il gère. diff --git a/fr/core-libraries/caching.rst b/fr/core-libraries/caching.rst index f64d00f1e3..416acc5961 100644 --- a/fr/core-libraries/caching.rst +++ b/fr/core-libraries/caching.rst @@ -32,7 +32,7 @@ votre propre système de mise en cache. Les moteurs de cache intégrés sont: * ``Wincache`` Utilise l'extension `Wincache `_. Wincache offre des fonctionnalités et des performances semblables à APC, mais optimisées pour Windows et IIS. -* ``XcacheEngine`` Similaire à APC, `Xcache `_. +* ``XcacheEngine`` `Xcache `_. est une extension PHP qui fournit des fonctionnalités similaires à APC. * ``MemcacheEngine`` Utilise l'extension `Memcache `_. Memcache fournit un cache très rapide qui peut être distribué au travers diff --git a/fr/deployment.rst b/fr/deployment.rst index d61fff7295..684d2712ed 100644 --- a/fr/deployment.rst +++ b/fr/deployment.rst @@ -54,7 +54,7 @@ le debug change les types de choses suivantes: En plus des éléments ci-dessus, beaucoup de plugins et d'extensions d'application utilisent ``debug`` pour modifier leur comportement. -Vous pouvez créer une variable d'environnement pour définir le niveau de +Vous pouvez créer une variable d'environnement pour définir le niveau de debug dynamiquement entre plusieurs environnements. Cela va éviter de déployer une application avec debug > 0 et vous permet de ne pas avoir à changer de niveau de debug chaque fois avant de déployer vers un environnement de diff --git a/fr/views.rst b/fr/views.rst index b1c3003a7a..a07660cf3d 100644 --- a/fr/views.rst +++ b/fr/views.rst @@ -482,7 +482,7 @@ de vues). Dans l'exemple ci-dessus, le fichier echo $helptext; //outputs "Oh, this text is very helpful." La méthode :php:meth:`View::element()` supporte aussi les options pour -l'element. Les options supportés sont 'cache' et 'callbacks'. Un exemple:: +l'element. Les options supportées sont 'cache' et 'callbacks'. Un exemple:: echo $this->element('helpbox', array( "helptext" => "Ceci est passé à l'element comme $helptext", From 3c1c8ef859997bbd8452a5b44cfdafcf107ac24e Mon Sep 17 00:00:00 2001 From: Ankit Pokhrel Date: Sat, 11 Oct 2014 19:10:09 +0545 Subject: [PATCH 032/292] 'Themed' should be 'Theme' Looks like a small typo there. While using assets from `app/webroot` the folder name should be `theme` instead of `themed`. Tested on ver. 2.5.4 and 2.5.5. for JS files. --- en/views/themes.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/views/themes.rst b/en/views/themes.rst index b0ed891e7c..710a8cd1be 100644 --- a/en/views/themes.rst +++ b/en/views/themes.rst @@ -83,7 +83,7 @@ directories in ``app/webroot`` with paths matching those used by CakePHP. - ``app/Plugin/DebugKit/webroot/js/my_file.js`` becomes ``app/webroot/debug_kit/js/my_file.js`` - ``app/View/Themed/Navy/webroot/css/navy.css`` becomes - ``app/webroot/themed/Navy/css/navy.css`` + ``app/webroot/theme/Navy/css/navy.css`` .. meta:: From bbcbbfb8b8d37dc399abc9006e5c8e4ace5b2982 Mon Sep 17 00:00:00 2001 From: Bryan Crowe Date: Sat, 11 Oct 2014 14:33:15 -0400 Subject: [PATCH 033/292] [fr][ja] Follow #1853 --- fr/views/themes.rst | 2 +- ja/views/themes.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/fr/views/themes.rst b/fr/views/themes.rst index 51b459890b..78aaa1579f 100644 --- a/fr/views/themes.rst +++ b/fr/views/themes.rst @@ -87,7 +87,7 @@ cakephp. - ``app/Plugin/DebugKit/webroot/js/my_file.js`` devient ``app/webroot/DebugKit/js/my_file.js``. - ``app/View/Themed/Navy/webroot/css/navy.css`` devient - ``app/webroot/themed/Navy/css/navy.css``. + ``app/webroot/theme/Navy/css/navy.css``. .. meta:: diff --git a/ja/views/themes.rst b/ja/views/themes.rst index fa5e613c72..31abefc53b 100644 --- a/ja/views/themes.rst +++ b/ja/views/themes.rst @@ -71,7 +71,7 @@ CakePHPによって使われている ``app/webroot`` のパスに一致する - ``app/Plugin/DebugKit/webroot/js/my_file.js`` は ``app/webroot/debug_kit/js/my_file.js`` になります。 - ``app/View/Themed/Navy/webroot/css/navy.css`` は - ``app/webroot/themed/Navy/css/navy.css`` になります。 + ``app/webroot/theme/Navy/css/navy.css`` になります。 .. meta:: From 2f8c023c719469d320bb55920bb0fa9b9faf89e7 Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Sun, 12 Oct 2014 02:36:03 +0800 Subject: [PATCH 034/292] [zh_CN] done /zh/models/associations-linking-models-together.rst --- .../associations-linking-models-together.rst | 960 ++++++++++++++++++ 1 file changed, 960 insertions(+) create mode 100644 zh/models/associations-linking-models-together.rst diff --git a/zh/models/associations-linking-models-together.rst b/zh/models/associations-linking-models-together.rst new file mode 100644 index 0000000000..685c33fc40 --- /dev/null +++ b/zh/models/associations-linking-models-together.rst @@ -0,0 +1,960 @@ +关联:将模型连接在一起 +###################### + +CakePHP 的最强大的特性之一就是由模型提供的连接关系的映射。在 CakePHP 中,模型间 +的连接是通过关联(*association*)来处理的。 + +在应用程序的不同对象间定义关联应当是一个自然的过程。例如:在一个食谱数据库中,一 +份食谱可能有多条评论,每条评论有一位作者,而每位作者又可能有多份食谱。定义这些关 +系运作的方式,使得你能以一种直观且强大的方式访问你的数据。 + +本节的目的是展示如何在 CakePHP 中计划、定义以和使用模型之间的关系。 + +虽然数据可能有多种来源,但在 web 应用程序中最常见的存储方式是关系型数据库。本节 +介绍的大部分内容将基于这种方式。 + +欲知与插件模型的关联,请参见 :ref:`plugin-models`。 + +关系类型 +-------- + +CakePHP 的四种关联类型是:hasOne、hasMany、belongsTo 和 hasAndBelongsToMany +(HABTM)。 + +============= ===================== ======================================= +关系 关联类型 例子 +============= ===================== ======================================= +一对一 hasOne 一个用户只有一份个人资料。 +------------- --------------------- --------------------------------------- +一对多 hasMany 一个用户可以有多份食谱。 +------------- --------------------- --------------------------------------- +多对一 belongsTo 多份食谱属于同一个用户。 +------------- --------------------- --------------------------------------- +多对多 hasAndBelongsToMany 多份食谱有且属于多种原料。 +============= ===================== ======================================= + +要定义关联,创建一个以要定义的关联命名的类变量。此变量有时候可以简单到只是一个字 +符串,但也可以复杂到是一个多维数组,来定义关联细节。 + +:: + + class User extends AppModel { + public $hasOne = 'Profile'; + public $hasMany = array( + 'Recipe' => array( + 'className' => 'Recipe', + 'conditions' => array('Recipe.approved' => '1'), + 'order' => 'Recipe.created DESC' + ) + ); + } + +在上面的例子中,第一个 'Recipe' 是所谓的 '别名(*Alias*)'。它是关系的标识,可以是 +任何东西。通常选择与它引用的类相同的名字。不过,**每个模型的别名在应用程序中必须 +唯一**。例如,下面是正确的:: + + class User extends AppModel { + public $hasMany = array( + 'MyRecipe' => array( + 'className' => 'Recipe', + ) + ); + public $hasAndBelongsToMany = array( + 'MemberOf' => array( + 'className' => 'Group', + ) + ); + } + + class Group extends AppModel { + public $hasMany = array( + 'MyRecipe' => array( + 'className' => 'Recipe', + ) + ); + public $hasAndBelongsToMany = array( + 'Member' => array( + 'className' => 'User', + ) + ); + } + +但是下面的代码在任何情况下都不大行得通:: + +这是因为在上面的 HABTM 关联中,别名 'Member' 既指向了 User 模型(在 Group 模型中), +又指向了 Group 模型(在 User 模型中)。在涉及多个模型时,为模型别名起不唯一的名字, +可能会引起预料不到的行为。 + +CakePHP 会自动在关联模型对象之间建立连接。所以,例如,在 ``User`` 模型中,可以用 +如下方式访问 ``Recipe`` 模型:: + + $this->Recipe->someFunction(); + +同样的,在控制器中,也可以简单地循着模型关系访问关联的模型:: + + $this->User->Recipe->someFunction(); + +.. note:: + + 记住,关联定义是'单向的'。如果定义了 User hasMany Recipe(用户有很多菜谱),这 + 对 Recipe 模型没有任何影响。需要定义 Recipe belongsTo User(菜谱属于用户)才能 + 从 Recipe 模型访问 User 模型。 + + Remember that associations are defined 'one way'. If you define + User hasMany Recipe, that has no effect on the Recipe Model. You + need to define Recipe belongsTo User to be able to access the User + model from your Recipe model. + +hasOne +------ + +让我们设置 User 模型以 hasOne 关系关联到 Profile 模型。 + +首先,数据库表需要有正确的键。要使 hasOne 关系运作,一个表必须定义指向另一个表的 +记录的外键。在本例中,profiles 表需要包含一个叫做 user\_id 的字段。其基本模式是: + +**hasOne:** *另一个* 模型包含外键。 + +======================================== ================== +关系 数据结构 +======================================== ================== +Apple hasOne Banana (苹果有一个香蕉) bananas.apple\_id +---------------------------------------- ------------------ +User hasOne Profile (用户有一份个人资料) profiles.user\_id +---------------------------------------- ------------------ +Doctor hasOne Mentor (博士有一位导师) mentors.doctor\_id +======================================== ================== + +.. note:: + + 关于这一点,并没有强制要求遵循 CakePHP 的约定。你能够很容易地在关联定义中覆 + 盖任何外键的使用。虽然如此,遵守规则仍将减少代码的重复,使其更易于阅读和维护。 + +User 模型文件会保存为 /app/Model/User.php。为了定义 'User hasOne Profile (用户有 +一份个人资料)' 的关联,为模型类添加 $hasOne 属性。记得要在 +/app/Model/Profile.php 文件中定义 Profile 模型,否则关联将无法工作:: + + class User extends AppModel { + public $hasOne = 'Profile'; + } + +有两种方法在模型文件中描述此关系。最简单的方法是设置 $hasOne 属性为一个包含关联 +模型的类名的字符串,就像我们上面做的那样。 + +如果需要更多的控制,可以使用数组语法定义关联。例如,你可能想要限制关联只包含某些 +记录。 + +:: + + class User extends AppModel { + public $hasOne = array( + 'Profile' => array( + 'className' => 'Profile', + 'conditions' => array('Profile.published' => '1'), + 'dependent' => true + ) + ); + } + +hasOne 关联数组可以包含的键有: + + +- **className**: 与当前模型关联的模型的类名。如果你要定义 'User hasOne Profile + (用户有一份个人资料)' 的关系,className 键应当是 'Profile'。 +- **foreignKey**: 另一模型中的外键名。如果需要定义多个 hasOne 关系,这个键非常 + 有用。其默认值为当前模型的以下划线分隔的单数模型名称,并后缀以 '\_id'。在上面 + 的例子中,就默认为 'user\_id'。 +- **conditions**: 兼容 find() 的条件数组或者是 SQL 字符串,例如 + array('Profile.approved' => true)。 +- **fields**: 在读取关联模型数据时,需要读取的字段的列表。默认返回所有的字段。 +- **order**: 兼容 find() 的排序子句或者 SQL 字符串,例如 + array('Profile.last_name' => 'ASC')。 +- **dependent**: 当 dependent 键被设置为 true,并且调用模型的 delete() 方法时参 + 数 cascade 也被设置为 true,关联模型的记录也会一起被删除。在本例中,我们将其 + 设置为 true 将导致删除一个 User 时也会删除她/他关联的 Profile。 + +一旦定义了关系,User 模型的 find 操作也会读取关联的 Profile 记录,如果存在的话:: + + //调用 $this->User->find() 的结果示例。 + + Array + ( + [User] => Array + ( + [id] => 121 + [name] => Gwoo the Kungwoo + [created] => 2007-05-01 10:31:01 + ) + [Profile] => Array + ( + [id] => 12 + [user_id] => 121 + [skill] => Baking Cakes + [created] => 2007-05-01 10:31:01 + ) + ) + +belongsTo +--------- + +现在我们可以从 User 模型访问 Profile 的数据,让我们在 Profile 模型中定义 +belongsTo 关联以获取相关的 User 数据。belongsTo 关联是 hasOne 和 hasMany +关联的自然补充:它让我们可以从另一个方向查看数据。 + +在为 belongsTo 关系定义数据库表的键时,请遵循如下约定: + +**belongsTo:** *当前模型* 包含外键。 + +========================================= ================== +关系 数据结构 +========================================= ================== +Banana belongsTo Apple (香蕉属于苹果) bananas.apple\_id +----------------------------------------- ------------------ +Profile belongsTo User (个人资料属于用户) profiles.user\_id +----------------------------------------- ------------------ +Mentor belongsTo Doctor (导师属于博士) mentors.doctor\_id +========================================= ================== + +.. tip:: + + 如果一个模型(表)包含一个外键,它 belongsTo 另一个模型(表)。 + +在 /app/Model/Profile.php 文件中的 Profile 模型里,我们可以使用如下字符串语法来 +定义 belongsTo 关联:: + + class Profile extends AppModel { + public $belongsTo = 'User'; + } + +我们也可以使用数组语法定义更为特定的关系:: + + class Profile extends AppModel { + public $belongsTo = array( + 'User' => array( + 'className' => 'User', + 'foreignKey' => 'user_id' + ) + ); + } + +belongsTo 关联数组可以包含的键有: + + +- **className**: 与当前模型关联的模型的类名。如果你要定义 'Profile belongsTo + User (个人资料属于用户)' 的关系,className 键应当是 'User'。 +- **foreignKey**: 当前模型中的外键。如果需要定义多个 belongsTo 关系,这特别方便。 + 其默认值为另一模型的以下划线分隔的单数模型名,后缀以 ``_id``。 +- **conditions**: 兼容 find() 的条件数组或者 SQL 字符串,例如 + ``array('User.active' => true)``。 +- **type**: SQL 查询使用的 join 类型。默认为 'LEFT',这也许不能在所有情况下都符 + 合你的需要。在你想要获取主模型和关联模型的所有记录、或者什么都不要时,'INNER' + (当和某些条件一起使用时)也许会有帮助。 +- **fields**: 在读取关联模型数据时,需要读取的字段的列表。默认返回所有的字段。 +- **order**: 兼容 find() 的排序子句或者 SQL 字符串,例如 + ``array('User.username' => 'ASC')``。 +- **counterCache**: 如果此键的值设置为 true,当你在做 ``save()`` 或者 + ``delete()`` 操作时,关联模型将自动递增或递减外键关联的表的 "[以下划线分隔的 + 单数模型名称]\_count" 列的值。如果它是一个字符串,那这就是要使用的列名。计数 + 器列的值表示关联记录的行数。也可以通过使用数组指定多个计数器缓存,详见 + :ref:`multiple-counterCache`。 +- **counterScope**: 可选的用于更新计数器缓存字段的条件数组。 + +一旦定义了关联,Profile 模型的 find 操作将同时获取相关的 User 记录,如果存在的话:: + + //调用 $this->Profile->find() 的结果示例。 + + Array + ( + [Profile] => Array + ( + [id] => 12 + [user_id] => 121 + [skill] => Baking Cakes + [created] => 2007-05-01 10:31:01 + ) + [User] => Array + ( + [id] => 121 + [name] => Gwoo the Kungwoo + [created] => 2007-05-01 10:31:01 + ) + ) + +计数器缓存(*counterCache*) - 缓存 count() +========================================= + +这个功能帮助你缓存相关数据的计数器。避免了手工调用 ``find('count')`` 方法计算记 +录的数量,而是让模型自动追踪关联的 ``$hasMany`` 模型的任何添加/删除操作,并递增/ +递减父模型表的专用整数字段。 + +这个字段的名称由单数模型名称后缀以下划线和单词 "count" 构成:: + + my_model_count + +比方说有一个叫 ``ImageComment`` 的模型和一个叫 ``Image`` 的模型,你就要在 +``images`` 表中添加一个新的整数字段,并命名为 ``image_comment_count``。 + +下面是更多的示例: + +========== ======================= ========================================= +模型 关联模型 例子 +========== ======================= ========================================= +User Image users.image\_count +---------- ----------------------- ----------------------------------------- +Image ImageComment images.image\_comment\_count +---------- ----------------------- ----------------------------------------- +BlogEntry BlogEntryComment blog\_entries.blog\_entry\_comment\_count +========== ======================= ========================================= + +一旦添加了计数器字段,就可以使用它了。要启用计数器缓存,在关联中添加 +``counterCache`` 键并将其值设置为 ``true``:: + + class ImageComment extends AppModel { + public $belongsTo = array( + 'Image' => array( + 'counterCache' => true, + ) + ); + } + +自此,你每次添加或删除一个关联到 ``Image`` 的 ``ImageComment``, +``image_comment_count`` 字段的数字都会自动调整。 + +计数器范围(*counterScope*) +========================== + +你还可以指定 ``counterScope``。这允许你指定一个简单的条件,告诉模型什么情况下更 +新(或者什么情况下不更新,取决于你如何看)计数器的值。 + +在我们的 Image 模型示例中,我们可以象下面这样指定:: + + class ImageComment extends AppModel { + public $belongsTo = array( + 'Image' => array( + 'counterCache' => 'active_comment_count', //custom field name + // 只有当 "ImageComment" 是 active = 1 时,才计数 + 'counterScope' => array( + 'ImageComment.active' => 1 + ) + ) + ); + } + +.. _multiple-counterCache: + +多个计数器缓存(*counterCache*) +============================== + +CakePHP 从 2.0 版本起,支持在单个模型关系中有多个 ``counterCache``。也可以为每个 +``counterCache`` 定义 ``counterScope``。假设有 ``User`` 模型和 ``Message`` 模型, +要统计每个用户的已读消息和未读消息的数量。 + +========= ====================== =========================================== +模型 字段 说明 +========= ====================== =========================================== +User users.messages\_read 对已读 ``Message`` 计数 +--------- ---------------------- ------------------------------------------- +User users.messages\_unread 对未读 ``Message`` 计数 +--------- ---------------------- ------------------------------------------- +Message messages.is\_read 判断一条 ``Message`` 是已读还是未读。 +========= ====================== =========================================== + +基于上面这样的设置,``belongsTo`` 应当像这样:: + + class Message extends AppModel { + public $belongsTo = array( + 'User' => array( + 'counterCache' => array( + 'messages_read' => array('Message.is_read' => 1), + 'messages_unread' => array('Message.is_read' => 0) + ) + ) + ); + } + +hasMany +------- + +下一步:定义一个 "User hasMany Comment (用户有多条评论)" 的关联。hasMany 关联将 +让我们可以在读取用户(*User*)记录的同时读取用户的评论。 + +在为 hasMany 关系定义数据库表的键时,请遵循如下约定: + +**hasMany:** *其它* 模型包含外键 + +======================================== ================== +关系 数据构 +======================================== ================== +User hasMany Comment (用户有多条评论) Comment.user\_id +---------------------------------------- ------------------ +Cake hasMany Virtue (蛋糕有多项优点) Virtue.cake\_id +---------------------------------------- ------------------ +Product hasMany Option (产品有多个选项) Option.product\_id +======================================== ================== + +在 /app/Model/User.php 文件的 User 模型中,我们可以使用如下字符串语法定义 hasMany +关联:: + + class User extends AppModel { + public $hasMany = 'Comment'; + } + +我们也可以使用数组语法定义更特定的关系:: + + class User extends AppModel { + public $hasMany = array( + 'Comment' => array( + 'className' => 'Comment', + 'foreignKey' => 'user_id', + 'conditions' => array('Comment.status' => '1'), + 'order' => 'Comment.created DESC', + 'limit' => '5', + 'dependent' => true + ) + ); + } + +hasMany 关联数组可以包含的键有: + + +- **className**: 与当前模型关联的模型的类名。如果你定义了 'User hasMany + Comment (用户有多条评论)' 关系,className 键的值应当为 'Comment'。 +- **foreignKey**: 另一个模型中的外键名。如果需要定义多个 hasMany 关系,这特别方 + 便。其默认值为当前模型以下划线分隔的单数模型名称后缀以 '\_id'。 +- **conditions**: 兼容 find() 的条件数组或者 SQL 字符串,例如 + array('Comment.visible' => true)。 +- **order**: 兼容 find() 的排序子句或者 SQL 字符串,例如 + array('Profile.last_name' => 'ASC')。 +- **limit**: 要返回的关联数据的最大行数。 +- **offset**: 在读取和关联之前,要跳过的关联数据行数(在当前查询条件和排序的情况 + 下)。 +- **dependent**: 当 dependent 设置为 true,就可以进行模型的递归删除。在本例中, + 当关联的 User 记录被删除时,Comment 记录也将被删除。 +- **exclusive**: 当 exclusive 设置为 true,将调用 deleteAll() 进行模型的递归删 + 除,而不是分别删除每条数据。这大大提高了性能,但可能并非在所有情况下都是最好 + 的选择。 +- **finderQuery**: 可供 CakePHP 用于读取关联模型记录的完整 SQL 查询语句。这应当 + 用于要求高度定制结果的场合。如果构建的查询语句要求使用关联模型 ID,可以在查询 + 语句中使用特殊标记 ``{$__cakeID__$}``。例如,如果 Apple 模型 hasMany Orange, + 查询语句就应当象这样: + ``SELECT Orange.* from oranges as Orange WHERE Orange.apple_id = {$__cakeID__$};`` 。 + + +一旦关联被建立,User 模型的 find 操作也将读取相关的 Comment 数据,如果存在的话:: + + //调用 $this->User->find() 的结果示例。 + + Array + ( + [User] => Array + ( + [id] => 121 + [name] => Gwoo the Kungwoo + [created] => 2007-05-01 10:31:01 + ) + [Comment] => Array + ( + [0] => Array + ( + [id] => 123 + [user_id] => 121 + [title] => On Gwoo the Kungwoo + [body] => The Kungwooness is not so Gwooish + [created] => 2006-05-01 10:31:01 + ) + [1] => Array + ( + [id] => 124 + [user_id] => 121 + [title] => More on Gwoo + [body] => But what of the 'Nut? + [created] => 2006-05-01 10:41:01 + ) + ) + ) + +要记住的一点是,还需要互补的 Comment belongsTo User (评论属于用户)关联,才能从两 +个方向获取数据。本节涵盖的内容让你能够从 User 模型获取 Comment 数据。在 Comment +模型中添加 Comment belongsTo User 关联,使你能够从 Comment 模型中获取 User 数据, +这样才构成完整的连接,允许信息以任一模型的视角流动。 + +hasAndBelongsToMany (HABTM) +--------------------------- + +好了。现在你已经可以认为自己是 CakePHP 模型关联的专业人士了。你已经深谙对象关系 +中占主要部分的三种关联。 + +现在我们来解决最后一种关系类型:hasAndBelongsToMany,或 HABTM。这种关联用于两个 +模型需要以不同方式多次重复连接的场合。 + +hasMany 与 HABTM 主要不同点在于,HABTM 中对象间的连接不是排他的。例如,以 HABTM +方式连接 Recipe 模型和 Ingredient 模型。用西红柿作为我奶奶的意大利面菜谱(Recipe) +的原料(Ingredient),并不会"用光"这种原料。我也可以把它用于色拉菜谱(Recipe)。 + +hasMany 关联对象间的连接是排他的。如果 User hasMnay Comments,一条评论仅连接到一 +个特定的用户,它不能再被用于(其它用户)。 + +继续。我们需要在数据库中设置一个额外的表,用来处理 HABTM 关联。这个新连接表的名 +字需要包含涉及的两个模型的名字,按字母顺序并且用下划线( \_ )间隔。表的内容应当有 +两个字段,为指向涉及的模型主键的外键(应当是整数类型)。为避免任何问题,不要为这个 +两个字段定义复合主键。如果应用程序要求唯一索引,你可以定义一个。如果你计划在这个 +表中加入任何额外的信息,或者使用 'with' 模型,你需要添加一个额外的主键字段(按照 +约定为 'id')。 + +**HABTM** 要求一个单独的连接表,其表名包含两个 *模型* 的名字。 + +========================= ================================================================ +关系 HABTM 表的字段 +========================= ================================================================ +Recipe HABTM Ingredient **ingredients_recipes**.id, **ingredients_recipes**.ingredient_id, **ingredients_recipes**.recipe_id +------------------------- ---------------------------------------------------------------- +Cake HABTM Fan **cakes_fans**.id, **cakes_fans**.cake_id, **cakes_fans**.fan_id +------------------------- ---------------------------------------------------------------- +Foo HABTM Bar **bars_foos**.id, **bars_foos**.foo_id, **bars_foos**.bar_id +========================= ================================================================ + + +.. note:: + + 按照约定,(两个模型的)表名是按字母顺序的。也可以在关联定义中使用自定义表名。 + +按照约定,确保表 **cakes** 和 **recipes** 应当使用 "id" 字段作为主键。如果它们与 +约定的不同,那就必须在模型的 :ref:`model-primaryKey` 中做(相应的)改变。 + +一旦建立了这个新表,我们就可以在模型文件中定义 HABTM 关联了。这次我们将直接跳到 +数组语法:: + + class Recipe extends AppModel { + public $hasAndBelongsToMany = array( + 'Ingredient' => + array( + 'className' => 'Ingredient', + 'joinTable' => 'ingredients_recipes', + 'foreignKey' => 'recipe_id', + 'associationForeignKey' => 'ingredient_id', + 'unique' => true, + 'conditions' => '', + 'fields' => '', + 'order' => '', + 'limit' => '', + 'offset' => '', + 'finderQuery' => '', + 'with' => '' + ) + ); + } + +HABTM 关联数组可以包含的键有: + +.. _ref-habtm-arrays: + +- **className**: 关联到当前模型的模型类名。如果你定义了 'Recipe HABTM + Ingredient (菜谱有许多且属于原料)' 的关系,这个类名应当是 'Ingredient'。 +- **joinTable**: 在本关联中使用的连接表的名字(如果当前表没有遵循 HABTM 连接表的 + 命名约定)。 +- **with**: 为连接表定义模型名。默认的情况下,CakePHP 将自动为你建立一个模型。 + 上例中,它被称为 IngredientsRecipe。可以使用这个键来覆盖默认的名字。连接表模 + 型能够象所有的“常规”模型那样用来直接访问连接表。通过创建带有这样名称和文件名 + 的模型类,可以向连接表搜索中加入任何自定义行为,例如加入更多的信息/列。 +- **foreignKey**: 当前模型的外键名称。在需要定义多个 HABTM 关系时,这特别方便。 + 该键的默认值为当前模型的以下划线分隔的单数模型名,后缀以 '\_id'。 +- **associationForeignKey**: 另一个模型中的外键名。在需要定义多个 HABTM 关系, + 这特别方便。该键的默认值为另一模型的以下划线分隔的单数模型名,后缀以 '\_id'。 +- **unique**: 布尔值或者字符串 ``keepExisting`` 。 + - 如果为 true (默认值),CakePHP 将先删除外键表中存在的关系记录,再插入新记录。 + 现有的关联在更新时需要再次传递。 + - 如果为 false,CakePHP 将插入指定的新关系记录,并且保留现有关系记录,这可能 + 导致重复的关系记录。 + - 如果设置为 ``keepExisting``,其行为与 `true` 类似,但是有一项额外的检查, + 如果要添加的任何记录与现有的关系记录重复,现有关系记录不被删除,而重复记录 + 则被忽略。这可用于,例如,当连接表中有其它数据需要保留时。 +- **conditions**: 兼容 find() 的条件数组或者 SQL 字符串。如果关联表有条件,应当 + 使用 'with' 模型,并且在关联表定义必要的 belongsTo 关联。 +- **fields**: 在读取关联模型数据时要读取的字段的列表。默认返回所有的字段。 +- **order**: 兼容 find() 的排序子句或者 SQL 字符串。 +- **limit**: 要返回的关联行的最大行数。 +- **offset**: 在读取和关联前要跳过的关联行的行数(给定当前的条件和排序) +- **finderQuery**: CakePHP 用来读取关联模型记录的完整 SQL 查询语句。这应当用在 + 要求高度定制结果的场合。 + +一旦定义了关联,Recipe 模型的 find 操作也会读取相关的 Ingredient 记录,如果存在 +的话:: + + //调用 $this->Recipe->find() 的结果示例。 + + Array + ( + [Recipe] => Array + ( + [id] => 2745 + [name] => Chocolate Frosted Sugar Bombs + [created] => 2007-05-01 10:31:01 + [user_id] => 2346 + ) + [Ingredient] => Array + ( + [0] => Array + ( + [id] => 123 + [name] => Chocolate + ) + [1] => Array + ( + [id] => 124 + [name] => Sugar + ) + [2] => Array + ( + [id] => 125 + [name] => Bombs + ) + ) + ) + +如果要想在使用 Ingredient 模型时获取 Recipe 数据,记得在 Ingredient 模型中定义 +HABTM 关联。 + +.. note:: + + HABTM 数据被视为完整的集合。每次添加新的数据关联,数据库中关联行的整个集合会 + 被删除并重新创建,所以应当总是传入整个数据集来保存。欲知使用 HABTM 的其它方法, + 请参见 :ref:`hasMany-through`。 + +.. tip:: + + 欲知关于保存 HABTM 对象的更多信息,请参见 :ref:`saving-habtm`。 + + +.. _hasMany-through: + +通过(连接模型)的 hasMany +------------------------ + +有时候需要在多对多关联中保存附加数据。考虑以下情况 + +`Student hasAndBelongsToMany Course` + +`Course hasAndBelongsToMany Student` + +换句话说,一名学生(*Student*)可以选修多门课程(*Course*),而一门课程(*Course*)也 +可以被多名学生(*Student*)选修。 这个简单的多对多关联需要一个类似于如下结构的表:: + + id | student_id | course_id + +现在,如果我们要保存学生在这门课程中出勤的天数以及他们的最终分数呢?需要的这张表 +将变成:: + + id | student_id | course_id | days_attended | grade + +问题是,hasAndBelongsToMany 不支持这类情况,因为 hasAndBelongsToMany 关联保存时, +先要删除这个关联。这些列中的额外数据会丢失,因为新插入的数据中没有这些数据。 + + .. versionchanged:: 2.1 + + 你可以将 ``unique`` 设置为 ``keepExisting`` 来防止在保存操作中丢失额外的数据。 + 请参阅 :ref:`HABTM association arrays `。 + +实现需求的方法是使用 **连接模型**,或者也称为 **hasMany through** 关联。即,关联 +自身也是一个模型。现在我们建立一个新的模型 CourseMembership。请看下面的模型。:: + + // Student.php + class Student extends AppModel { + public $hasMany = array( + 'CourseMembership' + ); + } + + // Course.php + + class Course extends AppModel { + public $hasMany = array( + 'CourseMembership' + ); + } + + // CourseMembership.php + + class CourseMembership extends AppModel { + public $belongsTo = array( + 'Student', 'Course' + ); + } + +CourseMembership 连接模型除了保存额外的元信息(即关联信息),还唯一地标识了一名给 +定学生对一门课程的参与(即出勤天数及分数)。 + +连接模型是非常有用的功能,借助于内置的 hasMany 和 belongsTo 关联及 saveAll 特性, +CakePHP 让使用它非常容易。 + +.. _dynamic-associations: + +动态创建和销毁关联 +------------------ + +有时候必须在运行时动态建立和销毁模型关联。这也许是因为以下任何几种原因: + + +- 想减少获取的关联数据的数据量,但是所有的关联都是在关联的第一级。 +- 想要改变定义关联的方式以便排序或者过滤关联数据。 + +这种关联的建立与取消由 CakePHP 模型的 bindModel() 和 unbindModel() 方法来完成。 +(还有一个非常有用的行为叫 "Containable"。欲知更多信息,请参阅手册中内置行为一节。) +让我们来设置几个模型,看看 bindModel() 和 unbindModel() 方法如何工作。我们从两个 +模型开始:: + + class Leader extends AppModel { + public $hasMany = array( + 'Follower' => array( + 'className' => 'Follower', + 'order' => 'Follower.rank' + ) + ); + } + + class Follower extends AppModel { + public $name = 'Follower'; + } + +现在,在 LeaderController 控制器中,我们能够使用 Leader 模型的 find() 方法获取一 +个 Leader 和与它关联的追随者(followers)。就像你上面看到的那样,Leader 模型的关联 +数组定义了 "Leader hasMany Followers" 关系。出于演示的目的,让我们在控制器动作中 +使用 unbindModel() 方法删除该关联:: + + public function some_action() { + // 这会获取 Leader 及其相关的 Followers + $this->Leader->find('all'); + + // 让我们删除 hasMany 关联…… + $this->Leader->unbindModel( + array('hasMany' => array('Follower')) + ); + + // 现在使用 find 函数将只返回 Leaders,而没有 Followers + $this->Leader->find('all'); + + // 注:unbindModel 方法只影响紧随其后的 find 方法。再往后调用 find 方法 + // 时仍将使用配置的关联信息。 + + // 我们已经在 unbindModel() 之后调用了 find('all'),所以这次又会获取 + // Leaders 及相关的 Followers…… + $this->Leader->find('all'); + } + +.. note:: + + 使用 bindModel() 和 unbindModel() 方法来添加和删除关联,仅在 *紧随其后* 的 + find 操作中有效,除非第二个参数设置为 false。如果第二个参数被设置为 *false*, + 在请求的余下阶段仍将保持这种(动态绑定的)效果。 + +以下是 unbindModel() 的基本用法模式:: + + $this->Model->unbindModel( + array('关联类型' => array('关联模型类名')) + ); + +现在我们成功地动态删除了一个关联。让我们来添加一个。我们至今尚没有 Principle 的 +Leader 模型需要一些关联的 Principle。我们的 Principle 模型文件几乎是空的,只有 +public $name 声明语句。让我们动态给我们的 Leader 关联一些 Principle (但记得,这 +仅在紧随其后的 find 操作中有效)。在 LeadersController 控制器中有如下函数:: + + public function another_action() { + // 在 leader.php 模型文件中没有 Leader hasMany Principles 关联,所以这里 + // 的 find 只读取了 Leaders。 + $this->Leader->find('all'); + + // 让我们用 bindModel() 方法为 Leader 模型添加一个新的关联: + $this->Leader->bindModel( + array('hasMany' => array( + 'Principle' => array( + 'className' => 'Principle' + ) + ) + ) + ); + + // 现在我们已经正确地设置了关联,我们可以调用一次 find 函数来获取 Leader + // 及其相关的 principle: + $this->Leader->find('all'); + } + +就是这样。bindModel() 方法的基本用法是封装在数组中的常规关联数组,该数组的键为要 +建立的关联的类型:: + + $this->Model->bindModel( + array('关联名称' => array( + '关联模型类名' => array( + // 这里是常规的关联的键…… + ) + ) + ) + ); + +虽然新绑定的模型在它的模型文件中不需要定义任何关联,但是要使新的关联正常工作,仍 +然需要为其设置正确的(数据库表的)键。 + +与同一模型的多个关系 +-------------------- + +有些情况下,一个模型与另一个模型有多种关系。例如,消息(Message)模型与用户(User) +模型有两种关系:一种是与发送消息的用户的关系,第二种是与接收消息的用户的关系。 +messages 表有一个 user\_id 字段,还有一个 recipient\_id 字段。这样的话消息 +(Message)模型看起来就象这样:: + + class Message extends AppModel { + public $belongsTo = array( + 'Sender' => array( + 'className' => 'User', + 'foreignKey' => 'user_id' + ), + 'Recipient' => array( + 'className' => 'User', + 'foreignKey' => 'recipient_id' + ) + ); + } + +Recipient 是 User 模型的别名。现在来瞧瞧 User 模型是什么样的:: + + class User extends AppModel { + public $hasMany = array( + 'MessageSent' => array( + 'className' => 'Message', + 'foreignKey' => 'user_id' + ), + 'MessageReceived' => array( + 'className' => 'Message', + 'foreignKey' => 'recipient_id' + ) + ); + } + +也可以建立自我关联,如下所示:: + + class Post extends AppModel { + + public $belongsTo = array( + 'Parent' => array( + 'className' => 'Post', + 'foreignKey' => 'parent_id' + ) + ); + + public $hasMany = array( + 'Children' => array( + 'className' => 'Post', + 'foreignKey' => 'parent_id' + ) + ); + } + +**获取关联记录的嵌套数组:** + +如果表里有 ``parent_id`` 字段,可以调用 :ref:`model-find-threaded` 使用单个查询 +来获取记录的嵌套数组,而不用设置任何关联。 + +.. _joining-tables: + +连接表 +------ + +在 SQL 中,你可以使用 JOIN 语句连接相关的表。这让你可以运行涉及多个表的复杂查询( +例如,按给定的几个标签(*tag*)搜索文章(*post*))。 + +在 CakePHP 中某些关联(belongsTo 和 hasOne)会自动进行连接(*join*)来读取数据,所以 +可以执行基于相关模型的数据的查询来读取模型数据。 + +但是这不适用于 hasMany 和 hasAndBelongsToMany 关联。这就需要强制进行连接(*join*)。 +只需要定义必要的连接(*join*),就可以把表联合在一起,并获得期望的查询结果。 + +.. note:: + + 谨记,你需要将递归(*recursion*)设置为 -1,才能正常工作: + $this->Channel->recursive = -1; + +在表间强制进行连接(*join*)时,需要使用 Model::find() 的"现代"语法,在 $options +数组中添加 'joins' 键。例如:: + + $options['joins'] = array( + array('table' => 'channels', + 'alias' => 'Channel', + 'type' => 'LEFT', + 'conditions' => array( + 'Channel.id = Item.channel_id', + ) + ) + ); + + $Item->find('all', $options); + +.. note:: + + 注意 'join' 数组没有键。 + +在上面的例子中,名为 Item 的模型左连接(*left-join*)到 channels 表。可以用模型名 +作为表的别名,以使读取的数据符合 CakePHP 的数据结构。 + +定义连接(*join*)所用的键如下: + + +- **table**: 要连接的表。 +- **alias**: 表的别名。与表关联的模型名是最好的选择。 +- **type**: 连接(*join*)的类型: inner、left 或者 right。 +- **conditions**: 执行连接(*join*)的条件。 + +使用 joins 选项,可以添加基于关联模型字段的条件:: + + $options['joins'] = array( + array('table' => 'channels', + 'alias' => 'Channel', + 'type' => 'LEFT', + 'conditions' => array( + 'Channel.id = Item.channel_id', + ) + ) + ); + + $options['conditions'] = array( + 'Channel.private' => 1 + ); + + $privateItems = $Item->find('all', $options); + +可以根据需要在 hasAndBelongsToMany 关联中运行若干个连接(*join*): + +假设有 Book hasAndBelongsToMany Tag (书籍有且属于多个标签)的关联。该关系使用 +books\_tags 表作为连接表,所以需要把 books 表连接(*join*)到 books\_tags 表,再把 +它与 tags 表连接(*join*):: + + $options['joins'] = array( + array('table' => 'books_tags', + 'alias' => 'BooksTag', + 'type' => 'inner', + 'conditions' => array( + 'Book.id = BooksTag.book_id' + ) + ), + array('table' => 'tags', + 'alias' => 'Tag', + 'type' => 'inner', + 'conditions' => array( + 'BooksTag.tag_id = Tag.id' + ) + ) + ); + + $options['conditions'] = array( + 'Tag.tag' => 'Novel' + ); + + $books = $Book->find('all', $options); + +使用连接(*join*)让你可以以最大的灵活性来控制 CakePHP 如何处理关联并获取数据。不 +过,在大多数情况下,你可以使用其它方式达到同样的目的,比如正确地定义关联,动态绑 +定模型,以及使用 Containable 行为。使用连接(*join*)这种特性应当很小心,因为如果 +和任何之前描述的关联模型的技术一起使用,在一些情况下,它可能会导致错误的 SQL 查 +询语句。 + + +.. meta:: + :title lang=zh_CN: Associations: Linking Models Together + :keywords lang=zh_CN: relationship types,relational mapping,recipe database,relational database,this section covers,web applications,recipes,models,cakephp,storage From 834ad2221e42d123683a9b0e0e3f5949d1172faa Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Sun, 12 Oct 2014 11:32:41 +0800 Subject: [PATCH 035/292] [zh_CN] add back code block deleted mistakenly --- .../associations-linking-models-together.rst | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/zh/models/associations-linking-models-together.rst b/zh/models/associations-linking-models-together.rst index 685c33fc40..8d0e28d78f 100644 --- a/zh/models/associations-linking-models-together.rst +++ b/zh/models/associations-linking-models-together.rst @@ -81,6 +81,32 @@ CakePHP 的四种关联类型是:hasOne、hasMany、belongsTo 和 hasAndBelong 但是下面的代码在任何情况下都不大行得通:: + class User extends AppModel { + public $hasMany = array( + 'MyRecipe' => array( + 'className' => 'Recipe', + ) + ); + public $hasAndBelongsToMany = array( + 'Member' => array( + 'className' => 'Group', + ) + ); + } + + class Group extends AppModel { + public $hasMany = array( + 'MyRecipe' => array( + 'className' => 'Recipe', + ) + ); + public $hasAndBelongsToMany = array( + 'Member' => array( + 'className' => 'User', + ) + ); + } + 这是因为在上面的 HABTM 关联中,别名 'Member' 既指向了 User 模型(在 Group 模型中), 又指向了 Group 模型(在 User 模型中)。在涉及多个模型时,为模型别名起不唯一的名字, 可能会引起预料不到的行为。 From 759ab09ae2916f8d1115f97fd35d7dbab64d8401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Mon, 13 Oct 2014 23:03:05 +0200 Subject: [PATCH 036/292] Correct isUnique example with multiple fields Closes https://github.com/cakephp/cakephp/issues/4869 --- en/models/data-validation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/en/models/data-validation.rst b/en/models/data-validation.rst index 96ebf015d8..c8a4f0e427 100644 --- a/en/models/data-validation.rst +++ b/en/models/data-validation.rst @@ -955,11 +955,11 @@ with usage examples. ); You can validate that a set of fields are unique by providing multiple - fields:: + fields and set ``$or`` to ``false``:: public $validate = array( 'email' => array( - 'rule' => array('isUnique', 'email', 'username'), + 'rule' => array('isUnique', array('email', 'username'), false), 'message' => 'This username & email combination has already been used.' ) ); From 2a0cefbe2cf0e0837add672a82ff27da1622810e Mon Sep 17 00:00:00 2001 From: antograssiot Date: Tue, 14 Oct 2014 06:16:53 +0200 Subject: [PATCH 037/292] [fr] follows #1863 --- fr/models/data-validation.rst | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/fr/models/data-validation.rst b/fr/models/data-validation.rst index 5790445017..0e63614628 100644 --- a/fr/models/data-validation.rst +++ b/fr/models/data-validation.rst @@ -948,9 +948,7 @@ complète de toutes les règles, illustrées par des exemples d'utilisation. .. php:method:: Model::isUnique() La donnée pour le champ doit être unique, elle ne peut être utilisée par - aucune autre ligne. - - :: + aucune autre ligne:: public $validate = array( 'login' => array( @@ -959,6 +957,19 @@ complète de toutes les règles, illustrées par des exemples d'utilisation. ) ); + You can validate that a set of fields are unique by providing multiple + fields and set ``$or`` to ``false``:: + + public $validate = array( + 'email' => array( + 'rule' => array('isUnique', array('email', 'username'), false), + 'message' => 'Cette combinaise nom & email est déjà utilisée.' + ) + ); + + Assurez-vous d'inclure le champ d'origine dans la liste des champs quand vous + joutezd'établir une règle unique sur plusieurs champs. + .. php:staticmethod:: luhn(string|array $check, boolean $deep = false) L'algorithme Luhn: Une formule de vérification de somme pour valider From 2d6532138f5b45848dda80fd91195d904404a4a3 Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Tue, 14 Oct 2014 11:42:57 +0800 Subject: [PATCH 038/292] [zh_CN] delete original text --- zh/models/associations-linking-models-together.rst | 5 ----- 1 file changed, 5 deletions(-) diff --git a/zh/models/associations-linking-models-together.rst b/zh/models/associations-linking-models-together.rst index 8d0e28d78f..a51fbf017f 100644 --- a/zh/models/associations-linking-models-together.rst +++ b/zh/models/associations-linking-models-together.rst @@ -126,11 +126,6 @@ CakePHP 会自动在关联模型对象之间建立连接。所以,例如,在 对 Recipe 模型没有任何影响。需要定义 Recipe belongsTo User(菜谱属于用户)才能 从 Recipe 模型访问 User 模型。 - Remember that associations are defined 'one way'. If you define - User hasMany Recipe, that has no effect on the Recipe Model. You - need to define Recipe belongsTo User to be able to access the User - model from your Recipe model. - hasOne ------ From 37e290982d80650b963dff2a2bb61025b31236ef Mon Sep 17 00:00:00 2001 From: mafeifan Date: Tue, 14 Oct 2014 22:33:06 +0800 Subject: [PATCH 039/292] [zh_CN] done /zh/models/data-validation.rst and /zh/models/data-validation/validating-data-from-the-controller.rst --- zh/models/data-validation.rst | 1111 +++++++++++++++++ .../validating-data-from-the-controller.rst | 61 + 2 files changed, 1172 insertions(+) create mode 100644 zh/models/data-validation.rst create mode 100644 zh/models/data-validation/validating-data-from-the-controller.rst diff --git a/zh/models/data-validation.rst b/zh/models/data-validation.rst new file mode 100644 index 0000000000..3813ec638c --- /dev/null +++ b/zh/models/data-validation.rst @@ -0,0 +1,1111 @@ +数据验证 +######## + +数据验证是任何应用程序的重要组成部分,因为这有助于确保模型中的数据符合应用程序的业务规则。 +例如,你也许要确保密码至少有八个字符,或者用户名是唯一的。定义验证规则使得表单的处理容易得多得多。 + +验证过程包含很多方面。本节中我们只涉及模型中的部分。基本上这意味着:当你调用模型的 save() 方法时会发生什么。 +有关如何处理验证错误显示的更多信息,请参看 :doc:`/core-libraries/helpers/form` 。 + +数据验证的第一步是在模型中建立验证规则。这需要用到在模型中定义的 Model::validate 数组,例如:: + + class User extends AppModel { + public $validate = array(); + } + +在上面的例子中,``$validate`` 数组被加到 User 模型中,但此数组还不包含任何验证规则。 +假设 users 表中有login,password,email和born这些字段,下面的例子中是一些针对这些字段的简单验证规则:: + + class User extends AppModel { + public $validate = array( + 'login' => 'alphaNumeric', + 'email' => 'email', + 'born' => 'date' + ); + } + +上面的例子展示了验证规则如何作用于模型的字段。login 字段只接受字母和数字,email 应当是合法的电子 +邮件格式,以及 born 应当是合法的日期。定义了验证规则之后,如果提交的数据不符合定义的规则, +CakePHP 就能够在表单中自动显示错误信息。 + +CakePHP 有许多验证规则,使用起来相当容易。一些内置的验证规则可以让你检查电子邮件,网址和信用卡的 +格式 – 不过我们稍后才会详细介绍这些。 + +这是一个利用内置验证规则的更复杂的例子:: + + class User extends AppModel { + public $validate = array( + 'login' => array( + 'alphaNumeric' => array( + 'rule' => 'alphaNumeric', + 'required' => true, + 'message' => 'Letters and numbers only' + ), + 'between' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Between 5 to 15 characters' + ) + ), + 'password' => array( + 'rule' => array('minLength', '8'), + 'message' => 'Minimum 8 characters long' + ), + 'email' => 'email', + 'born' => array( + 'rule' => 'date', + 'message' => 'Enter a valid date', + 'allowEmpty' => true + ) + ); + } + +login 字段有两个验证规则:它只能包含字母和数字,长度介于5到15之间。password 字段至少要有8个字符长。 +email 字段必须是合法的电子邮件,而 born 字段必须是合法的日期。另外,请注意有关这些验证规则 +失败时 CakePHP 显示的错误信息。 + +如上例所示,一个字段可以有多个验证规则。另外,如果内置的规则不符合你的要求,在必要时总是可以增加 +自己的验证规则。 + +现在你应该已经明白验证大致是如何工作的了,让我们再来看看这些规则在模型中是如何定义的。定义验证规则 +有三种不同的方式: 简单的数组,每个字段带一个规则,每个字段带有多个规则。 + + +简单的规则 +============ + +正如名称所说的,这是定义验证规则最简单的方式。定义其规则的通常语法为:: + + public $validate = array('fieldName' => 'ruleName'); + +其中,'fieldName' 是定义的规则针对的字段名称,'ruleName' 是预先定义的规则名称, +比如'alphaNumeric','email' 或者 'isUnique'。 + +例如,为确保用户提供正确格式的电子邮件地址,你可以使用这个规则:: + + public $validate = array('user_email' => 'email'); + + +每个字段带一个规则 +================== + +这种定义方式可以更好地控制验证规则工作。但在讨论这些之前,让我们先来看看给一个字段加一个规则的常用模式:: + + public $validate = array( + // 或者: array('ruleName', 'param1', 'param2' ...) + 'fieldName1' => array( + 'rule' => 'ruleName', + 'required' => true, + 'allowEmpty' => false, + 'on' => 'create', // 或者: 'update' + 'message' => 'Your Error Message' + ) + ); + +'rule' 键是必需的。如果只设置 'required' => true,表单验证不会正常工作。这是因为 'required' +实际上不是一个规则。 + +这里你可以看到,每个字段(上面只显示了一个字段)与一个数组联系在一起,该数组有5个键: 'rule', +'required','allowEmpty','on' 和 'message'。让我们看一下他们的具体含义。 + +rule +---- + +'rule' 键定义了验证的方法,可以是单一的值,也可以是一个数组。指定的 'rule' 可以是模型的方法名称, +核心验证类的方法,或者正则表达式。关于在缺省情况下可用的规则的更多信息,请参看 :ref:`core-validation-rules`。 + +如果某个规则不需要带任何参数,'rule' 就可以是单一值,例如:: + + public $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric' + ) + ); + +如果某个规则需要带一些参数(比如最大值,最小值或者范围),'rule' 就应当是一个数组:: + + public $validate = array( + 'password' => array( + 'rule' => array('minLength', 8) + ) + ); + +谨记,'rule' 对于基于数组定义的规则是必需的。 + +required +-------- + +这个键接受布尔值,``create`` 或者 ``update``。把这个键置为 ``true`` 就会认为该字段总是必需的。 +而把它设成 ``create`` 或 ``update`` 就会认为该字段只在创建或者更新操作时是必需的。 +如果 'required' 的值为真,则该字段在数据数组中必须存在。例如,如果验证规则如下定义:: + + public $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'required' => true + ) + ); + +传给模型的 save() 方法的数据中必须含有 login 字段的数据,否则验证就会失败。该键的缺省值为布尔类型 false。 + +``required => true`` 和验证规则 ``notEmpty()`` 并不是一回事。``required => true`` +意味着数组的*键*必须存在 - 这不代表必须有值。所以,如果字段在数据集中不存在,验证就会失败,但如果提交的值 +为空(''),验证有可能(取决于规则)会成功。 + +.. versionchanged:: 2.1 + 增加了对 ``create`` 和 ``update`` 的支持。 + +allowEmpty +---------- + +如果设为 ``false`` ,字段的值必须为 *非空*,而 "nonempty" 定义为 ``!empty($value) || is_numeric($value)`` 。 +对数字的检查是为了使 CakePHP 能正确处理 ``$value`` 为零的情况。 + +``required`` 和 ``allowEmpty`` 的区别可能令人迷惑。``'required' => true`` 意味着在 ``$this->data`` +中没有该字段的 *键* ,你就不能保存模型(使用 ``isset`` 检查); 然而,``'allowEmpty' => false`` +正如前所述,确保当前字段的 *值* 不为空。 + +on +-- + +'on'键可以设置为下列值之一: 'update' 或者 'create' 。这提供了一种机制,允许某个规则要么在创建 +新记录时起作用,要么在更新记录时起作用。 + +如果一条规则含有 'on' => 'create',该规则只会在新记录创建时起作用。类似的,如果定义为 'on' => 'update' , +则只会在记录更新时起作用。 + +'on' 的缺省值是 null。当 'on' 为 null 时,该规则在创建和更新时都会起作用。 + +message +------- + +message 键是为规则定义验证失败时显示的错误信息:: + + public $validate = array( + 'password' => array( + 'rule' => array('minLength', 8), + 'message' => '密码至少8个字符长' + ) + ); + +.. note:: + + 当验证失败默认显示的消息是"This field cannot be left blank." + +每个字段多条规则 +======================== + +上面给出的方法比简单的规则赋值提供了更多的灵活性,但我们可以更进一步,可以更精细地控制数据验证。 +下面介绍的方法可以让我们对一个模型字段设置多条验证规则。 + +如果想要给一个字段设置多条验证规则,大致这样:: + + public $validate = array( + 'fieldName' => array( + 'ruleName' => array( + 'rule' => 'ruleName', + // 额外的键,比如 on,required 等等,放在这里 ... + ), + 'ruleName2' => array( + 'rule' => 'ruleName2', + // 额外的键,比如 on,required 等等,放在这里 ... + ) + ) + ); + +可以看到,这很象我们在前一节做的。在前一节,对每个字段我们只有一个数组的验证参数。 +而现在,每个'字段名'有一个数组的规则索引,每个'规则名称'有一个单独数组的验证参数。 + +用一个实际的例子能够更好地说明:: + + public $validate = array( + 'login' => array( + 'loginRule-1' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Only alphabets and numbers allowed', + ), + 'loginRule-2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters' + ) + ) + ); + +上面的例子对 login 字段设置了2个规则: loginRule-1和 loginRule-2。可以看到,每个规则 +都由一个随意的名字标识。 + +当对一个字段使用多条规则时,'required' 和 'allowEmpty' 键只需在第一条规则中设置一次。 + +last +------- + +当一个字段有多条规则时,缺省情况下,如果一条规则验证失败,那么这条规则的错误信息就会返回, +而该字段的其它规则就不会继续执行了。如果你希望,即使在一条规则验证失败时,验证也继续执行, +就把该条规则的 ``last`` 设置为 ``false``。 + +在下面的例子中,即使 "rule1" 验证失败,"rule2" 也会继续执行,而且,如果 "rule2" 也失败,则两条失败 +规则的错误信息都会返回:: + + public $validate = array( + 'login' => array( + 'rule' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Only alphabets and numbers allowed', + 'last' => false + ), + 'rule2' => array( + 'rule' => array('minLength', 8), + 'message' => 'Minimum length of 8 characters' + ) + ) + ); + +当使用这种数组格式设置验证规则时,可以不带 ``message`` 键。请看下面的例子:: + + public $validate = array( + 'login' => array( + 'Only alphabets and numbers allowed' => array( + 'rule' => 'alphaNumeric', + ), + ) + ); + +如果 ``alphaNumeric`` 规则验证失败,因为没有设置 ``message`` 键,该规则的键 +'Only alphabets and numbers allowed' 就会作为错误信息返回。 + + +自定义验证规则 +======================= + +如果到此还没有找到你需要的验证规则,可以创建自己的验证规则。通过两种方法: 自定义正则表达式, +或者创建自定义验证方法。 + +使用自定义的正则表达式进行验证 +------------------------------------ + +如果可以使用正则表达式匹配需要的验证,那么可以设置自定义正则表达式作为字段验证规则:: + + public $validate = array( + 'login' => array( + 'rule' => '/^[a-z0-9]{3,}$/i', + 'message' => 'Only letters and integers, min 3 characters' + ) + ); + +上面的例子检查 login 字段是否只包含字母和数字,至少3个字符。 + +``rule`` 中的正则表达式必须由斜线界定起始。最后一个斜线之后的 'i' 是可以省略的, +表示正则表达式是大小写无关。 + +添加自定义的验证方法 +---------------------------------- + +有时候使用正则表达式检查数据是不够的。比如,如果你想确保一个折扣号码只能使用25次, +就需要添加自己的验证函数,如下所示:: + + class User extends AppModel { + + public $validate = array( + 'promotion_code' => array( + 'rule' => array('limitDuplicates', 25), + 'message' => 'This code has been used too many times.' + ) + ); + + public function limitDuplicates($check, $limit) { + // $check 的值为: array('promotion_code' => 'some-value') + // $limit 的值为: 25 + $existing_promo_count = $this->find('count', array( + 'conditions' => $check, + 'recursive' => -1 + )); + return $existing_promo_count < $limit; + } + } + +要验证的当前字段会以关联数组的形式传入函数的第一个参数,字段名为键,提交的数据为值。 + +如果要给验证函数传入其他参数,需要在 'rule' 数组中加入更多元素,再在你的函数中 +(在主要的 ``$check`` 参数之后)作为其他参数处理。 + +自定义的验证函数可以定义在模型中(正如上面的例子),也可以定义在一个模型实现的行为中。 +这包括映射方法(*mapped method*)。 + +模型/行为方法会优先考虑,之后才会查找 ``Validation`` 类的方法。这意味着你可以重载 +现存的验证方法(例如 ``alphaNumeric()``),或者在应用程序级别(通过 +给 ``AppModel`` 添加方法),或者在模型级别。 + +当编写一个可以用于多个字段的验证规则时,注意从 $check 提取字段的值。$check 数组传入 +时以表单名为键,以字段的值为其值。要被验证的整个记录保存在 $this->data 成员变量中:: + + class Post extends AppModel { + + public $validate = array( + 'slug' => array( + 'rule' => 'alphaNumericDashUnderscore', + 'message' => 'Slug can only be letters,' . + ' numbers, dash and underscore' + ) + ); + + public function alphaNumericDashUnderscore($check) { + // $data 数组用表单字段名为键传入 + // 必须提取其值,使函数通用 + $value = array_values($check); + $value = $value[0]; + + return preg_match('|^[0-9a-zA-Z_-]*$|', $value); + } + } + +.. note:: + + 自定义的验证方法必须有 ``public``。``protected`` 和 ``private`` 的验证方法是不支持的。 + +如果值合法,方法应该返回 ``true``。如果验证失败,返回 `false``。其它合法的返回值 +可以是字符串,作为错误信息显示。返回字符串意味着验证失败。字符串会覆盖 $validate 数组中 +设置的信息,作为字段不合法的原因,显示在视图的表单中。 + + +动态改变验证规则 +=================================== + +使用 ``$validate`` 属性来声明验证规则是为一个类定义静态规则的好方法。但难免会有一些情况下, +你想对预先定义的规则集动态添加、修改或删除验证规则。 + +所有的验证规则都保存在一个 ``ModelValidator`` 对象中,你模型中每个字段的验证规则集都在这里。 +定义新的规则简单到只需告诉该对象来存储需要的字段新的验证方法。 + + +添加新的验证规则 +--------------------------- + +.. versionadded:: 2.2 + +``ModelValidator`` 对象允许多种方法添加新字段到集合中。第一种是使用 ``add`` 方法:: + + // 在一个模型类中 + $this->validator()->add('password', 'required', array( + 'rule' => 'notEmpty', + 'required' => 'create' + )); + +这会为模型中的 `password` 字段添加一个规则。可以链接多个 add 方法来添加任意多个规则:: + + // 在一个模型类中 + $this->validator() + ->add('password', 'required', array( + 'rule' => 'notEmpty', + 'required' => 'create' + )) + ->add('password', 'size', array( + 'rule' => array('between', 8, 20), + 'message' => 'Password should be at least 8 chars long' + )); + +也可以为一个字段一次添加多个规则:: + + $this->validator()->add('password', array( + 'required' => array( + 'rule' => 'notEmpty', + 'required' => 'create' + ), + 'size' => array( + 'rule' => array('between', 8, 20), + 'message' => 'alphanumeric' + ) + )); + +或者,你可以用 validator 对象使用存取数组的方式直接对字段设置验证规则:: + + $validator = $this->validator(); + $validator['username'] = array( + 'unique' => array( + 'rule' => 'isUnique', + 'required' => 'create' + ), + 'alphanumeric' => array( + 'rule' => 'alphanumeric' + ) + ); + +修改现存的验证规则 +---------------------------------- + +.. versionadded:: 2.2 + +使用 validator 对象也可以修改当前的验证规则。有若干种方法可以修改现存规则, +对一个字段添加验证方法,或者从一个字段的验证规则集合中完全删除一条规则:: + + // 在一个模型类中 + $this->validator()->getField('password')->setRule('required', array( + 'rule' => 'required', + 'required' => true + )); + +你也可以用类似的方法完全替换掉一个字段的所有规则:: + + // 在一个模型类中 + $this->validator()->getField('password')->setRules(array( + 'required' => array(...), + 'otherRule' => array(...) + )); + +如果只要改变一个规则中的一个属性,可以直接设置 ``CakeValidationRule`` 对象的属性:: + + // 在一个模型类中 + $this->validator()->getField('password') + ->getRule('required')->message = 'This field cannot be left blank'; + +在定义验证规则属性可以使用的数组键,就可以作为 ``CakeValidationRule`` 中的属性的名字, +比如 'message' 和 'allowEmpty'。 + +类似于对集合添加新规则,也可以用存取数组的方式修改现存的规则:: + + $validator = $this->validator(); + $validator['username']['unique'] = array( + 'rule' => 'isUnique', + 'required' => 'create' + ); + + $validator['username']['unique']->last = true; + $validator['username']['unique']->message = '名字已经被占用'; + + +从集合中删除规则 +--------------------------- + +.. versionadded:: 2.2 + +可以完全删除一个字段的所有规则,或者一个字段规则集合中的一条规则:: + + // 完全删除一个字段的所有规则 + $this->validator()->remove('username'); + + // 删除字段 password 的 'required' 规则 + $this->validator()->remove('password', 'required'); + +另外,可以用数组访问的方式从集合中删除规则:: + + $validator = $this->validator(); + // 完全删除一个字段的所有规则 + unset($validator['username']); + + // 完删除 password 字段的 'required' 规则 + unset($validator['password']['required']); + +.. _core-validation-rules: + +核心验证规则 +===================== + +.. php:class:: Validation + +CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证容易得多。这个类有许多常用的验证方法, +就可以不必自己写了。下面,可以看到所有验证规则的完整列表,以及如何使用的例子。 + +.. php:staticmethod:: alphaNumeric(mixed $check) + + 字段的数据只能含有字母和数字。:: + + public $validate = array( + 'login' => array( + 'rule' => 'alphaNumeric', + 'message' => 'Usernames must only contain letters and numbers.' + ) + ); + +.. php:staticmethod:: between(string $check, integer $min, integer $max) + + 字段的数据长度必须在指定的数字范围内。必须提供最小值和最大值。 + Uses = not.:: + + public $validate = array( + 'password' => array( + 'rule' => array('between', 5, 15), + 'message' => 'Passwords must be between 5 and 15 characters long.' + ) + ); + + 数据的长度是"数据的字符串表示方式的字节数"。注意,在处理非 ASCII + 字符时可能会长于数据的字符数。 + + +.. php:staticmethod:: blank(mixed $check) + + 这个规则用来保证字段为空或者只含有空字符。空字符包括空格、制表符、回车和换行。:: + + public $validate = array( + 'id' => array( + 'rule' => 'blank', + 'on' => 'create' + ) + ); + + +.. php:staticmethod:: boolean(string $check) + + 字符的数据必须是布尔值。合法的值为 true 或 false,整数0或1,或者字符串'0'或'1'。:: + + public $validate = array( + 'myCheckbox' => array( + 'rule' => array('boolean'), + 'message' => 'myCheckbox 的值不正确' + ) + ); + + +.. php:staticmethod:: cc(mixed $check, mixed $type = 'fast', boolean $deep = false, string $regex = null) + + 这个规则用来检查数据是否是一个合法的信用卡号码。它接受3个参数: 'type','deep' 和 'regex'。 + + 'type' 键可以赋值为 'fast','all' 或者任意下面的值: + + - amex + - bankcard + - diners + - disc + - electron + - enroute + - jcb + - maestro + - mc + - solo + - switch + - visa + - voyager + + 如果 'type' 设置为 'fast',数据就会用主要的信用卡号码格式来检查。设置 'type' 为 'all', + 就会检查所有信用卡类型。你也可以设置 type 为一个你想匹配的类型的数组。 + + 'deep' 键应当设置为布尔值。如果设为 true,就会检查信用卡的 Luhn 算法(`http://en.wikipedia.org/wiki/Luhn\_algorithm `_)。缺省值为 false。 + + 'regex' 键允许提供自定义正则表达式,用来验证信用卡号码:: + + public $validate = array( + 'ccnumber' => array( + 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'message' => 'The credit card number you supplied was invalid.' + ) + ); + + +.. php:staticmethod:: comparison(mixed $check1, string $operator = null, integer $check2 = null) + + Comparison 用来比较数值。它支持"大于","小于","大于等于","小于等于","等于","不等于"。 + 下面是一些例子:: + + public $validate = array( + 'age' => array( + 'rule' => array('comparison', '>=', 18), + 'message' => 'Must be at least 18 years old to qualify.' + ) + ); + + public $validate = array( + 'age' => array( + 'rule' => array('comparison', 'greater or equal', 18), + 'message' => 'Must be at least 18 years old to qualify.' + ) + ); + + +.. php:staticmethod:: custom(mixed $check, string $regex = null) + + 当需要自定义的正则表达式时使用:: + + public $validate = array( + 'infinite' => array( + 'rule' => array('custom', '\u221E'), + 'message' => 'Please enter an infinite number.' + ) + ); + + +.. php:staticmethod:: date(string $check, mixed $format = 'ymd', string $regex = null) + + 这个规则确保数据是以合法的日期格式输入的。可以传入单个参数(可以是一个数组), + 用来检查输入日期的格式。参数的值可以为下列之一: + + - 'dmy' 例如27-12-2006或者27-12-06(分隔符可以是空格,英文句号,破折号,斜线) + - 'mdy' 例如12-27-2006或者12-27-06(分隔符可以是空格,英文句号,破折号,斜线) + - 'ymd' 例如2006-12-27或者06-12-27(分隔符可以是空格,英文句号,破折号,斜线) + - 'dMy' 例如27 December 2006或者27 Dec 2006 + - 'Mdy' 例如December 27, 2006或者Dec 27, 2006(逗号可以省略) + - 'My' 例如(December 2006或者Dec 2006) + - 'my' 例如12/2006或者12/06(分隔符可以是空格,英文句号,破折号,斜线) + + 如果没有提供该键,将使用缺省的键值 'ymd':: + + public $validate = array( + 'born' => array( + 'rule' => array('date', 'ymd'), + 'message' => 'Enter a valid date in YY-MM-DD format.', + 'allowEmpty' => true + ) + ); + + 虽然很多数据存储方式要求某个特定的日期格式,但是也许应该考虑承担麻烦的部分, + 接受众多的日期格式,然后再尝试进行转换,而不是强制用户使用指定的格式。能为用户做的越多越好。 + + 同时很多数据存储需要某种格式。可以考虑接收广泛的日期格式并且转换他们,而不是强制 + 用户使用给定的格式。为用户做的越多越好。 + + .. versionchanged:: 2.4 + 加入 ``ym`` 和 ``y`` 格式。 + +.. php:staticmethod:: datetime(array $check, mixed $dateFormat = 'ymd', string $regex = null) + + 这条规则确保数据是合法的 datetime 格式。可以传入一个参数(可以是数组) + 来指定日期的格式。参数的值可以是下面的一个或多个: + + - 'dmy' 例如27-12-2006或者27-12-06(分隔符可以是空格,英文句号,破折号,斜线) + - 'mdy' 例如12-27-2006或者12-27-06(分隔符可以是空格,英文句号,破折号,斜线) + - 'ymd' 例如2006-12-27或者06-12-27(分隔符可以是空格,英文句号,破折号,斜线) + - 'dMy' 例如27 December 2006或者27 Dec 2006 + - 'Mdy' 例如December 27, 2006或者Dec 27, 2006(逗号可以省略) + - 'My' 例如(December 2006或者Dec 2006) + - 'my' 例如12/2006或者12/06(分隔符可以是空格,英文句号,破折号,斜线) + + 如果没有提供该键,将使用缺省的键值 'ymd':: + + public $validate = array( + 'birthday' => array( + 'rule' => array('datetime', 'dmy'), + 'message' => 'Please enter a valid date and time.' + ) + ); + + 也可以传入第二个参数,指定一个定制的正则表达式。如果使用这个参数,这就会是 + 执行的唯一验证。 + + 注意,和 date()不同,datetime()会验证日期和时间。 + + +.. php:staticmethod:: decimal(integer $check, integer $places = null, string $regex = null) + + 这个规则确保数据是合法的 decimal 数值。可以传入参数来指定小数点后需要 + 多少位小数。如果没有参数传入,数据将会当做科学计数法的浮点数来验证, + 这样的话,如果小数点后面没有数字,验证就会失败:: + + public $validate = array( + 'price' => array( + 'rule' => array('decimal', 2) + ) + ); + + +.. php:staticmethod:: email(string $check, boolean $deep = false, string $regex = null) + + 这条规则检查数据是否是合法的电子邮件地址。给规则的第二个参数传入布尔值 + true,就会同时也验证电子邮件的主机地址是否也是合法的:: + + public $validate = array('email' => array('rule' => 'email')); + + public $validate = array( + 'email' => array( + 'rule' => array('email', true), + 'message' => 'Please supply a valid email address.' + ) + ); + + +.. php:staticmethod:: equalTo(mixed $check, mixed $compareTo) + + 这条规则确保数据的值等于给定的值,并且是相同的类型。 + + :: + + public $validate = array( + 'food' => array( + 'rule' => array('equalTo', 'cake'), + 'message' => 'This value must be the string cake' + ) + ); + + +.. php:staticmethod:: extension(mixed $check, array $extensions = array('gif', 'jpeg', 'png', 'jpg')) + + 这条规则检查合法的文件扩展名,比如.jpg或者.png。 + 允许多个扩展名以数组的形式传入。 + + :: + + public $validate = array( + 'image' => array( + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), + 'message' => 'Please supply a valid image.' + ) + ); + +.. php:staticmethod:: fileSize($check, $operator = null, $size = null) + + 这条规则用来检查文件大小。可以用 ``$operator`` 来决定要 + 使用的比较方法。所有 :php:func:`~Validation::comparison()` 支持的 + 操作符这里都可以使用。这个方法可以自动处理来自 ``$_FILES`` 的数组, + 它可以从 ``tmp_name`` 键读取,只要 ``$check`` 是个数组,并且含有这个键:: + + public $validate = array( + 'image' => array( + 'rule' => array('fileSize', '<=', '1MB'), + 'message' => 'Image must be less than 1MB' + ) + ); + + .. versionadded:: 2.3 + 这个方法是在2.3版本中添加的。 + +.. php:staticmethod:: inList(string $check, array $list) + + 这条规则确保数据限于一个给定的集合中。它需要以数组形式提供的一组值。如果数据能够和 + 给定的数组中的一个值匹配,字段就是合法的。 + + 例如:: + + public $validate = array( + 'function' => array( + 'allowedChoice' => array( + 'rule' => array('inList', array('Foo', 'Bar')), + 'message' => 'Enter either Foo or Bar.' + ) + ) + ); + + 默认区分大小写,可以设置 ``$caseInsensitive`` 为真来不区分大小写。 + +.. php:staticmethod:: ip(string $check, string $type = 'both') + + 这条规则确保提交的是合法的 IPv4 或 IPv6 的地址。允许 'both' (缺省值), + 'IPv4'或者'IPv6'。 + + :: + + public $validate = array( + 'clientip' => array( + 'rule' => array('ip', 'IPv4'), // 或者'IPv6', 或者'both'(缺省值) + 'message' => 'Please supply a valid IP address.' + ) + ); + + +.. php:method:: Model::isUnique() + + 字段的数据必须唯一,它不可以被其他任何数据行使用。 + + :: + + public $validate = array( + 'login' => array( + 'rule' => 'isUnique', + 'message' => 'This username has already been taken.' + ) + ); + + 可以提供多个字段来验证一组字段是否唯一:: + + public $validate = array( + 'email' => array( + 'rule' => array('isUnique', 'email', 'username'), + 'message' => 'This username & email combination has already been used.' + ) + ); + + 当跨多字段进行唯一规则验证时要确保字段列表中包含原始的字段。 + +.. php:staticmethod:: luhn(string|array $check, boolean $deep = false) + + Luhn 算法:一个校验码公式,来验证各种识别号码。更多信息,请参见 + `Luhn 算法 `_。 + + +.. php:staticmethod:: maxLength(string $check, integer $max) + + 这个规则确保数据在最大长度范围内。 + + :: + + public $validate = array( + 'login' => array( + 'rule' => array('maxLength', 15), + 'message' => 'Usernames must be no larger than 15 characters long.' + ) + ); + + 这里的长度是"数据的字符串表示的字节数"。注意,在处理非 ASCII + 字符时,这可能会长于数据的字符数。 + +.. php:staticmethod:: mimeType(mixed $check, array $mimeTypes) + + .. versionadded:: 2.2 + + 这个规则检查合法的 mimeType + + .. versionchanged:: 2.5 + + Since 2.5 ``$mimeTypes`` can be a regex string. + + :: + + public $validate = array( + 'image' => array( + 'rule' => array('mimeType', array('image/gif')), + 'message' => 'Invalid mime type.' + ), + 'logo' => array( + 'rule' => array('mimeType', '#image/.+#'), + 'message' => 'Invalid mime type.' + ), + ); + +.. php:staticmethod:: minLength(string $check, integer $min) + + 这个规则确保数据满足最小长度要求。 + + :: + + public $validate = array( + 'login' => array( + 'rule' => array('minLength', 8), + 'message' => 'Usernames must be at least 8 characters long.' + ) + ); + + 这里的长度是"数据的字符串表示的字节数"。注意,在处理非 ASCII + 字符时,这可能会长于数据的字符数。 + + +.. php:staticmethod:: money(string $check, string $symbolPosition = 'left') + + 这条规则确保数值是正确的财务金额。 + + 第二个参数指定货币符号在哪里(左/右)。 + + :: + + public $validate = array( + 'salary' => array( + 'rule' => array('money', 'left'), + 'message' => 'Please supply a valid monetary amount.' + ) + ); + +.. php:staticmethod:: multiple(mixed $check, mixed $options = array()) + + 用这条规则验证一个多选输入。它支持"in","max"和"min"参数。 + + :: + + public $validate = array( + 'multiple' => array( + 'rule' => array('multiple', array( + 'in' => array('do', 're', 'mi', 'fa', 'sol', 'la', 'ti'), + 'min' => 1, + 'max' => 3 + )), + 'message' => 'Please select one, two or three options' + ) + ); + + 默认区分大小写,可以设置 ``$caseInsensitive`` 为真来不区分大小写。 + +.. php:staticmethod:: notEmpty(mixed $check) + + 确保一个字段不为空的基本规则:: + + public $validate = array( + 'title' => array( + 'rule' => 'notEmpty', + 'message' => 'This field cannot be left blank' + ) + ); + + 不要对一个多选输入使用该规则,因为这会引起错误。请使用 "multiple"。 + + +.. php:staticmethod:: numeric(string $check) + + 检查传入的数据是一个正确的数:: + + public $validate = array( + 'cars' => array( + 'rule' => 'numeric', + 'message' => 'Please supply the number of cars.' + ) + ); + +.. php:staticmethod:: naturalNumber(mixed $check, boolean $allowZero = false) + + .. versionadded:: 2.2 + + 这个规则检查传入的数据是否是正确的自然数。 + 如果 ``$allowZero`` 设为 true,零也可以接受。 + + :: + + public $validate = array( + 'wheels' => array( + 'rule' => 'naturalNumber', + 'message' => 'Please supply the number of wheels.' + ), + 'airbags' => array( + 'rule' => array('naturalNumber', true), + 'message' => 'Please supply the number of airbags.' + ), + ); + + +.. php:staticmethod:: phone(mixed $check, string $regex = null, string $country = 'all') + + Phone 规则验证美国的电话号码。如果要验证美国以外的电话号码,可以在第二个参数用 + 一个正则表达式来处理其他的号码格式。 + + :: + + public $validate = array( + 'phone' => array( + 'rule' => array('phone', null, 'us') + ) + ); + + +.. php:staticmethod:: postal(mixed $check, string $regex = null, string $country = 'us') + + Postal 规则验证美国(us),加拿大(ca),英国(uk),意大利(it),德国(de)和比利时(be)的邮政编码。 + 对于其他的邮政编码格式,可以在第二个参数提供一个正则表达式。 + + :: + + public $validate = array( + 'zipcode' => array( + 'rule' => array('postal', null, 'us') + ) + ); + + +.. php:staticmethod:: range(string $check, integer $lower = null, integer $upper = null) + + 这条规则确保数值在一个给定范围内。如果没有指定范围,规则会检查数值是当前平台上的合法有限数。 + + :: + + public $validate = array( + 'number' => array( + 'rule' => array('range', -1, 11), + 'message' => 'Please enter a number between 0 and 10' + ) + ); + + + 上面的例子会接受大于0(比如0.01)而且小于10 (比如9.99)的任何数值。 + + .. note:: + + 上下限的值是不包括在内的。 + + +.. php:staticmethod:: ssn(mixed $check, string $regex = null, string $country = null) + + Ssn 规则验证美国(us),丹麦(dk),和荷兰(nl)的社会保险号码。对于其他的社会保险号码, + 可以指定正则表达式。 + + :: + + public $validate = array( + 'ssn' => array( + 'rule' => array('ssn', null, 'us') + ) + ); + + +.. php:staticmethod:: time(string $check) + + Time 验证规则决定传入的字符串是否是正确的时间。以24小时(HH:MM)或上午/下午([H]H:MM[a|p]m)来验证。 + 不允许/验证秒。 + +.. php:staticmethod:: uploadError(mixed $check) + + .. versionadded:: 2.2 + + 这条规则检查文件上载是否有错。 + + :: + + public $validate = array( + 'image' => array( + 'rule' => 'uploadError', + 'message' => 'Something went wrong with the upload.' + ), + ); + +.. php:staticmethod:: url(http://github.com/string%20$check,%20boolean%20$strict%20=%20false) + + 这条规则检查合法的网址格式。支持 http(s),ftp(s), + file,news,and gopher 协议:: + + public $validate = array( + 'website' => array( + 'rule' => 'url' + ) + ); + + 为确保协议在网址中,可以象这样使用严格模式:: + + public $validate = array( + 'website' => array( + 'rule' => array('url', true) + ) + ); + + 这个验证方法用到了复杂正则表达式,有时会在使用mod\php模块的Apache2的Windows环境下出现问题。 + +.. php:staticmethod:: userDefined(mixed $check, object $object, string $method, array $args = null) + + 执行用户定义的验证。 + + +.. php:staticmethod:: uuid(string $check) + + 检查数据是合法的 uuid: http://tools.ietf.org/html/rfc4122。 + + +本地化验证 +==================== + +验证规则 phone() 和 postal() 会把它们不知道如何处理的国家前缀交给适当命名的其他类来处理。 +例如,如果住在荷兰,就可以创建这样一个类:: + + class NlValidation { + public static function phone($check) { + // ... + } + public static function postal($check) { + // ... + } + } + +这个文件可以放在 ``APP/Validation/`` 或者 ``App/PluginName/Validation/``,但必须在使用之前 +用 App::uses() 导入。在模型验证中可以这样使用 NlValidation 类:: + + public $validate = array( + 'phone_no' => array('rule' => array('phone', null, 'nl')), + 'postal_code' => array('rule' => array('postal', null, 'nl')), + ); + +当模型数据被验证时,验证程序会知道它无法处理 ``nl`` 地区,就会尝试把任务 +交给 ``NlValidation::postal()``,该方法的返回值就会被用作验证通过/失败的标志。这个方法 +允许你创建一些类来处理一组或一个子集的地区,这是一个大的 switch 语句无法实现的。单个验证 +方法的用法并没有改变,只是增加了跳转到其他验证的功能。 + +.. tip:: + + Localized 插件已经包括许多可以使用的规则: + https://github.com/cakephp/localized + 另外,随意贡献你的本地化验证规则。 + +.. toctree:: + + data-validation/validating-data-from-the-controller + + +.. meta:: + :title lang=zh_CN: Data Validation + :keywords lang=zh_CN: validation rules,validation data,validation errors,data validation,credit card numbers,core libraries,password email,model fields,login field,model definition,php class,many different aspects,eight characters,letters and numbers,business rules,validation process,date validation,error messages,array,formatting \ No newline at end of file diff --git a/zh/models/data-validation/validating-data-from-the-controller.rst b/zh/models/data-validation/validating-data-from-the-controller.rst new file mode 100644 index 0000000000..13b330ab7f --- /dev/null +++ b/zh/models/data-validation/validating-data-from-the-controller.rst @@ -0,0 +1,61 @@ +从控制器验证数据 +################################### + +虽然通常只使用模型的 save 方法,但也许有时仅需要验证而不保存数据。例如,也许在真正的把数据 +保存到数据库之前,想要给用户显示一些额外的信息。验证数据的过程与保存数据相比,有细微的差别。 + +首先,将数据赋值给模型:: + + $this->ModelName->set($this->request->data); + +然后要检查数据是否正确,使用模型的 validates 方法,如果正确就会返回 true,否则就返回 false:: + + if ($this->ModelName->validates()) { + // 验证通过的逻辑 + } else { + // 验证未通过的逻辑 + $errors = $this->ModelName->validationErrors; + } + +也许更乐意用模型中定义的验证规则的子集来验证模型。比方说有一个 User 模型,含有 first\_name, +last\_name,email 和 password 字段。在这个例子中,当创建或者编辑一个用户时会验证整个这4个 +字段。然而当用户登录时只想验证电子邮件和密码字段。要做到这点可以传入一个选项数组指定要验证 +的字段:: + + if ($this->User->validates(array('fieldList' => array('email', 'password')))) { + // 有效 + } else { + // 无效 + } + +validates 方法调用 invalidFields 方法,后者填入模型的 validationErrors 属性。 +invalidFields 方法也把这个属性作为结果返回:: + + $errors = $this->ModelName->invalidFields(); // 包含 validationErrors 数组 + +在对 ``invalidFields()`` 的连续调用之间,验证错误列表不会清除。所以如果是在一个循环内 +验证,并且想要分开每组错误就不要使用 ``invalidFields()`` 。而是使用 ``validates()`` 方法 +再读取 ``validationErrors`` 模型属性。 + +重要的是要记住,在验证数据前数据必须赋值给模型。这不同于 save 方法可以允许数据作为参数传入。 +另外,记住调用 save 方法之前并没有必要调用 validates 方法,因为 save 方法在真的保存数据之前会自动 +验证数据。 + +要验证多个模型,应当使用下面的方法:: + + if ($this->ModelName->saveAll($this->request->data, array('validate' => 'only'))) { + // 验证通过 + } else { + // 验证未通过 + } + +如果在保存之前验证了数据,那么可以关闭验证以避免第二次验证:: + + if ($this->ModelName->saveAll($this->request->data, array('validate' => false))) { + // 不验证就保存 + } + + +.. meta:: + :title lang=zh_CN: Validating Data from the Controller + :keywords lang=zh_CN: password rules,validations,subset,array,logs,logic,email,first name last name,models,options,data model \ No newline at end of file From 3f5f24820c2accb53a8b7469a96afc134a3b2f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Wed, 15 Oct 2014 16:29:02 +0200 Subject: [PATCH 040/292] Changed public $_schema to protected I haven't found a reason why this needs to be so. --- en/models/model-attributes.rst | 2 +- fr/models/model-attributes.rst | 2 +- ja/models/model-attributes.rst | 2 +- sr/models/model-attributes.rst | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/en/models/model-attributes.rst b/en/models/model-attributes.rst index 651b3e61f4..29b8c422d2 100644 --- a/en/models/model-attributes.rst +++ b/en/models/model-attributes.rst @@ -175,7 +175,7 @@ Each field is described by: Example Usage:: - public $_schema = array( + protected $_schema = array( 'first_name' => array( 'type' => 'string', 'length' => 30 diff --git a/fr/models/model-attributes.rst b/fr/models/model-attributes.rst index 036d3ef725..e39d2a2a7b 100755 --- a/fr/models/model-attributes.rst +++ b/fr/models/model-attributes.rst @@ -182,7 +182,7 @@ du model. Chaque champ est décrit par: Exemple d'utilisation:: - public $_schema = array( + protected $_schema = array( 'first_name' => array( 'type' => 'string', 'length' => 30 diff --git a/ja/models/model-attributes.rst b/ja/models/model-attributes.rst index 149403f685..99e8941b96 100644 --- a/ja/models/model-attributes.rst +++ b/ja/models/model-attributes.rst @@ -169,7 +169,7 @@ data 使用例:: - public $_schema = array( + protected $_schema = array( 'first_name' => array( 'type' => 'string', 'length' => 30 diff --git a/sr/models/model-attributes.rst b/sr/models/model-attributes.rst index 651b3e61f4..29b8c422d2 100644 --- a/sr/models/model-attributes.rst +++ b/sr/models/model-attributes.rst @@ -175,7 +175,7 @@ Each field is described by: Example Usage:: - public $_schema = array( + protected $_schema = array( 'first_name' => array( 'type' => 'string', 'length' => 30 From 5639e933fd5268b758cc1330cafe2e88937296b3 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 15 Oct 2014 16:45:32 -0400 Subject: [PATCH 041/292] Document change in RedisEngine Refs cakephp/cakephp#4884 --- en/appendices/2-6-migration-guide.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 2619ff6b78..072c5cb21b 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -15,6 +15,14 @@ Basics.php the context. The new ``__x``, ``__xn``, ``__dx``, ``__dxn``, ``__dxc``, ``__dxcn``, and ``__xc`` functions provide access to the new features. +Cache +===== + +RedisEngine +----------- + +- The ``RedisEngine`` now has a default prefix of ``Inflector::slug(APP_DIR)``. + Console ======= From 942ccf735cfdf395ad3e7f11e6c902cf19cd727d Mon Sep 17 00:00:00 2001 From: mafeifan Date: Thu, 16 Oct 2014 23:26:57 +0800 Subject: [PATCH 042/292] [zh_CN] done zh/models/deleting-data.rst --- zh/models/deleting-data.rst | 58 +++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 zh/models/deleting-data.rst diff --git a/zh/models/deleting-data.rst b/zh/models/deleting-data.rst new file mode 100644 index 0000000000..cbfa9c038b --- /dev/null +++ b/zh/models/deleting-data.rst @@ -0,0 +1,58 @@ +删除数据 +######## + +CakePHP的模型类提供了多种从数据库中删除记录的方法。 + +.. _model-delete: + +delete方法 +====== + +``delete(int $id = null, boolean $cascade = true);`` + +删除指定$id的记录。默认情况下,会级联删除依赖于该记录的所有记录。 + +例如,当删除一个User,User关联了许多条Recipe记录(使用 'hasMany' 或'hasAndBelongsToMany' 来关联Recipes)。 + +- 如果$cascade设置为true,并且被关联的模型中dependent属性值为true,删除User的同时会删除关联的Recipe记录。 +- 如果$cascade设置为false,删除User不会删除关联的Recipe记录。 + +如果你使用的数据库支持外键和级联删除,会比CakePHP自带的级联删除效率更高。 +使用模型中的 ``Model::delete()`` 方法好处之一是支持使用行为及回调方法:: + + $this->Comment->delete($this->request->data('Comment.id')); + +在处理删除操作中,可以在自定义业务逻辑使用 ``beforeDelete`` 和 ``afterDelete`` 回调方法。 +这些方法位于模型和行为中。更多信息参见 +:doc:`/models/callback-methods`。 + +.. _model-deleteall: + +deleteAll +========= + +``deleteAll(mixed $conditions, $cascade = true, $callbacks = false)`` + +``deleteAll()`` 与 ``delete()`` 类似,将删除匹配条件的所有记录。 +``$conditions`` 作为条件可以是SQL语句片段或数组形式。 + +* **conditions** 匹配的条件 +* **cascade** 布尔型,设置true,会导致级联删除 +* **callbacks** 布尔型, 执行回调函数 + +执行成功返回true,失败返回false。 + +Example:: + + // 与 find() 方法类似,删除满足条件的记录 + $this->Comment->deleteAll(array('Comment.spam' => true), false); + +如果通过回调方法和(或)级联方式删除记录,这往往会导致更多的查询。在deleteAll() 中匹配的记录被删除前,关联会重置。如果是使用bindModel()或unbindModel()来改变关联,应该设置 **reset** 为 ``false``。 + +.. note:: + + 如果删除条件执行正确但没有找到匹配的记录,造成没有任何记录被删除,deleteAll() 也将返回true。 + +.. meta:: + :title lang=zh_CN: Deleting Data + :keywords lang=zh_CN: doc models,custom logic,callback methods,model class,database model,callbacks,information model,request data,deleteall,fragment,leverage,array,cakephp,failure,recipes,benefit,delete,data model From f9f59ef3b69f00e60101063905ba812a9d16c532 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Thu, 16 Oct 2014 22:21:21 +0200 Subject: [PATCH 043/292] [fr] corrections --- fr/models/data-validation.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fr/models/data-validation.rst b/fr/models/data-validation.rst index 0e63614628..46dbadc060 100644 --- a/fr/models/data-validation.rst +++ b/fr/models/data-validation.rst @@ -653,7 +653,7 @@ complète de toutes les règles, illustrées par des exemples d'utilisation. .. php:staticmethod:: blank(mixed $check) Cette règle est utilisée pour vérifier que le champ est laissé vide ou que - seulement des caractères blancs y sont présent. Les caractères blancs + seulement des caractères blancs y sont présents. Les caractères blancs incluent l'espace, la tabulation, le retour chariot et nouvelle ligne.:: public $validate = array( @@ -957,8 +957,8 @@ complète de toutes les règles, illustrées par des exemples d'utilisation. ) ); - You can validate that a set of fields are unique by providing multiple - fields and set ``$or`` to ``false``:: + Vous pouvez valider qu'un ensemble de champs sont uniques en fournissant + plusieurs champs et en paramétrant ``$or`` à ``false``:: public $validate = array( 'email' => array( @@ -968,7 +968,7 @@ complète de toutes les règles, illustrées par des exemples d'utilisation. ); Assurez-vous d'inclure le champ d'origine dans la liste des champs quand vous - joutezd'établir une règle unique sur plusieurs champs. + établissez une règle unique sur plusieurs champs. .. php:staticmethod:: luhn(string|array $check, boolean $deep = false) From e8b80c52b3c2cc7619a9e0ecdfd11183ee6c1281 Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Fri, 17 Oct 2014 17:39:38 +0800 Subject: [PATCH 044/292] [zh_CN] fix reference error --- zh/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zh/index.rst b/zh/index.rst index e1dc1cad4e..a7adf8a051 100644 --- a/zh/index.rst +++ b/zh/index.rst @@ -47,7 +47,7 @@ CakePHP CookBook 是一个公开地开发和社区可以编辑的文档项目。 模型(Models) ------------ -模型是应用程序的核心, 它们负责数据的验证、存储和读取。详情见:doc:`/models`。 +模型是应用程序的核心, 它们负责数据的验证、存储和读取。详情见 :doc:`/models`。 获取帮助 ============ From 53bf4105e7c48b8dabb4a70b25e9ddad1daba1ed Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Fri, 17 Oct 2014 19:01:39 +0800 Subject: [PATCH 045/292] [zh_CN] fix translation error --- zh/models/data-validation.rst | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/zh/models/data-validation.rst b/zh/models/data-validation.rst index 3813ec638c..feadf2c4c8 100644 --- a/zh/models/data-validation.rst +++ b/zh/models/data-validation.rst @@ -147,9 +147,10 @@ required 传给模型的 save() 方法的数据中必须含有 login 字段的数据,否则验证就会失败。该键的缺省值为布尔类型 false。 -``required => true`` 和验证规则 ``notEmpty()`` 并不是一回事。``required => true`` -意味着数组的*键*必须存在 - 这不代表必须有值。所以,如果字段在数据集中不存在,验证就会失败,但如果提交的值 -为空(''),验证有可能(取决于规则)会成功。 +``required => true`` 和验证规则 ``notEmpty()`` 并不是一回事。 +``required => true`` 意味着数组的 *键* 必须存在 - 这不代表必须有值。所以,如果字 +段在数据集中不存在,验证就会失败,但如果提交的值为空(''),验证有可能(取决于规则) +会成功。 .. versionchanged:: 2.1 增加了对 ``create`` 和 ``update`` 的支持。 @@ -361,7 +362,7 @@ last 自定义的验证方法必须有 ``public``。``protected`` 和 ``private`` 的验证方法是不支持的。 -如果值合法,方法应该返回 ``true``。如果验证失败,返回 `false``。其它合法的返回值 +如果值合法,方法应该返回 ``true``。如果验证失败,返回 ``false``。其它合法的返回值 可以是字符串,作为错误信息显示。返回字符串意味着验证失败。字符串会覆盖 $validate 数组中 设置的信息,作为字段不合法的原因,显示在视图的表单中。 @@ -647,9 +648,6 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 虽然很多数据存储方式要求某个特定的日期格式,但是也许应该考虑承担麻烦的部分, 接受众多的日期格式,然后再尝试进行转换,而不是强制用户使用指定的格式。能为用户做的越多越好。 - 同时很多数据存储需要某种格式。可以考虑接收广泛的日期格式并且转换他们,而不是强制 - 用户使用给定的格式。为用户做的越多越好。 - .. versionchanged:: 2.4 加入 ``ym`` 和 ``y`` 格式。 @@ -841,7 +839,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 .. versionchanged:: 2.5 - Since 2.5 ``$mimeTypes`` can be a regex string. + 从 2.5 版本开始 ``$mimeTypes`` 可以是正则表达式字符串. :: @@ -1038,8 +1036,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 .. php:staticmethod:: url(http://github.com/string%20$check,%20boolean%20$strict%20=%20false) - 这条规则检查合法的网址格式。支持 http(s),ftp(s), - file,news,and gopher 协议:: + 这条规则检查合法的网址格式。支持 http(s)、ftp(s)、file、news 和 gopher 协议:: public $validate = array( 'website' => array( @@ -1055,7 +1052,8 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 ) ); - 这个验证方法用到了复杂正则表达式,有时会在使用mod\php模块的Apache2的Windows环境下出现问题。 + 这个验证方法用到了复杂正则表达式,有时会在 Windows 平台上在使用 mod\_php + 模块的 Apache2 中出现问题。 .. php:staticmethod:: userDefined(mixed $check, object $object, string $method, array $args = null) @@ -1064,7 +1062,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 .. php:staticmethod:: uuid(string $check) - 检查数据是合法的 uuid: http://tools.ietf.org/html/rfc4122。 + 检查数据是合法的 UUID: http://tools.ietf.org/html/rfc4122。 本地化验证 @@ -1098,14 +1096,14 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 .. tip:: Localized 插件已经包括许多可以使用的规则: - https://github.com/cakephp/localized - 另外,随意贡献你的本地化验证规则。 + https://github.com/cakephp/localized。另外,随意贡献你的本地化验证规则。 .. toctree:: + :maxdepth: 1 data-validation/validating-data-from-the-controller .. meta:: :title lang=zh_CN: Data Validation - :keywords lang=zh_CN: validation rules,validation data,validation errors,data validation,credit card numbers,core libraries,password email,model fields,login field,model definition,php class,many different aspects,eight characters,letters and numbers,business rules,validation process,date validation,error messages,array,formatting \ No newline at end of file + :keywords lang=zh_CN: validation rules,validation data,validation errors,data validation,credit card numbers,core libraries,password email,model fields,login field,model definition,php class,many different aspects,eight characters,letters and numbers,business rules,validation process,date validation,error messages,array,formatting From 24150032d8abde138367f9c9716014ec4d3e0e16 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Fri, 17 Oct 2014 21:01:08 +0200 Subject: [PATCH 046/292] [fr] typos --- fr/development/exceptions.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fr/development/exceptions.rst b/fr/development/exceptions.rst index e9688c260d..6f87fc1292 100755 --- a/fr/development/exceptions.rst +++ b/fr/development/exceptions.rst @@ -106,7 +106,7 @@ pas été trouvés:: En utilisant les exceptions pour les erreurs HTTP, vous pouvez garder à la fois votre code propre, et donner les réponses complètement REST aux -appications clientes et aux utilisateurs. +applications clientes et aux utilisateurs. De plus, les exceptions de couche du framework suivantes sont disponibles, et seront lancées à partir de certains components du coeur de CakePHP: @@ -178,7 +178,7 @@ seront lancées à partir de certains components du coeur de CakePHP: Ces classes d'exception étendent toutes :php:exc:`CakeException`. En étendant CakeException, vous pouvez créer vos propres erreurs 'framework'. -Toutes les Exceptions standardes que CakePHP va aussi lancer les CakeException +Toutes les Exceptions standards que CakePHP va aussi lancer les CakeException étendues. .. versionadded:: 2.3 @@ -302,7 +302,7 @@ utilisé quand vous créez une exception:: Va créer un code de réponse ``501``, vous pouvez utiliser le code de statut HTTP que vous souhaitez. En développement, si votre exception n'a pas de template spécifique, et que vous utilisez un code égal ou supérieur -à ``500``, vous verrez le template ``error500``. Pour toute autre code +à ``500``, vous verrez le template ``error500``. Pour tout autre code d'erreur, vous aurez le template ``error400``. Si vous avez défini un template d'erreur pour votre exception personnalisée, ce template va être utilisé en mode développement. Si vous souhaitez votre propre gestionnaire d'exception @@ -351,7 +351,7 @@ ceci:: Vous pouvez lancer tout code que vous souhaitez à l'intérieur de ``handleException``. Le code ci-dessus afficherait simplement 'Oh noes! ' -plus le message d'exception. Vouspouvez définir des gestionnaires d'exception +plus le message d'exception. Vous pouvez définir des gestionnaires d'exception comme tout type de callback, même une fonction anonyme si vous utilisez PHP 5.3:: From 69524d1620ce597a8958e46a2b4adc9b3f40aa49 Mon Sep 17 00:00:00 2001 From: HOTTA Michihide Date: Sun, 19 Oct 2014 16:36:37 +0900 Subject: [PATCH 047/292] add ja/core-libraries/helpers/form.rst --- ja/core-libraries/helpers/form.rst | 1783 ++++++++++++++++++++++++++++ 1 file changed, 1783 insertions(+) create mode 100644 ja/core-libraries/helpers/form.rst diff --git a/ja/core-libraries/helpers/form.rst b/ja/core-libraries/helpers/form.rst new file mode 100644 index 0000000000..dba38e9181 --- /dev/null +++ b/ja/core-libraries/helpers/form.rst @@ -0,0 +1,1783 @@ +FormHelper +########## + +.. php:class:: FormHelper(View $view, array $settings = array()) + +FormHelperはフォーム作成時の力作業のほとんどを代行してくれます。 +フォームをすばやく作成する機能に特化して、バリデーション +(入力値の妥当性検査)や部品の配置、レイアウトを効率化します。 +FormHelperはまた柔軟でもあります。 +通常は組み込まれた規則に沿ってほとんどのことをやってくれますが、 +特定のメソッドを使って必要な機能だけを使うこともできます。 + +フォームの作成 +============== + +FormHelperの利点を活用するために最初に使うメソッドは ``create()`` +です。この特別なメソッドはフォームの開始タグを出力します。 + + +.. php:method:: create(string $model = null, array $options = array()) + + パラメータはすべてオプションです。パラメータなしで呼ばれた場合、 + 現在のURLを通して現在のコントローラーにサブミット(提出)するための + フォームを作ろうとしているものとみなします。フォームをサブミット + するためのデフォルトのメソッドはPOSTです。フォームの要素は DOM ID + とともに返されますが、このIDはモデル名とコントローラーのアクション + 名をキャメルケースにしたものとして生成されます。たとえば + UsersControllerビューの中から ``create()`` を呼んだ場合、 + 以下のように描画されたビューが得られます: + + .. code-block:: html + +
+ + .. note:: + + ``$model`` に対して ``false`` を渡すこともできます。こうすると、 + フォームデータは( ``$this->request->data['Model']`` サブ配列 + の中ではなく) ``$this->request->data`` 配列の中に格納されます。 + これは、データベースの中身とは関係ないちょっとしたフォームを + 作る時に便利です。 + + さらに ``create()`` メソッドでは、パラメータを使っていろいろな + カスタマイズができます。まず、モデル名の指定ができます。フォームに + モデル名を指定すると、フォームの *中身* を作っていることになります。 + すべての項目は(特に指定されない限り)そのモデルに所属するものと + 見なされ、そのモデルから参照されるモデルについても、そのフォームに + 関連付けられます。モデルを指定しない場合、現在のコントローラー + のデフォルトモデルを使っていると見なされます:: + + + // たとえば /recipes/add にいるとして、 + echo $this->Form->create('Recipe'); + + 出力: + + .. code-block:: php + + + + これはフォームデータをRecipesControllerの ``add()`` アクションに + POSTします。なお、これと同じロジックを使って編集フォームを作ることも + できます。FormHelperはフォームの追加や編集のどちらなのかを自動的に + 検出し、 ``$this->request->data`` を適切に利用します。もし + ``$this->request->data`` にフォームのモデルに関連する名前がついた + 配列要素が含まれていて、かつその配列に含まれるモデルのプライマリキー + の値が空でなければ、FormHelperはそのレコードの編集用フォームを作成 + します。たとえば http://site.com/recipes/edit/5 にアクセスすると、 + 以下の様な出力が得られます:: + + // Controller/RecipesController.php: + public function edit($id = null) { + if (empty($this->request->data)) { + $this->request->data = $this->Recipe->findById($id); + } else { + // ここに保存のためのロジックを置く + } + } + + // View/Recipes/edit.ctp: + // $this->request->data['Recipe']['id'] = 5 + // になるので編集フォームを作る + Form->create('Recipe'); ?> + + 出力: + + .. code-block:: html + + + + + .. note:: + + これは編集フォームなので hidden の入力項目が生成され、 + デフォルトの HTTP メソッドは上書きされます。 + + プラグイン内のモデル用にフォームを作る場合は、常に :term:`plugin syntax` + を使います。これで以下のように適切なフォームが生成されます:: + + echo $this->Form->create('ContactManager.Contact'); + + 配列 ``$options`` にはフォーム設定に関するほとんどのことを指定できます。 + この特別な配列には、フォームタグを作成する際のやり方に影響する、 + さまざまなキーバリューの組合せを数多く指定可能です。 + + .. versionchanged:: 2.0 + すべてのフォームに関するデフォルトの URL は、現在の URL の後ろに + 渡されたパラメータ、名前付きパラメータ、問合せ文字列をつけたものに + なりました。このデフォルトを変更するには、 ``$this->Form->create()`` + の第二引数の中に ``$options['url']`` を指定します。 + +create()のオプション +-------------------- +create()には多くのオプションがあります: + +* ``$options['type']`` このキーは生成するフォームのタイプを指定します。 + 有効な値は 'post', 'get', 'file', 'put', 'delete' です。 + + 'post' と 'get' は、フォームの送信用メソッドをこの通り変更します:: + + echo $this->Form->create('User', array('type' => 'get')); + + 出力はこうなります: + + .. code-block:: html + + + + タイプ 'file' はフォームの送信用メソッドを 'post' にして、かつフォーム + タグに "multipart/form-data" という enctype を追加します。これはフォーム + 内に何らかのファイル要素がある場合に指定されるべきものです。適切な + enctype 属性が抜けていると、ファイルのアップロードがうまく動きません:: + + echo $this->Form->create('User', array('type' => 'file')); + + 出力はこうなります: + + .. code-block:: html + + + + 'put' や 'delete' を使う場合、そのフォームは機能的に 'post' と同じですが、 + サブミットされる際、HTTP のリクエストメソッドが 'PUT' または 'DELETE' + に上書きされます。これにより、Web ブラウザにおける REST サポートを + CakePHP がエミュレートできるようになります。 + +* ``$options['action']`` 現在のコントローラーにおいて、特定のアクションに + 対してフォームデータを送り込むことができます。たとえば現在のコントローラー + の login() アクションにフォームデータを渡したい場合、$options 配列には + 以下のように指定します:: + + echo $this->Form->create('User', array('action' => 'login')); + + 出力はこうなります: + + .. code-block:: html + + + +* ``$options['url']`` 現在のコントローラー以外にフォームデータを渡したい + 場合、$options 配列の 'url' キーを使ってフォームアクションの URL + を指定します。指定された URL は作成中の CakePHP アプリケーションに + 対する相対値を指定できます:: + + echo $this->Form->create(null, array( + 'url' => array('controller' => 'recipes', 'action' => 'add') + )); + + 出力はこうなります: + + .. code-block:: html + + + + もしくは、外部ドメインも指定可能です:: + + echo $this->Form->create(null, array( + 'url' => 'http://www.google.com/search', + 'type' => 'get' + )); + + 出力はこうなります: + + .. code-block:: html + + + + さらにいろいろなタイプの URL を指定する例は、:php:meth:`HtmlHelper::url()` + メソッドを参照してみてください。 + +* ``$options['default']`` 'default' がブール値の false に設定されている場合、 + フォームの submit アクションが変更され、submit ボタンを押してもフォームが + submit されなくなります。そのフォームが AJAX を経由して submit するように + なっている場合は 'default' を false にしてフォームのデフォルトの挙動を + 抑止し、その代わり AJAX 経由でデータを取得して submit するようにできます。 + +* ``$options['inputDefaults']`` ``input()`` のデフォルトオプションの + 組合せを ``inputDefaults`` キーとしてセットすると、入力生成における + 標準の振る舞いをカスタマイズできます。:: + + echo $this->Form->create('User', array( + 'inputDefaults' => array( + 'label' => false, + 'div' => false + ) + )); + + これ以降に生成される入力項目は、すべてinputDefaultsで宣言された + オプションを継承します。defaultOptionsを上書きするには + input() 呼び出しで以下のようにオプションを指定します:: + + echo $this->Form->input('password'); // div も label も持たない + // label 要素を持つ + echo $this->Form->input( + 'username', + array('label' => 'Username') + ); + +フォームを閉じる +================ + +.. php:method:: end($options = null, $secureAttributes = array()) + + FormHelperにはフォームを完成させる ``end()`` メソッドがあります。 + 多くの場合 ``end()`` はフォームの閉じタグを出力するだけですが、 + :php:class:`SecurityComponent` が要求する hidden のフォーム要素を + FormHelper に挿入させることもできます: + + .. code-block:: php + + Form->create(); ?> + + + + Form->end(); ?> + + ``end()`` の第一パラメータで文字列が与えられると、FormHelperは + フォームの綴じタグと一緒にその文字列の名前のついた submit ボタンを + 出力します:: + + Form->end('Finish'); ?> + + 以下が出力されます: + + .. code-block:: html + +
+ +
+
+ + ``end()`` に配列を渡して詳細を指定することもできます:: + + $options = array( + 'label' => 'Update', + 'div' => array( + 'class' => 'glass-pill', + ) + ); + echo $this->Form->end($options); + + 以下が出力されます: + + .. code-block:: html + +
+
+ + 詳細は + `Form Helper API `_ + を参照してください。 + + .. note:: + + アプリケーション内で :php:class:`SecurityComponent` を使っている + 場合、タグを閉じる際は常に ``end()`` を使わなければなりません。 + + .. versionchanged:: 2.5 + 2.5 で ``$secureAttributes`` パラメータが追加されました。 + +.. _automagic-form-elements: + +フォーム要素の生成 +================== + +FormHelperでフォームのinput要素を作る方法はいくつかあります。まずは +``input()`` に注目してみましょう。このメソッドは与えられたモデル内の +項目を自動的に調べて、それらの項目に対応する適切な入力項目を作ります。 +内部的には ``input()`` はFormHelper内で他のメソッドに処理を委託します。 + +.. php:method:: input(string $fieldName, array $options = array()) + + それぞれの ``Model.field`` により以下の要素を生成します: + + * div のラッピング + * Label 要素 + * Input 要素 + * 適用できる場合はメッセージを含むエラー要素 + + 生成されるinputの型は(テーブルの)カラムのデータ型に依存します: + + カラムの型 + フォーム項目の型 + string (char, varchar, etc.) + text + boolean, tinyint(1) + checkbox + text + textarea + text, with name of password, passwd, または psword + password + text, with name of email + email + text, with name of tel, telephone, or phone + tel + date + day, month, and year selects + datetime, timestamp + day, month, year, hour, minute, および meridian selects + time + hour, minute, および meridian selects + binary + file + + ``$options`` パラメータで ``input()`` の挙動をカスタマイズできます。 + また生成されるデータを細やかに制御できます。 + + モデルの項目に関するバリデーションルールで ``allowEmpty =>true`` が + 指定されない場合、ラッピングする div には ``required`` というクラス + 名が付加されます。この振る舞いにおける一つの制限事項として、 + そのリクエストの間に入力項目のモデルがロードされている必要があります。 + そうでなければ :php:meth:`~FormHelper::create()` で指定されたモデルが + 直接関連付けられます。 + + .. versionadded:: 2.5 + binary 型が file 入力にマッピングされるようになりました。 + + .. versionadded:: 2.3 + + .. _html5-required: + + 2.3 から、バリデーションルールに基いて、HTML5の ``required`` 属性がinputに + 付加されるようになりました。options 配列で明示的に ``required`` キーを + セットしてその項目の定義を上書きすることもできます。フォーム全体を + トリガーすることでブラウザによるバリデーションをスキップするためには、 + :php:meth:`FormHelper::submit()` を使って生成した入力ボタンの + オプションに ``'formnovalidate' => true`` を指定するか、もしくは + :php:meth:`FormHelper::create()` の options で ``'novalidate' => true`` + をセットします。 + + + たとえば、あなたの User モデルには username (varchar), password (varchar), + approved (datetime) , quote (text) という項目があるとします。 + FormHelper の input() メソッドを使ってこれらすべてのフォーム項目に + 対する適切な input 項目を作ります:: + + echo $this->Form->create(); + + echo $this->Form->input('username'); //text + echo $this->Form->input('password'); //password + echo $this->Form->input('approved'); //day, month, year, hour, minute, + //meridian + echo $this->Form->input('quote'); //textarea + + echo $this->Form->end('Add'); + + 日付項目について、より具体的なオプションの例を以下に示します:: + + echo $this->Form->input('birth_dt', array( + 'label' => 'Date of birth', + 'dateFormat' => 'DMY', + 'minYear' => date('Y') - 70, + 'maxYear' => date('Y') - 18, + )); + + ``input()`` のオプションでは、後述する特別なオプションの他にも、 + input のタイプについての任意のオプションや、(たとえば onfocus + のように)任意のHTML属性を指定できます。``$options`` と + ``$htmlAttributes`` に関する詳細は :doc:`/core-libraries/helpers/html` + を参照してください。 + + User の hasAndBelongsToMany グループを考えます。コントローラーでは + select の options でキャメルケースの複数形の変数(このケースでは + group -> groups や ExtraFunkyModel -> extraFunkyModels)をセットします。 + コントローラーの action では以下のように指定します:: + + $this->set('groups', $this->User->Group->find('list')); + + そしてビューの中では、以下のシンプルなコードで複数の select が + 生成できます:: + + echo $this->Form->input('Group'); + + belongsTo や hasOne 関係を使うケースで select 項目を生成したい場合、 + Users コントローラーに以下のコードを追加します(User は Group に + belongsTo していると仮定しています):: + + $this->set('groups', $this->User->Group->find('list')); + + その後フォームビューに以下を追加します:: + + echo $this->Form->input('group_id'); + + あなたの使っているモデルの名前が、たとえば "UserGroup" のように + 2つ以上の単語で構成されている場合、set() でデータを渡す際の + データにつける名前は複数形のキャメルケースでなければなりません:: + + $this->set('userGroups', $this->UserGroup->find('list')); + // または + $this->set( + 'reallyInappropriateModelNames', + $this->ReallyInappropriateModelName->find('list') + ); + + .. note:: + + submit ボタンを作る際は `FormHelper::input()` の利用を避け、 + :php:meth:`FormHelper::submit()` の方を使ってください。 + +.. php:method:: inputs(mixed $fields = null, array $blacklist = null, $options = array()) + + ``$fields`` についての入力項目のセットを作成します。 ``$fields`` + が null の場合は全項目が対象となりますが、その場合でも現在の + モデルのうち ``$blacklist`` に定義されているものは除外されます。 + + コントローラー項目の出力の他にも、 ``$fields`` は ``fieldset`` や + ``legend`` キーと一緒に使うことで legend や fieldset の描画制御 + のためにも使われます。 + ``$this->Form->inputs(array('legend' => 'My legend'));`` + はカスタム legend を伴った input の組み合わせを生成します。 + ``$fields`` を通して個々の input をカスタマイズすることも可能です。:: + + echo $this->Form->inputs(array( + 'name' => array('label' => 'custom label') + )); + + 項目のコントロールの他にも、inputs() では以下のオプションが使えます。 + + - ``fieldset`` false にすることで fieldset を無効にします。 + 文字列が渡されると、それは fieldset 要素のクラス名として使われます。 + - ``legend`` false にすることで生成された input 項目についての + legend を無効にします。もしくは legend テキストをカスタマイズ + するための文字列を渡します。 + +項目名の命名規則 +---------------- + +フォームヘルパーは結構よくできています。フォームヘルパーのメソッドで +項目名を指定すれば、常に自動的に現在のモデル名を使って以下のような +書式で input タグを作ってくれます: + +.. code-block:: html + + + +これにより、そのフォームが対象とするモデルの input タグを生成する際、 +モデル名を省略できます。関連付けられたモデルや任意のモデルについての +input タグを生成する場合は、最初のパラメータとして モデル名.項目名 を +渡します。:: + + echo $this->Form->input('Modelname.fieldname'); + +同じ項目名で複数の項目を指定したい場合、すなわち一度の saveAll() +で配列として値を保存したい場合は以下の様な書式を使います:: + + echo $this->Form->input('Modelname.0.fieldname'); + echo $this->Form->input('Modelname.1.fieldname'); + +その出力は以下のようになります: + +.. code-block:: html + + + + + +FormHelper は日時項目の入力を生成する際、内部的に複数の 項目名-接尾辞 +を使います。もし項目名として ``year``, ``month``, ``day``, ``hour``, +``minute``, ``meridian`` を使っており、かる正確な入力値を得ることが +できない場合は、 ``name`` 属性をセットすることでデフォルトの振る舞いを +上書きすることができます:: + + echo $this->Form->input('Model.year', array( + 'type' => 'text', + 'name' => 'data[Model][year]' + )); + +オプション +---------- + +``FormHelper::input()`` は非常に多数のオプションをサポートしています。 +それ自身のオプション以外にも、 ``input()`` は生成された input のタイプや +HTML アトリビュートなどもオプションとして設定可能です。ここでは +``FormHelper::input()`` に特化したオプションを記載しています。 + +* ``$options['type']`` タイプを指定することで、モデルが推測したものに + 優先して、input のタイプを強制指定できます。 :ref:`automagic-form-elements` + で見つかったフィールドタイプの他にも HTML5 でサポートされている + 'file', 'password' 等のタイプも生成可能です:: + + echo $this->Form->input('field', array('type' => 'file')); + echo $this->Form->input('email', array('type' => 'email')); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ + +* ``$options['div']`` このオプションを使って、input を囲んでいる div の + 属性を指定できます。文字列を渡すと div のクラス名になります。 + 配列を渡すと div の属性として扱われますが、この場合はキー/値の形式で + 指定します。なおこのキーを false と指定すると、div の出力を行わなく + なります。 + + クラス名の指定:: + + echo $this->Form->input('User.name', array( + 'div' => 'class_name' + )); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ + 複数の属性の指定:: + + echo $this->Form->input('User.name', array( + 'div' => array( + 'id' => 'mainDiv', + 'title' => 'Div Title', + 'style' => 'display:block' + ) + )); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ + div 出力の抑制:: + + echo $this->Form->input('User.name', array('div' => false)); ?> + + 出力はこうなります: + + .. code-block:: html + + + + +* ``$options['label']`` input とともに指定されることの多い label + タグのテキストを文字列で指定します:: + + echo $this->Form->input('User.name', array( + 'label' => 'The User Alias' + )); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ + このキーに false を指定すると、label タグが出力されなくなります:: + + echo $this->Form->input('User.name', array('label' => false)); + + 出力はこうなります: + + .. code-block:: html + +
+ +
+ + これを配列で指定することで、 ``label`` エレメントに対する追加 + オプションを指定できます。この場合、label のテキストをカスタマイズ + するには ``text`` キーを使います:: + + echo $this->Form->input('User.name', array( + 'label' => array( + 'class' => 'thingy', + 'text' => 'The User Alias' + ) + )); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ +* ``$options['error']`` このキーを使うと、モデルが持つデフォルトの + エラーメッセージを上書きしたり、また、たとえば i18n メッセージを + セットしたりできます。これには多数のサブオプションがあり、これを + 使って外側のエレメントやそのクラス名をコントロールしたり、 + エラーメッセージの中の HTML をエスケープするかどうかなどを指定 + できます。 + + エラーメッセージ出力やフィールドのクラス名を無効にするには + error キーに false を設定します:: + + $this->Form->input('Model.field', array('error' => false)); + + エラーメッセージのみを無効にし、フィールドのクラス名は有効にするには + errorMessage キーを false にします:: + + $this->Form->input('Model.field', array('errorMessage' => false)); + + 外側のエレメントのタイプやそのクラスを変更するには以下の書式を + 使います:: + + $this->Form->input('Model.field', array( + 'error' => array( + 'attributes' => array('wrap' => 'span', 'class' => 'bzzz') + ) + )); + + エラーメッセージ出力において HTML が自動的にエスケープされるのを + 抑制するには、escape サブオプションを false にします:: + + $this->Form->input('Model.field', array( + 'error' => array( + 'attributes' => array('escape' => false) + ) + )); + + モデルのエラーメッセージをオーバーライドするには、 + バリデーションの rule 名にマッチしたキーを持つ配列を使います:: + + $this->Form->input('Model.field', array( + 'error' => array('tooShort' => __('This is not long enough')) + )); + + これまで見てきたように、モデルの中にあるそれぞれのバリデーション + ルールのためのエラーメッセージを設定できます。さらにフォームの + 中のメッセージに i18n を提供することも可能です。 + + .. versionadded:: 2.3 + ``errorMessage`` オプションのサポートは 2.3 で追加されました。 + +* ``$options['before']``, ``$options['between']``, ``$options['separator']``, + ``$options['after']`` + + input() メソッドの出力の中に何らかのマークアップを差し込みたい場合、 + これらのキーを使います:: + + echo $this->Form->input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---' + )); + + 出力はこうなります: + + .. code-block:: html + +
+ --before-- + + --between--- + + --after-- +
+ + radio input では、'separator' 属性を使ってそれぞれの input と + label のペアを分けるためのマークアップを挿入できます:: + + echo $this->Form->input('field', array( + 'before' => '--before--', + 'after' => '--after--', + 'between' => '--between---', + 'separator' => '--separator--', + 'options' => array('1', '2') + )); + + 出力はこうなります: + + .. code-block:: html + +
+ --before-- + + + --separator-- + + + --between--- + --after-- +
+ + ``date`` および ``datetime`` 型のエレメントでは、'separator' + 属性を使って select エレメントの間の文字列を変更できます。 + デフォルトは '-' です。 + +* ``$options['format']`` FormHelper が生成する HTML の順序もまた制御可能 + です。'format' オプションは文字列の配列を取り、希望するエレメントの + 並び順を表すテンプレートを指定します。サポートされている配列キーは + 以下の通りです: + ``array('before', 'input', 'between', 'label', 'after','error')`` + +* ``$options['inputDefaults']`` 複数の input() コールで同じオプションを + 使いたい場合、 `inputDefaults`` を使うことで繰り返し指定を避ける事が + できます:: + + echo $this->Form->create('User', array( + 'inputDefaults' => array( + 'label' => false, + 'div' => false + ) + )); + + その時点より先で生成されるすべての input において、inputDefaults + にあるオプション宣言が継承されます。input() コール時のオプション + 指定はデフォルトのオプションより優先されます:: + + // div も label もなし + echo $this->Form->input('password'); + + // label エレメントあり + echo $this->Form->input('username', array('label' => 'Username')); + + ここより先のデフォルトを変更するには + :php:meth:`FormHelper::inputDefaults()` が使えます。 + +特殊なタイプの入力を生成する +============================ + +一般的な ``input()`` メソッド以外にも、 ``FormHelper`` には様々に +異なったタイプの input を生成するための特別なメソッドがあります。 +これらは input ウィジェットそのものを生成するのに使えますが、 +さらに :php:meth:`~FormHelper::label()` や +:php:meth:`~FormHelper::error()` といった別のメソッドと組み合わせる +ことで、完全にカスタムメイドのフォームレイアウトを生成できます。 + +.. _general-input-options: + +一般的なオプション +------------------ + +input エレメントに関連するメソッドの多くは、一般的なオプションの +組合せをサポートしています。これらのオプションはすべて ``input()`` +でもサポートされています。繰り返しを減らすために、すべての input +メソッドで使える共通オプションを以下に示します: + +* ``$options['class']`` input のクラス名を指定できます:: + + echo $this->Form->input('title', array('class' => 'custom-class')); + +* ``$options['id']`` input の DOM id の値を強制的に設定します。 + +* ``$options['default']`` input フィールドのデフォルト値をセットする + のに使われます。この値は、フォームに渡されるデータにそのフィールド + に関する値が含まれていない場合(かまたは、一切データが渡されない場合) + に使われます。 + + 使用例:: + + echo $this->Form->input('ingredient', array('default' => 'Sugar')); + + select フィールドを持つ例("Medium" サイズがデフォルトで選択されます):: + + $sizes = array('s' => 'Small', 'm' => 'Medium', 'l' => 'Large'); + echo $this->Form->input( + 'size', + array('options' => $sizes, 'default' => 'm') + ); + + .. note:: + + checkbox をチェックする目的では ``default`` は使えません。 + その代わり、コントローラーで ``$this->request->data`` の中の + 値をセットするか、または input オプションの ``checked`` を true + にします。 + + 日付と時刻フィールドのデフォルト値は 'selected' キーでセットできます。 + + デフォルト値への代入の際 false を使うのは注意が必要です。 + false 値は input フィールドのオプションを無効または除外するのに + 使われます。そのため ``'default' => false`` では何の値もセット + されません。この場合は ``'default' => 0`` としてください。 + +前述のオプションに加えて、任意の HTML アトリビュートを混在させる +ことができます。特に規定のないオプション名は HTML アトリビュートとして +扱われ、生成された HTML の input エレメントに反映されます。 + +select, checkbox, radio に関するオプション +------------------------------------------ + +* ``$options['selected']`` は select 型の input (たとえば select, + date, time, datetime)と組み合わせて使われます。その項目の値に + 'selected' をセットすると、その input が描画される際にデフォルトで + その項目が選択されます:: + + echo $this->Form->input('close_time', array( + 'type' => 'time', + 'selected' => '13:30:00' + )); + + .. note:: + + date や datetime input の selected キーは UNIX のタイムスタンプ + で設定することもできます。 + +* ``$options['empty']`` true がセットされると、その input 項目を + 強制的に空にします。 + + select リストに渡される際、これはドロップダウンの値として空値を + 持つ空のオプションを作ります。単にオプションを空白にする代わりに、 + 何らかのテキストを表示しつつ空値を受け取りたい場合は empty に + 文字列を設定してください:: + + echo $this->Form->input('field', array( + 'options' => array(1, 2, 3, 4, 5), + 'empty' => '(choose one)' + )); + + 出力はこうなります: + + .. code-block:: html + +
+ + +
+ + .. note:: + + パスワードフィールドのデフォルト値を空値にしたい場合は、 + 'value' => '' の方を使ってください。 + + オプションはキー・バリューの組み合わせでも指定できます。 + +* ``$options['hiddenField']`` 一部の input タイプ(チェックボックス、 + ラジオボタン)では hidden フィールドが生成されるため、 + $this->request->data の中のキーは値を伴わない形式でも存在します: + + .. code-block:: html + + + + + これは ``$options['hiddenField'] = false`` とすることで無効にできます:: + + echo $this->Form->checkbox('published', array('hiddenField' => false)); + + 出力は以下のようになります: + + .. code-block:: html + + + + 1つのフォームの中でそれぞれグルーピングされた複数の input ブロック + を作りたい場合は、最初のものを除くすべての input でこのパラメータを + 使うべきです。ページ上の複数の場所に hidden input がある場合は + 最後のグループの input の値が保存されます。 + + この例では Tertiary Colors だけが渡され、Primary Colors は + オーバーライドされます: + + .. code-block:: html + +

Primary Colors

+ + + + + + + + +

Tertiary Colors

+ + + + + + + + + 2つ目の input グループで ``'hiddenField'`` を無効にすることで、 + この挙動を防ぐことができます。 + + hidden フィールドには 0 ではなく 'N' のように異なった値も + 設定できます:: + + echo $this->Form->checkbox('published', array( + 'value' => 'Y', + 'hiddenField' => 'N', + )); + +日時関連オプション +------------------ + +* ``$options['timeFormat']`` 時刻関連の入力に関する select input の書式を + 指定します。有効な値は ``12``, ``24``, ``null` です。 + +* ``$options['dateFormat']`` 日付関連の入力に関する select input の書式を + 指定します。有効な値は 'D', 'M', 'Y' の組み合わせまたは ``null`` です。 + 入力は dateFormat オプションで定義した順序で格納されます。 + +* ``$options['minYear'], $options['maxYear']`` date/datetime と組み合わせて + 使います。年の select フィールドで表示される値の最小値および/または + 最大値を定義します。 + +* ``$options['orderYear']`` date/datetime と組み合わせて、年の値を表示する + 順序を定義します。有効な値は 'asc', 'desc' で、デフォルトは 'desc' です。 + +* ``$options['interval']`` このオプションでは分の select ボックスにおける + 分間隔の数値を指定します:: + + echo $this->Form->input('Model.time', array( + 'type' => 'time', + 'interval' => 15 + )); + + この例では分の select で 15 分間隔で4つのオプションを生成します。 + +* ``$options['round']`` それぞれの命令で `up` または `down` を指定する + ことで強制的な端数の切り上げ/切り下げを指示します。デフォルトは null + で、これは `interval` にしたがって四捨五入します。 + + .. versionadded:: 2.4 + +フォーム要素固有のメソッド +========================== + +これまでの例では、すべてのエレメントが ``User`` モデルのフォームの配下で +作られていました。このため、生成された HTML のコードには User モデルを +参照するアトリビュートが含まれます。 +例:name=data[User][username], id=UserUsername + +.. php:method:: label(string $fieldName, string $text, array $options) + + label エレメントを作ります。``$fieldName`` は DOM id を生成する + のに使われます。``$text`` が指定されない場合は ``$fieldName`` + を活用することで label テキストが作られます:: + + echo $this->Form->label('User.name'); + echo $this->Form->label('User.name', 'Your username'); + + 出力はこのようになります: + + .. code-block:: html + + + + + ``$options`` は HTML アトリビュートの配列、またはクラス名として + 使われる文字列のいずれかを指定します:: + + echo $this->Form->label('User.name', null, array('id' => 'user-label')); + echo $this->Form->label('User.name', 'Your username', 'highlight'); + + 出力はこのようになります: + + .. code-block:: html + + + + +.. php:method:: text(string $name, array $options) + + FormHelper で利用可能なメソッドには、さらに特定のフォーム + エレメントを作成するものがあります。これらのメソッドの多くでは、 + 特別な $options パラメータを指定できます。ただしこの場合、 + $options は主に(フォームのエレメントの DOM id の値のような) + HTML タグのアトリビュートを指定するために使われます:: + + echo $this->Form->text('username', array('class' => 'users')); + + 以下が出力されます: + + .. code-block:: html + + + +.. php:method:: password(string $fieldName, array $options) + + パスワードフィールドを作成します:: + + echo $this->Form->password('password'); + + 以下が出力されます: + + .. code-block:: html + + + +.. php:method:: hidden(string $fieldName, array $options) + + hidden フォーム input を作成します。例:: + + echo $this->Form->hidden('id'); + + 以下が出力されます: + + .. code-block:: html + + + + フォームが編集されると(すなわち、配列 ``$this->request->data`` に + ``User`` モデルに渡されるべき情報が含まれている場合)、生成される + HTML の中に ``id`` フィールドに対応する値が自動的に追加されます。 + たとえば data[User][id] = 10 とすると、以下のようになります: + + .. code-block:: html + + + + .. versionchanged:: 2.0 + hidden フィールドは class アトリビュートを削除しなくなりました。 + これにより、hidden フィールドでバリデーションエラーが発生した場合、 + error-field というクラス名が適用されるようになります。 + +.. php:method:: textarea(string $fieldName, array $options) + + textarea の入力フィールドを生成します。:: + + echo $this->Form->textarea('notes'); + + 以下が出力されます: + + .. code-block:: html + + + + フォームが編集されると(すなわち、配列 ``$this->request->data`` に + ``User`` モデルに渡すために保存された情報が含まれている場合)、 + 生成される HTML には ``notes`` フィールドに対応する値が自動的に + 含まれます。例: + + .. code-block:: html + + + + .. note:: + + ``textarea`` input タイプでは ``$options`` アトリビュートの + ``'escape'`` キーにより、textarea の内容をエスケープするか + どうかを指定できます。デフォルトは ``true`` です。 + + :: + + echo $this->Form->textarea('notes', array('escape' => false); + // または .... + echo $this->Form->input( + 'notes', + array('type' => 'textarea', 'escape' => false) + ); + + + **Options** + + textarea() は :ref:`general-input-options` 以外にもいくつか + 特定のオプションをサポートしています: + + * ``$options['rows'], $options['cols']`` この2つのキーは行と + 列の数を指定します:: + + echo $this->Form->textarea( + 'textarea', + array('rows' => '5', 'cols' => '5') + ); + + 出力は以下のようになります: + + .. code-block:: html + + + +.. php:method:: checkbox(string $fieldName, array $options) + + フォームのチェックボックス要素を生成します。このメソッドはまた、 + そのフィールドについて送信される input データを制限するための + hidden 項目を生成します。:: + + echo $this->Form->checkbox('done'); + + 以下が出力されます: + + .. code-block:: html + + + + + 配列 $options を使って checkbox の値を指定することもできます:: + + echo $this->Form->checkbox('done', array('value' => 555)); + + 以下が出力されます: + + .. code-block:: html + + + + + FormHelper に hidden input を生成させたくない場合:: + + echo $this->Form->checkbox('done', array('hiddenField' => false)); + + 以下が出力されます: + + .. code-block:: html + + + + +.. php:method:: radio(string $fieldName, array $options, array $attributes) + + radio ボタンの組み合わせを生成します。 + + **Options** + + * ``$attributes['value']`` デフォルトで選択される値を設定します。 + + * ``$attributes['separator']`` ラジオボタンの間に置かれる HTML + (たとえば
)を指定します。 + + * ``$attributes['between']`` legend と最初の要素の間に挿入される + コンテンツを指定します。 + + * ``$attributes['disabled']`` これを ``true`` または ``'disabled'` + にすると、生成されたすべてのラジオボタンを無効にします。 + + * ``$attributes['legend']`` radio エレメントはデフォルトでは label + とフィールドセットで囲まれます。 ``$attributes['legend']`` を + false にするとこれらを取り除きます:: + + $options = array('M' => 'Male', 'F' => 'Female'); + $attributes = array('legend' => false); + echo $this->Form->radio('gender', $options, $attributes); + + 以下が出力されます: + + .. code-block:: html + + + + + + + + 何らかの理由で hidden input が不要な場合、 ``$attributes['value']`` + を選択される値もしくは false にすることで hidden を出力しなく + なります。 + + .. versionchanged:: 2.1 + ``$attributes['disabled']`` オプションは 2.1 で追加されました。 + +.. php:method:: select(string $fieldName, array $options, array $attributes) + + select 要素を作成します。 ``$options`` で項目を定義し、デフォルトで + 選択される値を ``$attributes['value']`` で指定します。``$attributes`` + 変数に '空の' キーを作って false を設定することで、デフォルトの + empty オプションを無効にします。:: + + $options = array('M' => 'Male', 'F' => 'Female'); + echo $this->Form->select('gender', $options); + + 以下が出力されます: + + .. code-block:: html + + + + ``select`` input タイプでは、 ``'escape'`` と呼ばれる特別な + ``$option`` 属性に真偽値を設定することで、select オプションの中身を + エンコードするかどうかを指定できます。デフォルトは true です:: + + $options = array('M' => 'Male', 'F' => 'Female'); + echo $this->Form->select('gender', $options, array('escape' => false)); + + * ``$attributes['options']`` このキーにより、select input または + ラジオボタンのグループについて、オプションをマニュアルで指定できます。 + 'type' に 'radio' と指定されない限り、FormHelper は目的とする + 出力を select input と仮定します:: + + echo $this->Form->select('field', array(1,2,3,4,5)); + + 出力は以下のようになります: + + .. code-block:: html + + + + オプションはキー/バリューの組み合わせでも指定できます:: + + echo $this->Form->select('field', array( + 'Value 1' => 'Label 1', + 'Value 2' => 'Label 2', + 'Value 3' => 'Label 3' + )); + + 出力は以下のようになります: + + .. code-block:: html + + + + select を optgroup 付きで作成したい場合は、データを階層的に指定 + します。これは複数のチェックボックスやラジオボタンでも有効ですが、 + optgroup では要素をフィールドセットで囲みます:: + + $options = array( + 'Group 1' => array( + 'Value 1' => 'Label 1', + 'Value 2' => 'Label 2' + ), + 'Group 2' => array( + 'Value 3' => 'Label 3' + ) + ); + echo $this->Form->select('field', $options); + + 出力は以下のようになります: + + .. code-block:: html + + + + * ``$attributes['multiple']`` input に対して 'multiple' が true に + セットされると、ひとつの select として出力されます:: + + echo $this->Form->select( + 'Model.field', + $options, + array('multiple' => true) + ); + + 一方、'multiple' を 'checkbox' にすると、関連するチェックボックス + の一覧を出力します:: + + $options = array( + 'Value 1' => 'Label 1', + 'Value 2' => 'Label 2' + ); + echo $this->Form->select('Model.field', $options, array( + 'multiple' => 'checkbox' + )); + + 出力は以下のようになります: + + .. code-block:: html + +
+ + +
+ + +
+
+ + +
+
+ + * ``$attributes['disabled']`` チェックボックスを生成する際、この + オプションをセットするとすべてもしくは特定のチェックボックスを + 無効にします。すべてのチェックボックスを無効にするには + 'disabled' を ``true`` にします:: + + $options = array( + 'Value 1' => 'Label 1', + 'Value 2' => 'Label 2' + ); + echo $this->Form->select('Model.field', $options, array( + 'multiple' => 'checkbox', + 'disabled' => array('Value 1') + )); + + 出力は以下のようになります: + + .. code-block:: html + +
+ + +
+ + +
+
+ + +
+
+ + .. versionchanged:: 2.3 + ``$attributes['disabled']`` の中の配列のサポートは 2.3 で + 追加されました。 + +.. php:method:: file(string $fieldName, array $options) + + フォームにファイルアップロードのための項目を追加するためには、 + まずフォームの enctype を "multipart/form-data" にする必要が + ありますので、create 関数で以下のようにしています:: + + echo $this->Form->create('Document', array( + 'enctype' => 'multipart/form-data' + )); + // または + echo $this->Form->create('Document', array('type' => 'file')); + + 次にフォームビューファイルに以下のいずれかを追加します:: + + echo $this->Form->input('Document.submittedfile', array( + 'between' => '
', + 'type' => 'file' + )); + + // または + + echo $this->Form->file('Document.submittedfile'); + + HTML 自体の制限により、'file' タイプの入力フィールドにデフォルト + 値を設定することはできません。フォームを表示するたびに毎回、 + 中の値は空に設定されます。 + + フォームの送信に際して file フィールドは、フォームを受信しようと + しているスクリプトに対して拡張された data 配列を提供します。 + + CakePHP が Windows サーバ上にインストールされている場合、上記の例 + について、送信されるデータ配列内の値は以下のように構成されます。 + Unix 環境では 'tmp\_name' が異なったパスになります:: + + $this->request->data['Document']['submittedfile'] = array( + 'name' => 'conference_schedule.pdf', + 'type' => 'application/pdf', + 'tmp_name' => 'C:/WINDOWS/TEMP/php1EE.tmp', + 'error' => 0, + 'size' => 41737, + ); + + この配列は PHP 自体によって生成されます。PHP が file フィールドを + 通してデータをどう処理しているのかについては、PHP マニュアルの + ファイルアップロードのセクション + ``_ を読んでみてください。 + +アップロードの検証 +------------------ + +モデルの中で定義できる、ファイルが正しくアップロードされたかどうかを +検証するためのバリデーションメソッドの例を以下に示します:: + + public function isUploadedFile($params) { + $val = array_shift($params); + if ((isset($val['error']) && $val['error'] == 0) || + (!empty( $val['tmp_name']) && $val['tmp_name'] != 'none') + ) { + return is_uploaded_file($val['tmp_name']); + } + return false; + } + +file input を生成します:: + + echo $this->Form->create('User', array('type' => 'file')); + echo $this->Form->file('avatar'); + +以下が出力されます: + +.. code-block:: html + +
+ + +.. note:: + + ``$this->Form->file()`` を使う場合、 ``$this->Form->create()`` + の中の type オプションを 'file' に設定することで、フォームの + エンコーディングのタイプを設定できます。 + +ボタンとsubmit要素の生成 +======================== + +.. php:method:: submit(string $caption, array $options) + + submit ボタンをキャプション ``$caption`` 付きで作成します。 + ``$caption`` が画像への URL の場合('.' 文字を含む場合)、 + submit ボタンは画像として描画されます。 + + デフォルトでは submit ボタンは ``div`` タグで括られます。 + これを避けるには ``$options['div'] = false`` を指定します:: + + echo $this->Form->submit(); + + 以下が出力されます: + + .. code-block:: html + +
+ + caption パラメーターではキャプション文字列の代わりに画像への + 相対または絶対 URL を指定できます:: + + echo $this->Form->submit('ok.png'); + + 以下が出力されます: + + .. code-block:: html + +
+ +.. php:method:: button(string $title, array $options = array()) + + 指定されたタイトルとデフォルトの "button" タイプで HTML のボタンを + 作成します。 ``$options['type']`` では以下の3つのいずれかを + 指定できます: + + #. submit: ``$this->Form->submit`` メソッドと同じ(デフォルト) + #. reset: フォームのリセットボタンを作成 + #. button: 標準のプッシュボタンを作成 + + :: + + echo $this->Form->button('A Button'); + echo $this->Form->button('Another Button', array('type' => 'button')); + echo $this->Form->button('Reset the Form', array('type' => 'reset')); + echo $this->Form->button('Submit Form', array('type' => 'submit')); + + 以下が出力されます: + + .. code-block:: html + + + + + + + + ``button`` タイプは ``escape`` オプションをサポートしています。 + これはそのボタンの $title を HTML エンティティでエンコードするか + どうかを表す真偽値で、デフォルトは false です:: + + echo $this->Form->button('Submit Form', array( + 'type' => 'submit', + 'escape' => true + )); + +.. php:method:: postButton(string $title, mixed $url, array $options = array ()) + + POST 経由でサブミットするための、 ```` で囲まれた `` + Form->end(); ?> + + +提交的数据数组如下。:: + + Array + ( + [Student] => Array + ( + [first_name] => Joe + [last_name] => Bloggs + ) + + [Course] => Array + ( + [name] => Cake + ) + + [CourseMembership] => Array + ( + [days_attended] => 5 + [grade] => A + ) + + ) + +在 CakePHP 中,使用这种数据结构调用 `saveAssociated` 方法,就能够很容易地同时保 +存这么多数据,并将 Student 和 Course 的外键赋值到 CouseMembership 内。如果我们运 +行 CourseMembershipsController 的 index 动作,从 find(‘all’) 中获取的数据结构就 +会是:: + + Array + ( + [0] => Array + ( + [CourseMembership] => Array + ( + [id] => 1 + [student_id] => 1 + [course_id] => 1 + [days_attended] => 5 + [grade] => A + ) + + [Student] => Array + ( + [id] => 1 + [first_name] => Joe + [last_name] => Bloggs + ) + + [Course] => Array + ( + [id] => 1 + [name] => Cake + ) + ) + ) + +当然,还有很多使用连接模型的方式。上面的方式假定你想要一次保存所有数据。存在这样 +的情况,你想单独地创建 Student 和 Course,稍后再把两者与 CourseMembership 关联起 +来。这样你可能有一个表单,允许通过现有学生和课程的列表或者 ID 输入项进行选择,以 +及 CourseMembership 的两个字段,例如:: + + // View/CourseMemberships/add.ctp + + Form->create('CourseMembership'); ?> + Form->input( + 'Student.id', + array( + 'type' => 'text', + 'label' => 'Student ID', + 'default' => 1 + ) + ); + ?> + Form->input( + 'Course.id', + array( + 'type' => 'text', + 'label' => 'Course ID', + 'default' => 1 + ) + ); + ?> + Form->input('CourseMembership.days_attended'); ?> + Form->input('CourseMembership.grade'); ?> + + Form->end(); ?> + +所得到的 POST 数据为:: + + Array + ( + [Student] => Array + ( + [id] => 1 + ) + + [Course] => Array + ( + [id] => 1 + ) + + [CourseMembership] => Array + ( + [days_attended] => 10 + [grade] => 5 + ) + ) + +利用 `saveAssociated` 方法,CakePHP 仍然可以很容易地把 Student id 和 Course id +放入 CourseMembership 中。 + +.. _saving-habtm: + +保存相关模型数据 (HABTM) +------------------------ + +保存通过 hasOne、belongsTo 和 hasMany 关联的模型非常简单:只需要将关联模型的 ID +填入外键字段。 一旦完成,只要调用模型的 ``save()`` 方法,所有数据就被正确地连接 +起来了。下面的示例是传递给 Tag 模型的 ``save()`` 方法的数据数组的格式:: + + Array + ( + [Recipe] => Array + ( + [id] => 42 + ) + [Tag] => Array + ( + [name] => Italian + ) + ) + +也可以使用这种格式调用 ``saveAll()`` 来保存多条记录和它们的 HABTM 关联(模型),使 +用下面这样的数组:: + + Array + ( + [0] => Array + ( + [Recipe] => Array + ( + [id] => 42 + ) + [Tag] => Array + ( + [name] => Italian + ) + ) + [1] => Array + ( + [Recipe] => Array + ( + [id] => 43 + ) + [Tag] => Array + ( + [name] => Pasta + ) + ) + [2] => Array + ( + [Recipe] => Array + ( + [id] => 51 + ) + [Tag] => Array + ( + [name] => Mexican + ) + ) + [3] => Array + ( + [Recipe] => Array + ( + [id] => 17 + ) + [Tag] => Array + ( + [name] => American (new) + ) + ) + ) + +将上面的数组传递给 ``saveAll()`` 方法将创建所包含的标签(*tag*),各自与它们相应的 +菜单(*recipe*)关联。 + +另一个有用的例子是,当需要保存多个标签(*Tag*)到文章(*Post*)中。这需要用以下的 +HABTM 数组格式传入关联的 HABTM 数据。注意,只需要传入关联的 HABTM 模型的 id,不 +论需要再怎样嵌套:: + + Array + ( + [0] => Array + ( + [Post] => Array + ( + [title] => 'Saving HABTM arrays' + ) + [Tag] => Array + ( + [Tag] => Array(1, 2, 5, 9) + ) + ) + [1] => Array + ( + [Post] => Array + ( + [title] => 'Dr Who's Name is Revealed' + ) + [Tag] => Array + ( + [Tag] => Array(7, 9, 15, 19) + ) + ) + [2] => Array + ( + [Post] => Array + ( + [title] => 'I Came, I Saw and I Conquered' + ) + [Tag] => Array + ( + [Tag] => Array(11, 12, 15, 19) + ) + ) + [3] => Array + ( + [Post] => Array + ( + [title] => 'Simplicity is the Ultimate Sophistication' + ) + [Tag] => Array + ( + [Tag] => Array(12, 22, 25, 29) + ) + ) + ) + +把上面的数组传入 ``saveAll($data, array('deep' => true))``,会在 posts_tags 连接 +表中填入 Tag 和 Post 之间的关联。 + +作为示例,我们来创建一个表单,用来创建新的标签(*tag*),动态生成正确的数据数组与 +某个菜单(*recipe*)关联。 + +最简单的表单可以象这样(我们假定 ``$recipe_id`` 已经设置为某值了):: + + Form->create('Tag'); ?> + Form->input( + 'Recipe.id', + array('type' => 'hidden', 'value' => $recipe_id) + ); ?> + Form->input('Tag.name'); ?> + Form->end('Add Tag'); ?> + +在这个例子中,你可以看到 ``Recipe.id`` 隐藏字段的值被设置为 tag 要连接的 recipe +的 ID。 + +当在控制器中调用 ``save()`` 方法时,它将自动将 HABTM 数据保存到数据库:: + + public function add() { + // Save the association + if ($this->Tag->save($this->request->data)) { + // do something on success + } + } + +调用上面这段代码,将创建新的 Tag 并与 Recipe 相关联,其 ID 为 +``$this->request->data['Recipe']['id']``。 + +其它我们可能希望呈现关联数据的方式,可以包括下拉列表。数据可以使用 +``find('list')`` 方法从模型中取出,并且赋给用模型名命名的视图变量。同名的输入项( +*input*)会自动把该数据放入 ```` 元素。例如,一个菜单( +*Recipe*)可以被贴上多个标签(*Tag*)。在这种情况下,数据以相同的方式从模型中取出, +但是表单的输入项(*input*)定义稍有不同。tag 名称使用 ``ModelName`` 约定来定义:: + + // 在控制器中: + $this->set('tags', $this->Recipe->Tag->find('list')); + + // 在视图中: + $this->Form->input('Tag'); + +使用上面这段代码,会创建多选的下拉列表(*drop down*),允许多个选项自动被保存到数 +据库中已添加或已保存的现有 Recipe 上。 + +自我 HABTM +~~~~~~~~~~ + +通常 HABTM 关联用于绑定2个模型,但是它也可以用于1个模型,不过这需要更加小心。 + +关键在于模型的设置 ``className``。简单地添加 ``Project`` HABTM ``Project`` 关联 +会引起保存数据时的错误。设置 ``className`` 为模型名称,并用别名作为键,就避免了 +这些问题。:: + + class Project extends AppModel { + public $hasAndBelongsToMany = array( + 'RelatedProject' => array( + 'className' => 'Project', + 'foreignKey' => 'projects_a_id', + 'associationForeignKey' => 'projects_b_id', + ), + ); + } + +创建表单元素,保存数据,都象以前一样,但是要使用别名。这样的代码:: + + $this->set('projects', $this->Project->find('list')); + $this->Form->input('Project'); + +就变成这样:: + + $this->set('relatedProjects', $this->Project->find('list')); + $this->Form->input('RelatedProject'); + +当 HABTM 变得复杂时怎么办? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +默认情况下,当保存 HasAndBelongsToMany 关系时,在保存新行之前 CakePHP 会先删除连 +接表中的所有(相关)行。 例如,一个 Club 有10个相关的 Children,然后更新 Club 为只 +有2个 children。这样,Club 将只有2个 Children,而不是12个。 + +也要注意,如果想要在连接中加入更多字段(何时创建或者其它数据),这在使用 HABTM 连 +接表时是可能的,不过重要的是要明白你有简单的解决办法。 + +两个模型间的 HasAndBelongsToMany 关联实际上是同时通过 hasMany 和 belongsTo 关联 +的三个模型关系的简写。 + +考虑这个例子:: + + Child hasAndBelongsToMany Club + +另一种看待它的方法是添加一个 Membership 模型:: + + Child hasMany Membership + Membership belongsTo Child, Club + Club hasMany Membership. + +这两个例子几乎是完全相同的。它们在数据库中使用了相同数量的命名字段,相同数量的模 +型。重要的区别是,"连接(*join*)" 模型命名不同,并且其行为更容易预知。 + +.. tip:: + + 当连接表包含两个外键以外的额外字段时,通过将数组的键 ``'unique'`` 设置为 + ``'keepExisting'``,能够防止丢失额外字段的值。你可以认为这与设置 + 'unique' => true 类似,但在保存操作过程中不会丢失额外字段的数据。另外,如果 + 你使用 bake 来创建模型,自动会设置成这样。参见 + :ref:`HABTM 关联数组 `。 + +不过,在大多数情况下,象上面的例子那样为连接表建立模型,设置 hasMany、belongsTo +关联,比使用 HABTM 关联更简单。 + +数据表 +====== + +虽然 CakePHP 可以有非数据库驱动的数据源,但大多数时候是数据库驱动的。CakePHP 被 +设计成与(数据库)无关,可以使用 MySQL、MSSQL、PostgreSQL 和其它数据库。你可以象平 +时那样创建数据库表。在创建模型类时,模型将自动映射到你创建的表上。按照约定,表名 +为小写、复数,多个单词的表名用下划线分隔。例如,名为 Ingredient 的模型对应的表名 +为 ingredients。名为 EventRegistration 的模型对应的表名为 event_registrations。 +CakePHP 会检视表来决定每个字段的数据类型,并使用这些信息自动化各种特性,比如输出 +视图中的表单字段。按照约定,字段名为小写并用下划线分隔。 + +使用 created 和 modified 列 +--------------------------- + +如果在数据库表中定义 ``created`` 和/或 ``modified`` 字段为 datetime 字段(缺省值 +null),CakePHP 能够识别这些字段,每当创建或保存一条记录到数据库时,自动填入这两 +个字段(除非要保存的数据中已经包含了这两个字段的值)。 + +在最初添加记录时,``created`` 和 ``modified`` 字段会被设置为当前日期和时间。每当 +保存现有记录时,modified 字段会被更新为当前日期和时间。 + +如果在调用 Model::save() 之前 ,$this->data 中包含了 ``created`` 或 ``modified`` +字段的数据(例如来自 Model::read 或者 Model::set 方法),那么这些值将从 +$this->data 中获取,而不会自动魔法更新。如果不希望那样,可以用 +``unset($this->data['Model']['modified'])`` 等。另一种方法,可以重载 +Model::save() 方法来帮你总是这么做:: + + class AppModel extends Model { + + public function save($data = null, $validate = true, $fieldList = array()) { + // 在每次调用 save 方法前清除 modified 字段值: + $this->set($data); + if (isset($this->data[$this->alias]['modified'])) { + unset($this->data[$this->alias]['modified']); + } + return parent::save($this->data, $validate, $fieldList); + } + + } + +.. meta:: + :title lang=zh_CN: Saving Your Data + :keywords lang=zh_CN: doc models,validation rules,data validation,flash message,null model,table php,request data,php class,model data,database table,array,recipes,success,reason,snap,data model + From 5e288bb9ded9861b47fa0390f2867c5cb6917caf Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Thu, 30 Oct 2014 01:30:50 +0800 Subject: [PATCH 064/292] [zh_CN] follow en changes --- .../cakephp-coding-conventions.rst | 138 ++++++++++++++---- zh/models/data-validation.rst | 107 +++++++------- 2 files changed, 162 insertions(+), 83 deletions(-) diff --git a/zh/contributing/cakephp-coding-conventions.rst b/zh/contributing/cakephp-coding-conventions.rst index 84205e6874..1cd1bcd847 100644 --- a/zh/contributing/cakephp-coding-conventions.rst +++ b/zh/contributing/cakephp-coding-conventions.rst @@ -9,6 +9,11 @@ CakePHP 开发人员将使用下面的编码规范。 `CakePHP Code Sniffer `_ 来检查你 的代码是否遵循了必要的规范。 +语言 +======== + +所有代码和注释应当以英文书写。 + 添加新特性 ========== @@ -228,57 +233,130 @@ CakePHP 开发人员将使用下面的编码规范。 ->subject('A great message') ->send(); -代码的注释 -========== +文档代码块(*DocBlocks*) +======================= + +所有的注释代码块,除了文件中的第一个代码块,之前总是应当有一个空行。 -所有的注释都应该是英文, 并且应该清楚地描述被注释的代码段。 +文件头文档代码块 +-------------------- -注释可以包括以下`phpDocumentor `_标签: +所有的 PHP 文件都应当包含一个文件头文档代码块,看起来应当象这样:: + + `_ 标签为: -* `@author `_ * `@copyright `_ +* `@link `_ +* `@since `_ +* `@license `_ + +类文档代码块 +------------ + +类文档代码块应当象这样:: + + /** + * 类的简短描述。 + * + * 类的详细描述。 + * 可使用多行。 + * + * @deprecated 3.0.0 在 2.6.0 版本中作废。将在 3.0.0 版本中移除。使用 Bar 代替。 + * @see Bar + * @link http://book.cakephp.org/2.0/en/foo.html + */ + class Foo { + + } + +类文档代码块可以包含如下 `phpDocumentor `_ 标签: + * `@deprecated `_ - 使用格式 ``@version `` ,其中 ``version``和``description`` - 是必须的。 -* `@example `_ -* `@ignore `_ + 使用 ``@version `` 格式,其中 ``version`` 和 + ``description`` 是必须的。 * `@internal `_ * `@link `_ +* `@property `_ * `@see `_ * `@since `_ -* `@version `_ +* `@uses `_ + +属性文档代码块 +-------------- -PhpDoc 标签非常类似于 Java 中的 JavaDoc 标签。标签只有出现在文档块(*DocBlock*)行 -的开头才会起作用, 例如:: +属性文档代码块应当象这样:: /** - * Tag example. + * @var string|null 属性的描述。 * - * @author 这个标签会被处理, 但这个@version会被忽略 - * @version 1.0 这个标签也会被处理 + * @deprecated 3.0.0 在 2.5.0 版本中作废。将在 3.0.0 版本中移除。使用 $_bla 代替。 + * @see Bar::$_bla + * @link http://book.cakephp.org/2.0/en/foo.html#properties */ + protected $_bar = null; -:: +属性文档代码块可以包含如下 `phpDocumentor `_ 标签: + +* `@deprecated `_ + 使用 ``@version `` 格式,其中 ``version`` 和 + ``description`` 是必须的。 +* `@internal `_ +* `@link `_ +* `@see `_ +* `@since `_ +* `@var `_ + +方法/函数文档代码块 +------------------- + +方法和函数文档代码块应当象这样:: /** - * 内嵌 phpDoc 的例子。 + * 方法的简短描述。 * - * 这个函数致力于与 foo() 联手统治世界。 + * 方法的详细描述。 + * 可使用多行。 * - * @return void - */ - function bar() { - } - - /** - * Foo 函数. + * @param string $param2 第一个参数。 + * @param array|null $param2 第二个参数。 + * @return array cakes 数组。 + * @throws Exception 如果出错。 * - * @return void - */ - function foo() { - } + * @link http://book.cakephp.org/2.0/en/foo.html#bar + * @deprecated 3.0.0 在 2.5.0 版本中作废。将在 3.0.0 版本中移除。使用 Bar::baz 代替。 + * @see Bar::baz + /* + public function bar($param1, $param2 = null) { + } + +方法和函数文档代码块可以包含如下 `phpDocumentor `_ 标签: -所有注释段, 除了一个文件中的第一段, 之前总是应当有一个空行。 +* `@deprecated `_ + 使用 ``@version `` 格式,其中 ``version`` 和 + ``description`` 是必须的。 +* `@internal `_ +* `@link `_ +* `@param `_ +* `@return `_ +* `@throws `_ +* `@see `_ +* `@since `_ +* `@uses `_ 变量类型 -------- diff --git a/zh/models/data-validation.rst b/zh/models/data-validation.rst index feadf2c4c8..5453e96236 100644 --- a/zh/models/data-validation.rst +++ b/zh/models/data-validation.rst @@ -20,7 +20,7 @@ public $validate = array( 'login' => 'alphaNumeric', 'email' => 'email', - 'born' => 'date' + 'born' => 'date' ); } @@ -37,23 +37,23 @@ CakePHP 有许多验证规则,使用起来相当容易。一些内置的验证 public $validate = array( 'login' => array( 'alphaNumeric' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', 'required' => true, - 'message' => 'Letters and numbers only' + 'message' => 'Letters and numbers only' ), 'between' => array( - 'rule' => array('between', 5, 15), + 'rule' => array('between', 5, 15), 'message' => 'Between 5 to 15 characters' ) ), 'password' => array( - 'rule' => array('minLength', '8'), + 'rule' => array('minLength', '8'), 'message' => 'Minimum 8 characters long' ), 'email' => 'email', 'born' => array( - 'rule' => 'date', - 'message' => 'Enter a valid date', + 'rule' => 'date', + 'message' => 'Enter a valid date', 'allowEmpty' => true ) ); @@ -93,11 +93,12 @@ email 字段必须是合法的电子邮件,而 born 字段必须是合法的 public $validate = array( // 或者: array('ruleName', 'param1', 'param2' ...) 'fieldName1' => array( - 'rule' => 'ruleName', - 'required' => true, + 'rule' => 'ruleName', + 'required' => true, 'allowEmpty' => false, - 'on' => 'create', // 或者: 'update' - 'message' => 'Your Error Message' + // 或者: 'update' + 'on' => 'create', + 'message' => 'Your Error Message' ) ); @@ -140,7 +141,7 @@ required public $validate = array( 'login' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', 'required' => true ) ); @@ -183,7 +184,7 @@ message 键是为规则定义验证失败时显示的错误信息:: public $validate = array( 'password' => array( - 'rule' => array('minLength', 8), + 'rule' => array('minLength', 8), 'message' => '密码至少8个字符长' ) ); @@ -221,11 +222,11 @@ message 键是为规则定义验证失败时显示的错误信息:: public $validate = array( 'login' => array( 'loginRule-1' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', 'message' => 'Only alphabets and numbers allowed', ), 'loginRule-2' => array( - 'rule' => array('minLength', 8), + 'rule' => array('minLength', 8), 'message' => 'Minimum length of 8 characters' ) ) @@ -249,12 +250,12 @@ last public $validate = array( 'login' => array( 'rule' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', 'message' => 'Only alphabets and numbers allowed', 'last' => false ), 'rule2' => array( - 'rule' => array('minLength', 8), + 'rule' => array('minLength', 8), 'message' => 'Minimum length of 8 characters' ) ) @@ -265,7 +266,7 @@ last public $validate = array( 'login' => array( 'Only alphabets and numbers allowed' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', ), ) ); @@ -287,7 +288,7 @@ last public $validate = array( 'login' => array( - 'rule' => '/^[a-z0-9]{3,}$/i', + 'rule' => '/^[a-z0-9]{3,}$/i', 'message' => 'Only letters and integers, min 3 characters' ) ); @@ -307,7 +308,7 @@ last public $validate = array( 'promotion_code' => array( - 'rule' => array('limitDuplicates', 25), + 'rule' => array('limitDuplicates', 25), 'message' => 'This code has been used too many times.' ) ); @@ -342,7 +343,7 @@ last public $validate = array( 'slug' => array( - 'rule' => 'alphaNumericDashUnderscore', + 'rule' => 'alphaNumericDashUnderscore', 'message' => 'Slug can only be letters,' . ' numbers, dash and underscore' ) @@ -510,7 +511,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'login' => array( - 'rule' => 'alphaNumeric', + 'rule' => 'alphaNumeric', 'message' => 'Usernames must only contain letters and numbers.' ) ); @@ -522,7 +523,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'password' => array( - 'rule' => array('between', 5, 15), + 'rule' => array('between', 5, 15), 'message' => 'Passwords must be between 5 and 15 characters long.' ) ); @@ -538,7 +539,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'id' => array( 'rule' => 'blank', - 'on' => 'create' + 'on' => 'create' ) ); @@ -549,7 +550,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'myCheckbox' => array( - 'rule' => array('boolean'), + 'rule' => array('boolean'), 'message' => 'myCheckbox 的值不正确' ) ); @@ -584,7 +585,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'ccnumber' => array( - 'rule' => array('cc', array('visa', 'maestro'), false, null), + 'rule' => array('cc', array('visa', 'maestro'), false, null), 'message' => 'The credit card number you supplied was invalid.' ) ); @@ -597,14 +598,14 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'age' => array( - 'rule' => array('comparison', '>=', 18), + 'rule' => array('comparison', '>=', 18), 'message' => 'Must be at least 18 years old to qualify.' ) ); public $validate = array( 'age' => array( - 'rule' => array('comparison', 'greater or equal', 18), + 'rule' => array('comparison', 'greater or equal', 18), 'message' => 'Must be at least 18 years old to qualify.' ) ); @@ -616,7 +617,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'infinite' => array( - 'rule' => array('custom', '\u221E'), + 'rule' => array('custom', '\u221E'), 'message' => 'Please enter an infinite number.' ) ); @@ -639,8 +640,8 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'born' => array( - 'rule' => array('date', 'ymd'), - 'message' => 'Enter a valid date in YY-MM-DD format.', + 'rule' => array('date', 'ymd'), + 'message' => 'Enter a valid date in YY-MM-DD format.', 'allowEmpty' => true ) ); @@ -668,7 +669,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'birthday' => array( - 'rule' => array('datetime', 'dmy'), + 'rule' => array('datetime', 'dmy'), 'message' => 'Please enter a valid date and time.' ) ); @@ -701,7 +702,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'email' => array( - 'rule' => array('email', true), + 'rule' => array('email', true), 'message' => 'Please supply a valid email address.' ) ); @@ -715,7 +716,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'food' => array( - 'rule' => array('equalTo', 'cake'), + 'rule' => array('equalTo', 'cake'), 'message' => 'This value must be the string cake' ) ); @@ -730,7 +731,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'image' => array( - 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), + 'rule' => array('extension', array('gif', 'jpeg', 'png', 'jpg')), 'message' => 'Please supply a valid image.' ) ); @@ -762,7 +763,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'function' => array( 'allowedChoice' => array( - 'rule' => array('inList', array('Foo', 'Bar')), + 'rule' => array('inList', array('Foo', 'Bar')), 'message' => 'Enter either Foo or Bar.' ) ) @@ -779,7 +780,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'clientip' => array( - 'rule' => array('ip', 'IPv4'), // 或者'IPv6', 或者'both'(缺省值) + 'rule' => array('ip', 'IPv4'), // 或者'IPv6', 或者'both'(缺省值) 'message' => 'Please supply a valid IP address.' ) ); @@ -793,16 +794,16 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'login' => array( - 'rule' => 'isUnique', + 'rule' => 'isUnique', 'message' => 'This username has already been taken.' ) ); - 可以提供多个字段来验证一组字段是否唯一:: + 可以提供多个字段并且设置 ``$or`` 为 ``false`` 来验证一组字段是否唯一:: public $validate = array( 'email' => array( - 'rule' => array('isUnique', 'email', 'username'), + 'rule' => array('isUnique', array('email', 'username'), false), 'message' => 'This username & email combination has already been used.' ) ); @@ -823,7 +824,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'login' => array( - 'rule' => array('maxLength', 15), + 'rule' => array('maxLength', 15), 'message' => 'Usernames must be no larger than 15 characters long.' ) ); @@ -845,11 +846,11 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'image' => array( - 'rule' => array('mimeType', array('image/gif')), + 'rule' => array('mimeType', array('image/gif')), 'message' => 'Invalid mime type.' ), 'logo' => array( - 'rule' => array('mimeType', '#image/.+#'), + 'rule' => array('mimeType', '#image/.+#'), 'message' => 'Invalid mime type.' ), ); @@ -862,7 +863,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'login' => array( - 'rule' => array('minLength', 8), + 'rule' => array('minLength', 8), 'message' => 'Usernames must be at least 8 characters long.' ) ); @@ -881,7 +882,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'salary' => array( - 'rule' => array('money', 'left'), + 'rule' => array('money', 'left'), 'message' => 'Please supply a valid monetary amount.' ) ); @@ -911,7 +912,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'title' => array( - 'rule' => 'notEmpty', + 'rule' => 'notEmpty', 'message' => 'This field cannot be left blank' ) ); @@ -925,7 +926,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'cars' => array( - 'rule' => 'numeric', + 'rule' => 'numeric', 'message' => 'Please supply the number of cars.' ) ); @@ -941,11 +942,11 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'wheels' => array( - 'rule' => 'naturalNumber', + 'rule' => 'naturalNumber', 'message' => 'Please supply the number of wheels.' ), 'airbags' => array( - 'rule' => array('naturalNumber', true), + 'rule' => array('naturalNumber', true), 'message' => 'Please supply the number of airbags.' ), ); @@ -987,13 +988,13 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'number' => array( - 'rule' => array('range', -1, 11), - 'message' => 'Please enter a number between 0 and 10' + 'rule' => array('range', -1, 11), + 'message' => 'Please enter a number between -1 and 11' ) ); - 上面的例子会接受大于0(比如0.01)而且小于10 (比如9.99)的任何数值。 + 上面的例子会接受大于-1(比如 -0.99)而且小于11(比如 10.99)的任何数值。 .. note:: @@ -1029,7 +1030,7 @@ CakePHP 的 Validation 类有许多验证规则,可以使模型数据的验证 public $validate = array( 'image' => array( - 'rule' => 'uploadError', + 'rule' => 'uploadError', 'message' => 'Something went wrong with the upload.' ), ); From 0b16a10257f1905ace245f15072000fade1ea871 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Wed, 29 Oct 2014 20:25:06 +0100 Subject: [PATCH 065/292] [fr] corrections --- fr/models/associations-linking-models-together.rst | 2 +- fr/models/callback-methods.rst | 4 ++-- fr/models/deleting-data.rst | 2 +- fr/models/saving-your-data.rst | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/fr/models/associations-linking-models-together.rst b/fr/models/associations-linking-models-together.rst index 36140d316b..017c7cf17a 100644 --- a/fr/models/associations-linking-models-together.rst +++ b/fr/models/associations-linking-models-together.rst @@ -1039,7 +1039,7 @@ ci-dessous:: ); } -**Récupérer un tableau imbriqué d'enregitrements associés:** +**Récupérer un tableau imbriqué d'enregistrements associés:** Si votre table a un champ ``parent_id``, vous pouvez aussi utiliser :ref:`model-find-threaded` pour récupérer un tableau imbriqué d'enregistrements diff --git a/fr/models/callback-methods.rst b/fr/models/callback-methods.rst index 848df6b3ea..cb9d854985 100644 --- a/fr/models/callback-methods.rst +++ b/fr/models/callback-methods.rst @@ -112,7 +112,7 @@ beforeSave ``beforeSave(array $options = array())`` Placez toute logique de pré-enregistrement dans cette fonction. Cette fonction -s'exécute immediatement après que les données du model ont été validées avec +s'exécute immédiatement après que les données du model ont été validées avec succès, mais juste avant que les données ne soient sauvegardées. Cette fonction devrait toujours retourner true si voulez que l'opération d'enregistrement se poursuive. @@ -182,7 +182,7 @@ dépendent de cet enregistrement soient aussi supprimés. .. tip:: - Assurez vous que beforeDelete() retourne true, ou votre + Assurez-vous que beforeDelete() retourne true, ou votre suppression ne va pas marcher. :: diff --git a/fr/models/deleting-data.rst b/fr/models/deleting-data.rst index 60497af6bf..f2ab2775a0 100644 --- a/fr/models/deleting-data.rst +++ b/fr/models/deleting-data.rst @@ -72,7 +72,7 @@ bindModel() ou unbindModel() pour changer les associations, vous devrez définir deleteAll() retournera true même si aucun enregistrement n'est supprimé, puisque les conditions pour la requête de suppression est un succès et - qu'aucun enregitrement correspondant ne reste. + qu'aucun enregistrement correspondant ne reste. .. meta:: diff --git a/fr/models/saving-your-data.rst b/fr/models/saving-your-data.rst index 9ab5a25fa3..fda5a0db6c 100644 --- a/fr/models/saving-your-data.rst +++ b/fr/models/saving-your-data.rst @@ -484,7 +484,7 @@ l'opération de sauvegarde. Si aucun des enregistrements du model associé n'existe pour l'instant dans le système (par exemple, vous voulez sauvegarder un nouveau User et ses -enregitrements du Profile lié en même temps), vous aurez besoin de sauvegarder +enregistrements du Profile lié en même temps), vous aurez besoin de sauvegarder d'abord le model principal, ou le model parent. Pour avoir une bonne idée de la façon de faire, imaginons que nous ayons une From 1c93c5fbc85405798df244c76ddfc04e21aebb01 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 29 Oct 2014 21:52:08 -0400 Subject: [PATCH 066/292] Remove third level nav elements. Having extra nav elements is kind of nice but kind of a pain in the ass as well. It creates painful maintenance busy work that is totally not necessary as both the book and the api have version pickers. --- themes/cakephp/layout.html | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/themes/cakephp/layout.html b/themes/cakephp/layout.html index b03a20d940..d2d26d64de 100644 --- a/themes/cakephp/layout.html +++ b/themes/cakephp/layout.html @@ -39,24 +39,8 @@
  • Documentation
  • Community From 73c9bb42a0a27450df915ded23cff8080ac4e230 Mon Sep 17 00:00:00 2001 From: Mark Story Date: Wed, 29 Oct 2014 21:53:46 -0400 Subject: [PATCH 067/292] Remove CSS for third level nav. --- themes/cakephp/static/csf-navbar.css | 42 ---------------------------- 1 file changed, 42 deletions(-) diff --git a/themes/cakephp/static/csf-navbar.css b/themes/cakephp/static/csf-navbar.css index 605137fda7..858aa11566 100644 --- a/themes/cakephp/static/csf-navbar.css +++ b/themes/cakephp/static/csf-navbar.css @@ -137,45 +137,3 @@ #cakephp-global-navigation .second-level a:hover { color: #BD8A5D; } - - -/** - * Third level navigation - * ------------------------------------ - */ -#cakephp-global-navigation .third-level { - border: 0; - background: none; - -webkit-box-shadow: none; - -moz-box-shadow: none; - box-shadow: none; -} -#cakephp-global-navigation .cake-version ul { - display: block; - padding-left: 14px; -} -#cakephp-global-navigation .cake-version li { - display: inline; - float: left; - width: auto; - border-right: none; - background: none; -} -#cakephp-global-navigation .cake-version a { - padding: 0 3px; - background-color: transparent; - display: inline; -} -#cakephp-global-navigation li:hover li { - background-color: inherit; -} -#cakephp-global-navigation .cake-version ul, -#cakephp-global-navigation .cake-version li, -#cakephp-global-navigation .cake-version a { - line-height: 15px; - height: 15px; -} -#cakephp-global-navigation .cake-version a:hover { - color: #BD8A5D; - text-decoration: underline; -} From fd173865b2b6d8a3b6db31cb147cbf02ebb365c0 Mon Sep 17 00:00:00 2001 From: oh240 Date: Thu, 30 Oct 2014 17:07:08 +0900 Subject: [PATCH 068/292] Added add() method to RecipesController REST example in japanese Copy / Pasting the example worked fine except for the missing POST /recipes.json example: add() method was missing in japanese. --- ja/development/rest.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ja/development/rest.rst b/ja/development/rest.rst index ff3b2a5010..77963fa535 100644 --- a/ja/development/rest.rst +++ b/ja/development/rest.rst @@ -81,6 +81,19 @@ POSTリクエストの中の、 *\_method* の値を使う方法は、ブラウ '_serialize' => array('recipe') )); } + + public function add() { + $this->Recipe->create(); + if ($this->Recipe->save($this->request->data)) { + $message = 'Saved'; + } else { + $message = 'Error'; + } + $this->set(array( + 'message' => $message, + '_serialize' => array('message') + )); + } public function edit($id) { $this->Recipe->id = $id; From 87b0eb30dd0242433f61c0c8fede1c00902b9752 Mon Sep 17 00:00:00 2001 From: Zhu Ming Date: Fri, 31 Oct 2014 19:11:26 +0800 Subject: [PATCH 069/292] [zh_CN] done /zh/models/callback-methods.rst --- zh/models/callback-methods.rst | 198 +++++++++++++++++++++++++++++++++ 1 file changed, 198 insertions(+) create mode 100644 zh/models/callback-methods.rst diff --git a/zh/models/callback-methods.rst b/zh/models/callback-methods.rst new file mode 100644 index 0000000000..83b74b9f39 --- /dev/null +++ b/zh/models/callback-methods.rst @@ -0,0 +1,198 @@ +回调(*Callback*)方法 +#################### + +如果你需要在模型操作之前或之后要插入一些处理逻辑,请使用模型的回调函数。这些函数 +可以定义在模型类中(包括 AppModel)。一定要注意这些特殊函数各自的预期返回值。 + +当使用回调函数时,应该记住行为(*behavior*)的回调函数在模型的回调函数 **之前** 执 +行。 + +beforeFind +========== + +``beforeFind(array $query)`` + +该回调函数在任何与 find 相关的操作前被调用。传入该回调的 ``$query`` 参数包含当前 +查询的信息,比如:conditions、fields,等。 + +如果不希望开始该查询操作(可能基于相关的 ``$query`` 选项而做出的决定),请返回 +*false*。否则,返回可能修改过的 ``$query`` ,或者任何你想传递给 find 及其它类似 +方法的信息。 + +可以使用该回调方法基于用户的角色来限制其查询操作,或者根据当前负载作出缓存的决定。 + +afterFind +========= + +``afterFind(array $results, boolean $primary = false)`` + +使用此回调函数来修改从 find 操作返回的结果,或者执行任何其它 find 之后的逻辑。传 +入该回调的 $results 参数包含模型的 find 操作返回的结果,即,象这样:: + + $results = array( + 0 => array( + 'ModelName' => array( + 'field1' => 'value1', + 'field2' => 'value2', + ), + ), + ); + +该回调函数的返回值应该是触发该回调的 find 操作的(可能是经过修改的)结果。 + +``$primary`` 参数表示当前模型是查询发起的模型,还是作为关联来查询的模型。如果模 +型是作为关联来查询的,``$results`` 的格式会不同;不是通常从 find 操作得到的结果, +会得到如下的内容:: + + $results = array( + 'field_1' => 'value1', + 'field_2' => 'value2' + ); + +.. warning:: + + 期望 ``$primary`` 为 true 的代码,如果使用递归 find,可能会遇到来自 PHP 的致 + 命错误 "Cannot use string offset as an array"。 + +下面是如何使用 afterfind 回调来格式化日期的例子:: + + public function afterFind($results, $primary = false) { + foreach ($results as $key => $val) { + if (isset($val['Event']['begindate'])) { + $results[$key]['Event']['begindate'] = $this->dateFormatAfterFind( + $val['Event']['begindate'] + ); + } + } + return $results; + } + + public function dateFormatAfterFind($dateString) { + return date('d-m-Y', strtotime($dateString)); + } + +beforeValidate +============== + +``beforeValidate(array $options = array())`` + +使用此回调函数来在验证之前修改模型数据,有必要也可以修改验证规则。这个函数也必须 +返回 *true*,否则当前的 save() 操作会终止。 + +afterValidate +============== + +``afterValidate()`` + +在检查数据的错误之后调用。如果需要,用该回调来执行任何数据的清理和准备。 + +beforeSave +========== + +``beforeSave(array $options = array())`` + +在此回调函数内放入任何保存之前的逻辑。这个函数会紧接着在验证数据成功之后、但在数 +据保存之前执行。如果想要保存操作成功,此函数也应该返回 true。 + +对于数据保存前需要进行的处理逻辑,该回调函数非常方便。如果存储引擎需要日期使用特 +定的格式,可以从 $this->data 访问并进行修改。 + +下面是个用 beforeSave 回调进行日期转换的例子。例子中的代码用于一个应用程序,其 +begindate 字段在数据库中的格式为 YYYY-MM-DD,而显示为格式 DD-MM-YYYY。当然这很容 +易改变。在适当的模型中使用下面的代码。 + +:: + + public function beforeSave($options = array()) { + if (!empty($this->data['Event']['begindate']) && + !empty($this->data['Event']['enddate']) + ) { + + $this->data['Event']['begindate'] = $this->dateFormatBeforeSave( + $this->data['Event']['begindate'] + ); + $this->data['Event']['enddate'] = $this->dateFormatBeforeSave( + $this->data['Event']['enddate'] + ); + } + return true; + } + + public function dateFormatBeforeSave($dateString) { + return date('Y-m-d', strtotime($dateString)); + } + +.. tip:: + + 请确保 beforeSave() 回调返回 true,否则保存会失败。 + +afterSave +========= + +``afterSave(boolean $created, array $options = array())`` + +如果需要在每次保存操作后执行一些逻辑,可以将这些逻辑放在该回调方法中。保存的数据 +会在 ``$this->data``中。 + +如果是插入新记录(而不是更新记录),参数 ``$created`` 会为 true。 + +``$options`` 数组参数就是传给 ``Model::save()`` 方法的同一个参数。 + +beforeDelete +============ + +``beforeDelete(boolean $cascade = true)`` + +在此回调函数内放置任何删除之前的逻辑。若要删除操作继续,此函数应该返回 true,要 +终止则返回 false。 + +如果依赖于该记录的记录也要删除,则参数 ``$cascade`` 会为 ``true``。 + +.. tip:: + + 请确保 beforeDelete() 回调返回 true,否则删除会失败。 + +:: + + // 使用 app/Model/ProductCategory.php + // 在下面的例子中,如果一个产品类别还包含产品,则不允许删除此类别。 + // 在 ProductsController.php 中调用 $this->Product->delete($id) 来设置 + // $this->id。 + // 假设 'ProductCategory hasMany Product',可以在模型中使用 $this->Product。 + public function beforeDelete($cascade = true) { + $count = $this->Product->find("count", array( + "conditions" => array("product_category_id" => $this->id) + )); + if ($count == 0) { + return true; + } else { + return false; + } + } + +afterDelete +=========== + +``afterDelete()`` + +在这个回调函数里放置每次删除之后要执行的逻辑。 + +:: + + // 也许在从数据库中删除一条记录之后,也要删除相关联的文件 + public function afterDelete() { + $file = new File($this->data['SomeModel']['file_path']); + $file->delete(); + } + +onError +======= + +``onError()`` + +任何问题发生时被调用。 + + +.. meta:: + :title lang=zh_CN: Callback Methods + :keywords lang=zh_CN: querydata,query conditions,model classes,callback methods,special functions,return values,counterparts,array,logic,decisions From 8188ad3e8d12015593badc14a1dbb5fa560032e2 Mon Sep 17 00:00:00 2001 From: Yves Date: Sat, 1 Nov 2014 01:21:18 +0100 Subject: [PATCH 070/292] Update rest.rst --- fr/development/rest.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fr/development/rest.rst b/fr/development/rest.rst index 700d09bfa4..37177d8e71 100644 --- a/fr/development/rest.rst +++ b/fr/development/rest.rst @@ -172,7 +172,7 @@ Le XML rendu va au final ressembler à ceci:: Créer la logique pour l'action edit est un peu vicieux, mais pas de beaucoup. -Puisque nous fournissons un API qui sort du XML, c'est un chois naturel pour +Puisque nous fournissons un API qui sort du XML, c'est un choix naturel pour recevoir le XML en entrée. Ne vous inquiétez pas, les classes :php:class:`RequestHandler` et :php:class:`Router` facilitent beaucoup les choses. Si une requête POST ou PUT a un content-type XML, From 0093dd5a06bd0d32c976a80d66d71c98eebb0838 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Sat, 1 Nov 2014 18:57:13 +0100 Subject: [PATCH 071/292] [fr] typos --- fr/core-utility-libraries/set.rst | 2 +- fr/core-utility-libraries/string.rst | 2 +- fr/models/associations-linking-models-together.rst | 2 +- fr/models/datasources.rst | 8 ++++---- fr/models/model-attributes.rst | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fr/core-utility-libraries/set.rst b/fr/core-utility-libraries/set.rst index f4aa478386..525f0d45f5 100644 --- a/fr/core-utility-libraries/set.rst +++ b/fr/core-utility-libraries/set.rst @@ -1506,7 +1506,7 @@ quelles options sont disponibles. :rtype: array - Trie un tableau selon toute valeur, determiné par un chemin Set-compatible:: + Trie un tableau selon toute valeur, déterminé par un chemin Set-compatible:: $a = array( 0 => array('Person' => array('name' => 'Jeff')), diff --git a/fr/core-utility-libraries/string.rst b/fr/core-utility-libraries/string.rst index cd978b6a74..0d8df6a51e 100644 --- a/fr/core-utility-libraries/string.rst +++ b/fr/core-utility-libraries/string.rst @@ -264,7 +264,7 @@ d'une ``View``, utilisez la classe ``String``:: du résultat. Extrait un excerpt de ``$haystack`` surrounding the ``$needle`` - avec un nombre de caractères de chaque côté determiné par ``$radius``, + avec un nombre de caractères de chaque côté déterminé par ``$radius``, et prefix/suffix with ``$ending``. Cette méthode est spécialement pratique pour les résultats recherchés. La chaîne requêtée ou les mots clés peuvent être montrés dans le document résultant.:: diff --git a/fr/models/associations-linking-models-together.rst b/fr/models/associations-linking-models-together.rst index 017c7cf17a..fa1c79d670 100644 --- a/fr/models/associations-linking-models-together.rst +++ b/fr/models/associations-linking-models-together.rst @@ -441,7 +441,7 @@ User users.messages\_read Compte les ``Message`` lus --------- ---------------------- ------------------------------------------- User users.messages\_unread Compte les ``Message`` non lus --------- ---------------------- ------------------------------------------- -Message messages.is\_read Determines si un ``Message`` est lu ou non. +Message messages.is\_read Détermines si un ``Message`` est lu ou non. ========= ====================== =========================================== Avec la configuration de votre ``belongsTo`` qui ressemblerait à cela:: diff --git a/fr/models/datasources.rst b/fr/models/datasources.rst index 2f265cc367..2b31afa8bb 100644 --- a/fr/models/datasources.rst +++ b/fr/models/datasources.rst @@ -156,7 +156,7 @@ allons l'appeler ``FarAwaySource`` et nous allons la placer dans } /** - * calculate() est pour determiner la façon dont nous allons compter + * calculate() est pour déterminer la façon dont nous allons compter * les enregistrements et est requis pour faire fonctionner ``update()`` * et ``delete()``. * @@ -179,14 +179,14 @@ allons l'appeler ``FarAwaySource`` et nous allons la placer dans * méthode calculate() ci-dessus. Nous pouvons soit vérifier la * source du dépôt, soit une autre façon pour récupérer le compte * de l\'enregistrement. Ici nous allons simplement retourner 1 - * ainsi ``update()`` et ``delete()`` vont estimer que l\'enregistrment + * ainsi ``update()`` et ``delete()`` vont estimer que l\'enregistrement * existe. */ if ($queryData['fields'] === 'COUNT') { return array(array(array('count' => 1))); } /** - * Maintenant nous récupèrons, décodons et retournons les données du dépôt. + * Maintenant nous récupérons, décodons et retournons les données du dépôt. */ $queryData['conditions']['apiKey'] = $this->config['apiKey']; $json = $this->Http->get('http://example.com/api/list.json', $queryData['conditions']); @@ -216,7 +216,7 @@ allons l'appeler ``FarAwaySource`` et nous allons la placer dans /** * Implémente le U dans CRUD. Appel à ``Model::save()`` avec $Model->id - * défini se trouve ici. Selon la source du dépôt, vous pouvez just appeler + * défini se trouve ici. Selon la source du dépôt, vous pouvez juste appeler * ``$this->create()``. */ public function update(Model $model, $fields = null, $values = null, $conditions = null) { diff --git a/fr/models/model-attributes.rst b/fr/models/model-attributes.rst index e39d2a2a7b..a0b57fc277 100755 --- a/fr/models/model-attributes.rst +++ b/fr/models/model-attributes.rst @@ -70,7 +70,7 @@ primaryKey ========== Chaque table a normalement une clé primaire, ``id``. Vous pouvez changer -le nom du champ que le model utlilise en clé primaire. Ceci est courant +le nom du champ que le model utilise en clé primaire. Ceci est courant quand on configure CakePHP pour utiliser une table d'une base de données existante. From b549c806eb4c044851794515284f84602d60aefc Mon Sep 17 00:00:00 2001 From: Mark Story Date: Sat, 1 Nov 2014 21:28:26 -0400 Subject: [PATCH 072/292] Add documentation for userFields. See cakephp/cakephp#5027 --- en/appendices/2-6-migration-guide.rst | 8 ++++++++ en/core-libraries/components/authentication.rst | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index 072c5cb21b..b51cf2007c 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -37,6 +37,14 @@ Shell - ``overwrite()`` has been added to allow generating progress bars or to avoid outputting too many lines by replacing text that has been already outputted to the screen. +Controller +========== + +AuthComponent +------------- + +- ``AuthComponent`` had the ``userFields`` option added. + Behavior ======== diff --git a/en/core-libraries/components/authentication.rst b/en/core-libraries/components/authentication.rst index 97fc597df8..14c2d171c5 100644 --- a/en/core-libraries/components/authentication.rst +++ b/en/core-libraries/components/authentication.rst @@ -115,6 +115,12 @@ keys. .. versionadded:: 2.4 +- ``userFields`` The list of fields to fetch from the ``userModel``. This option + is helpful when you have a wide user table and do not need all the columns in + the session. By default all fields are fetched. + + .. versionadded:: 2.6 + To configure different fields for user in ``$components`` array:: // Pass settings in $components array @@ -128,6 +134,7 @@ To configure different fields for user in ``$components`` array:: ) ); + Do not put other Auth configuration keys (like authError, loginAction etc) within the authenticate or Form element. They should be at the same level as the authenticate key. The setup above with other Auth configuration From e6275b234b971d83797481c1c6c92fc98f2676d6 Mon Sep 17 00:00:00 2001 From: antograssiot Date: Sun, 2 Nov 2014 21:40:09 +0100 Subject: [PATCH 073/292] [fr] corrections --- fr/appendices/2-3-migration-guide.rst | 2 +- fr/models/additional-methods-and-properties.rst | 2 +- fr/models/virtual-fields.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fr/appendices/2-3-migration-guide.rst b/fr/appendices/2-3-migration-guide.rst index 939b8795f1..7491f9d9ea 100755 --- a/fr/appendices/2-3-migration-guide.rst +++ b/fr/appendices/2-3-migration-guide.rst @@ -271,7 +271,7 @@ FormHelper en ajoutant ``'novalidate' => true`` dans les options de FormHelper::create(). - :php:meth:`FormHelper::input()` génère maintenant les elements d'input de type ``tel`` et ``email`` basé sur les noms de champ si l'option ``type`` - n'est pas specifiée. + n'est pas spécifiée. HtmlHelper ---------- diff --git a/fr/models/additional-methods-and-properties.rst b/fr/models/additional-methods-and-properties.rst index 83b65c9971..f4c63698a9 100644 --- a/fr/models/additional-methods-and-properties.rst +++ b/fr/models/additional-methods-and-properties.rst @@ -50,7 +50,7 @@ champ unique. :php:meth:`Model::escapeField(string $field = null, string $alias = null)` ========================================================================== -Echappe le nom du champ et ajoute le nom du model. L'echappement est fait en +Echappe le nom du champ et ajoute le nom du model. L'échappement est fait en fonction des règles du driver de la base de données courante. :php:meth:`Model::exists($id)` diff --git a/fr/models/virtual-fields.rst b/fr/models/virtual-fields.rst index 07e1a21e98..963d34203c 100644 --- a/fr/models/virtual-fields.rst +++ b/fr/models/virtual-fields.rst @@ -156,7 +156,7 @@ de notre colonne en utilisant la forme ``MonModel__MonChamp`` comme ceci:: $this->Timelog->query("SELECT project_id, SUM(id) as Timelog__TotalHours FROM timelogs AS Timelog GROUP BY project_id;"); -Lancer la requête de nouveau après avoir specifié le champ virtuel résultera +Lancer la requête de nouveau après avoir spécifié le champ virtuel résultera en un groupement plus propre des valeurs:: Array From f817648ef39db6910e2271e5d5f719da07fef75b Mon Sep 17 00:00:00 2001 From: Jonas Date: Mon, 3 Nov 2014 13:17:33 +0100 Subject: [PATCH 074/292] Make note of xdebug support for fatal stack traces https://github.com/cakephp/cakephp/pull/5053 --- en/development/debugging.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/en/development/debugging.rst b/en/development/debugging.rst index d33ef67290..0263029faf 100644 --- a/en/development/debugging.rst +++ b/en/development/debugging.rst @@ -205,6 +205,13 @@ primarily provides a toolbar in the rendered HTML, that provides a plethora of information about your application and the current request. You can download `DebugKit `_ from GitHub. +xdebug +====== + +If your environment supplies the xdebug php extension, fatal errors will show +additional xdebug stack trace details. Details about +`xdebug `_ . + .. meta:: :title lang=en: Debugging :description lang=en: Debugging CakePHP with the Debugger class, logging, basic debugging and using the DebugKit plugin. From 2df1ca48be655fb28ff88c3e83570f4c1bd58eb7 Mon Sep 17 00:00:00 2001 From: euromark Date: Mon, 3 Nov 2014 16:04:22 +0100 Subject: [PATCH 075/292] Docs for PaginatorHelper::meta() --- en/appendices/2-6-migration-guide.rst | 5 +++++ en/core-libraries/helpers/paginator.rst | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/en/appendices/2-6-migration-guide.rst b/en/appendices/2-6-migration-guide.rst index b51cf2007c..3073fb72b6 100644 --- a/en/appendices/2-6-migration-guide.rst +++ b/en/appendices/2-6-migration-guide.rst @@ -140,3 +140,8 @@ FormHelper the message. - The ``maxlength`` attribute will now also be applied to textareas, when the corresponding DB field is of type varchar, as per HTML specs. + +PaginatorHelper +--------------- + +- :php:meth:`PaginatorHelper::meta()` has been added to output the meta-links (rel prev/next) for a paginated result set. diff --git a/en/core-libraries/helpers/paginator.rst b/en/core-libraries/helpers/paginator.rst index 1843dd1c32..f7d0bf50b0 100644 --- a/en/core-libraries/helpers/paginator.rst +++ b/en/core-libraries/helpers/paginator.rst @@ -596,6 +596,24 @@ Other Methods .. versionadded:: 2.4 The ``param()`` method was added in 2.4. +.. php:method:: meta(array $options = array()) + + Returns the meta-links for a paginated result set:: + + echo $this->Paginator->meta(); // Example output for page 5 + /* + + */ + + You can also append the output of the meta function to the named block:: + + $this->Paginator->meta(array('block' => true)); + + If true is passed, the "meta" block is used. + +.. versionadded:: 2.6 + The ``meta()`` method was added in 2.6. + .. meta:: :title lang=en: PaginatorHelper :description lang=en: The Pagination helper is used to output pagination controls such as page numbers and next/previous links. From 47370ecd37235cfdc75b9d882a07bd9a967b1111 Mon Sep 17 00:00:00 2001 From: euromark Date: Mon, 3 Nov 2014 16:12:25 +0100 Subject: [PATCH 076/292] Wording --- en/core-libraries/helpers/paginator.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/en/core-libraries/helpers/paginator.rst b/en/core-libraries/helpers/paginator.rst index f7d0bf50b0..fdec189b83 100644 --- a/en/core-libraries/helpers/paginator.rst +++ b/en/core-libraries/helpers/paginator.rst @@ -598,7 +598,7 @@ Other Methods .. php:method:: meta(array $options = array()) - Returns the meta-links for a paginated result set:: + Outputs the meta-links for a paginated result set:: echo $this->Paginator->meta(); // Example output for page 5 /* From a791ea3d2a546e8576616698b9ae3662ea291aa9 Mon Sep 17 00:00:00 2001 From: cake17 Date: Mon, 3 Nov 2014 19:33:18 +0100 Subject: [PATCH 077/292] [fr] follow #1943 --- fr/development/debugging.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fr/development/debugging.rst b/fr/development/debugging.rst index a38a60f112..b53109d016 100644 --- a/fr/development/debugging.rst +++ b/fr/development/debugging.rst @@ -30,7 +30,7 @@ aussi la ligne et le fichier dont ils sont originaires par défaut. La sortie de cette fonction est seulement montrée si la variable de debug du coeur a été définie à une valeur supérieure à 0. -.. versionchanged:: 2.1 +.. versionchanged:: 2.1 La sortie de ``debug()`` ressemble plus ``var_dump()``, et utilise :php:class:`Debugger` en interne. @@ -118,7 +118,7 @@ est défini à une valeur supérieure à 0. //Dans PostsController::index() pr( Debugger::trace() ); - + //sorties PostsController::index() - APP/Controller/DownloadsController.php, line 48 Dispatcher::_invoke() - CORE/lib/Cake/Routing/Dispatcher.php, line 265 @@ -141,14 +141,14 @@ est défini à une valeur supérieure à 0. de lignes $context autour.:: pr( Debugger::excerpt(ROOT.DS.LIBS.'debugger.php', 321, 2) ); - + //sortira ce qui suit. Array ( [0] => * @access public [1] => */ [2] => function excerpt($file, $line, $context = 2) { - + [3] => $data = $lines = array(); [4] => $data = @explode("\n", file_get_contents($file)); ) @@ -209,6 +209,12 @@ une pléthore d'informations sur votre application et la requête courante. Vous pouvez télécharger `DebugKit `_ sur github. +xdebug +====== + +Si votre environnement a l'extension php xdebug, des erreurs fatales vont +montrer des détails de stack trace supplémentaires de xdebug. Plus de détails +sur `xdebug `_ . .. meta:: :title lang=fr: Debugger From 02366e580df007c2429b92c2777bbb7d406e5e8d Mon Sep 17 00:00:00 2001 From: antograssiot Date: Mon, 3 Nov 2014 21:31:45 +0100 Subject: [PATCH 078/292] [fr] corrections orthographiques --- fr/models/associations-linking-models-together.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/fr/models/associations-linking-models-together.rst b/fr/models/associations-linking-models-together.rst index fa1c79d670..34b9dbca2c 100644 --- a/fr/models/associations-linking-models-together.rst +++ b/fr/models/associations-linking-models-together.rst @@ -139,7 +139,7 @@ simplement en poursuivant les associations de votre model:: .. note:: - Rappelez-vous que les associations sont définis dans 'un sens'. Si vous + Rappelez-vous que les associations sont définies dans 'un sens'. Si vous définissez User hasMany Recipe, cela n'a aucun effet sur le model Recipe. Vous avez besoin de définir Recipe belongsTo User pour pouvoir accéder au model User à partir du model Recipe. @@ -371,7 +371,7 @@ et le mot "count":: Disons que vous avez un model appelé ``ImageComment`` et un model appelé ``Image``, vous ajouteriez un nouveau champ numérique (INT) à la -table ``images`` et l'appeleriez ``image_comment_count``. +table ``images`` et l'appelleriez ``image_comment_count``. Ici vous trouverez quelques exemples supplémentaires: @@ -796,7 +796,7 @@ association many to many. Considérons ce qui suit En d'autres termes, un Student peut avoir plusieurs (many) Courses et un Course peut être pris par plusieurs (many) Students. C'est une association -simple de many to many nécéssitant une table comme ceci:: +simple de many to many nécessitant une table comme ceci:: id | student_id | course_id @@ -808,7 +808,7 @@ souhaiterions serait comme ceci:: Le problème est que hasAndBelongsToMany ne va pas supporter ce type de scénario parce que quand les associations hasAndBelongsToMany sont sauvegardées, -l'association est d'abord supprimée. Vous perderiez les données supplémentaires +l'association est d'abord supprimée. Vous perdriez les données supplémentaires dans les colonnes qui ne seraient pas remplacées dans le nouvel ajout. .. versionchanged:: 2.1 From 188f22e45726308df2cb03b6ef19099058c98506 Mon Sep 17 00:00:00 2001 From: zx4357685 Date: Tue, 4 Nov 2014 16:02:51 +0900 Subject: [PATCH 079/292] getPeady=>getReady getPeady=>getReady --- zh/getting-started/cakephp-conventions.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zh/getting-started/cakephp-conventions.rst b/zh/getting-started/cakephp-conventions.rst index 51f7e29708..6c78e69b92 100644 --- a/zh/getting-started/cakephp-conventions.rst +++ b/zh/getting-started/cakephp-conventions.rst @@ -127,7 +127,7 @@ CakePHP 不支持复合主键。如果你要直接操作连接表的数据,请 ========== 视图模版文件依照它们显示的控制器方法来命名,并以下划线分隔。例如:在 -PeopleController 中的 getPeady() 方法将调用视图模版文件 +PeopleController 中的 getReady() 方法将调用视图模版文件 /app/View/People/get\_ready.ctp。 基本的模式是 /app/View/控制器/以下划线分隔的方法名.ctp。 From ff8ef784888d2f42142ba5806693b28b0df2312d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20W=C3=BCrth?= Date: Tue, 4 Nov 2014 12:54:39 +0100 Subject: [PATCH 080/292] Removed serbian translation for 2.0, too Refs https://github.com/cakephp/docs/pull/1948 Turns out the serbian translation isn't kept up to date for 2.0 either. No wonder when the initial maintainer "Aleksa-Savic" disappeared. The commits in this tree are not many: https://github.com/cakephp/docs/commits/master/sr --- sr/Makefile | 132 -- sr/_static/img/basic_mvc.png | Bin 30422 -> 0 bytes sr/_static/img/code-coverage.png | Bin 82337 -> 0 bytes sr/_static/img/typical-cake-request.png | Bin 19786 -> 0 bytes sr/appendices.rst | 71 - sr/appendices/2-0-migration-guide.rst | 1292 ------------ sr/appendices/2-1-migration-guide.rst | 363 ---- sr/appendices/2-2-migration-guide.rst | 327 --- sr/appendices/2-3-migration-guide.rst | 326 --- sr/appendices/2-4-migration-guide.rst | 294 --- sr/appendices/cakephp-development-process.rst | 56 - sr/appendices/glossary.rst | 70 - .../migrating-from-cakephp-1-2-to-1-3.rst | 778 -------- sr/appendices/new-features-in-cakephp-1-3.rst | 544 ----- sr/appendices/new-features-in-cakephp-2-0.rst | 296 --- sr/appendices/new-features-in-cakephp-2-1.rst | 213 -- sr/appendices/phpunit-migration-hints.rst | 200 -- sr/cakephp-overview.rst | 24 - .../understanding-model-view-controller.rst | 108 - .../what-is-cakephp-why-use-it.rst | 62 - sr/cakephp-overview/where-to-get-help.rst | 110 - sr/conf.py | 22 - sr/console-and-shells.rst | 1008 ---------- sr/console-and-shells/acl-shell.rst | 81 - .../code-generation-with-bake.rst | 233 --- sr/console-and-shells/cron-jobs.rst | 23 - sr/console-and-shells/i18n-shell.rst | 101 - .../schema-management-and-migrations.rst | 193 -- sr/console-and-shells/testsuite-shell.rst | 17 - sr/console-and-shells/upgrade-shell.rst | 81 - sr/contents.rst | 9 - sr/contributing.rst | 19 - .../cakephp-coding-conventions.rst | 429 ---- sr/contributing/code.rst | 141 -- sr/contributing/documentation.rst | 376 ---- sr/contributing/tickets.rst | 54 - sr/controllers.rst | 821 -------- sr/controllers/components.rst | 303 --- sr/controllers/pages-controller.rst | 28 - sr/controllers/request-response.rst | 832 -------- sr/controllers/scaffolding.rst | 139 -- sr/core-libraries.rst | 75 - sr/core-libraries/behaviors/acl.rst | 120 -- sr/core-libraries/behaviors/containable.rst | 442 ----- sr/core-libraries/behaviors/translate.rst | 357 ---- sr/core-libraries/behaviors/tree.rst | 819 -------- sr/core-libraries/caching.rst | 419 ---- sr/core-libraries/collections.rst | 170 -- .../components/access-control-lists.rst | 869 -------- .../components/authentication.rst | 979 --------- sr/core-libraries/components/cookie.rst | 181 -- sr/core-libraries/components/email.rst | 52 - sr/core-libraries/components/pagination.rst | 359 ---- .../components/request-handling.rst | 315 --- .../components/security-component.rst | 332 ---- sr/core-libraries/components/sessions.rst | 172 -- sr/core-libraries/events.rst | 459 ----- .../global-constants-and-functions.rst | 328 --- sr/core-libraries/helpers/cache.rst | 150 -- sr/core-libraries/helpers/form.rst | 1763 ----------------- sr/core-libraries/helpers/html.rst | 1108 ----------- sr/core-libraries/helpers/js.rst | 881 -------- sr/core-libraries/helpers/number.rst | 31 - sr/core-libraries/helpers/paginator.rst | 592 ------ sr/core-libraries/helpers/rss.rst | 278 --- sr/core-libraries/helpers/session.rst | 125 -- sr/core-libraries/helpers/text.rst | 95 - sr/core-libraries/helpers/time.rst | 55 - .../internationalization-and-localization.rst | 244 --- sr/core-libraries/logging.rst | 405 ---- sr/core-libraries/toc-behaviors.rst | 17 - sr/core-libraries/toc-components.rst | 17 - sr/core-libraries/toc-general-purpose.rst | 10 - sr/core-libraries/toc-helpers.rst | 24 - sr/core-libraries/toc-utilities.rst | 22 - sr/core-utility-libraries/app.rst | 404 ---- sr/core-utility-libraries/email.rst | 423 ---- sr/core-utility-libraries/file-folder.rst | 564 ------ sr/core-utility-libraries/hash.rst | 868 -------- sr/core-utility-libraries/httpsocket.rst | 384 ---- sr/core-utility-libraries/inflector.rst | 76 - sr/core-utility-libraries/number.rst | 364 ---- sr/core-utility-libraries/router.rst | 16 - sr/core-utility-libraries/sanitize.rst | 30 - sr/core-utility-libraries/security.rst | 134 -- sr/core-utility-libraries/set.rst | 1596 --------------- sr/core-utility-libraries/string.rst | 297 --- sr/core-utility-libraries/time.rst | 436 ---- sr/core-utility-libraries/xml.rst | 336 ---- sr/deployment.rst | 80 - sr/development.rst | 25 - sr/development/configuration.rst | 833 -------- sr/development/debugging.rst | 205 -- sr/development/dispatch-filters.rst | 229 --- sr/development/errors.rst | 131 -- sr/development/exceptions.rst | 448 ----- sr/development/rest.rst | 214 -- sr/development/routing.rst | 1110 ----------- sr/development/sessions.rst | 324 --- sr/development/testing.rst | 1327 ------------- sr/epub-contents.rst | 9 - sr/getting-started.rst | 35 - .../a-typical-cakephp-request.rst | 56 - sr/getting-started/cakephp-conventions.rst | 202 -- .../cakephp-folder-structure.rst | 89 - sr/getting-started/cakephp-structure.rst | 109 - sr/index.rst | 9 - sr/installation.rst | 207 -- sr/installation/advanced-installation.rst | 183 -- sr/installation/url-rewriting.rst | 270 --- sr/models.rst | 136 -- .../additional-methods-and-properties.rst | 114 -- .../associations-linking-models-together.rst | 1130 ----------- sr/models/behaviors.rst | 344 ---- sr/models/callback-methods.rst | 231 --- sr/models/data-validation.rst | 1267 ------------ .../validating-data-from-the-controller.rst | 81 - sr/models/datasources.rst | 319 --- sr/models/deleting-data.rst | 73 - sr/models/model-attributes.rst | 254 --- sr/models/retrieving-your-data.rst | 1100 ---------- sr/models/saving-your-data.rst | 972 --------- sr/models/transactions.rst | 63 - sr/models/virtual-fields.rst | 216 -- sr/pdf-contents.rst | 26 - sr/plugins.rst | 445 ----- sr/tutorials-and-examples.rst | 25 - .../blog-auth-example/auth.rst | 411 ---- sr/tutorials-and-examples/blog/blog.rst | 204 -- sr/tutorials-and-examples/blog/part-two.rst | 721 ------- .../part-two.rst | 199 -- .../simple-acl-controlled-application.rst | 405 ---- sr/views.rst | 807 -------- sr/views/helpers.rst | 304 --- sr/views/json-and-xml-views.rst | 131 -- sr/views/media-view.rst | 97 - sr/views/themes.rst | 87 - 137 files changed, 44626 deletions(-) delete mode 100644 sr/Makefile delete mode 100644 sr/_static/img/basic_mvc.png delete mode 100644 sr/_static/img/code-coverage.png delete mode 100644 sr/_static/img/typical-cake-request.png delete mode 100644 sr/appendices.rst delete mode 100644 sr/appendices/2-0-migration-guide.rst delete mode 100644 sr/appendices/2-1-migration-guide.rst delete mode 100644 sr/appendices/2-2-migration-guide.rst delete mode 100644 sr/appendices/2-3-migration-guide.rst delete mode 100644 sr/appendices/2-4-migration-guide.rst delete mode 100644 sr/appendices/cakephp-development-process.rst delete mode 100644 sr/appendices/glossary.rst delete mode 100644 sr/appendices/migrating-from-cakephp-1-2-to-1-3.rst delete mode 100644 sr/appendices/new-features-in-cakephp-1-3.rst delete mode 100644 sr/appendices/new-features-in-cakephp-2-0.rst delete mode 100644 sr/appendices/new-features-in-cakephp-2-1.rst delete mode 100644 sr/appendices/phpunit-migration-hints.rst delete mode 100644 sr/cakephp-overview.rst delete mode 100644 sr/cakephp-overview/understanding-model-view-controller.rst delete mode 100644 sr/cakephp-overview/what-is-cakephp-why-use-it.rst delete mode 100644 sr/cakephp-overview/where-to-get-help.rst delete mode 100644 sr/conf.py delete mode 100644 sr/console-and-shells.rst delete mode 100644 sr/console-and-shells/acl-shell.rst delete mode 100644 sr/console-and-shells/code-generation-with-bake.rst delete mode 100644 sr/console-and-shells/cron-jobs.rst delete mode 100644 sr/console-and-shells/i18n-shell.rst delete mode 100644 sr/console-and-shells/schema-management-and-migrations.rst delete mode 100644 sr/console-and-shells/testsuite-shell.rst delete mode 100644 sr/console-and-shells/upgrade-shell.rst delete mode 100644 sr/contents.rst delete mode 100644 sr/contributing.rst delete mode 100644 sr/contributing/cakephp-coding-conventions.rst delete mode 100644 sr/contributing/code.rst delete mode 100644 sr/contributing/documentation.rst delete mode 100644 sr/contributing/tickets.rst delete mode 100644 sr/controllers.rst delete mode 100644 sr/controllers/components.rst delete mode 100644 sr/controllers/pages-controller.rst delete mode 100644 sr/controllers/request-response.rst delete mode 100644 sr/controllers/scaffolding.rst delete mode 100644 sr/core-libraries.rst delete mode 100644 sr/core-libraries/behaviors/acl.rst delete mode 100644 sr/core-libraries/behaviors/containable.rst delete mode 100644 sr/core-libraries/behaviors/translate.rst delete mode 100644 sr/core-libraries/behaviors/tree.rst delete mode 100644 sr/core-libraries/caching.rst delete mode 100644 sr/core-libraries/collections.rst delete mode 100644 sr/core-libraries/components/access-control-lists.rst delete mode 100644 sr/core-libraries/components/authentication.rst delete mode 100644 sr/core-libraries/components/cookie.rst delete mode 100644 sr/core-libraries/components/email.rst delete mode 100644 sr/core-libraries/components/pagination.rst delete mode 100644 sr/core-libraries/components/request-handling.rst delete mode 100644 sr/core-libraries/components/security-component.rst delete mode 100644 sr/core-libraries/components/sessions.rst delete mode 100644 sr/core-libraries/events.rst delete mode 100644 sr/core-libraries/global-constants-and-functions.rst delete mode 100644 sr/core-libraries/helpers/cache.rst delete mode 100644 sr/core-libraries/helpers/form.rst delete mode 100644 sr/core-libraries/helpers/html.rst delete mode 100644 sr/core-libraries/helpers/js.rst delete mode 100644 sr/core-libraries/helpers/number.rst delete mode 100644 sr/core-libraries/helpers/paginator.rst delete mode 100644 sr/core-libraries/helpers/rss.rst delete mode 100644 sr/core-libraries/helpers/session.rst delete mode 100644 sr/core-libraries/helpers/text.rst delete mode 100644 sr/core-libraries/helpers/time.rst delete mode 100644 sr/core-libraries/internationalization-and-localization.rst delete mode 100644 sr/core-libraries/logging.rst delete mode 100644 sr/core-libraries/toc-behaviors.rst delete mode 100644 sr/core-libraries/toc-components.rst delete mode 100644 sr/core-libraries/toc-general-purpose.rst delete mode 100644 sr/core-libraries/toc-helpers.rst delete mode 100644 sr/core-libraries/toc-utilities.rst delete mode 100644 sr/core-utility-libraries/app.rst delete mode 100644 sr/core-utility-libraries/email.rst delete mode 100644 sr/core-utility-libraries/file-folder.rst delete mode 100644 sr/core-utility-libraries/hash.rst delete mode 100644 sr/core-utility-libraries/httpsocket.rst delete mode 100644 sr/core-utility-libraries/inflector.rst delete mode 100644 sr/core-utility-libraries/number.rst delete mode 100644 sr/core-utility-libraries/router.rst delete mode 100644 sr/core-utility-libraries/sanitize.rst delete mode 100644 sr/core-utility-libraries/security.rst delete mode 100644 sr/core-utility-libraries/set.rst delete mode 100644 sr/core-utility-libraries/string.rst delete mode 100644 sr/core-utility-libraries/time.rst delete mode 100644 sr/core-utility-libraries/xml.rst delete mode 100644 sr/deployment.rst delete mode 100644 sr/development.rst delete mode 100644 sr/development/configuration.rst delete mode 100644 sr/development/debugging.rst delete mode 100644 sr/development/dispatch-filters.rst delete mode 100644 sr/development/errors.rst delete mode 100644 sr/development/exceptions.rst delete mode 100644 sr/development/rest.rst delete mode 100644 sr/development/routing.rst delete mode 100644 sr/development/sessions.rst delete mode 100644 sr/development/testing.rst delete mode 100644 sr/epub-contents.rst delete mode 100644 sr/getting-started.rst delete mode 100644 sr/getting-started/a-typical-cakephp-request.rst delete mode 100644 sr/getting-started/cakephp-conventions.rst delete mode 100644 sr/getting-started/cakephp-folder-structure.rst delete mode 100644 sr/getting-started/cakephp-structure.rst delete mode 100644 sr/index.rst delete mode 100644 sr/installation.rst delete mode 100644 sr/installation/advanced-installation.rst delete mode 100644 sr/installation/url-rewriting.rst delete mode 100644 sr/models.rst delete mode 100644 sr/models/additional-methods-and-properties.rst delete mode 100644 sr/models/associations-linking-models-together.rst delete mode 100644 sr/models/behaviors.rst delete mode 100644 sr/models/callback-methods.rst delete mode 100644 sr/models/data-validation.rst delete mode 100644 sr/models/data-validation/validating-data-from-the-controller.rst delete mode 100644 sr/models/datasources.rst delete mode 100644 sr/models/deleting-data.rst delete mode 100644 sr/models/model-attributes.rst delete mode 100644 sr/models/retrieving-your-data.rst delete mode 100644 sr/models/saving-your-data.rst delete mode 100644 sr/models/transactions.rst delete mode 100644 sr/models/virtual-fields.rst delete mode 100644 sr/pdf-contents.rst delete mode 100644 sr/plugins.rst delete mode 100644 sr/tutorials-and-examples.rst delete mode 100644 sr/tutorials-and-examples/blog-auth-example/auth.rst delete mode 100644 sr/tutorials-and-examples/blog/blog.rst delete mode 100644 sr/tutorials-and-examples/blog/part-two.rst delete mode 100644 sr/tutorials-and-examples/simple-acl-controlled-application/part-two.rst delete mode 100644 sr/tutorials-and-examples/simple-acl-controlled-application/simple-acl-controlled-application.rst delete mode 100644 sr/views.rst delete mode 100644 sr/views/helpers.rst delete mode 100644 sr/views/json-and-xml-views.rst delete mode 100644 sr/views/media-view.rst delete mode 100644 sr/views/themes.rst diff --git a/sr/Makefile b/sr/Makefile deleted file mode 100644 index deacedfd12..0000000000 --- a/sr/Makefile +++ /dev/null @@ -1,132 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = ../build -PYTHON = python -LANG = sr - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees/$(LANG) $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -help: - @echo "Please use \`make ' where is one of" - @echo " html to make standalone HTML files" - @echo " dirhtml to make HTML files named index.html in directories" - @echo " singlehtml to make a single large HTML file" - @echo " pickle to make pickle files" - @echo " json to make JSON files" - @echo " htmlhelp to make HTML files and a HTML help project" - @echo " qthelp to make HTML files and a qthelp project" - @echo " devhelp to make HTML files and a Devhelp project" - @echo " epub to make an epub" - @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" - @echo " latexpdf to make LaTeX files and run them through pdflatex" - @echo " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @echo " linkcheck to check all external links for integrity" - @echo " doctest to run all doctests embedded in the documentation (if enabled)" - -clean: - -rm -rf $(BUILDDIR)/* - -html: - $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html/$(LANG) - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/html/$(LANG)." - -dirhtml: - $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml - @echo - @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." - -singlehtml: - $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml - @echo - @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." - -pickle: - $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle - @echo - @echo "Build finished; now you can process the pickle files." - -json: - $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json - @echo - @echo "Build finished; now you can process the JSON files." - -htmlhelp: - $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp/$(LANG) - @echo - @echo "Build finished; now you can run HTML Help Workshop with the" \ - ".hhp project file in $(BUILDDIR)/htmlhelp/$(LANG)." - -qthelp: - $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp - @echo - @echo "Build finished; now you can run "qcollectiongenerator" with the" \ - ".qhcp project file in $(BUILDDIR)/qthelp, like this:" - @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/CakePHPCookbook.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/CakePHPCookbook.qhc" - -devhelp: - $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp - @echo - @echo "Build finished." - @echo "To view the help file:" - @echo "# mkdir -p $$HOME/.local/share/devhelp/CakePHPCookbook" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/CakePHPCookbook" - @echo "# devhelp" - -epub: - $(SPHINXBUILD) -b epub -D master_doc='epub-contents' $(ALLSPHINXOPTS) $(BUILDDIR)/epub/$(LANG) - @echo - @echo "Build finished. The epub file is in $(BUILDDIR)/epub/$(LANG)." - -latex: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex/$(LANG) - @echo - @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex/$(LANG)." - @echo "Run \`make' in that directory to run these through (pdf)latex" \ - "(use \`make latexpdf' here to do that automatically)." - -latexpdf: - $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex/$(LANG) - @echo "Running LaTeX files through pdflatex..." - make -C $(BUILDDIR)/latex/$(LANG) all-pdf - @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex/$(LANG)." - -text: - $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text - @echo - @echo "Build finished. The text files are in $(BUILDDIR)/text." - -man: - $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man - @echo - @echo "Build finished. The manual pages are in $(BUILDDIR)/man." - -changes: - $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes - @echo - @echo "The overview file is in $(BUILDDIR)/changes." - -linkcheck: - $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck - @echo - @echo "Link check complete; look for any errors in the above output " \ - "or in $(BUILDDIR)/linkcheck/output.txt." - -doctest: - $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest - @echo "Testing of doctests in the sources finished, look at the " \ - "results in $(BUILDDIR)/doctest/output.txt." diff --git a/sr/_static/img/basic_mvc.png b/sr/_static/img/basic_mvc.png deleted file mode 100644 index 099d13461e324b0239c37102694b1804f5f432d1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30422 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 KG#;zjhW!ujz^||X diff --git a/sr/_static/img/code-coverage.png b/sr/_static/img/code-coverage.png deleted file mode 100644 index 5eff05469abfca7ffb505e5ee2812708ce5105a9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 82337 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 diff --git a/sr/appendices.rst b/sr/appendices.rst deleted file mode 100644 index fa818df38c..0000000000 --- a/sr/appendices.rst +++ /dev/null @@ -1,71 +0,0 @@ -Appendices -########## - -Appendices contain information regarding the new features -introduced in 2.x, and the migration path from 1.3 to 2.0. - -2.4 Migration Guide -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/2-4-migration-guide - -2.3 Migration Guide -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/2-3-migration-guide - -2.2 Migration Guide -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/2-2-migration-guide - -2.1 Migration Guide -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/2-1-migration-guide - appendices/new-features-in-cakephp-2-1 - -2.0 Migration Guide -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/2-0-migration-guide - appendices/new-features-in-cakephp-2-0 - appendices/phpunit-migration-hints - -Migration from 1.2 to 1.3 -========================= - -.. toctree:: - :maxdepth: 1 - - appendices/migrating-from-cakephp-1-2-to-1-3 - appendices/new-features-in-cakephp-1-3 - -General Information -=================== - -.. toctree:: - :maxdepth: 1 - - appendices/cakephp-development-process - appendices/glossary - - -.. meta:: - :title lang=en: Appendices - :keywords lang=en: migration guide,migration path,new features,glossary diff --git a/sr/appendices/2-0-migration-guide.rst b/sr/appendices/2-0-migration-guide.rst deleted file mode 100644 index 4e079c23f8..0000000000 --- a/sr/appendices/2-0-migration-guide.rst +++ /dev/null @@ -1,1292 +0,0 @@ -2.0 Migration Guide -################### - -This page summarizes the changes from CakePHP 1.3 that will assist in a project -migration to 2.0, as well as for a developer reference to get up to date with -the changes made to the core since the CakePHP 1.3 branch. Be sure to read the -other pages in this guide for all the new features and API changes. - -.. tip:: - - Be sure to checkout the :ref:`upgrade-shell` included in the 2.0 core to help you - migrate your 1.3 code to 2.0. - -PHP Version Support -=================== - -CakePHP 2.x supports PHP Version 5.2.8 and above. PHP4 support has been dropped. -For developers that are still working with production PHP4 environments, the -CakePHP 1.x versions continue to support PHP4 for the lifetime of their -development and support lifetime. - -The move to PHP 5 means all methods and properties have been updated with -visibility keywords. If your code is attempting access to private or protected -methods from a public scope, you will encounter errors. - -While this does not really constitute a large framework change, it means that -access to tighter visibility methods and variables is now not possible. - -File and Folder naming -====================== - -In CakePHP 2.0 we rethought the way we are structuring our files and folders. -Given that PHP 5.3 is supporting namespaces we decided to prepare our code base -for adopting in a near future this PHP version, so we adopted the -https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-0.md. At first -we glanced at the internal structure of CakePHP 1.3 and realized that after all -these years there was no clear organization in the files, nor did the directory -structure really hint where each file should be located. With this change we -would be allowed to experiment a little with (almost) automatic class loading -for increasing the overall framework performance. - -Biggest roadblock for achieving this was maintaining some sort of backwards -compatibility in the way the classes are loaded right now, and we definitely did -not want to become a framework of huge class prefixes, having class names like -``My_Huge_Class_Name_In_Package``. We decided adopting a strategy of keeping simple -class names while offering a very intuitive way of declaring class locations and -clear migration path for future PHP 5.3 version of CakePHP. First let's -highlight the main changes in file naming standard we adopted: - -File names ----------- - -All files containing classes should be named after the class it contains. No -file should contain more than one class. So, no more lowercasing and -underscoring your file names. Here are some examples: - -* ``my_things_controller.php`` becomes ``MyThingsController.php`` -* ``form.php`` (a Helper) becomes ``FormHelper.php`` -* ``session.php`` (a Component) becomes ``SessionComponent.php`` - -This makes file naming a lot more clear and consistent across applications, -and also avoids a few edge cases where the file loader would get confused in the -past and load files it should not. - -Folder Names ------------- - -Most folders should be also CamelCased, especially when containing classes. -Think of namespaces, each folder represents a level in the namespacing -hierarchy, folders that do not contain classes, or do not constitute a -namespace on themselves, should be lowercased. - -CamelCased Folders: - -* Config -* Console -* Controller -* Controller/Component -* Lib -* Locale -* Model -* Model/Behavior -* Plugin -* Test -* Vendor -* View -* View/Helper - -lowercased Folders: - -* tmp -* webroot - -htaccess (URL Rewriting) -=============================================== -In your ``app/webroot/.htaccess`` replace line ``RewriteRule ^(.*)$ index.php?url=$1 [QSA,L]`` with ``RewriteRule ^(.*)$ index.php [QSA,L]`` - -AppController / AppModel / AppHelper / AppShell -=============================================== - -The ``app/app_controller.php``, ``app/app_model.php``, ``app/app_helper.php`` are now located and -named as ``app/Controller/AppController.php``, ``app/Model/AppModel.php`` and ``app/View/Helper/AppHelper.php`` respectively. - -Also all shell/task now extend AppShell. You can have your custom AppShell.php at ``app/Console/Command/AppShell.php`` - -Internationalization / Localization -=================================== - -:php:func:`__()` (Double underscore shortcut function) always returns the translation -(not echo anymore). - -If you want to echo the result of the translation, use:: - - echo __('My Message'); - -This change includes all shortcut translation methods:: - - __() - __n() - __d() - __dn() - __dc() - __dcn() - __c() - -Alongside this, if you pass additional parameters, the translation will call -`sprintf `_ with these -parameters before returning. For example:: - - // Will return something like "Called: MyClass:myMethod" - echo __('Called: %s:%s', $className, $methodName); - -It is valid for all shortcut translation methods. - -More information about the specifiers, you can see in -`sprintf `_ function. - - -Class location and constants changed -==================================== - -The constants ``APP`` and ``CORE_PATH`` -have consistent values between the web and console environments. In previous -versions of CakePHP these values changed depending on your environment. - -Basics.php -========== - -- ``getMicrotime()`` has been removed. Use the native ``microtime(true)`` - instead. -- ``e()`` was removed. Use ``echo``. -- ``r()`` was removed. Use ``str_replace``. -- ``a()`` was removed. ``Use array()`` -- ``aa()`` was removed. Use ``array()`` -- ``up()`` was removed. Use ``strtoupper()`` -- ``low()`` was removed. Use ``strtolower()`` -- ``params()`` was removed. It was not used anywhere in CakePHP. -- ``ife()`` was removed. Use a ternary operator. -- ``uses()`` was removed. Use ``App::import()`` instead. -- Compatibility functions for PHP4 have been removed. -- PHP5 constant has been removed. -- Global var called ``$TIME_START`` was removed use the constant - ``TIME_START`` or ``$_SERVER['REQUEST_TIME']`` instead. - -Removed Constants ------------------ - -A number of constants were removed, as they were no longer accurate, or -duplicated. - -* APP_PATH -* BEHAVIORS -* COMPONENTS -* CONFIGS -* CONSOLE_LIBS -* CONTROLLERS -* CONTROLLER_TESTS -* ELEMENTS -* HELPERS -* HELPER_TESTS -* LAYOUTS -* LIB_TESTS -* LIBS -* MODELS -* MODEL_TESTS -* SCRIPTS -* VIEWS - -CakeRequest -=========== - -This new class encapsulates the parameters and functions related to an incoming -request. It replaces many features inside ``Dispatcher``, -``RequestHandlerComponent`` and Controller. It also replaces -``$this->params`` array in all places. ``CakeRequest`` implements -``ArrayAccess`` so many interactions with the old params array do not need to -change. See the CakeRequest new features for more information. - -Request handling, $_GET['url'] and .htaccess files -================================================== - -CakePHP no longer uses ``$_GET['url']`` for handling application request paths. -Instead it uses ``$_SERVER['PATH_INFO']``. This provides a more uniform way of -handling requests between servers with URL rewriting and those without. Because -of these changes, you'll need to update your .htaccess files and -``app/webroot/index.php``, as these files were changed to accommodate the -changes. Additionally ``$this->params['url']['url']`` no longer exists. Instead -you should be using $this->request->url to access the same value. -This attribute now contains the url without the leading slash ``/`` prepended. - -Note: For the homepage itself (``http://domain/``) $this->request->url now returns -boolean ``false`` instead of ``/``. Make sure you check on that accordingly:: - - if (!$this->request->url) {} // instead of $this->request->url === '/' - -Components -========== - -Component is now the required base class for all components. You should update -your components and their constructors, as both have changed:: - - class PrgComponent extends Component { - public function __construct(ComponentCollection $collection, - $settings = array()) { - parent::__construct($collection, $settings); - } - } - -As with helpers it is important to call ``parent::__construct()`` in components with -overridden constructors. Settings for a component are also passed into the -constructor now, and not the ``initialize()`` callback. This makes getting well -constructed objects easier, and allows the base class to handle setting the -properties up. - -Since settings have been moved to the component constructor, the -``initialize()`` callback no longer receives ``$settings`` as its 2nd parameter. -You should update your components to use the following method signature:: - - public function initialize(Controller $controller) { } - -Additionally, the initialize() method is only called on components that are -enabled. This usually means components that are directly attached to the -controller object. - -Deprecated callbacks removed ----------------------------- - -All the deprecated callbacks in Component have not been transferred to -ComponentCollection. Instead you should use the `trigger()` method to interact -with callbacks. If you need to trigger a callback you could do so by calling:: - - $this->Components->trigger('someCallback', array(&$this)); - -Changes in disabling components -------------------------------- - -In the past you were able to disable components via `$this->Auth->enabled = -false;` for example. In CakePHP 2.0 you should use the ComponentCollection's -disable method, `$this->Components->disable('Auth');`. Using the enabled -property will not work. - -AclComponent ------------- - -- ``AclComponent`` implementations are now required to implement - ``AclInterface``. -- ``AclComponent::adapter()`` has been added to allow runtime modification of - the ``ACL`` implementation the component uses. -- ``AclComponent::grant()`` has been deprecated, it will be removed in a future - version. Use ``AclComponent::allow()`` instead. -- ``AclComponent::revoke()`` has been deprecated, it will be removed in a - future version. Use AclComponent::deny() instead. - -RequestHandlerComponent ------------------------ - -Many of RequestHandlerComponent's methods are just proxies for ``CakeRequest`` -methods. The following methods have been deprecated and will be removed in -future versions: - -- ``isSsl()`` -- ``isAjax()`` -- ``isPost()`` -- ``isPut()`` -- ``isFlash()`` -- ``isDelete()`` -- ``getReferer()`` -- ``getClientIp()`` -- ``accepts()``, ``prefers()``, ``requestedWith()`` All deal in mapped content - types now. They no longer work with mime-types. You can use - ``RequestHandler::setContent()`` to create new content types. -- ``RequestHandler::setContent()`` no longer accepts an array as a single - argument, you must supply both arguments. - -SecurityComponent ------------------ - -SecurityComponent no longer handles Basic and Digest Authentication. These are -both handled by the new AuthComponent. The following methods have been removed -from SecurityComponent: - -- requireLogin() -- generateDigestResponseHash() -- loginCredentials() -- loginRequest() -- parseDigestAuthData() - -In addition the following properties were removed: - -- $loginUsers -- $requireLogin - -Moving these features to AuthComponent was done to provide a single place for -all types of authentication and to streamline the roles of each component. - -AuthComponent -------------- - -The AuthComponent was entirely re-factored for 2.0, this was done to help reduce -developer confusion and frustration. In addition, AuthComponent was made more -flexible and extensible. You can find out more in -the :doc:`/core-libraries/components/authentication` guide. - -EmailComponent --------------- - -The EmailComponent has been deprecated and has created a new library class to -send e-mails. See :doc:`/core-utility-libraries/email` Email changes for more details. - -SessionComponent ----------------- - -Session component has lost the following methods. - -* activate() -* active() -* __start() - -cakeError removed -================= - -The ``cakeError()`` method has been removed. It's recommended that you switch all -uses of ``cakeError`` to use exceptions. ``cakeError`` was removed because it -was simulating exceptions. Instead of simulation, real exceptions are used in -CakePHP 2.0. - -Error handling -============== - -The error handling implementation has dramatically changed in 2.0. Exceptions -have been introduced throughout the framework, and error handling has been -updated to offer more control and flexibility. You can read more in the -:doc:`/development/exceptions` and :doc:`/development/errors` section. - -Lib classes -=========== - -App ---- - -The API for ``App::build()`` has changed to ``App::build($paths, $mode).`` It -now allows you to either append, prepend or reset/replace existing paths. The -$mode param can take any of the following 3 values: App::APPEND, -App::PREPEND, ``App::RESET``. The default behavior of the function remains the -same (ie. Prepending new paths to existing list). - -App::path() -~~~~~~~~~~~ - -* Now supports plugins, App::path('Controller', 'Users') will return the folder - location of the controllers in the Users plugin. -* Won't merge core paths anymore, it will - only return paths defined in App::build() or default ones in app (or - corresponding plugin). - -App::build() -~~~~~~~~~~~~ - -* Will not merge app path with core paths anymore. - -App::objects() -~~~~~~~~~~~~~~ - -* Now supports plugins, App::objects('Users.Model') will return the models in - plugin Users. -* Returns array() instead of false for empty results or invalid types. -* Does not return core objects anymore, App::objects('core') will return array(). -* Returns the complete class name. - -App class lost the following properties, use method App::path() to access their value - -* App::$models -* App::$behaviors -* App::$controllers -* App::$components -* App::$datasources -* App::$libs -* App::$views -* App::$helpers -* App::$plugins -* App::$vendors -* App::$locales -* App::$shells - -App::import() -~~~~~~~~~~~~~ - -* No longer looks for classes recursively, it strictly uses the values for the - paths defined in App::build(). -* Will not be able to load App::import('Component', 'Component') use - App::uses('Component', 'Controller'); -* Using App::import('Lib', 'CoreClass') to load core classes is no longer - possible. -* Importing a non-existent file, supplying a wrong type or package name, or null - values for $name and $file parameters will result in a false return value. -* App::import('Core', 'CoreClass') is no longer supported, use App::uses() - instead and let the class autoloading do the rest. -* Loading Vendor files does not look recursively in the vendors folder, it will - also no longer convert the file to underscored as it did in the past. - -App::core() -~~~~~~~~~~~ - -* First parameter is no longer optional, it will always return one path -* It can't be used anymore to get the vendors paths -* It will only accept new style package names - -Class loading with App::uses() -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Although there has been a huge refactoring in how the classes are loaded, in very -few occasions you will need to change your application code to respect the way you were -used to doing it. The biggest change is the introduction of a new method:: - - App::uses('AuthComponent', 'Controller/Component'); - -We decided the function name should emulate PHP 5.3's ``use`` keyword, just as a way -of declaring where a class name should be located. The first parameter of -:php:meth:`App::uses()` is the complete name of the class you intend to load, -and the second one, the package name (or namespace) where it belongs to. The -main difference with CakePHP 1.3's :php:meth:`App::import()` is that the former -won't actually import the class, it will just setup the system so when the class -is used for the first time it will be located. - -Some examples on using :php:meth:`App::uses()` when migrating from -:php:meth:`App::import()`:: - - App::import('Controller', 'Pages'); - // becomes - App::uses('PagesController', 'Controller'); - - App::import('Component', 'Auth'); - // becomes - App::uses('AuthComponent', 'Controller/Component'); - - App::import('View', 'Media'); - // becomes - App::uses('MediaView', 'View'); - - App::import('Core', 'Xml'); - // becomes - App::uses('Xml', 'Utility'); - - App::import('Datasource', 'MongoDb.MongoDbSource'); - // becomes - App::uses('MongoDbSource', 'MongoDb.Model/Datasource'); - -All classes that were loaded in the past using ``App::import('Core', $class);`` -will need to be loaded using ``App::uses()`` referring to the correct package. -See the API to locate the classes in their new folders. Some examples:: - - App::import('Core', 'CakeRoute'); - // becomes - App::uses('CakeRoute', 'Routing/Route'); - - App::import('Core', 'Sanitize'); - // becomes - App::uses('Sanitize', 'Utility'); - - App::import('Core', 'HttpSocket'); - // becomes - App::uses('HttpSocket', 'Network/Http'); - -In contrast to how :php:meth:`App::import()` worked in the past, the new class -loader will not locate classes recursively. This led to an impressive -performance gain even on develop mode, at the cost of some seldom used features -that always caused side effects. To be clear again, the class loader will only -fetch the class in the exact package in which you told it to find it. - -App::build() and core paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -:php:meth:`App::build()` will not merge app paths with core paths anymore. - -Examples:: - - App::build(array('controllers' => array('/full/path/to/controllers'))); - //becomes - App::build(array('Controller' => array('/full/path/to/Controller'))); - - App::build(array('helpers' => array('/full/path/to/controllers'))); - //becomes - App::build(array('View/Helper' => array('/full/path/to/View/Helper'))); - -CakeLog -------- - -- Log streams now need to implement :php:class:`CakeLogInterface`. Exceptions will be raised - if a configured logger does not. - -Cache ------ - -- :php:class:`Cache` is now a static class, it no longer has a getInstance() method. -- CacheEngine is now an abstract class. You cannot directly create instances of - it anymore. -- CacheEngine implementations must extend CacheEngine, exceptions will be - raised if a configured class does not. -- FileCache now requires trailing slashes to be added to the path setting when - you are modifying a cache configuration. -- Cache no longer retains the name of the last configured cache engine. This - means that operations you want to occur on a specific engine need to have the - $config parameter equal to the config name you want the operation to occur - on. - -:: - - Cache::config('something'); - Cache::write('key', $value); - - // would become - Cache::write('key', $value, 'something'); - -Router ------- - -- You can no longer modify named parameter settings with - ``Router::setRequestInfo()``. You should use ``Router::connectNamed()`` to - configure how named parameters are handled. -- Router no longer has a ``getInstance()`` method. It is a static class, call - its methods and properties statically. -- ``Router::getNamedExpressions()`` is deprecated. Use the new router - constants. ``Router::ACTION``, ``Router::YEAR``, ``Router::MONTH``, - ``Router::DAY``, ``Router::ID``, and ``Router::UUID`` instead. -- ``Router::defaults()`` has been removed. Delete the core routes file - inclusion from your applications routes.php file to disable default routing. - Conversely if you want default routing, you will have to add an include to - ``Cake/Config/routes.php`` in your routes file. -- When using Router::parseExtensions() the extension parameter is no longer - under ``$this->params['url']['ext']``. Instead it is available at - ``$this->request->params['ext']``. -- Default plugin routes have changed. Plugin short routes are no longer built - in for any actions other than index. Previously ``/users`` and ``/users/add`` - would map to the UsersController in the Users plugin. In 2.0, only the - ``index`` action is given a short route. If you wish to continue using short - routes, you can add a route like:: - - Router::connect( - '/users/:action', - array('controller' => 'users', 'plugin' => 'users') - ); - - To your routes file for each plugin you need short routes on. - -Your app/Config/routes.php file needs to be updated adding this line at the bottom of the file:: - - require CAKE . 'Config' . DS . 'routes.php'; - -This is needed in order to generate the default routes for your application. If you do not wish to have such routes, -or want to implement your own standard you can include your own file with custom router rules. - -Dispatcher ----------- - -- Dispatcher has been moved inside of cake/libs, you will have to update your - ``app/webroot/index.php`` file. -- ``Dispatcher::dispatch()`` now takes two parameters. The request and - response objects. These should be instances of ``CakeRequest`` & - ``CakeResponse`` or a subclass thereof. -- ``Dispatcher::parseParams()`` now only accepts a ``CakeRequest`` object. -- ``Dispatcher::baseUrl()`` has been removed. -- ``Dispatcher::getUrl()`` has been removed. -- ``Dispatcher::uri()`` has been removed. -- ``Dispatcher::$here`` has been removed. - -Configure ---------- - -- ``Configure::read()`` with no parameter no longer returns the value of - 'debug' instead it returns all values in Configure. Use - ``Configure::read('debug');`` if you want the value of debug. -- ``Configure::load()`` now requires a ConfigReader to be setup. Read - :ref:`loading-configuration-files` for more information. -- ``Configure::store()`` now writes values to a given Cache configuration. Read - :ref:`loading-configuration-files` for more information. - -Scaffold --------- - -- Scaffold 'edit' views should be renamed to 'form'. This was done to make - scaffold and bake templates consistent. - - - ``views/scaffolds/edit.ctp`` -> ``View/Scaffolds/form.ctp`` - - ``views/posts/scaffold.edit.ctp`` -> ``View/Posts/scaffold.form.ctp`` - -Xml ---- - -- The class Xml was completely re-factored. Now this class does not manipulate - data anymore, and it is a wrapper to SimpleXMLElement. You can use the following - methods: - - - ``Xml::build()``: static method that you can pass an xml string, array, path - to file or url. The result will be a SimpleXMLElement instance or an - exception will be thrown in case of error. - - ``Xml::fromArray():`` static method that returns a SimpleXMLElement from an - array. - - ``Xml::toArray()``: static method that returns an array from - SimpleXMLElement. - -You should see the :php:class:`Xml` documentation for more information on the changes made to -the Xml class. - -Inflector ---------- - -- Inflector no longer has a ``getInstance()`` method. -- ``Inflector::slug()`` no longer supports the $map argument. Use - ``Inflector::rules()`` to define transliteration rules. - -CakeSession ------------ - -CakeSession is now a fully static class, both ``SessionHelper`` and -``SessionComponent`` are wrappers and sugar for it. It can now easily be used -in models or other contexts. All of its methods are called statically. - -Session configuration has also changed :doc:`see the session section for more -information ` - -HttpSocket ----------- - -- HttpSocket doesn't change the header keys. Following other places in core, - the HttpSocket does not change the headers. :rfc:`2616` says that headers are case - insensitive, and HttpSocket preserves the values the remote host sends. -- HttpSocket returns responses as objects now. Instead of arrays, HttpSocket - returns instances of HttpResponse. See the :php:class:`HttpSocket` - documentation for more information. -- Cookies are stored internally by host, not per instance. This means that, if - you make two requests to different servers, cookies from domain1 won't be sent - to domain2. This was done to avoid possible security problems. - - -Helpers -======= - -Constructor changed -------------------- - -In order to accommodate View being removed from the ClassRegistry, the signature -of Helper::__construct() was changed. You should update any subclasses to use -the following:: - - public function __construct(View $View, $settings = array()) - -When overriding the constructor you should always call `parent::__construct` as -well. `Helper::__construct` stores the view instance at `$this->_View` for -later reference. The settings are not handled by the parent constructor. - -HelperCollection added ----------------------- - -After examining the responsibilities of each class involved in the View layer, -it became clear that View was handling much more than a single task. The -responsibility of creating helpers is not central to what View does, and was -moved into HelperCollection. HelperCollection is responsible for loading and -constructing helpers, as well as triggering callbacks on helpers. By default, -View creates a HelperCollection in its constructor, and uses it for subsequent -operations. The HelperCollection for a view can be found at `$this->Helpers` - -The motivations for refactoring this functionality came from a few issues. - -* View being registered in ClassRegistry could cause registry poisoning issues - when requestAction or the EmailComponent were used. -* View being accessible as a global symbol invited abuse. -* Helpers were not self contained. After constructing a helper, you had to - manually construct several other objects in order to get a functioning object. - -You can read more about HelperCollection in the -:doc:`/core-libraries/collections` documentation. - -Deprecated properties ---------------------- - -The following properties on helpers are deprecated, you should use the request -object properties or Helper methods instead of directly accessing these -properties as they will be removed in a future release. - -- ``Helper::$webroot`` is deprecated, use the request object's webroot - property. -- ``Helper::$base`` is deprecated, use the request object's base property. -- ``Helper::$here`` is deprecated, use the request object's here property. -- ``Helper::$data`` is deprecated, use the request object's data property. -- ``Helper::$params`` is deprecated, use the ``$this->request`` instead. - -XmlHelper, AjaxHelper and JavascriptHelper removed --------------------------------------------------- - -The AjaxHelper and JavascriptHelper have been removed as they were deprecated in -version 1.3. The XmlHelper was removed, as it was made obsolete and redundant -with the improvements to :php:class:`Xml`. The ``Xml`` class should be used to -replace previous usage of XmlHelper. - -The AjaxHelper, and JavascriptHelper are replaced with the JsHelper and HtmlHelper. - -JsHelper --------- - -- ``JsBaseEngineHelper`` is now abstract, you will need to implement all the - methods that previously generated errors. - -PaginatorHelper ---------------- - -- ``PaginatorHelper::sort()`` now takes the title and key arguments in the - reverse order. $key will always be first now. This was done to prevent - needing to swap arguments when adding a second one. -- PaginatorHelper had a number of changes to the paging params used internally. - The default key has been removed. -- PaginatorHelper now supports generating links with paging parameters in the - querystring. - -There have been a few improvements to pagination in general. For more -information on that you should read the new pagination features page. - -FormHelper ----------- - -$selected parameter removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The ``$selected`` parameter was removed from several methods in ``FormHelper``. -All methods now support a ``$attributes['value']`` key now which should be used -in place of ``$selected``. This change simplifies the ``FormHelper`` methods, -reducing the number of arguments, and reduces the duplication that ``$selected`` -created. The effected methods are: - -- FormHelper::select() -- FormHelper::dateTime() -- FormHelper::year() -- FormHelper::month() -- FormHelper::day() -- FormHelper::hour() -- FormHelper::minute() -- FormHelper::meridian() - -Default URLs on forms is the current action -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The default url for all forms, is now the current url including passed, named, -and querystring parameters. You can override this default by supplying -``$options['url']`` in the second parameter of ``$this->Form->create()``. - -FormHelper::hidden() -~~~~~~~~~~~~~~~~~~~~ - -Hidden fields no longer remove the class attribute. This means that if there are -validation errors on hidden fields, the ``error-field`` class name will be -applied. - -CacheHelper ------------ - -CacheHelper has been fully decoupled from View, and uses helper callbacks to -generate caches. You should remember to place CacheHelper after other helpers -that modify content in their ``afterRender`` and ``afterLayout`` callbacks. If -you don't some changes will not be part of the cached content. - -CacheHelper also no longer uses ```` to indicate un-cached -regions. Instead it uses special HTML/XML comments. ```` and -````. This helps CacheHelper generate valid markup and still -perform the same functions as before. You can read more CacheHelper and View -changes. - -Helper Attribute format more flexible -------------------------------------- - -The Helper class has more 3 protected attributes: - -* ``Helper::_minimizedAttributes``: array with minimized attributes (ie: - ``array('checked', 'selected', ...)``); -* ``Helper::_attributeFormat``: how attributes will be generated (ie: - ``%s="%s"``); -* ``Helper::_minimizedAttributeFormat``: how minimized attributes will be - generated: (ie ``%s="%s"``) - -By default the values used in CakePHP 1.3 were not changed. But now you can -use boolean attributes from HTML, like ````. To -this, just change ``$_minimizedAttributeFormat`` in your AppHelper to ``%s``. - -To use with Html/Form helpers and others, you can write:: - - $this->Form->checkbox('field', array('checked' => true, 'value' => 'some_value')); - -Other facility is that minimized attributes can be passed as item and not as -key. For example:: - - $this->Form->checkbox('field', array('checked', 'value' => 'some_value')); - -Note that ``checked`` have a numeric key. - -Controller -========== - -- Controller's constructor now takes two parameters. A CakeRequest, and - CakeResponse objects. These objects are used to populate several deprecated - properties and will be set to $request and $response inside the controller. -- ``Controller::$webroot`` is deprecated, use the request object's webroot - property. -- ``Controller::$base`` is deprecated, use the request object's base property. -- ``Controller::$here`` is deprecated, use the request object's here property. -- ``Controller::$data`` is deprecated, use the request object's data property. -- ``Controller::$params`` is deprecated, use the ``$this->request`` instead. -- ``Controller::$Component`` has been moved to ``Controller::$Components``. See - the :doc:`/core-libraries/collections` documentation for more information. -- ``Controller::$view`` has been renamed to ``Controller::$viewClass``. - ``Controller::$view`` is now used to change which view file is rendered. -- ``Controller::render()`` now returns a CakeResponse object. - -The deprecated properties on Controller will be accessible through a ``__get()`` -method. This method will be removed in future versions, so it's recommended that -you update your application. - -Controller now defines a maxLimit for pagination. This maximum limit is set to -100, but can be overridden in the $paginate options. - - -Pagination ----------- - -Pagination has traditionally been a single method in Controller, this created a -number of problems though. Pagination was hard to extend, replace, or modify. For -2.0 pagination has been extracted into a component. :php:meth:`Controller::paginate()` still -exists, and serves as a convenience method for loading and using the -:php:class:`PaginatorComponent`. - -For more information on the new features offered by pagination in 2.0, see the -:doc:`/core-libraries/components/pagination` documentation. - -View -==== - -View no longer registered in ClassRegistry ------------------------------------------- - -The view being registered ClassRegistry invited abuse and affectively created a -global symbol. In 2.0 each Helper receives the current `View` instance in its -constructor. This allows helpers access to the view in a similar fashion as in -the past, without creating global symbols. You can access the view instance at -`$this->_View` in any helper. - -Deprecated properties ---------------------- - -- ``View::$webroot`` is deprecated, use the request object's webroot property. -- ``View::$base`` is deprecated, use the request object's base property. -- ``View::$here`` is deprecated, use the request object's here property. -- ``View::$data`` is deprecated, use the request object's data property. -- ``View::$params`` is deprecated, use the ``$this->request`` instead. -- ``View::$loaded`` has been removed. Use the ``HelperCollection`` to access - loaded helpers. -- ``View::$model`` has been removed. This behavior is now on :php:class:`Helper` -- ``View::$modelId`` has been removed. This behavior is now on - :php:class:`Helper` -- ``View::$association`` has been removed. This behavior is now on - :php:class:`Helper` -- ``View::$fieldSuffix`` has been removed. This behavior is now on - :php:class:`Helper` -- ``View::entity()`` has been removed. This behavior is now on - :php:class:`Helper` -- ``View::_loadHelpers()`` has been removed, used ``View::loadHelpers()`` - instead. -- How ``View::element()`` uses caching has changed, see below for more - information. -- View callbacks have been shifted around, see below for more information -- API for ``View::element()`` has changed. Read here for more info. - -The deprecated properties on View will be accessible through a ``__get()`` -method. This method will be removed in future versions, so it's recommended that -you update your application. - -Removed methods ---------------- - -* ``View::_triggerHelpers()`` Use ``$this->Helpers->trigger()`` instead. -* ``View::_loadHelpers()`` Use ``$this->loadHelpers()`` instead. Helpers now lazy - load their own helpers. - -Added methods -------------- - -* ``View::loadHelper($name, $settings = array());`` Load a single helper. -* ``View::loadHelpers()`` Loads all the helpers indicated in ``View::$helpers``. - -View->Helpers -------------- - -By default View objects contain a :php:class:`HelperCollection` at ``$this->Helpers``. - -Themes ------- - -To use themes in your Controller you no longer set ``public $view = 'Theme';``. -Use ``public $viewClass = 'Theme';`` instead. - -Callback positioning changes ----------------------------- - -beforeLayout used to fire after scripts_for_layout and content_for_layout were -prepared. In 2.0, beforeLayout is fired before any of the special variables are -prepared, allowing you to manipulate them before they are passed to the layout. -The same was done for beforeRender. It is now fired well before any view -variables are manipulated. In addition to these changes, helper callbacks always -receive the name of the file about to be rendered. This combined with helpers -being able to access the view through ``$this->_View`` and the current view -content through ``$this->_View->output`` gives you more power than ever before. - -Helper callback signature changes ---------------------------------- - -Helper callbacks now always get one argument passed in. For beforeRender and -afterRender it is the view file being rendered. For beforeLayout and afterLayout -it is the layout file being rendered. Your helpers function signatures should -look like:: - - public function beforeRender($viewFile) { - - } - - public function afterRender($viewFile) { - - } - - public function beforeLayout($layoutFile) { - - } - - public function afterLayout($layoutFile) { - - } - - -Element caching, and view callbacks have been changed in 2.0 to help provide you -with more flexibility and consistency. :doc:`Read more about those -changes `. - -CacheHelper decoupled ---------------------- - -In previous versions there was a tight coupling between :php:class:`CacheHelper` -and :php:class:`View`. For 2.0 this coupling has been removed and CacheHelper -just uses callbacks like other helpers to generate full page caches. - - -CacheHelper ```` tags changed -------------------------------------------- - -In previous versions, CacheHelper used a special ```` tag as -markers for output that should not be part of the full page cache. These tags -were not part of any XML schema, and were not possible to validate in HTML or -XML documents. For 2.0, these tags have been replaced with HTML/XML comments:: - - becomes - becomes - -The internal code for full page view caches has also changed, so be sure to -clear out view cache files when updating. - -MediaView changes ------------------ - -:php:func:`MediaView::render()` now forces download of unknown file types -instead of just returning false. If you want you provide an alternate download -filename you now specify the full name including extension using key 'name' in -the array parameter passed to the function. - - -PHPUnit instead of SimpleTest -============================= - -All of the core test cases and supporting infrastructure have been ported to use -PHPUnit 3.5. Of course you can continue to use SimpleTest in your application by -replacing the related files. No further support will be given for SimpleTest and -it is recommended that you migrate to PHPUnit as well. For some additional -information on how to migrate your tests see PHPUnit migration hints. - -No more group tests -------------------- - -PHPUnit does not differentiate between group tests and single test cases in the -runner. Because of this, the group test options, and support for old style group -tests has been removed. It is recommended that GroupTests be ported to -``PHPUnit_Framework_Testsuite`` subclasses. You can find several examples of this -in CakePHP's test suite. Group test related methods on ``TestManager`` have also -been removed. - -Testsuite shell ---------------- - -The testsuite shell has had its invocation simplified and expanded. You no -longer need to differentiate between ``case`` and ``group``. It is assumed that -all tests are cases. In the past you would have done -``cake testsuite app case models/post`` you can now do ``cake testsuite app -Model/Post``. - - -The testsuite shell has been refactored to use the PHPUnit cli tool. It now -supports all the command line options supported by PHPUnit. -``cake testsuite help`` will show you a list of all possible modifiers. - -Models -====== - -Model relationships are now lazy loaded. You can run into a situation where -assigning a value to a nonexistent model property will throw errors:: - - $Post->inexistentProperty[] = 'value'; - -will throw the error "Notice: Indirect modification of overloaded property -$inexistentProperty has no effect". Assigning an initial value to the property -solves the issue:: - - $Post->nonexistentProperty = array(); - $Post->nonexistentProperty[] = 'value'; - -Or just declare the property in the model class:: - - class Post { - public $nonexistentProperty = array(); - } - -Either of these approaches will solve the notice errors. - -The notation of ``find()`` in CakePHP 1.2 is no longer supported. Finds should use -notation ``$model->find('type', array(PARAMS))`` in CakePHP 1.3. - -- ``Model::$_findMethods`` is now ``Model::$findMethods``. This property is now - public and can be modified by behaviors. - - - -Database objects ----------------- - -CakePHP 2.0 introduces some changes to Database objects that should not greatly -affect backwards compatibility. The biggest one is the adoption of PDO for -handling database connections. If you are using a vanilla installation of PHP 5 -you will already have installed the needed extensions, but you may need to -activate individual extensions for each driver you wish to use. - -Using PDO across all DBOs let us homogenize the code for each one and provide -more reliable and predictable behavior for all drivers. It also allowed us to -write more portable and accurate tests for database related code. - -The first thing users will probably miss is the "affected rows" and "total rows" -statistics, as they are not reported due to the more performant and lazy design -of PDO, there are ways to overcome this issue but very specific to each -database. Those statistics are not gone, though, but could be missing or even -inaccurate for some drivers. - -A nice feature added after the PDO adoption is the ability to use prepared -statements with query placeholders using the native driver if available. - -List of Changes -~~~~~~~~~~~~~~~ - -* DboMysqli was removed, we will support DboMysql only. -* API for DboSource::execute has changed, it will now take an array of query - values as second parameter:: - - public function execute($sql, $params = array(), $options = array()) - - became:: - - public function execute($sql, $options = array(), $params = array()) - - third parameter is meant to receive options for logging, currently it only - understands the "log" option. - -* DboSource::value() looses its third parameter, it was not used anyways -* DboSource::fetchAll() now accepts an array as second parameter, to pass values - to be bound to the query, third parameter was dropped. Example:: - - $db->fetchAll( - 'SELECT - * from users - WHERE - username = ? - AND - password = ?', - array('jhon', '12345') - ); - $db->fetchAll( - 'SELECT - * from users - WHERE - username = :username - AND - password = :password', - array('username' => 'jhon', 'password' => '12345') - ); - -The PDO driver will automatically escape those values for you. - -* Database statistics are collected only if the "fullDebug" property of the - corresponding DBO is set to true. -* New method DboSource::getConnection() will return the PDO object in case you - need to talk to the driver directly. -* Treatment of boolean values changed a bit to make it more cross-database - friendly, you may need to change your test cases. -* Postgresql support was immensely improved, it now correctly creates schemas, - truncate tables, and is easier to write tests using it. -* DboSource::insertMulti() will no longer accept sql string, just pass an array - of fields and a nested array of values to insert them all at once -* TranslateBehavior was refactored to use model virtualFields, this makes the - implementation more portable. -* All tests cases with Mysql related stuff were moved to the corresponding - driver test case. This left the DboSourceTest file a bit skinny. -* Transaction nesting support. Now it is possible to start a transaction several - times. It will only be committed if the commit method is called the same - amount of times. -* Sqlite support was greatly improved. The major difference with cake 1.3 is - that it will only support Sqlite 3.x . It is a great alternative for - development apps, and quick at running test cases. -* Boolean column values will be casted to php native boolean type automatically, - so make sure you update your test cases and code if you were expecting the - returned value to be a string or an integer: If you had a "published" column in - the past using mysql all values returned from a find would be numeric in the - past, now they are strict boolean values. - -Behaviors -========= - -BehaviorCollection ------------------- - -- ``BehaviorCollection`` no longer ``strtolower()'s`` mappedMethods. Behavior - mappedMethods are now case sensitive. - -AclBehavior and TreeBehavior ----------------------------- - -- No longer supports strings as configuration. Example:: - - public $actsAs = array( - 'Acl' => 'Controlled', - 'Tree' => 'nested' - ); - - became:: - - public $actsAs = array( - 'Acl' => array('type' => 'Controlled'), - 'Tree' => array('type' => 'nested') - ); - - -Plugins -======= - -Plugins no longer magically append their plugin prefix to components, helpers -and models used within them. You must be explicit with the components, models, -and helpers you wish to use. In the past:: - - public $components = array('Session', 'Comments'); - -Would look in the controller's plugin before checking app/core components. It -will now only look in the app/core components. If you wish to use objects from a -plugin you must put the plugin name:: - - public $components = array('Session', 'Comment.Comments'); - -This was done to reduce hard to debug issues caused by magic misfiring. It also -improves consistency in an application, as objects have one authoritative way to -reference them. - -Plugin App Controller and Plugin App Model ------------------------------------------- - -The plugin AppController and AppModel are no longer located directly in the -plugin folder. They are now placed into the plugin's Controller and Model -folders as such:: - - /app - /Plugin - /Comment - /Controller - CommentAppController.php - /Model - CommentAppModel.php - -Console -======= - -Much of the console framework was rebuilt for 2.0 to address many of the -following issues: - -- Tightly coupled. -- It was difficult to make help text for shells. -- Parameters for shells were tedious to validate. -- Plugin tasks were not reachable. -- Objects with too many responsibilities. - -Backwards incompatible Shell API changes ----------------------------------------- - -- ``Shell`` no longer has an ``AppModel`` instance. This ``AppModel`` instance - was not correctly built and was problematic. -- ``Shell::_loadDbConfig()`` has been removed. It was not generic enough to - stay in Shell. You can use the ``DbConfigTask`` if you need to ask the user - to create a db config. -- Shells no longer use ``$this->Dispatcher`` to access stdin, stdout, and - stderr. They have ``ConsoleOutput`` and ``ConsoleInput`` objects to handle - that now. -- Shells lazy load tasks, and use ``TaskCollection`` to provide an interface - similar to that used for Helpers, Components, and Behaviors for on the fly - loading of tasks. -- ``Shell::$shell`` has been removed. -- ``Shell::_checkArgs()`` has been removed. Configure a ``ConsoleOptionParser`` -- Shells no longer have direct access to ``ShellDispatcher``. You should use - the ``ConsoleInput``, and ``ConsoleOutput`` objects instead. If you need to - dispatch other shells, see the section on 'Invoking other shells from your - shell'. - -Backwards incompatible ShellDispatcher API changes --------------------------------------------------- - -- ``ShellDispatcher`` no longer has stdout, stdin, stderr file handles. -- ``ShellDispatcher::$shell`` has been removed. -- ``ShellDispatcher::$shellClass`` has been removed. -- ``ShellDispatcher::$shellName`` has been removed. -- ``ShellDispatcher::$shellCommand`` has been removed. -- ``ShellDispatcher::$shellPaths`` has been removed, use - ``App::path('shells');`` instead. -- ``ShellDispatcher`` no longer uses 'help' as a magic method that has special - status. Instead use the ``--help/-h`` options, and an option parser. - -Backwards incompatible Shell Changes ------------------------------------- - -- Bake's ControllerTask no longer takes ``public`` and ``admin`` as passed - arguments. They are now options, indicated like ``--admin`` and ``--public``. - -It's recommended that you use the help on shells you use to see what if any -parameters have changed. It's also recommended that you read the console new -features for more information on new APIs that are available. - -Debugging -========= - -The ``debug()`` function now defaults to outputting HTML safe strings. This is -disabled if being used in the console. The ``$showHtml`` option for ``debug()`` -can be set to false to disable HTML-safe output from debug. - -ConnectionManager -================= - -``ConnectionManager::enumConnectionObjects()`` will now return the current -configuration for each connection created, instead of an array with filename, -class name and plugin, which wasn't really useful. - -When defining database connections you will need to make some changes to the way -configs were defined in the past. Basically in the database configuration class, -the key "driver" is not accepted anymore, only "datasource", in order to make it -more consistent. Also, as the datasources have been moved to packages you will -need to pass the package they are located in. Example:: - - public $default = array( - 'datasource' => 'Database/Mysql', - 'persistent' => false, - 'host' => 'localhost', - 'login' => 'root', - 'password' => 'root', - 'database' => 'cake', - ); - - -.. meta:: - :title lang=en: 2.0 Migration Guide - :description lang=en: This page summarizes the changes from CakePHP 1.3 that will assist in a project migration to 2.0, as well as for a developer reference to get up to date with the changes made to the core since the CakePHP 1.3 branch. - :keywords lang=en: cakephp upgrade,cakephp migration,migration guide,1.3 to 2.0,update cakephp,backwards compatibility,api changes,x versions,directory structure,new features diff --git a/sr/appendices/2-1-migration-guide.rst b/sr/appendices/2-1-migration-guide.rst deleted file mode 100644 index 759be4a36c..0000000000 --- a/sr/appendices/2-1-migration-guide.rst +++ /dev/null @@ -1,363 +0,0 @@ -2.1 Migration Guide -################### - -CakePHP 2.1 is a fully API compatible upgrade from 2.0. This page outlines the -changes and improvements made for 2.1. - -AppController, AppHelper, AppModel and AppShell -=============================================== - -These classes are now required to be part of the app directory, as they were -removed from the CakePHP core. If you do not already have these classes, you -can use the following while upgrading:: - - // app/View/Helper/AppHelper.php - App::uses('Helper', 'View'); - class AppHelper extends Helper { - } - - // app/Model/AppModel.php - App::uses('Model', 'Model'); - class AppModel extends Model { - } - - // app/Controller/AppController.php - App::uses('Controller', 'Controller'); - class AppController extends Controller { - } - - // app/Console/Command/AppShell.php - App::uses('Shell', 'Console'); - class AppShell extends Shell { - } - -If your application already has these files/classes you don't need to do -anything. -Additionally if you were using the core PagesController, you would need to copy -this to your app/Controller directory as well. - -.htaccess files -=============== - -The default ``.htaccess`` files have changed, you should remember to update them -or update your webservers URL re-writing scheme to match the changes done in -``.htaccess`` - - -Models -====== - -- The ``beforeDelete`` callback will be fired before behaviors beforeDelete callbacks. - This makes it consistent with the rest of the events triggered in the model layer. -- ``Model::find('threaded')`` now accepts ``$options['parent']`` if using other field - then ``parent_id``. Also if the model has TreeBehavior attached and set up with other - parent field, the threaded find will by default use that. -- Parameters for queries using prepared statements will now be part of the SQL - dump. -- Validation arrays can now be more specific with when a field is required. - The ``required`` key now accepts ``create`` and ``update``. These values will - make a field required when creating or updating. -- Model now has a ``schemaName`` property. If your application switches - datasources by modifying :php:attr:`Model::$useDbConfig` you should also - modify ``schemaName`` or use :php:meth:`Model::setDataSource()` method which - handles this for you. - -CakeSession ------------ - -.. versionchanged:: 2.1.1 - CakeSession no longer sets the P3P header, as this is the responsibility of your application. - More info see ticket `#2515 `_ in lighthouse - -Behaviors -========= - -TranslateBehavior ------------------ - -- :php:class:`I18nModel` has been moved into a separate file. - -Exceptions -========== - -The default exception rendering now includes more detailed stack traces -including file excerpts and argument dumps for all functions in the stack. - - -Utility -======= - -Debugger --------- - -- :php:func:`Debugger::getType()` has been added. It can be used to get the type of - variables. -- :php:func:`Debugger::exportVar()` has been modified to create more readable - and useful output. - -debug() -------- - -`debug()` now uses :php:class:`Debugger` internally. This makes it consistent -with Debugger, and takes advantage of improvements made there. - -Set ---- - -- :php:func:`Set::nest()` has been added. It takes in a flat array and returns a nested array - -File ----- - -- :php:meth:`File::info()` includes filesize & mimetype information. -- :php:meth:`File::mime()` was added. - -Cache ------ - -- :php:class:`CacheEngine` has been moved into a separate file. - -Configure ---------- - -- :php:class:`ConfigReaderInterface` has been moved into a separate file. - -App ---- - -- :php:meth:`App::build()` now has the ability to register new packages using - ``App::REGISTER``. See :ref:`app-build-register` for more information. -- Classes that could not be found on configured paths will be searched inside - ``APP`` as a fallback path. This makes autoloading nested directories in - ``app/Vendor`` easier. - -Console -======= - -Test Shell ----------- - -A new TestShell has been added. It reduces the typing required to run unit -tests, and offers a file path based UI:: - - ./Console/cake test app Model/Post - ./Console/cake test app Controller/PostsController - ./Console/cake test Plugin View/Helper/MyHelper - -The old testsuite shell and its syntax are still available. - -General -------- - -- Generated files no longer contain timestamps with the generation datetime. - -Routing -======= - -Router ------- - -- Routes can now use a special ``/**`` syntax to include all trailing arguments - as a single passed argument. See the section on :ref:`connecting-routes` for - more information. -- :php:meth:`Router::resourceMap()` was added. -- :php:meth:`Router::defaultRouteClass()` was added. This method allows you to - set the default route class used for all future routes that are connected. - -Network -======= - -CakeRequest ------------ - -- Added ``is('requested')`` and ``isRequested()`` for detecting requestAction. - -CakeResponse ------------- - -- Added :php:meth:`CakeResponse::cookie()` for setting cookies. -- Added a number of methods for :ref:`cake-response-caching` - -Controller -========== - -Controller ----------- - -- :php:attr:`Controller::$uses` was modified the default value is now ``true`` - instead of false. Additionally different values are handled slightly - differently, but will behave the same in most cases. - - - ``true`` Will load the default model and merge with AppController. - - An array will load those models and merge with AppController. - - An empty array will not load any models other than those declared in the - base class. - - ``false`` will not load any models, and will not merge with the base class - either. - - -Components -========== - -AuthComponent -------------- - -- :php:meth:`AuthComponent::allow()` no longer accepts ``allow('*')`` as a wildcard - for all actions. Just use ``allow()``. This unifies the API between allow() - and deny(). -- ``recursive`` option was added to all authentication adapters. Allows you to - more easily control the associations stored in the session. - - -AclComponent ------------- - -- :php:class:`AclComponent` no longer lowercases and inflects the class name used for - ``Acl.classname``. Instead it uses the provided value as is. -- Acl backend implementations should now be put in ``Controller/Component/Acl``. -- Acl implementations should be moved into the Component/Acl directory from - Component. For example if your Acl class was called ``CustomAclComponent``, - and was in ``Controller/Component/CustomAclComponent.php``. - It should be moved into ``Controller/Component/Acl/CustomAcl.php``, and be - named ``CustomAcl``. -- :php:class:`DbAcl` has been moved into a separate file. -- :php:class:`IniAcl` has been moved into a separate file. -- :php:class:`AclInterface` has been moved into a separate file. - -Helpers -======= - -TextHelper ----------- - -- :php:meth:`TextHelper::autoLink()`, :php:meth:`TextHelper::autoLinkUrls()`, - :php:meth:`TextHelper::autoLinkEmails()` now HTML escape their input by - default. You can control this with the ``escape`` option. - -HtmlHelper ----------- - -- :php:meth:`HtmlHelper::script()` had a ``block`` option added. -- :php:meth:`HtmlHelper::scriptBlock()` had a ``block`` option added. -- :php:meth:`HtmlHelper::css()` had a ``block`` option added. -- :php:meth:`HtmlHelper::meta()` had a ``block`` option added. -- The ``$startText`` parameter of :php:meth:`HtmlHelper::getCrumbs()` can now be - an array. This gives more control and flexibility over the first crumb link. -- :php:meth:`HtmlHelper::docType()` now defaults to HTML5. -- :php:meth:`HtmlHelper::image()` now has a ``fullBase`` option. -- :php:meth:`HtmlHelper::media()` has been added. You can use this method to - create HTML5 audio/video elements. -- :term:`plugin syntax` support has been added for - :php:meth:`HtmlHelper::script()`, :php:meth:`HtmlHelper::css()`, :php:meth:`HtmlHelper::image()`. - You can now easily link to plugin assets using ``Plugin.asset``. -- :php:meth:`HtmlHelper::getCrumbList()` had the ``$startText`` parameter added. - - -View -==== - -- :php:attr:`View::$output` is deprecated. -- ``$content_for_layout`` is deprecated. Use ``$this->fetch('http://github.com/content');`` - instead. -- ``$scripts_for_layout`` is deprecated. Use the following instead:: - - echo $this->fetch('http://github.com/meta'); - echo $this->fetch('http://github.com/css'); - echo $this->fetch('http://github.com/script'); - - ``$scripts_for_layout`` is still available, but the :ref:`view blocks ` API - gives a more extensible & flexible replacement. -- The ``Plugin.view`` syntax is now available everywhere. You can use this - syntax anywhere you reference the name of a view, layout or element. -- The ``$options['plugin']`` option for :php:meth:`~View::element()` is - deprecated. You should use ``Plugin.element_name`` instead. - -Content type views ------------------- - -Two new view classes have been added to CakePHP. A new :php:class:`JsonView` -and :php:class:`XmlView` allow you to easily generate XML and JSON views. You -can learn more about these classes in the section on -:doc:`/views/json-and-xml-views` - -Extending views ---------------- - -:php:class:`View` has a new method allowing you to wrap or 'extend' a -view/element/layout with another file. See the section on -:ref:`extending-views` for more information on this feature. - -Themes ------- - -The ``ThemeView`` class is deprecated in favor of the ``View`` class. Simply -setting ``$this->theme = 'MyTheme'`` will enable theme support, and all custom -View classes which extend from ``ThemeView`` should extend ``View``. - -View blocks ------------ - -View blocks are a flexible way to create slots or blocks in your views. Blocks -replace ``$scripts_for_layout`` with a more robust and flexible API. See the -section on :ref:`view-blocks` for more information. - - -Helpers -======= - -New callbacks -------------- - -Two new callbacks have been added to Helpers. -:php:meth:`Helper::beforeRenderFile()` and :php:meth:`Helper::afterRenderFile()` -these new callbacks are fired before/after every view fragment is rendered. -This includes elements, layouts and views. - -CacheHelper ------------ - -- ```` tags now work inside elements correctly. - -FormHelper ----------- - -- FormHelper now omits disabled fields from the secured fields hash. This makes - working with :php:class:`SecurityComponent` and disabled inputs easier. -- The ``between`` option when used in conjunction with radio inputs, now behaves - differently. The ``between`` value is now placed between the legend and first - input elements. -- The ``hiddenField`` option with checkbox inputs can now be set to a specific - value such as 'N' rather than just 0. -- The ``for`` attribute for date + time inputs now reflects the first generated - input. This may result in the for attribute changing for generated datetime - inputs. -- The ``type`` attribute for :php:meth:`FormHelper::button()` can be removed now. It still - defaults to 'submit'. -- :php:meth:`FormHelper::radio()` now allows you to disable all options. - You can do this by setting either ``'disabled' => true`` or ``'disabled' => 'disabled'`` - in the ``$attributes`` array. - -PaginatorHelper ---------------- - -- :php:meth:`PaginatorHelper::numbers()` now has a ``currentClass`` option. - - -Testing -======= - -- Web test runner now displays the PHPUnit version number. -- Web test runner now defaults to displaying app tests. -- Fixtures can be created in different datasources other than $test. -- Models loaded using the ClassRegistry and using another datasource will get - their datasource name prepended with ``test_`` (e.g datasource `master` will - try to use `test_master` in the testsuite) -- Test cases are generated with class specific setup methods. - -Events -====== - -- A new generic events system has been built and it replaced the way callbacks - were dispatched. This should not represent any change to your code. -- You can dispatch your own events and attach callbacks to them at will, useful - for inter-plugin communication and easier decoupling of your classes. diff --git a/sr/appendices/2-2-migration-guide.rst b/sr/appendices/2-2-migration-guide.rst deleted file mode 100644 index 441eb6cfe9..0000000000 --- a/sr/appendices/2-2-migration-guide.rst +++ /dev/null @@ -1,327 +0,0 @@ -2.2 Migration Guide -################### - -CakePHP 2.2 is a fully API compatible upgrade from 2.0/2.1. This page outlines the -changes and improvements made for 2.2. - -.. _required-steps-to-upgrade-2-2: - -Required steps to upgrade -========================= - -When upgrading to CakePHP 2.2 its important to add a few new configuration -values to ``app/Config/bootstrap.php``. Adding these will ensure consistent -behavior with 2.1.x:: - - // Enable the Dispatcher filters for plugin assets, and - // CacheHelper. - Configure::write('Dispatcher.filters', array( - 'AssetDispatcher', - 'CacheDispatcher' - )); - - // Add logging configuration. - CakeLog::config('debug', array( - 'engine' => 'FileLog', - 'types' => array('notice', 'info', 'debug'), - 'file' => 'debug', - )); - CakeLog::config('error', array( - 'engine' => 'FileLog', - 'types' => array('warning', 'error', 'critical', 'alert', 'emergency'), - 'file' => 'error', - )); - -You will also need to modify ``app/Config/core.php``. Change the value of -:php:const:`LOG_ERROR` to :php:const:`LOG_ERR`:: - - define('LOG_ERROR', LOG_ERR); - -When using ``Model::validateAssociated()`` or ``Model::saveAssociated()`` and -primary model validation fails, the validation errors of associated models are no longer wiped out. -``Model::$validationErrors`` will now always show all the errors. -You might need to update your test cases to reflect this change. - -Console -======= - -I18N extract shell ------------------- - -- An option was added to overwrite existing POT files by default:: - - ./Console/cake i18n extract --overwrite - - -Models -====== - -- ``Model::find('count')`` will now call the custom find methods with - ``$state = 'before'`` and ``$queryData['operation'] = 'count'``. - In many cases custom finds already return correct counts for pagination, - but ``'operation'`` key allows more flexibility to build other queries, - or drop joins which are required for the custom finder itself. - As the pagination of custom find methods never worked quite well it required - workarounds for this in the model level, which are now no longer needed. -- ``Model::find('first')`` will now return an empty array when no records are found. - -Datasources -=========== - -- Dbo datasources now supports real nested transactions. If you need to use this - feature in your application, enable it using - ``ConnectionManager::getDataSource('default')->useNestedTransactions = true;`` - -Testing -======= - -- The webrunner now includes links to re-run a test with debug output. -- Generated test cases for Controller now subclass - :php:class:`ControllerTestCase`. - - -Error Handling -============== - -- When repeat exceptions, or exception are raised when rendering error pages, - the new ``error`` layout will be used. It's recommended to not use additional - helpers in this layout as its intended for development level errors only. This - fixes issues with fatal errors in rendering error pages due to helper usage in - the ``default`` layout. -- It is important to copy the ``app/View/Layouts/error.ctp`` into your app - directory. Failing to do so will make error page rendering fail. -- You can now configure application specific console error handling. By setting - ``Error.consoleHandler``, and ``Exception.consoleHandler`` you can define the - callback that will handle errors/exceptions raised in console applications. -- The handler configured in ``Error.handler`` and ``Error.consoleHandler`` will - receive fatal error codes (ie. ``E_ERROR``, ``E_PARSE``, ``E_USER_ERROR``). - -Exceptions ----------- - -- The :php:class:`NotImplementedException` was added. - - -Core -==== - -Configure ---------- - -- :php:meth:`Configure::dump()` was added. It is used to persist configuration - data in durable storage like files. Both :php:class:`PhpReader` and - :php:class:`IniReader` work with it. -- A new config parameter 'Config.timezone' is available in which you can set - users' timezone string. eg. You can do ``Configure::write('Config.timezone', - 'Europe/Paris')``. If a method of ``CakeTime`` class is called with - ``$timezone`` parameter as null and 'Config.timezone' is set, then the value - of 'Config.timezone' will be used. This feature allows you to set users' - timezone just once instead of passing it each time in function calls. - - -Controller -========== - -AuthComponent -------------- - -- The options for adapters defined in :php:attr:`AuthComponent::$authenticate` - now accepts a ``contain`` option. This is used to set containable options for - when user records are loaded. - -CookieComponent ---------------- - -- You can now encrypt cookie values with the rijndael cipher. This requires - the `mcrypt `_ extension to be installed. Using - rijndael gives cookie values actual encryption, and is recommended in place of - the XOR cipher available in previous releases. The XOR cipher is still the - default cipher scheme to maintain compatibility with previous releases. You - can read more in the :php:meth:`Security::rijndael()` documentation. - -Pagination -========== - -- Paginating custom finders will now return correct counts, see Model changes - for more info. - - -Network -======= - -CakeEmail ---------- - -- :php:meth:`CakeEmail::charset()` and :php:meth:`CakeEmail::headerCharset()` - were added. -- Legacy Japanese encodings are now handled correctly. ``ISO-2202-JP`` is used - when the encoding is ``ISO-2202-JP-MS`` which works around a number of issues - in mail clients when dealing with the CP932 and Shift_JIS encodings. -- :php:meth:`CakeEmail::theme()` was added. -- :php:meth:`CakeEmail::domain()` was added. You can use this method to set the - domain name used when sending email from a CLI script or if you want to - control the hostname used to send email. -- You can now define ``theme`` and ``helpers`` in your EmailConfig class. - -CakeRequest ------------ - -- CakeRequest will now automatically decode - ``application/x-www-form-urlencoded`` request bodies on ``PUT`` and ``DELETE`` - requests. This data will be available as ``$this->data`` just like POST data - is. - -Utility -======= - -Set ---- - -- The :php:class:`Set` class is now deprecated, and replaced by the :php:class:`Hash` class. - Set will not be removed until 3.0. -- :php:meth:`Set::expand()` was added. - -Hash ----- - -The :php:class:`Hash` class was added in 2.2. It replaced Set providing a more -consistent, reliable and performant API to doing many of the same tasks Set -does. See the :doc:`/core-utility-libraries/hash` page for more detail. - -CakeTime --------- - -- The ``$userOffset`` parameter has been replaced with ``$timezone`` parameter - in all relevant functions. So instead of numeric offset you can now pass in a - timezone string or DateTimeZone object. Passing numeric offsets for - ``$timezone`` parameter is still possible for backwards compatibility. -- :php:meth:`CakeTime::timeAgoInWords()` had the ``accuracy`` option added. - This option allows you to specify how accurate formatted times should be. - -- New methods added: - - * :php:meth:`CakeTime::toServer()` - * :php:meth:`CakeTime::timezone()` - * :php:meth:`CakeTime::listTimezones()` - -- The ``$dateString`` parameter in all methods now accepts a DateTime object. - - -Helpers -======= - -FormHelper ----------- - -- FormHelper now better handles adding required classes to inputs. It now - honors the ``on`` key. -- :php:meth:`FormHelper::radio()` now supports an ``empty`` which works similar - to the empty option on ``select()``. -- Added :php:meth:`FormHelper::inputDefaults()` to set common properties for - each of the inputs generated by the helper - -TimeHelper ----------- - -- Since 2.1, TimeHelper uses the CakeTime class for all its relevant methods. - The ``$userOffset`` parameter has been replaced with ``$timezone`` parameter. -- :php:meth:`TimeHelper::timeAgoInWords()` has the ``element`` option added. - This allows you to specify an HTML element to wrap the formatted time. - -HtmlHelper ----------- - -- :php:meth:`HtmlHelper::tableHeaders()` now supports setting attributes per - table cell. - - -Routing -======= - -Dispatcher ----------- - -- Event listeners can now be attached to the dispatcher calls, those will have - the ability to change the request information or the response before it is - sent to the client. Check the full documentation for this new features in - :doc:`/development/dispatch-filters` -- With the addition of :doc:`/development/dispatch-filters` you'll need to - update ``app/Config/bootstrap.php``. See - :ref:`required-steps-to-upgrade-2-2`. - -Router ------- - -- :php:meth:`Router::setExtensions()` has been added. With the new method you can - now add more extensions to be parsed, for example within a plugin routes file. - -Cache -===== - -Redis Engine ------------- - -A new caching engine was added using the `phpredis extension -`_ it is configured similarly to the -Memcache engine. - -Cache groups ------------- - -It is now possible to tag or label cache keys under groups. This makes it -simpler to mass-delete cache entries associated to the same label. Groups are -declared at configuration time when creating the cache engine:: - - Cache::config(array( - 'engine' => 'Redis', - ... - 'groups' => array('post', 'comment', 'user') - )); - -You can have as many groups as you like, but keep in mind they cannot be -dynamically modified. - -The :php:meth:`Cache::clearGroup()` class method was added. It takes the group -name and deletes all entries labeled with the same string. - -Log -=== - -Changes in :php:class:`CakeLog` now require, some additional configuration in -your ``app/Config/bootstrap.php``. See :ref:`required-steps-to-upgrade-2-2`, -and :doc:`/core-libraries/logging`. - -- The :php:class:`CakeLog` class now accepts the same log levels as defined in - `RFC 5424 `_. Several convenience - methods have also been added: - - * :php:meth:`CakeLog::emergency($message, $scope = array())` - * :php:meth:`CakeLog::alert($message, $scope = array())` - * :php:meth:`CakeLog::critical($message, $scope = array())` - * :php:meth:`CakeLog::error($message, $scope = array())` - * :php:meth:`CakeLog::warning($message, $scope = array())` - * :php:meth:`CakeLog::notice($message, $scope = array())` - * :php:meth:`CakeLog::info($message, $scope = array())` - * :php:meth:`CakeLog::debug($message, $scope = array())` - -- A third argument ``$scope`` has been added to :php:meth:`CakeLog::write`. - See :ref:`logging-scopes`. -- A new log engine: :php:class:`ConsoleLog` has been added. - -Model Validation -================ - -- A new object ``ModelValidator`` was added to delegate the work of validating - model data, it should be transparent to the application and fully backwards - compatible. It also exposes a rich API to add, modify and remove validation - rules. Check docs for this object in :doc:`/models/data-validation`. - -- Custom validation functions in your models need to have "public" visibility - so that they are accessible by ``ModelValidator``. - -- New validation rules added: - - * :php:meth:`Validation::naturalNumber()` - * :php:meth:`Validation::mimeType()` - * :php:meth:`Validation::uploadError()` - diff --git a/sr/appendices/2-3-migration-guide.rst b/sr/appendices/2-3-migration-guide.rst deleted file mode 100644 index 2c47db0d9c..0000000000 --- a/sr/appendices/2-3-migration-guide.rst +++ /dev/null @@ -1,326 +0,0 @@ -2.3 Migration Guide -################### - -CakePHP 2.3 is a fully API compatible upgrade from 2.2. This page outlines -the changes and improvements made in 2.3. - -Constants -========= - -An application can now easily define :php:const:`CACHE` and :php:const:`LOGS`, -as they are conditionally defined by CakePHP now. - -Caching -======= - -- FileEngine is always the default cache engine. In the past a number of people - had difficulty setting up and deploying APC correctly both in cli + web. - Using files should make setting up CakePHP simpler for new developers. - -- `Configure::write('Cache.viewPrefix', 'YOURPREFIX');` has been added to `core.php` - to allow multiple domains/languages per setup. - -Component -========= - -AuthComponent -------------- -- A new property ``AuthComponent::$unauthorizedRedirect`` has been added. - - - For default ``true`` value user is redirected to referrer URL upon authorization failure. - - If set to a string or array user is redirected to that URL. - - If set to false a ForbiddenException exception is thrown instead of redirecting. - -- A new authenticate adapter has been added to support blowfish/bcrypt hashed - passwords. You can now use ``Blowfish`` in your ``$authenticate`` array to - allow bcrypt passwords to be used. - -- :php:meth:`AuthComponent::redirect()` has been deprecated. - Use :php:meth:`AuthComponent::redirectUrl()` instead. - -PaginatorComponent ------------------- - -- PaginatorComponent now supports the ``findType`` option. This can be used to - specify what find method you want used for pagination. This is a bit easier - to manage and set than the 0'th index. - -- PaginatorComponent now throws a `NotFoundException` when trying to access a page - which is out of range (i.e. requested page is greater than total page count). - -SecurityComponent ------------------ - -- SecurityComponent now supports the ``unlockedActions`` option. This can be used to - disable all security checks for any actions listed in this option. - -RequestHandlerComponent ------------------------ - -- :php:meth:`RequestHandlerComponent::viewClassMap()` has been added, which is used to map a type - to view class name. You can add ``$settings['viewClassMap']`` for automatically setting - the correct viewClass based on extension/content type. - -CookieComponent ---------------- - -- :php:meth:`CookieComponent::check()` was added. This method works the same as - :php:meth:`CakeSession::check()` does. - -Console -======= - -- The ``server`` shell was added. You can use this to start the PHP5.4 - webserver for your CakePHP application. -- Baking a new project now sets the application's cache prefix to the name of - the application. - -I18n -==== - -L10n ----- - -- ``nld`` is now the default locale for Dutch as specified by ISO 639-3 and ``dut`` its alias. - The locale folders have to be adjusted accordingly (from `/Locale/dut/` to `/Locale/nld/`). -- Albanian is now ``sqi``, Basque is now ``eus``, Chinese is now ``zho``, Tibetan is now ``bod``, - Czech is now ``ces``, Farsi is now ``fas``, French is now ``fra``, Icelandic is now ``isl``, - Macedonian is now ``mkd``, Malaysian is now ``msa``, Romanian is now ``ron``, Serbian is now ``srp`` - and Slovak is now ``slk``. The corresponding locale folders have to be adjusted, as well. - -Core -==== - -CakePlugin ----------- - -- :php:meth:`CakePlugin::load()` can now take a new ``ignoreMissing`` option. Setting it to true will - prevent file include errors when you try to load routes or bootstrap but they don't exist for a plugin. - So essentially you can now use the following statement which will load all plugins and their routes and - bootstrap for whatever plugin it can find:: - ``CakePlugin::loadAll(array(array('routes' => true, 'bootstrap' => true, 'ignoreMissing' => true)))`` - - -Configure ---------- - -- :php:meth:`Configure::check()` was added. This method works the same as - :php:meth:`CakeSession::check()` does. - -- :php:meth:`ConfigReaderInterface::dump()` was added. Please ensure any custom readers you have now - implement a ``dump()`` method. - -- The ``$key`` parameter of :php:meth:`IniReader::dump()` now supports keys like `PluginName.keyname` - similar to ``PhpReader::dump()``. - -Error -===== - -Exceptions ----------- - -- CakeBaseException was added, which all core Exceptions now extend. The base exception - class also introduces the ``responseHeader()`` method which can be called on created Exception instances - to add headers for the response, as Exceptions don't reuse any response instance. - -Model -===== - -- Support for the biginteger type was added to all core datasources, and - fixtures. -- Support for ``FULLTEXT`` indexes was added for the MySQL driver. - - -Models ------- - -- ``Model::find('list')`` now sets the ``recursive`` based on the max - containment depth or recursive value. When list is used with - ContainableBehavior. -- ``Model::find('first')`` will now return an empty array when no records are found. - -Validation ----------- - -- Missing validation methods will **always** trigger errors now instead of - only in development mode. - -Network -======= - -SmtpTransport -------------- - -- TLS/SSL support was added for SMTP connections. - -CakeRequest ------------ - -- :php:meth:`CakeRequest::onlyAllow()` was added. -- :php:meth:`CakeRequest::query()` was added. - -CakeResponse ------------- - -- :php:meth:`CakeResponse::file()` was added. -- The content types `application/javascript`, `application/xml`, - `application/rss+xml` now also send the application charset. - -CakeEmail ---------- - -- The ``contentDisposition`` option was added to - :php:meth:`CakeEmail::attachments()`. This allows you to disable the - Content-Disposition header added to attached files. - -HttpSocket ----------- - -- :php:class:`HttpSocket` now verifies SSL certificates by default. If you are - using self-signed certificates or connecting through proxies you may need to - use some of the new options to augment this behavior. See - :ref:`http-socket-ssl-options` for more information. -- ``HttpResponse`` was renamed to ``HttpSocketResponse``. This - avoids a common issue with the HTTP PECL extension. There is an - ``HttpResponse`` class provided as well for compatibility reasons. - -Routing -======= - -Router ------- - -- Support for ``tel:``, ``sms:`` were added to :php:meth:`Router::url()`. - -View -==== - -- MediaView is deprecated, and you can use new features in - :php:class:`CakeResponse` to achieve the same results. -- Serialization in Json and Xml views has been moved to ``_serialize()`` -- beforeRender and afterRender callbacks are now being called in Json and Xml - views when using view templates. -- :php:meth:`View::fetch()` now has a ``$default`` argument. This argument can - be used to provide a default value should a block be empty. -- :php:meth:`View::prepend()` has been added to allow prepending content to - existing block. -- :php:class:`XmlView` now uses the ``_rootNode`` view variable to customize the - top level XML node. -- :php:meth:`View::elementExists()` was added. You can use this method to check - if elements exist before using them. -- :php:meth:`View::element()` had the ``ignoreMissing`` option added. You can - use this to suppress the errors triggered by missing view elements. -- :php:meth:`View::startIfEmpty()` was added. - -Layout ------- - -- The doctype for layout files in the app folder and the bake templates in the - cake package has been changed from XHTML to HTML5. - -Helpers -======= - -- New property ``Helper::$settings`` has been added for your helper setting. The - ``$settings`` parameter of ``Helper::__construct()`` is merged with - ``Helper::$settings``. - -FormHelper ----------- - -- :php:meth:`FormHelper::select()` now accepts a list of values in the disabled - attribute. Combined with ``'multiple' => 'checkbox'``, this allows you to - provide a list of values you want disabled. -- :php:meth:`FormHelper::postLink()` now accepts a ``method`` key. This allows - you to create link forms using HTTP methods other than POST. -- When creating inputs with :php:meth:`FormHelper::input()` you can now set the - ``errorMessage`` option to false. This will disable the error message display, - but leave the error class names intact. -- The FormHelper now also adds the HTML5 ``required`` attribute to your input - elements based on validation rules for a field. If you have a "Cancel" button - in your form which submits the form then you should add ``'formnovalidate' => true`` - to your button options to prevent the triggering of validation in HTML. You - can also prevent the validation triggering for the whole form by adding - ``'novalidate' => true`` in your FormHelper::create() options. -- :php:meth:`FormHelper::input()` now generates input elements of type ``tel`` - and ``email`` based on field names if ``type`` option is not specified. - -HtmlHelper ----------- - -- :php:meth:`HtmlHelper::getCrumbList()` now has the ``separator``, - ``firstClass`` and ``lastClass`` options. These allow you to better control - the HTML this method generates. - -TextHelper ----------- - -- :php:meth:`TextHelper::tail()` was added to truncate text starting from the end. -- `ending` in :php:meth:`TextHelper::truncate()` is deprecated in favor of `ellipsis` - -PaginatorHelper ---------------- - -- :php:meth:`PaginatorHelper::numbers()` now has a new option ``currentTag`` to - allow specifying extra tag for wrapping current page number. -- For methods: :php:meth:`PaginatorHelper::prev()` and :php:meth:`PaginatorHelper::next()` it - is now possible to set the ``tag`` option to ``false`` to disable the wrapper. - Also a new option `disabledTag` has been added for these two methods. - - -Testing -======= - -- A core fixture for the default ``cake_sessions`` table was added. You can use - it by adding ``core.cake_sessions`` to your fixture list. -- :php:meth:`CakeTestCase::getMockForModel()` was added. This simplifies getting - mock objects for models. - -Utility -======= - -CakeNumber ----------- - -- :php:meth:`CakeNumber::fromReadableSize()` was added. -- :php:meth:`CakeNumber::formatDelta()` was added. -- :php:meth:`CakeNumber::defaultCurrency()` was added. - -Folder ------- - -- :php:meth:`Folder::copy()` and :php:meth:`Folder::move()` now support the - ability to merge the target and source directories in addition to - skip/overwrite. - - -String ------- - -- :php:meth:`String::tail()` was added to truncate text starting from the end. -- `ending` in :php:meth:`String::truncate()` is deprecated in favor of `ellipsis` - -Debugger --------- - -- :php:meth:`Debugger::exportVar()` now outputs private and protected properties - in PHP >= 5.3.0. - -Security --------- - -- Support for `bcrypt `_ - was added. See the :php:class:`Security::hash()` documentation for more - information on how to use bcrypt. - -Validation ----------- - -- :php:meth:`Validation::fileSize()` was added. - -ObjectCollection ----------------- - -- :php:meth:`ObjectCollection::attached()` was deprecated in favor of the new - method :php:meth:`ObjectCollection::loaded()`. This unifies the access to the - ObjectCollection as load()/unload() already replaced attach()/detach(). diff --git a/sr/appendices/2-4-migration-guide.rst b/sr/appendices/2-4-migration-guide.rst deleted file mode 100644 index 73094f212a..0000000000 --- a/sr/appendices/2-4-migration-guide.rst +++ /dev/null @@ -1,294 +0,0 @@ -2.4 Migration Guide -################### - -CakePHP 2.4 is a fully API compatible upgrade from 2.3. This page outlines -the changes and improvements made in 2.4. - -Console -======= - -- Logged notice messages will now be colourized in terminals that support - colours. -- ConsoleShell is now deprecated. - -SchemaShell ------------ - -- ``cake schema generate`` now supports the ``--exclude`` parameter. -- The constant ``CAKEPHP_SHELL`` is now deprecated and will be removed in CakePHP 3.0. - -BakeShell ---------- - -- ``cake bake model`` now supports baking ``$behaviors``. Finding `lft`, `rght` and `parent_id` fields - in your table it will add the Tree behavior, for example. You can also extend the ModelTask to support your own - behaviors to be recognized. -- ``cake bake`` for views, models, controllers, tests and fixtures now supports a ``-f`` or ``--force`` parameter to - force overwriting of files. -- Tasks in core can now be aliased in the same way you would Helpers, Components and Behaviors - -FixtureTask ------------ - -- ``cake bake fixture`` now supports a ``--schema`` parameter to allow baking all fixtures with noninteractive "all" - while using schema import. - -Core -==== - -Constants ---------- - -- Constants ``IMAGES_URL``, ``JS_URL``, ``CSS_URL`` have been deprecated and - replaced with config variables ``App.imageBaseUrl``, ``App.jsBaseUrl``, - ``App.cssBaseUrl`` respectively. - -- Constants ``IMAGES``, ``JS``, ``CSS`` have been deprecated. - -Object ------- - -- :php:meth:`Object::log()` had the ``$scope`` parameter added. - - -Components -========== - -AuthComponent -------------- -- AuthComponent now supports proper stateless mode when using 'Basic' or 'Digest' - authenticators. Starting of session can be prevented by setting :php:attr:`AuthComponent::$sessionKey` - to false. Also now when using only 'Basic' or 'Digest' you are no longer - redirected to login page. For more info check the :php:class:`AuthComponent` page. -- Property :php:attr:`AuthComponent::$authError` can be set to boolean ``false`` to suppress flash message from being displayed. - -PasswordHasher --------------- -- Authenticating objects now use new password hasher objects for password hash - generation and checking. See :ref:`hashing-passwords` for more info. - -DbAcl ------ - -- DbAcl now uses ``INNER`` joins instead of ``LEFT`` joins. This improves - performance for some database vendors. - -Model -===== - -Models ------- - -- :php:meth:`Model::save()`, :php:meth:`Model::saveField()`, :php:meth:`Model::saveAll()`, - :php:meth:`Model::saveAssociated()`, :php:meth:`Model::saveMany()` - now take a new ``counterCache`` option. You can set it to false to avoid - updating counter cache values for the particular save operation. -- :php:meth:`Model::clear()` was added. - -Datasource ----------- - -- Mysql, Postgres, and SQLserver now support a 'settings' array in the - connection definition. This key => value pair will be issued as ``SET`` commands when the - connection is created. -- Mysql driver now supports SSL options. - - -View -==== - -JsonView --------- - -- JSONP support has been added to :php:class:`JsonView`. -- The ``_serialize`` key now supports renaming serialized variables. -- When debug > 0 JSON will be pretty printed. - -XmlView -------- - -- The ``_serialize`` key now supports renaming serialized variables. -- When debug > 0 XML will be pretty printed. - -HtmlHelper ----------- - -- The API for :php:meth:`HtmlHelper::css()` has been been simplified. You can - now provide an array of options as the second argument. When you do, the - ``rel`` attribute defaults to 'stylesheet'. -- New option ``escapeTitle`` added to :php:meth:`HtmlHelper::link()` to control - escaping of only link title and not attributes. - -TextHelper ----------- - -- :php:meth:`TextHelper::autoParagraph()` has been added. It allows to - automatically convert text into HTML paragraphs. - -PaginatorHelper ---------------- - -- :php:meth:`PaginatorHelper::param()` has been added. -- The first page no longer contains ``/page:1`` or ``?page=1`` in the URL. This helps prevent - duplicate content issues where you would need to use canonical or noindex otherwise. - -FormHelper ----------- - -- The ``round`` option was added to :php:meth:`FormHelper::dateTime()`. Can be set to ``up`` or ``down`` - to force rounding in either direction. Defaults to null which rounds half up according to ``interval``. - -Network -======= - -CakeRequest ------------ - -- :php:meth:`CakeRequest::param()` has been added. -- :php:meth:`CakeRequest::is()` has been modified to support an array of types and will return true if the request matches any type. -- :php:meth:`CakeRequest::isAll()` has been added to check that a request matches all the given types. - -CakeResponse ------------- - -- :php:meth:`CakeResponse::location()` has been added to get or set the redirect location header. - -CakeEmail ---------- - -- Logged email messages now have the scope of ``email`` by default. If you are - not seeing email contents in your logs, be sure to add the ``email`` scope to - your logging configuration. -- :php:meth:`CakeEmail::emailPattern()` was added. This method can be used to - relax email validation rules. This is useful when dealing with certain - Japanese hosts that allow non-compliant addresses to be used. -- :php:meth:`CakeEmail::attachments()` now allows you to provide the file - contents directly using the ``data`` key. -- Configuration data is now correctly merged with transport classes. - -HttpSocket ----------- - -- :php:meth:`HttpSocket::patch()` has been added. - - -I18n -==== - -L10n ----- - -- ``ell`` is now the default locale for Greek as specified by ISO 639-3 and ``gre`` its alias. - The locale folders have to be adjusted accordingly (from `/Locale/gre/` to `/Locale/ell/`). -- ``fas`` is now the default locale for Farsi as specified by ISO 639-3 and ``per`` its alias. - The locale folders have to be adjusted accordingly (from `/Locale/per/` to `/Locale/fas/`). -- ``sme`` is now the default locale for Sami as specified by ISO 639-3 and ``smi`` its alias. - The locale folders have to be adjusted accordingly (from `/Locale/smi/` to `/Locale/sme/`). -- ``mkd`` replaces ``mk`` as default locale for Macedonian as specified by ISO 639-3. - The corresponding locale folders have to be adjusted, as well. -- Catalog code ``in`` has been dropped in favor of ``id`` (Indonesian), - ``e`` has been dropped in favor of ``el`` (Greek), - ``n`` has been dropped in favor of ``nl`` (Dutch), - ``p`` has been dropped in favor of ``pl`` (Polish), - ``sz`` has been dropped in favor of ``se`` (Sami). -- Kazakh has been added with ``kaz`` as locale and ``kk`` as catalog code. -- Kalaallisut has been added with ``kal`` as locale and ``kl`` as catalog code. -- The constant ``DEFAULT_LANGUAGE`` has been deprecated in favor of Configure value ``Config.language``. - -Logging -======= - -- Log engines do not need the suffix ``Log`` anymore in their setup configuration. So for the - FileLog engine it suffices to define ``'engine' => 'File'`` now. This unifies the way engines - are named in configuration (see Cache engines for example). - Note: If you have a Log engine like ``DatabaseLogger`` that does not follow the convention to - use a suffix ``Log`` for your class name you have to adjust your class name to ``DatabaseLog``. - You should also avoid class names like ``SomeLogLog`` which include the suffix twice at the end. - -FileLog -------- - -- Two new config options ``size`` and ``rotate`` have been added for :ref:`FileLog ` engine. -- In debug mode missing directories will now be automatically created to avoid unnecessary errors thrown. - -SyslogLog ---------- - -- The new logging engine :ref:`SyslogLog ` was added to stream messages to syslog. - -Cache -===== - -FileEngine ----------- - -- In debug mode missing directories will now be automatically created to avoid unnecessary errors thrown. - -Utility -======= - -General -------- - -- :php:func:`pr()` no longer outputs HTML when running in cli mode. - -Sanitize --------- - -- ``Sanitize`` class has been deprecated. - -Validation ----------- - -- :php:meth:`Validation::date()` now supports the ``y`` and ``ym`` formats. -- The country code of :php:meth:`Validation::phone()` for Canada has been changed from ``can`` to - ``ca`` to unify the country codes for validation methods according to ISO 3166 (two letter codes). - -CakeNumber ----------- - -- The currencies ``AUD``, ``CAD`` and ``JPY`` have been added. -- The symbols for ``GBP`` and ``EUR`` are now UTF-8. If you upgrade a non-UTF-8 application, - make sure that you update the static ``$_currencies`` attribute with the appropriate - HTML entity symbols (``£`` and ``€``) before you use those currencies. -- The ``fractionExponent`` option was added to - :php:meth:`CakeNumber::currency()`. - -CakeTime --------- - -- :php:meth:`CakeTime::isPast()` and :php:meth:`CakeTime::isFuture()` were - added. -- :php:meth:`CakeTime::timeAgoInWords()` has two new options to customize the output strings: - ``relativeString`` (defaults to ``%s ago``) and ``absoluteString`` (defaults to ``on %s``). -- :php:meth:`CakeTime::timeAgoInWords()` uses fuzzy terms when time is below thresholds. - - -Xml ---- - -- New option ``pretty`` has been added to :php:meth:`Xml::fromArray()` to return nicely formatted Xml - - -Error -===== - -ErrorHandler ------------- - -- New configuration option ``skipLog`` has been added, to allow skipping certain - Exception types to be logged. ``Configure::write('Exception.skipLog', array('NotFoundException', 'ForbiddenException'));`` - will avoid these exceptions and the ones extending them to be be logged when - ``'Exception.log'`` config is ``true`` - -Routing -======= - -Router ------- - -- :php:meth:`Router::fullBaseUrl()` was added together with ``App.fullBaseUrl`` Configure value. They replace - :php:const:`FULL_BASE_URL` which is now deprecated. -- :php:meth:`Router::parse()` now parses query string arguments. - - diff --git a/sr/appendices/cakephp-development-process.rst b/sr/appendices/cakephp-development-process.rst deleted file mode 100644 index a2fc2f09b9..0000000000 --- a/sr/appendices/cakephp-development-process.rst +++ /dev/null @@ -1,56 +0,0 @@ -CakePHP Development Process -########################### - -Here we attempt to explain the process we use when developing the -CakePHP framework. We rely heavily on community interaction through -tickets and IRC chat. IRC is the best place to find members of the -`development team `_ and discuss -ideas, the latest code, and make general comments. If something more -formal needs to be proposed or there is a problem with a release, the -ticket system is the best place to share your thoughts. - -We currently maintain 4 versions of CakePHP. - -- **stable** : Tagged releases intended for production where stability - is more important than features. Issues filed against these releases - will be fixed in the related branch, and be part of the next release. -- **maintenance branch** : Development branches become maintenance - branches once a stable release point has been reached. Maintenance - branches are where all bugfixes are committed before making their way - into a stable release. Maintenance branches have the same name as the - major version they are for example *1.2*. If you are using a stable - release and need fixes that haven't made their way into a stable - release check here. -- **development branches** : Development branches contain leading edge - fixes and features. They are named after the version number they are - for example *1.3*. Once development branches have reached a stable - release point they become maintenance branches, and no further new - features are introduced unless absolutely necessary. -- **feature branches** : Feature branches contain unfinished or - possibly unstable features and are recommended only for power users - interested in the most advanced feature set and willing to contribute - back to the community. Feature branches are named with the following - convention *version-feature*. An example would be *1.3-router* Which - would contain new features for the Router for 1.3. - -Hopefully this will help you understand what version is right for you. -Once you pick your version you may feel compelled to contribute a bug -report or make general comments on the code. - -- If you are using a stable version or maintenance branch, please submit - tickets or discuss with us on IRC. -- If you are using the development branch or feature branch, the first - place to go is IRC. If you have a comment and cannot reach us in IRC - after a day or two, please submit a ticket. - -If you find an issue, the best answer is to write a test. The best -advice we can offer in writing tests is to look at the ones included in -the core. - -As always, if you have any questions or comments, visit us at #cakephp -on irc.freenode.net. - - -.. meta:: - :title lang=en: CakePHP Development Process - :keywords lang=en: maintenance branch,community interaction,community feature,necessary feature,stable release,ticket system,advanced feature,power users,feature set,chat irc,leading edge,router,new features,members,attempt,development branches,branch development diff --git a/sr/appendices/glossary.rst b/sr/appendices/glossary.rst deleted file mode 100644 index 80ff5cefc1..0000000000 --- a/sr/appendices/glossary.rst +++ /dev/null @@ -1,70 +0,0 @@ -Glossary -######## - -.. glossary:: - - routing array - An array of attributes that are passed to :php:meth:`Router::url()`. - They typically look like:: - - array('controller' => 'posts', 'action' => 'view', 5) - - HTML attributes - An array of key => values that are composed into HTML attributes. For example:: - - // Given - array('class' => 'my-class', 'target' => '_blank') - - // Would generate - class="my-class" target="_blank" - - If an option can be minimized or accepts it's name as the value, then ``true`` - can be used:: - - // Given - array('checked' => true) - - // Would generate - checked="checked" - - plugin syntax - Plugin syntax refers to the dot separated class name indicating classes - are part of a plugin. E.g. ``DebugKit.Toolbar`` The plugin is DebugKit, - and the class name is Toolbar. - - dot notation - Dot notation defines an array path, by separating nested levels with ``.`` - For example:: - - Asset.filter.css - - Would point to the following value:: - - array( - 'Asset' => array( - 'filter' => array( - 'css' => 'got me' - ) - ) - ) - - CSRF - Cross Site Request Forgery. Prevents replay attacks, double - submissions and forged requests from other domains. - - routes.php - A file in APP/Config that contains routing configuration. - This file is included before each request is processed. - It should connect all the routes your application needs so - requests can be routed to the correct controller + action. - - DRY - Don't repeat yourself. Is a principle of software development aimed at - reducing repetition of information of all kinds. In CakePHP DRY is used - to allow you to code things once and re-use them across your - application. - - -.. meta:: - :title lang=en: Glossary - :keywords lang=en: html attributes,array class,array controller,glossary glossary,target blank,dot notation,routing configuration,forgery,replay,router,syntax,config,submissions diff --git a/sr/appendices/migrating-from-cakephp-1-2-to-1-3.rst b/sr/appendices/migrating-from-cakephp-1-2-to-1-3.rst deleted file mode 100644 index 5c49c43bd8..0000000000 --- a/sr/appendices/migrating-from-cakephp-1-2-to-1-3.rst +++ /dev/null @@ -1,778 +0,0 @@ -Migrating from CakePHP 1.2 to 1.3 -################################# - -This guide summarizes many of the changes necessary when migrating -from a 1.2 to 1.3 CakePHP core. Each section contains relevant -information for the modifications made to existing methods as well -as any methods that have been removed/renamed. - -**App File Replacements (important)** - - -- webroot/index.php: Must be replaced due to changes in - bootstrapping process. -- config/core.php: Additional settings have been put in place - which are required for PHP 5.3. -- webroot/test.php: Replace if you want to run unit tests. - -Removed Constants -~~~~~~~~~~~~~~~~~ - -The following constants have been removed from CakePHP. If your -application depends on them you must define them in -``app/config/bootstrap.php`` - - -- ``CIPHER_SEED`` - It has been replaced with Configure class var - ``Security.cipherSeed`` which should be changed in - ``app/config/core.php`` -- ``PEAR`` -- ``INFLECTIONS`` -- ``VALID_NOT_EMPTY`` -- ``VALID_EMAIL`` -- ``VALID_NUMBER`` -- ``VALID_YEAR`` - -Configuration and application bootstrapping -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Bootstrapping Additional Paths.** - -In your app/config/bootstrap.php you may have variables like -``$pluginPaths`` or ``$controllerPaths``. -There is a new way to add those paths. As of 1.3 RC1 the -``$pluginPaths`` variables will no longer work. You must use -``App::build()`` to modify paths. - -:: - - App::build(array( - 'plugins' => array( - '/full/path/to/plugins/', - '/next/full/path/to/plugins/' - ), - 'models' => array( - '/full/path/to/models/', - '/next/full/path/to/models/' - ), - 'views' => array( - '/full/path/to/views/', - '/next/full/path/to/views/' - ), - 'controllers' => array( - '/full/path/to/controllers/', - '/next/full/path/to/controllers/' - ), - 'datasources' => array( - '/full/path/to/datasources/', - '/next/full/path/to/datasources/' - ), - 'behaviors' => array( - '/full/path/to/behaviors/', - '/next/full/path/to/behaviors/' - ), - 'components' => array( - '/full/path/to/components/', - '/next/full/path/to/components/' - ), - 'helpers' => array( - '/full/path/to/helpers/', - '/next/full/path/to/helpers/' - ), - 'vendors' => array( - '/full/path/to/vendors/', - '/next/full/path/to/vendors/' - ), - 'shells' => array( - '/full/path/to/shells/', - '/next/full/path/to/shells/' - ), - 'locales' => array( - '/full/path/to/locale/', - '/next/full/path/to/locale/' - ), - 'libs' => array( - '/full/path/to/libs/', - '/next/full/path/to/libs/' - ) - )); - -Also changed is the order in which bootstrapping occurs. In the -past ``app/config/core.php`` was loaded **after** -``app/config/bootstrap.php``. This caused any ``App::import()`` in -an application bootstrap to be un-cached and considerably slower -than a cached include. In 1.3 core.php is loaded and the core cache -configs are created **before** bootstrap.php is loaded. - -**Loading custom inflections** - -``inflections.php`` has been removed, it was an unnecessary file -hit, and the related features have been refactored into a method to -increase their flexibility. You now use ``Inflector::rules()`` to -load custom inflections:: - - Inflector::rules('singular', array( - 'rules' => array( - '/^(bil)er$/i' => '\1', - '/^(inflec|contribu)tors$/i' => '\1ta' - ), - 'uninflected' => array('singulars'), - 'irregular' => array('spins' => 'spinor') - )); - -Will merge the supplied rules into the infection sets, with the -added rules taking precedence over the core rules. - -File renames and internal changes -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Library Renames** - -Core libraries of libs/session.php, libs/socket.php, -libs/model/schema.php and libs/model/behavior.php have been renamed -so that there is a better mapping between filenames and main -classes contained within (as well as dealing with some name-spacing -issues): - - -- session.php -> cake\_session.php - - - - App::import('Core', 'Session') -> App::import('Core', - 'CakeSession') - -- socket.php -> cake\_socket.php - - - - App::import('Core', 'Socket') -> App::import('Core', - 'CakeSocket') - -- schema.php -> cake\_schema.php - - - - App::import('Model', 'Schema') -> App::import('Model', - 'CakeSchema') - -- behavior.php -> model\_behavior.php - - - - App::import('Core', 'Behavior') -> App::import('Core', - 'ModelBehavior') - - -In most cases, the above renaming will not affect userland code. - -**Inheritance from Object** - -The following classes no longer extend Object: - - -- Router -- Set -- Inflector -- Cache -- CacheEngine - -If you were using Object methods from these classes, you will need -to not use those methods. - -Controller & Components -~~~~~~~~~~~~~~~~~~~~~~~ - -**Controller** - - -- ``Controller::set()`` no longer changes variables from - ``$var_name`` to ``$varName``. Variables always appear in the view - exactly as you set them. - -- ``Controller::set('title', $var)`` no longer sets - ``$title_for_layout`` when rendering the layout. - ``$title_for_layout`` is still populated by default. But if you - want to customize it, use - ``$this->set('title_for_layout', $var)``. - -- ``Controller::$pageTitle`` has been removed. Use - ``$this->set('title_for_layout', $var);`` instead. - -- Controller has two new methods ``startupProcess`` and - ``shutdownProcess``. These methods are responsible for handling the - controller startup and shutdown processes. - -**Component** - - -- ``Component::triggerCallback`` has been added. It is a generic - hook into the component callback process. It supplants - ``Component::startup()``, ``Component::shutdown()`` and - ``Component::beforeRender()`` as the preferred way to trigger - callbacks. - -**CookieComponent** - - -- ``del`` is deprecated use ``delete`` - -**AclComponent + DbAcl** - -Node reference checks done with paths are now less greedy and will -no longer consume intermediary nodes when doing searches. In the -past given the structure: - -:: - - ROOT/ - Users/ - Users/ - edit - -The path ``ROOT/Users`` would match the last Users node instead of -the first. In 1.3, if you were expecting to get the last node you -would need to use the path ``ROOT/Users/Users`` - -**RequestHandlerComponent** - - -- ``getReferrer`` is deprecated use ``getReferer`` - -**SessionComponent & SessionHelper** - - -- ``del`` is deprecated use ``delete`` - -``SessionComponent::setFlash()`` second param used to be used for -setting the layout and accordingly rendered a layout file. This has -been modified to use an element. If you specified custom session -flash layouts in your applications you will need to make the -following changes. - - -#. Move the required layout files into app/views/elements -#. Rename the $content\_for\_layout variable to $message -#. Make sure you have ``echo $session->flash();`` in your layout - -``SessionComponent`` and ``SessionHelper`` are not automatically -loaded. -Both ``SessionComponent`` and ``SessionHelper`` are no longer -automatically included without you asking for them. SessionHelper -and SessionComponent now act like every other component and must be -declared like any other helper/component. You should update -``AppController::$components`` and ``AppController::$helpers`` to -include these classes to retain existing behavior:: - - var $components = array('Session', 'Auth', ...); - var $helpers = array('Session', 'Html', 'Form' ...); - -These change were done to make CakePHP more explicit and -declarative in what classes you the application developer want to -use. In the past there was no way to avoid loading the Session -classes without modifying core files. Which is something we want -you to be able to avoid. In addition Session classes were the only -magical component and helper. This change helps unify and normalize -behavior amongst all classes. - -Library Classes -~~~~~~~~~~~~~~~ - -**CakeSession** - - -- ``del`` is deprecated use ``delete`` - -**SessionComponent** - - -- ``SessionComponent::setFlash()`` now uses an *element* instead - of a *layout* as its second parameter. Be sure to move any flash - layouts from app/views/layouts to app/views/elements and change - instances of $content\_for\_layout to $message. - -**Folder** - - -- ``mkdir`` is deprecated use ``create`` -- ``mv`` is deprecated use ``move`` -- ``ls`` is deprecated use ``read`` -- ``cp`` is deprecated use ``copy`` -- ``rm`` is deprecated use ``delete`` - -**Set** - - -- ``isEqual`` is deprecated. Use == or ===. - -**String** - - -- ``getInstance`` is deprecated, call String methods statically. - -**Router** - -``Routing.admin`` is deprecated. It provided an inconsistent -behavior with other prefix style routes in that it was treated -differently. Instead you should use ``Routing.prefixes``. Prefix -routes in 1.3 do not require additional routes to be declared -manually. All prefix routes will be generated automatically. To -update simply change your core.php:: - - //from: - Configure::write('Routing.admin', 'admin'); - - //to: - Configure::write('Routing.prefixes', array('admin')); - -See the New features guide for more information on using prefix -routes. A small change has also been done to routing params. Routed -params should now only consist of alphanumeric chars, - and \_ or -``/[A-Z0-9-_+]+/``:: - - Router::connect('/:$%@#param/:action/*', array(...)); // BAD - Router::connect('/:can/:anybody/:see/:m-3/*', array(...)); //Acceptable - -For 1.3 the internals of the Router were heavily refactored to -increase performance and reduce code clutter. The side effect of -this is two seldom used features were removed, as they were -problematic and buggy even with the existing code base. First path -segments using full regular expressions was removed. You can no -longer create routes like:: - - Router::connect( - '/([0-9]+)-p-(.*)/', - array('controller' => 'products', 'action' => 'show') - ); - -These routes complicated route compilation and impossible to -reverse route. If you need routes like this, it is recommended that -you use route parameters with capture patterns. Next mid-route -greedy star support has been removed. It was previously possible to -use a greedy star in the middle of a route:: - - Router::connect( - '/pages/*/:event', - array('controller' => 'pages', 'action' => 'display'), - array('event' => '[a-z0-9_-]+') - ); - -This is no longer supported as mid-route greedy stars behaved -erratically, and complicated route compiling. Outside of these two -edge-case features and the above changes the router behaves exactly -as it did in 1.2 - -Also, people using the 'id' key in array-form URLs will notice that -Router::url() now treats this as a named parameter. If you -previously used this approach for passing the ID parameter to -actions, you will need to rewrite all your $html->link() and -$this->redirect() calls to reflect this change. - -:: - - // old format: - $url = array('controller' => 'posts', 'action' => 'view', 'id' => $id); - // use cases: - Router::url(http://github.com/$url); - $html->link($url); - $this->redirect($url); - // 1.2 result: - /posts/view/123 - // 1.3 result: - /posts/view/id:123 - // correct format: - $url = array('controller' => 'posts', 'action' => 'view', $id); - -**Dispatcher** - -``Dispatcher`` is no longer capable of setting a controller's -layout/viewPath with request parameters. Control of these -properties should be handled by the Controller, not the Dispatcher. -This feature was also undocumented, and untested. - -**Debugger** - - -- ``Debugger::checkSessionKey()`` has been renamed to - ``Debugger::checkSecurityKeys()`` -- Calling ``Debugger::output("text")`` no longer works. Use - ``Debugger::output("txt")``. - -**Object** - - -- ``Object::$_log`` has been removed. ``CakeLog::write`` is now - called statically. See :doc:`/core-libraries/logging` - for more information on changes made to logging. - -**Sanitize** - - -- ``Sanitize::html()`` now actually always returns escaped - strings. In the past using the ``$remove`` parameter would skip - entity encoding, returning possibly dangerous content. -- ``Sanitize::clean()`` now has a ``remove_html`` option. This - will trigger the ``strip_tags`` feature of ``Sanitize::html()``, - and must be used in conjunction with the ``encode`` parameter. - -**Configure and App** - - -- Configure::listObjects() replaced by App::objects() -- Configure::corePaths() replaced by App::core() -- Configure::buildPaths() replaced by App::build() -- Configure no longer manages paths. -- Configure::write('modelPaths', array...) replaced by - App::build(array('models' => array...)) -- Configure::read('modelPaths') replaced by App::path('models') -- There is no longer a debug = 3. The controller dumps generated - by this setting often caused memory consumption issues making it an - impractical and unusable setting. The ``$cakeDebug`` variable has - also been removed from ``View::renderLayout`` You should remove - this variable reference to avoid errors. -- ``Configure::load()`` can now load configuration files from - plugins. Use ``Configure::load('plugin.file');`` to load - configuration files from plugins. Any configuration files in your - application that use ``.`` in the name should be updated to use - ``_`` - -**Cache** - -In addition to being able to load CacheEngines from app/libs or -plugins, Cache underwent some refactoring for CakePHP1.3. These -refactorings focused around reducing the number and frequency of -method calls. The end result was a significant performance -improvement with only a few minor API changes which are detailed -below. - -The changes in Cache removed the singletons used for each Engine -type, and instead an engine instance is made for each unique key -created with ``Cache::config()``. Since engines are not singletons -anymore, ``Cache::engine()`` was not needed and was removed. In -addition ``Cache::isInitialized()`` now checks cache -*configuration names*, not cache *engine names*. You can still use -``Cache::set()`` or ``Cache::engine()`` to modify cache -configurations. Also checkout the -:doc:`/appendices/new-features-in-cakephp-1-3` for -more information on the additional methods added to ``Cache``. - -It should be noted that using an app/libs or plugin cache engine -for the default cache config can cause performance issues as the -import that loads these classes will always be uncached. It is -recommended that you either use one of the core cache engines for -your ``default`` configuration, or manually include the cache -engine class before configuring it. Furthermore any non-core cache -engine configurations should be done in -``app/config/bootstrap.php`` for the same reasons detailed above. - -Model Databases and Datasources -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Model** - - -- ``Model::del()`` and ``Model::remove()`` have been removed in - favor of ``Model::delete()``, which is now the canonical delete - method. -- ``Model::findAll``, findCount, findNeighbours, removed. -- Dynamic calling of setTablePrefix() has been removed. - tableprefix should be with the ``$tablePrefix`` property, and any - other custom construction behavior should be done in an overridden - ``Model::__construct()``. -- ``DboSource::query()`` now throws warnings for un-handled model - methods, instead of trying to run them as queries. This means, - people starting transactions improperly via the - ``$this->Model->begin()`` syntax will need to update their code so - that it accesses the model's DataSource object directly. -- Missing validation methods will now trigger errors in - development mode. -- Missing behaviors will now trigger a cakeError. -- ``Model::find(first)`` will no longer use the id property for - default conditions if no conditions are supplied and id is not - empty. Instead no conditions will be used -- For Model::saveAll() the default value for option 'validate' is - now 'first' instead of true - -**Datasources** - - -- DataSource::exists() has been refactored to be more consistent - with non-database backed datasources. Previously, if you set - ``var $useTable = false; var $useDbConfig = 'custom';``, it was - impossible for ``Model::exists()`` to return anything but false. - This prevented custom datasources from using ``create()`` or - ``update()`` correctly without some ugly hacks. If you have custom - datasources that implement ``create()``, ``update()``, and - ``read()`` (since ``Model::exists()`` will make a call to - ``Model::find('count')``, which is passed to - ``DataSource::read()``), make sure to re-run your unit tests on - 1.3. - -**Databases** - -Most database configurations no longer support the 'connect' key -(which has been deprecated since pre-1.2). Instead, set -``'persistent' => true`` or false to determine whether or not a -persistent database connection should be used - -**SQL log dumping** - -A commonly asked question is how can one disable or remove the SQL -log dump at the bottom of the page?. In previous versions the HTML -SQL log generation was buried inside DboSource. For 1.3 there is a -new core element called ``sql_dump``. ``DboSource`` no longer -automatically outputs SQL logs. If you want to output SQL logs in -1.3, do the following: - -:: - - echo $this->element('sql_dump'); - -You can place this element anywhere in your layout or view. The -``sql_dump`` element will only generate output when -``Configure::read('debug')`` is equal to 2. You can of course -customize or override this element in your app by creating -``app/views/elements/sql_dump.ctp``. - -View and Helpers -~~~~~~~~~~~~~~~~ - -**View** - - -- ``View::renderElement`` removed. Use ``View::element()`` - instead. -- Automagic support for ``.thtml`` view file extension has been - removed either declare ``$this->ext = 'thtml';`` in your - controllers, or rename your views to use ``.ctp`` -- ``View::set('title', $var)`` no longer sets - ``$title_for_layout`` when rendering the layout. - ``$title_for_layout`` is still populated by default. But if you - want to customize it, use ``$this->set('title_for_layout', $var)``. -- ``View::$pageTitle`` has been removed. Use - ``$this->set('title_for_layout', $var);`` instead. -- The ``$cakeDebug`` layout variable associated with debug = 3 has - been removed. Remove it from your layouts as it will cause errors. - Also see the notes related to SQL log dumping and Configure for - more information. - -All core helpers no longer use ``Helper::output()``. The method was -inconsistently used and caused output issues with many of -FormHelper's methods. If you previously overrode -``AppHelper::output()`` to force helpers to auto-echo you will need -to update your view files to manually echo helper output. - -**TextHelper** - - -- ``TextHelper::trim()`` is deprecated, used ``truncate()`` - instead. -- ``TextHelper::highlight()`` no longer has: -- an ``$highlighter`` parameter. Use ``$options['format']`` - instead. -- an ``$considerHtml``parameter. Use ``$options['html']`` instead. -- ``TextHelper::truncate()`` no longer has: -- an ``$ending`` parameter. Use ``$options['ending']`` instead. -- an ``$exact`` parameter. Use ``$options['exact']`` instead. -- an ``$considerHtml``parameter. Use ``$options['html']`` - instead. - -**PaginatorHelper** - -PaginatorHelper has had a number of enhancements applied to make -styling easier. -``prev()``, ``next()``, ``first()`` and ``last()`` - -The disabled state of these methods now defaults to ```` tags -instead of ``
    `` tags. - -passedArgs are now auto merged with URL options in paginator. - -``sort()``, ``prev()``, ``next()`` now add additional class names -to the generated html. ``prev()`` adds a class of prev. ``next()`` -adds a class of next. ``sort()`` will add the direction currently -being sorted, either asc or desc. - -**FormHelper** - - -- ``FormHelper::dateTime()`` no longer has a ``$showEmpty`` - parameter. Use ``$attributes['empty']`` instead. -- ``FormHelper::year()`` no longer has a ``$showEmpty`` parameter. - Use ``$attributes['empty']`` instead. -- ``FormHelper::month()`` no longer has a ``$showEmpty`` - parameter. Use ``$attributes['empty']`` instead. -- ``FormHelper::day()`` no longer has a ``$showEmpty`` parameter. - Use ``$attributes['empty']`` instead. -- ``FormHelper::minute()`` no longer has a ``$showEmpty`` - parameter. Use ``$attributes['empty']`` instead. -- ``FormHelper::meridian()`` no longer has a ``$showEmpty`` - parameter. Use ``$attributes['empty']`` instead. -- ``FormHelper::select()`` no longer has a ``$showEmpty`` - parameter. Use ``$attributes['empty']`` instead. -- Default URLs generated by form helper no longer contain 'id' - parameter. This makes default URLs more consistent with documented - userland routes. Also enables reverse routing to work in a more - intuitive fashion with default FormHelper URLs. -- ``FormHelper::submit()`` Can now create other types of inputs - other than type=submit. Use the type option to control the type of - input generated. -- ``FormHelper::button()`` Now creates `` - - - - - - The ``button`` input type supports the ``escape`` option, which accepts a - bool and determines whether to HTML entity encode the $title of the button. - Defaults to false:: - - echo $this->Form->button('Submit Form', array( - 'type' => 'submit', - 'escape' => true - )); - -.. php:method:: postButton(string $title, mixed $url, array $options = array ()) - - Create a ``