diff --git a/config/conf.py b/config/conf.py index 6b7f6cc14d..87cad35de4 100644 --- a/config/conf.py +++ b/config/conf.py @@ -115,3 +115,5 @@ .. |phpversion| replace:: **8.2** .. |minphpversion| replace:: 8.1 """ + +# todo_include_todos = True diff --git a/en/controllers.rst b/en/controllers.rst index 831593d0ba..3af2f63fe9 100644 --- a/en/controllers.rst +++ b/en/controllers.rst @@ -68,8 +68,8 @@ is invoked at the end of a Controller's constructor for this kind of use:: { public function initialize(): void { - // Always enable the CSRF component. - $this->loadComponent('Csrf'); + // Always enable the FormProtection component. + $this->loadComponent('FormProtection'); } } @@ -320,8 +320,8 @@ actions, you can call ``addClasses()`` within your action too:: // Rest of the action code } -If within your controller actions you need to process request or load data -differently based on the controller action you can use +If within your controller actions you need to process the request or load data +differently based on the content type you can use :ref:`check-the-request`:: // In a controller action @@ -510,7 +510,7 @@ want loaded, and any configuration data for them:: public function initialize(): void { parent::initialize(); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); $this->loadComponent('Comments', Configure::read('Comments')); } diff --git a/en/controllers/components.rst b/en/controllers/components.rst index 774fa65fec..fff1c088ab 100644 --- a/en/controllers/components.rst +++ b/en/controllers/components.rst @@ -25,7 +25,7 @@ Configuring Components ====================== Many of the core components require configuration. One example would be -the :doc:`/controllers/components/form-protection`. Configuration for these components, +the :doc:`/controllers/components/form-protection`. Configuration for these components, and for components in general, is usually done via ``loadComponent()`` in your Controller's ``initialize()`` method or via the ``$components`` array:: @@ -37,9 +37,8 @@ Controller's ``initialize()`` method or via the ``$components`` array:: $this->loadComponent('FormProtection', [ 'unlockedActions' => ['index'], ]); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } - } You can configure components at runtime using the ``setConfig()`` method. Often, @@ -58,7 +57,7 @@ to read and write configuration data:: $this->FormProtection->getConfig('unlockedActions'); // Set config - $this->Csrf->setConfig('cookieName', 'token'); + $this->Flash->setConfig('key', 'myFlash'); As with helpers, components will automatically merge their ``$_defaultConfig`` property with constructor configuration to create the ``$_config`` property @@ -193,12 +192,12 @@ component, through which we can access an instance of it:: // In a controller // Make the new component available at $this->Math, - // as well as the standard $this->Csrf + // as well as the standard $this->Flash public function initialize(): void { parent::initialize(); $this->loadComponent('Math'); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } When including Components in a Controller you can also declare a @@ -214,7 +213,7 @@ the Component:: 'precision' => 2, 'randomGenerator' => 'srand', ]); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } The above would pass the array containing precision and randomGenerator to diff --git a/en/intro/cakephp-folder-structure.rst b/en/intro/cakephp-folder-structure.rst index 93f52b7cf5..9fec23c0be 100644 --- a/en/intro/cakephp-folder-structure.rst +++ b/en/intro/cakephp-folder-structure.rst @@ -55,7 +55,7 @@ View .. note:: - The folder ``Shell`` is not present by default. + The folder ``Command`` is not present by default. You can add it when you need it. .. meta:: diff --git a/en/intro/conventions.rst b/en/intro/conventions.rst index 5aa060569e..a2bf72cee2 100644 --- a/en/intro/conventions.rst +++ b/en/intro/conventions.rst @@ -191,11 +191,13 @@ creating classes and files that you'd need to create anyway. +------------+-----------------------------+-------------------------+------------------------------------------------------+ | Controller | ArticlesController | MenuLinksController | Plural, CamelCased, end in Controller | +------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Templates | Articles/index.php | MenuLinks/index.php | View template files are named after | +| | Articles/add.php | MenuLinks/add.php | the controller functions they | +| | Articles/get_list.php | MenuLinks/get_list.php | display, in an underscored form | ++------------+-----------------------------+-------------------------+------------------------------------------------------+ | Behavior | ArticlesBehavior.php | MenuLinksBehavior.php | | +------------+-----------------------------+-------------------------+------------------------------------------------------+ -| View | ArticlesView.php | MenuLinksView.php | View template files are named after | -| | | | the controller functions they | -| | | | display, in an underscored form | +| View | ArticlesView.php | MenuLinksView.php | | +------------+-----------------------------+-------------------------+------------------------------------------------------+ | Helper | ArticlesHelper.php | MenuLinksHelper.php | | +------------+-----------------------------+-------------------------+------------------------------------------------------+ diff --git a/en/plugins.rst b/en/plugins.rst index c311374819..869811525a 100644 --- a/en/plugins.rst +++ b/en/plugins.rst @@ -117,7 +117,7 @@ appropriate parts of your application. The hooks are: * ``services`` Used to register application container services By default all plugins hooks are enabled. You can disable hooks by using the -related options of the ``plugin load`` command:: +related options of the ``plugin load`` command: .. code-block:: console @@ -195,7 +195,7 @@ You can reference a plugin's controllers, models, components, behaviors, and helpers by prefixing the name of the plugin. For example, say you wanted to use the ContactManager plugin's -ContactInfoHelper to output formatted contact information in +``ContactInfoHelper`` to output formatted contact information in one of your views. In your controller, using ``addHelper()`` could look like this:: diff --git a/en/views/json-and-xml-views.rst b/en/views/json-and-xml-views.rst index 4f37ade368..04d7005bcc 100644 --- a/en/views/json-and-xml-views.rst +++ b/en/views/json-and-xml-views.rst @@ -60,7 +60,7 @@ serialize:: public function index() { - // Set the view vars that have to be serialized. + // Set the view vars $this->set('articles', $this->paginate()); // Specify which view vars JsonView should serialize. $this->viewBuilder()->setOption('serialize', 'articles'); @@ -84,7 +84,7 @@ You can also define ``serialize`` as an array of view variables to combine:: { // Some code that created $articles and $comments - // Set the view vars that have to be serialized. + // Set the view vars $this->set(compact('articles', 'comments')); // Specify which view vars JsonView should serialize. @@ -238,3 +238,6 @@ total control over view class selection you can directly choose the view class:: } } +.. meta:: + :title lang=en: JSON and XML views + :keywords lang=en: json,xml,presentation layer,view,ajax,logic,syntax,templates,cakephp diff --git a/es/appendices.rst b/es/appendices.rst index b8b89fc1b0..5c2ef86c3c 100644 --- a/es/appendices.rst +++ b/es/appendices.rst @@ -1,18 +1,35 @@ Apéndices ######### -En los apéndices encontrarás información relacionada a las nuevas características +En los apéndices encontrarás información relacionada con las nuevas características introducidas en cada versión, así como también las guías de migración entre versiones. -Guía de Migración a 4.x +Guía de Migración a 5.x ======================= .. toctree:: :maxdepth: 1 - appendices/4-0-migration-guide + appendices/5-0-upgrade-guide + appendices/5-0-migration-guide -Información General +Retrocompatiblidad +=================== + +Si quieres utilizar funcionalidades de 3.x o 4.x o ir migrando poco a poco +el `Plugin Shim `__ puede ayudarte a mitigar algunos +de los cambios que rompen la compatibilidad. + +Antecompatiblidad +================= + +La Antecompatibilidad puede preparar tu aplicación 4.x para la próxima versión 5.x. + +Si quieres utilizar una funcionalidad de 5.x en 4.x, revisa el `Plugin Shim +`__. Este plugin te ayuda a mitigar los +problemas de compatibilidad y a llevar funcionalidades de 5.x a 4.x. + +General Information =================== .. toctree:: diff --git a/es/appendices/4-0-migration-guide.rst b/es/appendices/4-0-migration-guide.rst deleted file mode 100644 index 4a690f257d..0000000000 --- a/es/appendices/4-0-migration-guide.rst +++ /dev/null @@ -1,15 +0,0 @@ -4.0 Migration Guide -################### - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: CakePHP Development Process - :keywords lang=es: 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/es/appendices/5-0-migration-guide.rst b/es/appendices/5-0-migration-guide.rst new file mode 100644 index 0000000000..034972f6af --- /dev/null +++ b/es/appendices/5-0-migration-guide.rst @@ -0,0 +1,414 @@ +5.0 Guía de migración +##################### + +CakePHP 5.0 contiene cambios importantes, y no es compatible con versiones anteriores +de 4.x. Antes de intentar actualizar a la version 5.0, primero actualice a la version 4.5 y resuelva +todas las advertencias de obsolescencia. + +Consulte :doc:`/appendices/5-0-upgrade-guide` para obtener instrucciones paso a paso de +como actualizar a la versión 5.0. + +Características obsoletas eliminadas +==================================== + +Todos los métodos, propiedades y funcionalidades que emitían advertencias de obsolencias +a partir de la versión 4.5 se han eliminado. + +Cambios importantes +=================== + +Además de la eliminación de características obsoletas, se han realizado +cambios importantes: + +Global +------ + +- Se han añadido declaraciones de tipo a todos los parámetros de función y devoluciones siempre que ha sido posible. Estos + están pensados para que coincidan con las anotaciones de docblock, pero incluyen correcciones para anotaciones incorrectas. +- Se han añadido declaraciones de tipo a todas las propiedades de clase siempre que ha sido posible. También se han corregido + algunas anotaciones incorrectas. +- Se han eliminado las constantes ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``, ``WEEK``, ``MONTH``, ``YEAR``. +- Las funciones globales son ahora opcionales. Si tu aplicación utiliza alias de funciones globales, asegúrase + de añadir ``require CAKE . 'functions.php'`` al ``config/bootstrap.php`` de tu aplicación. +- Se ha eliminado el uso de ``#[\AllowDynamicProperties]`` en todas las partes. Se utilizaba para las siguientes clases: + - ``Command/Command`` + - ``Console/Shell`` + - ``Controller/Component`` + - ``Controller/Controller`` + - ``Mailer/Mailer`` + - ``View/Cell`` + - ``View/Helper`` + - ``View/View`` +- Se han actualizado las versiones compatibles del motor de base de datos: + - MySQL (5.7 o superior) + - MariaDB (10.1 o superior) + - PostgreSQL (9.6 o superior) + - Microsoft SQL Server (2012 o superior) + - SQLite 3 + +Auth +---- + +- `Auth` ha sido eliminado. Usa los plugins `cakephp/authentication `__ y + `cakephp/authorization `__ en su lugar. + +Cache +----- + +- El motor ``Wincache`` ha sido eliminado. La extension wincache no es compatible + con PHP 8. + +Consola +------- + +- ``BaseCommand::__construct()`` ha sido eliminado. +- Se ha eliminado ``ConsoleIntegrationTestTrait::useCommandRunner()`` porque ya no es necesario. +- ``Shell`` Ha sido eliminado y debe ser sustituido por `Command `__ +- Ahora ``BaseCommand`` emite los eventos ``Command.beforeExecute`` and ``Command.afterExecute`` + cuando el método ``execute()`` del comando es invocado por el framework. + +Connection +---------- + +- Se ha eliminado ``Connection::prepare()``. En su lugar, puede utilizar ``Connection::execute()`` + para ejecutar una consulta SQL especificando en la cadena SQL los parámetros y los tipos en una sola llamada. +- Se ha eliminado ``Connection::enableQueryLogging()``. Si no ha habilitado el registro + a través de la configuración de conexión, puedes configurar más adelante la instancia del registrador para que + el controlador habilite el registro de consultas ``$connection->getDriver()->setLogger()``. + +Controlador +----------- + +- La firma del método para ``Controller::__construct()`` ha cambiado. + Por lo tanto, tienes que ajustar el código en consecuencia si estás sobreescribiendo el constructor. +- Después de la carga, los componentes ya no se establecen como propiedades dinámicas. En su lugar + ``Controller`` usa ``__get()`` para proporcionar acceso a las propiedades de los componentes. Este + cambio puede afectar a las aplicaciones que usan ``property_exists()`` en los componentes. +- Se ha renombrado la devolución de llamada del evento ``Controller.shutdown`` de los componentes de + ``shutdown`` a ``afterFilter`` para que coincida con el del controlador. Esto hace que las devoluciones de llamada + sean más coherentes. +- ``PaginatorComponent`` ha sido eliminado y tienes que reemplazarlo llamando a ``$this->paginate()`` en tu controlador o + usando ``Cake\Datasource\Paging\NumericPaginator`` directamente. +- ``RequestHandlerComponent`` ha sido eliminado. Consulte la guía `4.4 migration `__ para saber como actualizarlo. +- Se ha eliminado ``SecurityComponent``. Usa ``FormProtectionComponent`` para la protección contra la manipulación de formularios + o ``HttpsEnforcerMiddleware`` para forzar el uso de solicitudes HTTPS en su lugar. +- ``Controller::paginate()`` ya no acepta opciones de consulta como ``contain`` para su + argumento ``$settings``. En su lugar debes usar la opción ``finder`` + ``$this->paginate($this->Articles, ['finder' => 'published'])``. O puede + crear la consulta requerida de antemano y luego pasarla a ``paginate()`` + ``$query = $this->Articles->find()->where(['is_published' => true]); $this->paginate($query);``. + +Core +---- + +- La función ``getTypeName()`` ha sido desechada. En su lugar usa ``get_debug_type()`` de PHP. +- La dependencia de ``league/container`` se actualizó a ``4.x``. Esto requerirá + la adición de typehints a tus implementaciones de ``ServiceProvider``. +- ``deprecationWarning()`` ahora tiene un parámetro ``$version``. +- La opción de configuración ``App.uploadedFilesAsObjects`` se ha eliminado + junto con el soporte para arrays con forma carga de archivos PHP en todo el framework. +- ``ClassLoader`` ha sido eliminado. En su lugar, utiliza composer para generar archivos de carga automática. + +Base de datos +------------- + +- ``DateTimeType`` y ``DateType`` ahora siempre devuelven objetos inmutables. + Además, la interfaz para los objetos ``Date`` refleja la interfaz ``ChronosDate`` + que carece de todos los métodos relacionados con el tiempo que estaban presentes en CakePHP 4.x. +- ``DateType::setLocaleFormat()`` ya no acepta array. +- ``Query`` ahora solo acepta parámetros ``\Closure`` en lugar de ``callable``. Los callables se pueden convertir + a closures usando la nueva sintaxis de array de primera clase de PHP 8.1. +- ``Query::execute()`` ya no ejecuta el resultado de la ejeción de la consulta. Debe utilizar ``Query::all()`` en su lugar. +- ``TableSchemaAwareInterface`` fue eliminado. +- ``Driver::quote()`` fue eliminado. En su lugar, utiliza declaraciones preparadas. +- ``Query::orderBy()`` fue añadido para reemplazar ``Query::order()``. +- ``Query::groupBy()`` fue añadido para reemplazar ``Query::group()``. +- ``SqlDialectTrait`` se ha eliminado y toda su funcionalidad se ha movido a la propia clase ``Driver``. +- ``CaseExpression`` ha sido eliminado y debe ser reemplazado por + ``QueryExpression::case()`` o ``CaseStatementExpression`` +- ``Connection::connect()`` ha sido eliminado. Usar ``$connection->getDriver()->connect()`` en su lugar. +- ``Connection::disconnect()`` ha sido eliminado. Usar ``$connection->getDriver()->disconnect()`` en su lugar. +- ``cake.database.queries`` ha sido añadido como alternativa al scope ``queriesLog``. + +Datasource +---------- + +- El método ``getAccessible()`` ha sido añadido a ``EntityInterface``. Las implementaciones que no son ORM + tienen que implementar este método ahora. +- El método ``aliasField()`` ha sido añadido a ``RepositoryInterface``. Las implementaciones que no son ORM + tienen que implementar este método ahora. + +Eventos +------- + +- Las cargas útiles de eventos deben ser un array. Otros objetos como ``ArrayAccess`` ya no se convierten en array y ahora lanzarán un ``TypeError``. +- Se recomienda ajustar los handlers de eventos para que sean métodos void y usar ``$event->setResult()`` en lugar de devolver el resultado. + +Error +----- + +- ``ErrorHandler`` y ``ConsoleErrorHandler`` han sido eliminados. Consulte la guía `4.4 migration `__ para saber como actualizarlo. +- ``ExceptionRenderer`` ha sido eliminado y debe ser reemplazado por ``WebExceptionRenderer`` +- ``ErrorLoggerInterface::log()`` ha sido eliminado y debe ser reemplazado por ``ErrorLoggerInterface::logException()`` +- ``ErrorLoggerInterface::logMessage()`` ha sido eliminado y debe ser reemplazado por ``ErrorLoggerInterface::logError()`` + +Filesystem +---------- + +- El paquete de Filesystem se ha eliminado, y la clase ``Filesystem`` se ha movido al paquete de Utility. + +Http +---- + +- ``ServerRequest`` ya no es compatible con ``files`` como arrays. Este + behavior se ha deshabilitado de forma predeterminada desde la version 4.1.0. Los datos ``files`` + ahora siempre contendrán objetos ``UploadedFileInterfaces``. + +I18n +---- + +- Se cambió el nombre de ``FrozenDate`` a `Date` y el de ``FrozenTime`` a `DateTime`. +- ``Time`` ahora extiende de ``Cake\Chronos\ChronosTime`` y. por lo tanto, es inmutable. +- ``Date::parseDateTime()`` ha sido eliminado. +- ``Date::parseTime()`` ha sido eliminado. +- ``Date::setToStringFormat()`` y ``Date::setJsonEncodeFormat()`` ya no aceptan un array. +- ``Date::i18nFormat()`` y ``Date::nice()`` ya no aceptan un parámetro de zona horaria. +- Los archivos de traducción en la carpeta de vendor con prefijo como (``FooBar/Awesome``) ahora tendrán + ese prefijo en el nombre del archivo de traducción, por ejemplo, ``foo_bar_awesome.po`` para evitar colisiones + con otro fichero ``awesome.po`` correspondiente con el plugin (``Awesome``). + +Log +--- + +- La configuración del motor de registros ahora utiliza ``null`` en lugar de ``false`` para desactivar los scopes. + Así que en lugar de ``'scopes' => false`` necesitas usar ``'scopes' => null`` en la configuración de tu log. + +Mailer +------ + +- Se ha eliminado ``Email``. Usar `Mailer `__ en su lugar. +- ``cake.mailer`` se ha añadido como alternativa al scope ``email``. + +ORM +--- + +- ``EntityTrait::has()`` ahora devuelve ``true`` cuando existe un atributo y es estable + en ``null``. En versiones anteriores de CakePHP esto devolvía ``false``. + Consulte las notas de la version 4.5.0 para saber como adoptar este comportamiento en 4.x. +- ``EntityTrait::extractOriginal()`` ahora devuelve solo los campos existentes, similar a ``extractOriginalChanged()``. +- Ahora se requiere que los argumentos de un `Finder` sean arrays asociativos, como siempre se esperó que fueran. +- ``TranslateBehavior`` ahora tiene como valor predeterminado la estrategia ``ShadowTable``. Si está + utilizando la estrategia ``Eav`` deberá actualizar la configuración de tu behavior para conservar + el comportamiento anterior. +- La opción ``allowMultipleNulls`` para la regla ``isUnique`` ahora es true de forma predeterminada, + coincidiendo con el comportamiento original de 3.x. +- ``Table::query()`` se ha eliminado en favor de funciones específicas de tipo de consulta. +- ``Table::updateQuery()``, ``Table::selectQuery()``, ``Table::insertQuery()``, y + ``Table::deleteQuery()`` se añadieron y ahora devuelven los nuevos objetos de consulta de tipo específico. +- Se añadieron ``SelectQuery``, ``InsertQuery``, ``UpdateQuery`` y ``DeleteQuery`` que representan + solo un tipo de consulta y no permiten cambiar entre tipos de consulta, sin llamar a funciones no relacionadas + con el tipo de consulta especifico. +- ``Table::_initializeSchema()`` ha sido eliminado y debe ser reemplazado llamando a + ``$this->getSchema()`` dentro del método ``initialize()``. +- ``SaveOptionsBuilder`` ha sido eliminado. En su lugar, utilice un array normal para las opciones. + +Enrutamiento +------------ + +- Los métodos estáticos ``connect()``, ``prefix()``, ``scope()`` y ``plugin()`` del ``Router`` han sido eliminados y + deben ser reemplazados llamando a sus variantes de método no estáticos a través de la instancia ``RouteBuilder``. +- ``RedirectException`` ha sido eliminado. Usar ``\Cake\Http\Exception\RedirectException`` en su lugar. + +TestSuite +--------- + +- ``TestSuite`` fue eliminado. En su lugar, los usuarios deben usar variables de entorno + para personalizar la configuración de las pruebas unitarias. +- ``TestListenerTrait`` fue eliminado. PHPUnit dejó de dar soporte a estos listeners. + Ver documentación :doc:`/appendices/phpunit10` +- ``IntegrationTestTrait::configRequest()`` ahora fusiona la configuración cuando se llama varias + veces en lugar de reemplazar la configuración actualmente presente. + +Validaciones +------------ + +- ``Validation::isEmpty()`` ya no es compatible con la subida de ficheros en forma + arrays. El soporte para la subida de ficheros en forma de array también se ha eliminado de + ``ServerRequest`` por lo que no debería ver esto como un problema fuera de las pruebas. +- Anteriormente, la mayoría de los mensajes de error de validacion de datos eran simplemente ``El valor proporcionado no es válido``. + Ahora, los mensajes de error de validación de datos están redactados con mayor precisión. + Por ejemplo, ``El valor proporcionado debe ser mayor o igual que \`5\```. + +Vistas +------ + +- Las opciones de ``ViewBuilder`` ahora son verdaderamente asociativas (string keys). +- ``NumberHelper`` y ``TextHelper`` ya no aceptan la configuración de ``engine``. +- ``ViewBuilder::setHelpers()`` el parámetro ``$merge`` fue eliminado. Usar ``ViewBuilder::addHelpers()`` en su lugar. +- Dentro ``View::initialize()``, preferentemente usar ``addHelper()`` en lugar de ``loadHelper()``. + De todas formas, todas las configuraciones de helpers se cargarán después. +- ``View\Widget\FileWidget`` ya no es compatible con la subida de ficheros en forma + arrays. Esto está alineado con los cambios en ``ServerRequest`` y ``Validation``. +- ``FormHelper`` ya no estable ``autocomplete=off`` en los campos de token CSRF. Esto + fue una solución para un error de Safari que no es relevante. + +Obsolescencias +============== + +A continuación se muestra una lista de métodos, propiedades y comportamientos en desuso. Estas +características seguirán funcionando en la versión 5.x y se eliminarán en la versión 6.0. + +Base de datos +------------- + +- ``Query::order()`` ha quedado obsoleto. Utiliza ``Query::orderBy()`` en su lugar + ahora que los métodos ``Connection`` ya no son proxy. Esto alinea el nombre de la función + con la instrucción SQL. +- ``Query::group()`` ha quedado obsoleto. Utiliza ``Query::groupBy()`` en su lugar + ahora que los métodos ``Connection`` ya no son proxy. Esto alinea el nombre de la función + con la instrucción SQL. + +ORM +--- + +- Llamar a ``Table::find()`` con opciones de array está obsoleto. Utiliza `named arguments `__ + en su lugar. Por ejemplo, en lugar de ``find('all', ['conditions' => $array])`` usar + ``find('all', conditions: $array)``. De manera similar, para las opciones de finders personalizados, en lugar + de ``find('list', ['valueField' => 'name'])`` usar ``find('list', valueField: 'name')`` + o varios argumentos como ``find(type: 'list', valueField: 'name', conditions: $array)``. + +Nuevas características +====================== + +Comprobación de tipos mejorada +------------------------------ + +CakePHP 5 aprovecha la función de sistema de tipos expandidos disponible en PHP 8.1+. +CakePHP también usa ``assert()`` para proporcionar mensajes de error mejorados y una solidez de tipo adicional. +En el modo de producción, puede configurar PHP para que no genere código para ``assert()`` lo que mejora el rendimiento de la aplicación. +Consulte :ref:`symlink-assets` para saber cómo hacerlo. + +Colecciones +----------- + +- Se añadió ``unique()`` que filtra el valor duplicado especificado por la devolución de llamada proporcionada. +- ``reject()`` ahora soporta una devolución de llamada predeterminada que filtra los valores verdaderos, + que es el inverso del comportamiento predeterminado de ``filter()`` + +Core +---- + +- El método ``services()`` se añadió a ``PluginInterface``. +- ``PluginCollection::addFromConfig()`` se ha añadido a :ref:`simplify plugin loading `. + +Base de datos +------------- + +- ``ConnectionManager`` ahora soporta roles de conexión de lectura y escritura. Los roles se pueden configurar + con claves de ``read`` y ``write`` en la configuración de conexión que anulan la configuración compartida. +- Se añadió ``Query::all()`` que ejecuta devoluciones de llamada del decorador de resultados y devuelve un conjunto de resultados para consultas seleccionadas. +- Se añadió ``Query::comment()`` para agregar un comentario SQL a la consulta ejecutada. Esto facilita la depuración de consultas. +- ``EnumType`` fue añadido para permitir el mapeo entre enumeraciones respaldadas por PHP y una cadena o columna entera. +- ``getMaxAliasLength()`` y ``getConnectionRetries()`` se añadieron a ``DriverInterface``. +- Los drivers compatibles ahora agregan automáticamente el incremento automático solo a las claves primarias enteras denominadas "id" + en lugar de a todas las claves primarias enteras. Si se establece 'autoIncrement' como false, siempre se deshabilita en todos los drivers compatibles. + +Http +---- + +- Se ha añadido soporte para 'factories interface' `PSR-17 `__. + Esto permite ``cakephp/http`` proporcionar una implementación de cliente a + bibliotecas que permiten la resolución automática de interfaces como php-http. +- Se añadieron ``CookieCollection::__get()`` y ``CookieCollection::__isset()`` para añadir + formas ergonómicas de acceder a las cookies sin excepciones. + +ORM +--- + +Campos de entidad obligatorios +------------------------------ + +Las entidades tienen una nueva funcionalidad de opt-in que permite hacer que las entidades manejen +propiedades de manera más estricta. El nuevo comportamiento se denomina 'required fields'. Cuando +es habilitado, el acceso a las propiedades que no están definidas en la entidad generará +excepciones. Esto afecta a los siguientes usos:: + + $entity->get(); + $entity->has(); + $entity->getOriginal(); + isset($entity->attribute); + $entity->attribute; + +Los campos se consideran definidos si pasan ``array_key_exists``. Esto incluye +valores nulos. Debido a que esta puede ser una característica tediosa de habilitar, se aplazó a +5.0. Nos gustaría recibir cualquier comentario que tenga sobre esta función, +ya que estamos considerando hacer que este sea el comportamiento predeterminado en el futuro. + + +Typed Finder Parameters +----------------------- + +Los finders de las tablas ahora pueden tener argumentos escritos según sea necesario en lugar de un array de opciones. +Por ejemplo, un finder para obtener publicaciones por categoría o usuario:: + + public function findByCategoryOrUser(SelectQuery $query, array $options) + { + if (isset($options['categoryId'])) { + $query->where(['category_id' => $options['categoryId']]); + } + if (isset($options['userId'])) { + $query->where(['user_id' => $options['userId']]); + } + + return $query; + } + +Ahora se pueden escribir como:: + + public function findByCategoryOrUser(SelectQuery $query, ?int $categoryId = null, ?int $userId = null) + { + if ($categoryId) { + $query->where(['category_id' => $categoryId]); + } + if ($userId) { + $query->where(['user_id' => $userId]); + } + + return $query; + } + +El finder puede ser llamado como ``find('byCategoryOrUser', userId: $somevar)``. +Incluso puedes incluir los argumentos con nombre especial para establecer cláusulas de consulta. +``find('byCategoryOrUser', userId: $somevar, conditions: ['enabled' => true])``. + +Un cambio similar se ha aplicado al método ``RepositoryInterface::get()``:: + + public function view(int $id) + { + $author = $this->Authors->get($id, [ + 'contain' => ['Books'], + 'finder' => 'latest', + ]); + } + +Ahora se pueden escribir como:: + + public function view(int $id) + { + $author = $this->Authors->get($id, contain: ['Books'], finder: 'latest'); + } + +TestSuite +--------- + +- Se ha añadido ``IntegrationTestTrait::requestAsJson()`` para establecer encabezados JSON para la siguiente solicitud. + +Instalador de plugins +--------------------- +- El instalador de plugins se ha actualizado para manejar automáticamente la carga automática de clases para los plugins + de tu aplicación. Por lo tanto, puedes eliminar el espacio de nombres para las asignaciones de rutas de + tus plugins del ``composer.json`` y simplemente ejecutar ``composer dumpautoload``. + +.. meta:: + :title lang=es: 5.0 Guía de migración + :keywords lang=es: 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/es/appendices/5-0-upgrade-guide.rst b/es/appendices/5-0-upgrade-guide.rst new file mode 100644 index 0000000000..921bf05820 --- /dev/null +++ b/es/appendices/5-0-upgrade-guide.rst @@ -0,0 +1,77 @@ +5.0 Guía de actualización +######################### + +En primer lugar, compruebe que su aplicación se está ejecutando en la última versión de CakePHP 4.x. + +Arreglar avisos de obsolescencia +================================ + +Una vez que su aplicación se ejecuta en la última version de CakePHP 4.x, activar advertencias de obsoletos en **config/app.php**:: + + 'Error' => [ + 'errorLevel' => E_ALL, + ] + +Ahora que puede ver todas las advertencias, asegúrese de que están corregidas antes de proceder con la actualización. + +Algunas obsolescencia potencialmente impactantes que debes asegurarte de haber abordado +son: + +- ``Table::query()`` was deprecated in 4.5.0. Use ``selectQuery()``, + ``updateQuery()``, ``insertQuery()`` and ``deleteQuery()`` instead. + +Actualiza a PHP 8.1 +=================== + +Si no estas ejecutando en **PHP 8.1 o superior**, tendrás que actualizar PHP antes de actualizar CakePHP. + +.. note:: + CakePHP 5.0 requiere **un mínimo de PHP 8.1**. + +.. _upgrade-tool-use: + +Usar la herramienta de actualización +==================================== + +.. note:: + La herramienta de actualización sólo funciona en aplicaciones que se ejecutan en cakePHP 4.x. No puedes ejecutar la herramienta de actualización después de actualizar a CakePHP 5.0. + +Debido a que CakePHP 5 aprovecha los tipos de unión y ``mixed``, existen muchos +cambios incompatibles con versiones anteriores relativas a las definiciones de los métodos y cambios de nombre archivos. +Para ayudar a acelerar los arreglos de estos cambios tediosos, existe una herramienta CLI de actualización: + +.. code-block:: console + + # Instalar la herramienta de actualización + git clone https://github.com/cakephp/upgrade + cd upgrade + git checkout 5.x + composer install --no-dev + +Con la herramienta de actualización instalada, ahora puedes ejecutarla en su aplicación o +plugin:: + + bin/cake upgrade rector --rules cakephp50 + bin/cake upgrade rector --rules chronos3 + +Actualizar dependencias de CakePHP +================================== + +Después de aplicar las refactorizaciones de Rector necesitas actualizar CakePHP, sus plugins, PHPUnit +y tal vez otras dependencias en el ``composer.json``. +Este proceso depende de gran medida de tu aplicación por lo que te recomendamos que compares el +``composer.json`` con el que está presente en `cakephp/app +`__. + +After the version strings are adjusted in your ``composer.json`` execute +``composer update -W`` and check its output. + +Actualiza los archivos de la aplicación basándose en las últimas plantillas +=========================================================================== + +A continuación, asegúrate de que el resto de tu aplicación esté actualizado basándose en la última version de `cakephp/app +`__. + +.. meta:: + :title lang=es: 5.0 Guía de actualización + :keywords lang=es: 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/es/appendices/cakephp-development-process.rst b/es/appendices/cakephp-development-process.rst index 770870ec5c..b3e4c24174 100644 --- a/es/appendices/cakephp-development-process.rst +++ b/es/appendices/cakephp-development-process.rst @@ -1,15 +1,60 @@ CakePHP Development Process ########################### -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +Los proyectos de CakePHP en general siguen `semver `__. Ésto significa que: - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +- Las versiones se numeran en el formato **A.B.C** +- Las versiones **A** son *lanzamientos principales*. Contienen cambios importantes y + requerirán una cantidad significativa de trabajo para actualizar desde una version **A** inferior. +- Las versiones **A.B** son *lanzamientos de mejoras*. Cada versión será compatible con + las anteriores, pero puede marcar algunas características como **obsoletas**. Si es absolutamente + necesario realizar un cambio que rompa la compatibilidad, se indicará en la guía de migración para ese lanzamiento. +- Las versiones **A.B.C** son *lanzamientos de parches*. Deben ser compatibles con el lanzamiento de parche anterior. La excepción + a esta regla es si se descubre un problema de seguridad y la única solución es romper una API existente. - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Consulta el :doc:/contributing/backwards-compatibility para ver lo que consideramos como compatible con versiones previas y cambios que rompen la compatibilidad. + +Lanzamientos Principales +========================= + +Los lanzamientos principales introducen nuevas características y pueden eliminar funcionalidades que se hayan +marcado como obsoletas en un lanzamiento anterior. Estos lanzamientos se encuentran en las ramas ``next`` +que coinciden con su número de versión, como ``5.next``. Una vez que se lanzan, se promocionan a la rama +``master`` y luego la rama ``5.next`` se utiliza para futuros lanzamientos de características. + +Lanzamientos de Mejoras +======================== + +Los lanzamientos de mejoras son donde se envían nuevas funcionalidades o extensiones a las funcionalidades +existentes. Cada serie de lanzamientos que recibe actualizaciones tendrá una rama ``next``, por ejemplo, ``4.next``. +Si deseas contribuir con una nueva característica, por favor dirígete a estas ramas. + +Lanzamientos de Parches +======================== + +Los lanzamientos de parches corrigen errores en el código/documentación existente y siempre deben ser compatibles +con los lanzamientos de parches anteriores de la misma serie. Estos lanzamientos +se crean a partir de las ramas estables. Las ramas estables a menudo se nombran según la serie de lanzamientos, como ``3.x``. + +Frecuencia de Lanzamiento +========================== + +- Los *Lanzamientos Principales* se entregan aproximadamente cada dos o tres años. Este período de tiempo nos obliga a + ser deliberados y considerados con los cambios que rompen la compatibilidad, y brinda tiempo a la comunidad para + ponerse al día sin sentir que se están quedando atrás. +- Los *Lanzamientos de Mejoras* se entregan cada cinco a ocho meses. +- Los *Lanzamientos de Parches* se entregan inicialmente cada dos semanas. A medida que un lanzamiento de características madura, esta frecuencia se relaja a una entrega mensual. + +Política de Obsolescencia +========================== + +Antes de que una característica pueda ser eliminada en un lanzamiento principal, necesita ser marcada como obsoleta. Cuando +una funcionalidad se marca como obsoleta en el lanzamiento **A.x**, seguirá funcionando durante el resto de todos los lanzamientos +**A.x**. Las obsolescencias generalmente se indican mediante advertencias en PHP. Puedes habilitar las advertencias de obsolescencia +agregando ``E_USER_DEPRECATED`` al valor de ``Error.level`` de tu aplicación. + +El comportamiento marcado como obsoleto no se elimina hasta el próximo lanzamiento principal. Por ejemplo, un comportamiento marcado como obsoleto en ``4.1`` se eliminará en ``5.0``. .. meta:: - :title lang=es: CakePHP Development Process - :keywords lang=es: 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 + :title lang=es: Proceso de Desarrollo CakePHP + :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/es/appendices/glossary.rst b/es/appendices/glossary.rst index f424acae5c..644cab68c2 100644 --- a/es/appendices/glossary.rst +++ b/es/appendices/glossary.rst @@ -3,8 +3,8 @@ Glosario .. glossary:: - array de rutas - Un array de atributos que son pasados a :php:meth:`Router::url()`. + arreglo de enrutamiento + Un arreglo de atributos que son pasados a :php:meth:`Router::url()`. Típicamente se ve algo así:: ['controller' => 'Posts', 'action' => 'view', 5] @@ -85,4 +85,4 @@ Glosario .. meta:: :title lang=es: Glosario - :keywords lang=en: html attributes,array class,array controller,glossary glossary,target blank,dot notation,routing configuration,forgery,replay,router,syntax,config,submissions + :keywords lang=en: html attributes,array class,array controller,glossary,target blank,dot notation,routing configuration,forgery,replay,router,syntax,config,submissions diff --git a/es/appendices/migration-guides.rst b/es/appendices/migration-guides.rst new file mode 100644 index 0000000000..ed157548e7 --- /dev/null +++ b/es/appendices/migration-guides.rst @@ -0,0 +1,16 @@ +Guías de migración +################## + +Las guías de migración contienen información relativa a las nuevas funcionalidades introducidas en +cada versión y la manera de migrar entre 4.x y 5.x + +.. toctree:: + :maxdepth: 1 + + ./5-0-upgrade-guide + ./5-0-migration-guide + ./phpunit10 + +.. meta:: + :title lang=es: Guías de migración + :keywords lang=es: 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/es/appendices/phpunit10.rst b/es/appendices/phpunit10.rst new file mode 100644 index 0000000000..f9071f0ef6 --- /dev/null +++ b/es/appendices/phpunit10.rst @@ -0,0 +1,69 @@ +Actualización a PHPUnit 10 +########################## + +Con CakePHP 5 la version mínima de PHPUnit ha cambiado de ``^8.5 || ^9.3`` a ``^10.1``. +Esto introduce algunos cambios importantes tanto por parte de PHPUnit como por parte de CakePHP. + +Ajustes de phpunit.xml +====================== + +Se recomienda dejar que PHPUnit actualice su archivo de configuración a través del siguiente comando:: + + vendor/bin/phpunit --migrate-configuration + +.. note:: + + ¡Asegúrese de que ya está en PHPUnit 10 a través de ``vendor/bin/phpunit --version`` antes de ejecutar este comando! + +Una vez hayas ejecutado este comando, tu ``phpunit.xml`` tendrá mayoría de los cambios recomendados. + +Nuevo sistema de eventos +------------------------ + +PHPUnit 10 eliminó el antiguo sistema de hook e introdujo un nuevo `Sistema de eventos +`_ +Lo que requiere que se ajuste el siguiente código en su ``phpunit.xml`` desde:: + + + + + +a:: + + + + + +``->withConsecutive()`` ha sido eliminado +========================================= + +Puedes convertir el metodo ``->withConsecutive()`` eliminado +en una solución provisional que funcione como puede ver aquí:: + + ->withConsecutive(['firstCallArg'], ['secondCallArg']) + +debe convertirse a:: + + ->with( + ...self::withConsecutive(['firstCallArg'], ['secondCallArg']) + ) + +se ha añadido el método estático ``self::withConsecutive()`` a través del método ``Cake\TestSuite\PHPUnitConsecutiveTrait`` +a la clase base ``Cake\TestSuite\TestCase`` para que no tenga que agregar manualmente este trait a tus clases de TestCase. + +Los proveedores de datos tienen que ser estáticos +================================================= + +Si tus testcases aprovechan la función de proveedor de datos de PHPUnit entonces +tienes que ajustar tus proveedores de datos para que sean estáticos:: + + public function myProvider(): array + +debe convertirse en:: + + public static function myProvider(): array + + +.. meta:: + :title lang=es: Actualización a PHPUnit 10 + :keywords lang=es: 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/es/console-and-shells.rst b/es/console-and-shells.rst deleted file mode 100644 index b7220cb2ab..0000000000 --- a/es/console-and-shells.rst +++ /dev/null @@ -1,34 +0,0 @@ -Shells, Tasks & Console Tools -############################# - -.. php:namespace:: Cake\Console - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -More Topics -=========== - -.. toctree:: - :maxdepth: 1 - - console-and-shells/helpers - console-and-shells/repl - console-and-shells/cron-jobs - console-and-shells/i18n-shell - console-and-shells/completion-shell - console-and-shells/plugin-shell - console-and-shells/routes-shell - console-and-shells/upgrade-shell - console-and-shells/server-shell - console-and-shells/cache - -.. meta:: - :title lang=es: Shells, Tasks & Console Tools - :keywords lang=es: shell scripts,system shell,application classes,background tasks,line script,cron job,request response,system path,acl,new projects,shells,specifics,parameters,i18n,cakephp,directory,maintenance,ideal,applications,mvc diff --git a/es/console-and-shells/cron-jobs.rst b/es/console-and-shells/cron-jobs.rst deleted file mode 100644 index 70be87e75b..0000000000 --- a/es/console-and-shells/cron-jobs.rst +++ /dev/null @@ -1,15 +0,0 @@ -Running Shells as Cron Jobs -########################### - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Running Shells as cronjobs - :keywords lang=es: cronjob,bash script,crontab diff --git a/es/console-and-shells/i18n-shell.rst b/es/console-and-shells/i18n-shell.rst deleted file mode 100644 index 2bfe87ffc2..0000000000 --- a/es/console-and-shells/i18n-shell.rst +++ /dev/null @@ -1,15 +0,0 @@ -I18N Shell -########## - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: I18N shell - :keywords lang=es: pot files,locale default,translation tools,message string,app locale,php class,validation,i18n,translations,shell,models diff --git a/es/console-and-shells/routes-shell.rst b/es/console-and-shells/routes-shell.rst deleted file mode 100644 index 03e8e1b6a5..0000000000 --- a/es/console-and-shells/routes-shell.rst +++ /dev/null @@ -1,11 +0,0 @@ -Routes Shell -############ - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. diff --git a/es/console-and-shells/server-shell.rst b/es/console-and-shells/server-shell.rst deleted file mode 100644 index 42e18df23d..0000000000 --- a/es/console-and-shells/server-shell.rst +++ /dev/null @@ -1,11 +0,0 @@ -Server Shell -############ - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. diff --git a/es/console-and-shells/upgrade-shell.rst b/es/console-and-shells/upgrade-shell.rst deleted file mode 100644 index 498b97b7de..0000000000 --- a/es/console-and-shells/upgrade-shell.rst +++ /dev/null @@ -1,17 +0,0 @@ -.. _upgrade-shell: - -Upgrade Shell -############# - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Upgrade Shell - :keywords lang=es: api docs,shell,upgrade diff --git a/es/console-commands.rst b/es/console-commands.rst new file mode 100644 index 0000000000..a7b47f1436 --- /dev/null +++ b/es/console-commands.rst @@ -0,0 +1,176 @@ +Comandos de Consola +################### + +.. php:namespace:: Cake\Console + +Además de ser un `framework` web, CakePHP también ofrece un `framework` +de consola para crear herramientas y aplicaciones de línea de comandos. Las aplicaciones +de consola son ideales para gestionar una variedad de tareas de mantenimiento +que aprovechan la configuración existente de tu aplicación, modelos, complementos y lógica de dominio. + +CakePHP proporciona varias herramientas de consola para interactuar con sus características, como i18n +y enrutamiento, lo que te permite inspeccionar tu aplicación y generar archivos relacionados. + +La Consola CakePHP +=================== + +La Console CakePHP utiliza un sistema de tipo `dispatcher` para cargar comandos, analizar sus +argumentos e invocar el comando correcto. Aunque los ejemplos a continuación usan bash, el +console de CakePHP es compatible con cualquier shell de Unix (\*nix) y Windows. + +Una aplicación CakePHP tiene un directorio **src/Command** que contiene sus comandos. También incluye +un ejecutable en el directorio **bin** + +.. code-block:: console + + $ cd /path/to/app + $ bin/cake + +.. note:: + + Para Windows, el comando es ``bin\cake`` (note el `backslash`) + +Ejecutar la console sin argumentos listará todos los comandos disponibles. Tú +puedes, de esta manera, ejecutar cualquiera de los comandos listados usando su nombre: + +.. code-block:: console + + # ejecutar el comando server + bin/cake server + + # ejecutar el comando migrations + bin/cake migrations -h + + # ejecutar bake (con un prefijo de `plugin`) + bin/cake bake.bake -h + +Los comandos de los `plugins` pueden ser invocados sin un prefijo de `plugin` si el nombre del +comando no coincide con un comando de la aplicación o del `framework`. En el caso de que dos `plugins` +proporcionen un comando con el mismo nombre, el `plugin` que se ha cargado primero obtendrá el alias corto. Siempre +puedes utilizar el formato ``plugin.command`` para hacer referencia de manera inequívoca a un comando. + + +Aplicaciones de Consola +======================= + +Por defecto, CakePHP descubrirá automáticamente todos los comandos en tu aplicación y sus complementos. Puede que +desees reducir el número de comandos expuestos al construir aplicaciones de consola independientes. Puedes utilizar +el método ``console()`` de tu clase ``Application`` para limitar qué comandos se exponen y renombrar los comandos que se exponen:: + + // en src/Application.php + namespace App; + + use App\Command\UserCommand; + use App\Command\VersionCommand; + use Cake\Console\CommandCollection; + use Cake\Http\BaseApplication; + + class Application extends BaseApplication + { + public function console(CommandCollection $commands): CommandCollection + { + // Agregar por clase + $commands->add('user', UserCommand::class); + + // Agregar instancia + $commands->add('version', new VersionCommand()); + + return $commands; + } + } + +En el ejemplo anterior, los únicos comandos disponibles serían ``help``, ``version`` y ``user``. +Revisa la sección :ref:`plugin-commands` sobre como agregar comandos en los `plugins`. + +.. note:: + + Cuando agregas múltiples comandos que usan la misma clase, el comando ``help`` mostrará la opción más corta. + +.. _renaming-commands: +.. index:: nested commands, subcommands + +Renombrando Comandos +==================== + +Hay casos en los cuales querrás renombrar comandos para crear comandos anidados o subcomandos. Mientras que +el descubrimiento automático de comandos no hará esto, tu pueds registrar tus comandos para darles el nombre +que desees. + +Puedes personalizar los nombre de los comandos definiéndolo en tu método ``console()``:: + + public function console(CommandCollection $commands): CommandCollection + { + // Agregar comandos anidados (subcomandos) + $commands->add('user dump', UserDumpCommand::class); + $commands->add('user:show', UserShowCommand::class); + + // Renombrar un comando completamente + $commands->add('lazer', UserDeleteCommand::class); + + return $commands; + } + +Cuando utilizas el método ``console()`` en tu aplicación, recuerda llamar +``$commands->autoDiscover()`` para agregar los comandos de CakePHP, de tu aplicación y +de tus `plugins`. + +Si necesitas renombrar/eliminar cualquier comando disponible, puedes usar el evento ``Console.buildCommands`` en +tu manejador de eventos para modificarlos. + +Comandos +======== + +Echa un vistazo al capítulo :doc:`/console-commands/commands` sobre como crear tu primer +comando. Luego aprende más sobre comandos. + +.. toctree:: + :maxdepth: 1 + + console-commands/commands + console-commands/input-output + console-commands/option-parsers + console-commands/cron-jobs + +Comandos provistos por CakePHP +============================== + +.. toctree:: + :maxdepth: 1 + + console-commands/cache + console-commands/completion + console-commands/i18n + console-commands/plugin + console-commands/schema-cache + console-commands/routes + console-commands/server + console-commands/repl + +Enrutando en el ambiente de consola +=================================== + +In command-line interface (CLI), specifically your console commands, +``env('HTTP_HOST')`` and other webbrowser specific environment variables are not +set. + +If you generate reports or send emails that make use of ``Router::url()`` those +will contain the default host ``http://localhost/`` and thus resulting in +invalid URLs. In this case you need to specify the domain manually. +You can do that using the Configure value ``App.fullBaseUrl`` from your +bootstrap or config, for example. + +For sending emails, you should provide Email class with the host you want to +send the email with:: + + use Cake\Mailer\Email; + + $email = new Email(); + $email->setDomain('www.example.org'); + +This asserts that the generated message IDs are valid and fit to the domain the +emails are sent from. + + +.. meta:: + :title lang=es: Comandos, Tareas & Herramientas de Consola + :keywords lang=es: shell scripts,system shell,application classes,background tasks,line script,cron job,request response,system path,acl,new projects,commands,specifics,parameters,i18n,cakephp,directory,maintenance,ideal,applications,mvc diff --git a/es/console-commands/cache.rst b/es/console-commands/cache.rst new file mode 100644 index 0000000000..a44a002be6 --- /dev/null +++ b/es/console-commands/cache.rst @@ -0,0 +1,14 @@ +Herramienta de caché +#################### + +Para ayudarlo a administrar mejor los datos almacenados en caché desde un entorno CLI, un comando de consola +está disponible para borrar los datos almacenados en caché que tiene su aplicación:: + + // Borrar una configuración de caché + bin/cake cache clear + + // Borrar todas las configuraciones de caché + bin/cake cache clear_all + + // Borrar un grupo de caché + bin/cake cache clear_group diff --git a/es/console-commands/commands.rst b/es/console-commands/commands.rst new file mode 100644 index 0000000000..5d80bcd7c0 --- /dev/null +++ b/es/console-commands/commands.rst @@ -0,0 +1,547 @@ +Objetos de comando +################## + +.. php:namespace:: Cake\Console +.. php:class:: Command + +CakePHP viene con una serie de comandos integrados para acelerar tu desarrollo y automatización de tareas rutinarias. +Puede utilizar estas mismas bibliotecas para crear comandos para su aplicación y complementos. + +Creando un comando +================== + +Creemos nuestro primer comando. Para este ejemplo, crearemos un comando simple Hola mundo. En el directorio +**src/Command** de su aplicación, cree **HelloCommand.php**. Coloca el siguiente código dentro:: + + out('Hello world.'); + + return static::CODE_SUCCESS; + } + } + +Las clases de comando deben implementar un método ``execute()`` que haga la mayor parte del trabajo. Este método se +llama cuando se invoca un comando. Llamemos a nuestro primer comando de aplicación, ejecute:: + +.. code-block:: console + + bin/cake hello + +Debería ver el siguiente resultado:: + + Hello world. + +Nuestro método ``execute()`` no es muy interesante, leamos algunas entradas desde la línea de comando:: + + addArgument('name', [ + 'help' => 'What is your name', + ]); + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + $io->out("Hello {$name}."); + + return static::CODE_SUCCESS; + } + } + + +Después de guardar este archivo, debería poder ejecutar el siguiente comando:: + +.. code-block:: console + + bin/cake hello jillian + + # Outputs + Hello jillian + +Cambiar el nombre del comando predeterminado +============================================ + +CakePHP usará convenciones para generar el nombre que usan sus comandos en la línea de comando. Si desea sobrescribir +el nombre generado, implemente el método ``defaultName()`` en tu comando:: + + public static function defaultName(): string + { + return 'oh_hi'; + } + +Lo anterior haría que nuestro ``HelloCommand`` fuera accesible mediante ``cake oh_hi`` en lugar de ``cake hello``. + +Definición de argumentos y opciones +=================================== + +Como vimos en el último ejemplo, podemos usar el método ``buildOptionParser()`` para definir argumentos. También +podemos definir opciones. Por ejemplo, podríamos agregar una opción ``yell`` a nuestro ``HelloCommand``:: + + // ... + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser + ->addArgument('name', [ + 'help' => 'What is your name', + ]) + ->addOption('yell', [ + 'help' => 'Shout the name', + 'boolean' => true, + ]); + + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + if ($args->getOption('yell')) { + $name = mb_strtoupper($name); + } + $io->out("Hello {$name}."); + + return static::CODE_SUCCESS; + } + +Consulte la sección :doc:`/console-commands/option-parsers` para obtener más información. + +Creando la salida +================= + +Los comandos proporcionan la instancia ``ConsoleIo`` cuando se ejecutan. Este objeto le permite interactuar con +``stdout``, ``stderr`` y crear archivos. Consulte la sección :doc:`/console-commands/input-output` para obtener +más información. + +Usar modelos en comandos +======================== + +Utilice modelos en comandos. A menudo necesitará acceso a la lógica de negocio de su aplicación en los comandos +de la consola. Puede cargar modelos en comandos, tal como lo haría en un controlador usando ``$this->fetchTable()`` +ya que el comando usa ``LocatorAwareTrait``:: + + addArgument('name', [ + 'help' => 'What is your name' + ]); + + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + $user = $this->fetchTable()->findByUsername($name)->first(); + + $io->out(print_r($user, true)); + + return static::CODE_SUCCESS; + } + } + +El comando anterior buscará un usuario por nombre de usuario y mostrará la información almacenada en la base de datos. + +Códigos de salida y detención de la ejecución +============================================= + +Cuando sus comandos alcanzan un error irrecuperable, puede utilizar el método ``abort()`` para finalizar la ejecución:: + + // ... + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + if (strlen($name) < 5) { + // Detener la ejecución, enviar a stderr y establecer el código de salida en 1 + $io->error('Name must be at least 4 characters long.'); + $this->abort(); + } + + return static::CODE_SUCCESS; + } + +También puedes usar ``abort()`` en el objeto ``$io`` para emitir un mensaje y código:: + + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + if (strlen($name) < 5) { + // Detener la ejecución, enviar a stderr y establecer el código de salida en 99 + $io->abort('Name must be at least 4 characters long.', 99); + } + + return static::CODE_SUCCESS; + } + +Puede pasar cualquier código de salida que desee a ``abort()``. + +.. tip:: + + Evite los códigos de salida 64 - 78, ya que tienen significados específicos descritos por ``sysexits.h``. + Evite los códigos de salida superiores a 127, ya que se utilizan para indicar la salida del proceso mediante + una señal, como SIGKILL o SIGSEGV. + + Puede leer más sobre los códigos de salida convencionales en la página del manual de sysexit en la mayoría de + los sistemas Unix (``man sysexits``), o en la página de ayuda ``Códigos de error del sistema`` en Windows. + +Llamar a otros comandos +======================== + +Es posible que necesite llamar a otros comandos desde tu comando. Puedes usar ``executeCommand`` para hacer eso:: + + // Puede pasar una variedad de opciones y argumentos de CLI. + $this->executeCommand(OtherCommand::class, ['--verbose', 'deploy']); + + // Puede pasar una instancia del comando si tiene argumentos de constructor + $command = new OtherCommand($otherArgs); + $this->executeCommand($command, ['--verbose', 'deploy']); + +.. note:: + + Al llamar a ``executeCommand()`` en un bucle, se recomienda pasar la instancia ``ConsoleIo`` del comando principal + como tercer argumento opcional para evitar un posible límite de "archivos abiertos" que podría ocurrir en algunos + entornos. + +Configurando descripción del comando +==================================== + +Es posible que desee establecer una descripción de comando a través de:: + + class UserCommand extends Command + { + public static function getDescription(): string + { + return 'My custom description'; + } + } + +Esto mostrará la descripción en Cake CLI: + +.. code-block:: console + + bin/cake + + App: + - user + └─── My custom description + +Así como en la sección de ayuda de tu comando: + +.. code-block:: console + + cake user --help + My custom description + + Usage: + cake user [-h] [-q] [-v] + +.. _console-integration-testing: + +Pruebas de comandos +=================== + +Para facilitar las pruebas de aplicaciones de consola, CakePHP viene con un rasgo (trait) +``ConsoleIntegrationTestTrait`` que puede usarse para probar aplicaciones de consola y comparar sus resultados. + +Para comenzar a probar su aplicación de consola, cree un caso de prueba que utilice el rasgo +``Cake\TestSuite\ConsoleIntegrationTestTrait``. Este rasgo contiene un método ``exec()`` que se utiliza +para ejecutar su comando. Puede pasar la misma cadena que usaría en la CLI a este método. + +.. note:: + + Para CakePHP 4.4 en adelante, se debe utilizar el espacio de nombres + ``Cake\Console\TestSuite\ConsoleIntegrationTestTrait``. + +Comencemos con un comando muy simple, ubicado en **src/Command/UpdateTableCommand.php**:: + + namespace App\Command; + + use Cake\Command\Command; + use Cake\Console\Arguments; + use Cake\Console\ConsoleIo; + use Cake\Console\ConsoleOptionParser; + + class UpdateTableCommand extends Command + { + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser->setDescription('My cool console app'); + + return $parser; + } + } + +Para escribir una prueba de integración para este comando, crearíamos un caso de prueba en +**tests/TestCase/Command/UpdateTableTest.php** que use el rasgo ``Cake\TestSuite\ConsoleIntegrationTestTrait``. +Este comando no hace mucho por el momento, pero probemos que la descripción de nuestro comando +se muestre en ``stdout``:: + + namespace App\Test\TestCase\Command; + + use Cake\TestSuite\ConsoleIntegrationTestTrait; + use Cake\TestSuite\TestCase; + + class UpdateTableCommandTest extends TestCase + { + use ConsoleIntegrationTestTrait; + + public function testDescriptionOutput() + { + $this->exec('update_table --help'); + $this->assertOutputContains('My cool console app'); + } + } + +¡Nuestra prueba pasa! Si bien este es un ejemplo muy trivial, muestra que la creación de un caso de +prueba de integración para aplicaciones de consola puede seguir las convenciones de la línea de comandos. +Sigamos agregando más lógica a nuestro comando:: + + namespace App\Command; + + use Cake\Command\Command; + use Cake\Console\Arguments; + use Cake\Console\ConsoleIo; + use Cake\Console\ConsoleOptionParser; + use Cake\I18n\DateTime; + + class UpdateTableCommand extends Command + { + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser + ->setDescription('My cool console app') + ->addArgument('table', [ + 'help' => 'Table to update', + 'required' => true + ]); + + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io): int + { + $table = $args->getArgument('table'); + $this->fetchTable($table)->updateQuery() + ->set([ + 'modified' => new DateTime() + ]) + ->execute(); + + return static::CODE_SUCCESS; + } + } + +Este es un comando más completo que tiene opciones requeridas y lógica relevante. +Modifique su caso de prueba al siguiente fragmento de código:: + + namespace Cake\Test\TestCase\Command; + + use Cake\Command\Command; + use Cake\I18n\DateTime; + use Cake\TestSuite\ConsoleIntegrationTestTrait; + use Cake\TestSuite\TestCase; + + class UpdateTableCommandTest extends TestCase + { + use ConsoleIntegrationTestTrait; + + protected $fixtures = [ + // Se supone que tienes un UsersFixture + 'app.Users', + ]; + + public function testDescriptionOutput() + { + $this->exec('update_table --help'); + $this->assertOutputContains('My cool console app'); + } + + public function testUpdateModified() + { + $now = new DateTime('2017-01-01 00:00:00'); + DateTime::setTestNow($now); + + $this->loadFixtures('Users'); + + $this->exec('update_table Users'); + $this->assertExitCode(Command::CODE_SUCCESS); + + $user = $this->getTableLocator()->get('Users')->get(1); + $this->assertSame($user->modified->timestamp, $now->timestamp); + + DateTime::setTestNow(null); + } + } + +Como puede ver en el método ``testUpdateModified``, estamos probando que nuestro comando actualice +la tabla que pasamos como primer argumento. Primero, afirmamos que el comando salió con el código +de estado adecuado, ``0``. Luego verificamos que nuestro comando hizo su trabajo, es decir, actualizamos +la tabla que proporcionamos y configuramos la columna ``modificada`` a la hora actual. + +Recuerde, ``exec()`` tomará la misma cadena que escriba en su CLI, por lo que puede incluir +opciones y argumentos en su cadena de comando. + +Prueba de shells interactivos +----------------------------- + +Las consolas suelen ser interactivas. Probar comandos interactivos con el rasgo +``Cake\TestSuite\ConsoleIntegrationTestTrait`` solo requiere pasar las entradas que espera como segundo +parámetro de ``exec()``. Deben incluirse como una matriz en el orden esperado. + +Continuando con nuestro comando de ejemplo, agreguemos una confirmación interactiva. +Actualice la clase de comando a lo siguiente:: + + namespace App\Command; + + use Cake\Command\Command; + use Cake\Console\Arguments; + use Cake\Console\ConsoleIo; + use Cake\Console\ConsoleOptionParser; + use Cake\I18n\DateTime; + + class UpdateTableCommand extends Command + { + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + $parser + ->setDescription('My cool console app') + ->addArgument('table', [ + 'help' => 'Table to update', + 'required' => true + ]); + + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io): int + { + $table = $args->getArgument('table'); + if ($io->ask('Are you sure?', 'n', ['y', 'n']) !== 'y') { + $io->error('You need to be sure.'); + $this->abort(); + } + $this->fetchTable($table)->updateQuery() + ->set([ + 'modified' => new DateTime() + ]) + ->execute(); + + return static::CODE_SUCCESS; + } + } + +Ahora que tenemos un subcomando interactivo, podemos agregar un caso de prueba que pruebe que recibimos +la respuesta adecuada y otro que pruebe que recibimos una respuesta incorrecta. +Elimine el método ``testUpdateModified`` y agregue los siguientes métodos a +**tests/TestCase/Command/UpdateTableCommandTest.php**:: + + + public function testUpdateModifiedSure() + { + $now = new DateTime('2017-01-01 00:00:00'); + DateTime::setTestNow($now); + + $this->loadFixtures('Users'); + + $this->exec('update_table Users', ['y']); + $this->assertExitCode(Command::CODE_SUCCESS); + + $user = $this->getTableLocator()->get('Users')->get(1); + $this->assertSame($user->modified->timestamp, $now->timestamp); + + DateTime::setTestNow(null); + } + + public function testUpdateModifiedUnsure() + { + $user = $this->getTableLocator()->get('Users')->get(1); + $original = $user->modified->timestamp; + + $this->exec('my_console best_framework', ['n']); + $this->assertExitCode(Command::CODE_ERROR); + $this->assertErrorContains('You need to be sure.'); + + $user = $this->getTableLocator()->get('Users')->get(1); + $this->assertSame($original, $user->timestamp); + } + +En el primer caso de prueba, confirmamos la pregunta y se actualizan los registros. En la segunda prueba +no confirmamos y los registros no se actualizan, y podemos verificar que nuestro mensaje de error +fue escrito en ``stderr``. + +Metodos de aserción +------------------- + +El rasgo ``Cake\TestSuite\ConsoleIntegrationTestTrait`` proporciona una serie de métodos de aserción +que ayudan a afirmar contra la salida de la consola:: + + // afirmar que el comando salió con éxito + $this->assertExitSuccess(); + + // afirmar que el comando salió como un error + $this->assertExitError(); + + // afirmar que el comando salió con el código esperado + $this->assertExitCode($expected); + + // afirmar que la salida estándar contiene un texto + $this->assertOutputContains($expected); + + // afirmar que stderr contiene una texto + $this->assertErrorContains($expected); + + // afirmar que la salida estándar coincide con una expresión regular + $this->assertOutputRegExp($expected); + + // afirmar que stderr coincide con una expresión regular + $this->assertErrorRegExp($expected); + +Ciclo de vida de las devoluciones de llamada +============================================= + +Al igual que los controladores, los comandos ofrecen eventos de ciclo de vida que le permiten observar +el marco que llama al código de su aplicación. Los comandos tienen: + +- ``Command.beforeExecute`` Se llama antes que el método ``execute()`` de un comando. + Al evento se le pasa el parámetro ``ConsoleArguments`` como ``args``. + Este evento no se puede detener ni reemplazar su resultado. +- ``Command.afterExecute`` Se llama después de que se completa el método ``execute()`` + de un comando. El evento contiene ``ConsoleArguments`` como ``args`` y el resultado + del comando como ``result``. Este evento no se puede detener ni reemplazar su resultado. diff --git a/es/console-commands/completion.rst b/es/console-commands/completion.rst new file mode 100644 index 0000000000..351841f754 --- /dev/null +++ b/es/console-commands/completion.rst @@ -0,0 +1,182 @@ +Herramienta de completación +########################### + +Trabajar con la consola le brinda al desarrollador muchas posibilidades, pero tener que conocer y escribir +completamente esos comandos puede resultar tedioso. Especialmente cuando se desarrollan nuevos shells donde +los comandos difieren por minuto de iteración. Completion Shells ayuda en este asunto al proporcionar una +API para escribir scripts de completación para shells como bash, zsh, fish, etc. + +Sub Comandos +============ + +El Shell de completación consta de varios subcomandos para ayudar al desarrollador a crear su script +de finalización. Cada uno para un paso diferente en el proceso de autocompletar. + +Comandos +-------- + +Para los comandos del primer paso, se generan los comandos de Shell disponibles, incluido el nombre del +complemento cuando corresponda. (Todas las posibilidades devueltas, para este y otros subcomandos, están +separadas por un espacio). Por ejemplo:: + + bin/cake Completion commands + +Regresará:: + + acl api bake command_list completion console i18n schema server test testsuite upgrade + +Su secuencia de comandos de completación puede seleccionar los comandos relevantes de esa lista para continuar. +(Para este y los siguientes subcomandos). + +subcomandos +----------- + +Una vez que se ha elegido el comando preferido, los subCommands entran como segundo paso y generan +el posible subcomando para el comando de shell dado. Por ejemplo:: + + bin/cake Completion subcommands bake + +Regresará:: + + controller db_config fixture model plugin project test view + +opciones +-------- + +Como tercera y última opción, genera opciones para el (sub)comando dado, tal como se establece en ``getOptionParser``. +(Incluidas las opciones predeterminadas heredadas de Shell). +Por ejemplo:: + + bin/cake Completion options bake + +Regresará:: + + --help -h --verbose -v --quiet -q --everything --connection -c --force -f --plugin -p --prefix --theme -t + +También puede pasar un argumento adicional que sea el subcomando del shell: generará las opciones +específicas de este subcomando. + +Cómo habilitar el autocompletado de Bash para la consola CakePHP +================================================================ + +Primero, asegúrese de que la biblioteca **bash-completion** esté instalada. +Si no, lo haces con el siguiente comando:: + + apt-get install bash-completion + +Cree un archivo llamado **cake** en **/etc/bash_completion.d/** y coloque el +:ref:`bash-completion-file-content` dentro de él. + +Guarde el archivo y luego reinicie su consola. + +.. note:: + + Si está utilizando MacOS X, puede instalar la biblioteca **bash-completion** usando **homebrew** + con el comando ``brew install bash-completion``. + El directorio de destino para el archivo **cake** será **/usr/local/etc/bash_completion.d/**. + +.. _bash-completion-file-content: + +Contenido del archivo de completación de Bash +---------------------------------------------- + +Este es el código que debes colocar dentro del archivo **cake** en la ubicación correcta para obtener el autocompletado al usar la consola CakePHP: + +.. code-block:: bash + + # + # Bash completion file for CakePHP console + # + + _cake() + { + local cur prev opts cake + COMPREPLY=() + cake="${COMP_WORDS[0]}" + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" + + if [[ "$cur" == -* ]] ; then + if [[ ${COMP_CWORD} = 1 ]] ; then + opts=$(${cake} Completion options) + elif [[ ${COMP_CWORD} = 2 ]] ; then + opts=$(${cake} Completion options "${COMP_WORDS[1]}") + else + opts=$(${cake} Completion options "${COMP_WORDS[1]}" "${COMP_WORDS[2]}") + fi + + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + if [[ ${COMP_CWORD} = 1 ]] ; then + opts=$(${cake} Completion commands) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + fi + + if [[ ${COMP_CWORD} = 2 ]] ; then + opts=$(${cake} Completion subcommands $prev) + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + if [[ $COMPREPLY = "" ]] ; then + _filedir + return 0 + fi + return 0 + fi + + opts=$(${cake} Completion fuzzy "${COMP_WORDS[@]:1}") + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + if [[ $COMPREPLY = "" ]] ; then + _filedir + return 0 + fi + return 0; + } + + complete -F _cake cake bin/cake + +Usando el autocompletado +======================== + +Una vez habilitado, el autocompletado se puede usar de la misma manera que para otros comandos integrados, +usando la tecla **TAB**. +Se proporcionan tres tipos de autocompletado. El siguiente resultado proviene de una nueva instalación de CakePHP. + +Comandos +-------- + +Salida de muestra para comandos de autocompletar: + +.. code-block:: console + + $ bin/cake + bake i18n schema_cache routes + console migrations plugin server + +Subcomandos +----------- + +Salida de muestra para el autocompletado de subcomandos: + +.. code-block:: console + + $ bin/cake bake + behavior helper command + cell mailer command_helper + component migration template + controller migration_snapshot test + fixture model + form plugin + +Opciones +-------- + +Salida de muestra para el autocompletado de opciones de subcomandos: + +.. code-block:: console + + $ bin/cake bake - + -c --everything --force --help --plugin -q -t -v + --connection -f -h -p --prefix --quiet --theme --verbose + diff --git a/es/console-commands/cron-jobs.rst b/es/console-commands/cron-jobs.rst new file mode 100644 index 0000000000..e521ca01bc --- /dev/null +++ b/es/console-commands/cron-jobs.rst @@ -0,0 +1,49 @@ +Ejecutar shells como trabajos cron +################################## + +Una cosa común que se puede hacer con un shell es ejecutarlo como un cronjob para limpiar la base de datos +de vez en cuando o enviar boletines. Esto es trivial de configurar, por ejemplo:: + + */5 * * * * cd /full/path/to/root && bin/cake myshell myparam + # * * * * * command to execute + # │ │ │ │ │ + # │ │ │ │ │ + # │ │ │ │ \───── day of week (0 - 6) (0 to 6 are Sunday to Saturday, + # | | | | or use names) + # │ │ │ \────────── month (1 - 12) + # │ │ \─────────────── day of month (1 - 31) + # │ \──────────────────── hour (0 - 23) + # \───────────────────────── min (0 - 59) + +Puedes ver más información aquí: https://es.wikipedia.org/wiki/Cron_(Unix) + +.. tip:: + + Utilice ``-q`` (o `--quiet`) para silenciar cualquier salida de cronjobs. + +Trabajos cron en hosting compartido +----------------------------------- + +En algunos servidores compartidos ``cd /full/path/to/root && bin/cake mycommand myparam`` +Puede que no funcione. En su lugar puedes usar +``php /full/path/to/root/bin/cake.php mycomando myparam``. + +.. note:: + + register_argc_argv has to be turned on by including ``register_argc_argv + = 1`` in your php.ini. If you cannot change register_argc_argv globally, + you can tell the cron job to use your own configuration by + specifying it with ``-d register_argc_argv=1`` parameter. Example: ``php + -d register_argc_argv=1 /full/path/to/root/bin/cake.php myshell + myparam`` + + register_argc_argv debe activarse incluyendo ``register_argc_argv = 1`` + en su php.ini. Si no puede cambiar register_argc_argv globalmente, puede + indicarle al trabajo cron que use su propia configuración especificándola + con el parámetro ``-d register_argc_argv=1``. + Ejemplo: ``php -d register_argc_argv=1 /full/path/to/root/bin/cake.php myshell + myparam`` + +.. meta:: + :title lang=es: Ejecutar shells como trabajos cron + :keywords lang=es: cronjob,bash script,crontab diff --git a/es/console-commands/i18n.rst b/es/console-commands/i18n.rst new file mode 100644 index 0000000000..1710c4a546 --- /dev/null +++ b/es/console-commands/i18n.rst @@ -0,0 +1,98 @@ +Herramienta de internacionalización (i18n) +########################################## + +Las características i18n de CakePHP usan `archivos po `_ +como fuente de traducción. Los archivos PO se integran con herramientas de traducción de uso común +como `Poedit `_. + +Los comandos i18n proporcionan una forma rápida de generar archivos de plantilla po. +Estos archivos de plantilla luego se pueden entregar a los traductores para que puedan +traducir los textos en su aplicación. Una vez que haya terminado las traducciones, +los archivos pueden ser fusionados con traducciones existentes para ayudar a actualizar sus traducciones. + +Generando archivos POT +====================== + +POT files can be generated for an existing application using the ``extract`` +command. This command will scan your entire application for ``__()`` style +function calls, and extract the message string. Each unique string in your +application will be combined into a single POT file: + +Los archivos POT se pueden generar para una aplicación existente usando el +comando ``extract``. Este comando escaneará toda su aplicación en busca de +llamadas a funciones de estilo ``__()`` y extraerá la cadena del mensaje. +Cada cadena única en su aplicación se combinará en un único archivo POT: + +.. code-block:: console + + bin/cake i18n extract + +Lo anterior ejecutará el comando de extracción. El resultado de este comando será +el archivo **resources/locales/default.pot**. Utilice el archivo pot como plantilla +para crear archivos po. Si está creando archivos po manualmente a partir del +archivo pot, asegúrese de configurar correctamente la línea de encabezado ``Plural-Forms``. + +Generando archivos POT para complementos +----------------------------------------- + +Puede generar un archivo POT para un complemento específico usando: + +.. code-block:: console + + bin/cake i18n extract --plugin + +Esto generará los archivos POT necesarios utilizados en los complementos. + +Extraer de varias carpetas a la vez +----------------------------------- + +A veces, es posible que necesites extraer textos de más de un directorio de tu +aplicación. Por ejemplo, si está definiendo algunas cadenas en el directorio +``config`` de su aplicación, probablemente desee extraer textos de este directorio +así como del directorio ``src``. Puedes hacerlo usando la opción ``--paths``. +Se necesita una lista de rutas absolutas separadas por comas para extraer: + +.. code-block:: console + + bin/cake i18n extract --paths /var/www/app/config,/var/www/app/src + +Excluyendo carpetas +------------------- + +Puede pasar una lista separada por comas de las carpetas que desea excluir. +Se ignorará cualquier ruta que contenga un segmento de ruta con los valores +proporcionados: + +.. code-block:: console + + bin/cake i18n extract --exclude vendor,tests + +Omitir advertencias de sobrescritura para archivos POT existentes +----------------------------------------------------------------- + +Al agregar ``--overwrite``, el script de shell ya no le advertirá si ya existe +un archivo POT y lo sobrescribirá de forma predeterminada: + +.. code-block:: console + + bin/cake i18n extract --overwrite + +Extracción de mensajes de las bibliotecas principales de CakePHP +---------------------------------------------------------------- + +De forma predeterminada, el script de extracción le preguntará si desea extraer +los mensajes utilizados en el código de CakePHP. +Configura ``--extract-core`` en ``yes`` o ``no`` para establecer el comportamiento +predeterminado: + +.. code-block:: console + + bin/cake i18n extract --extract-core yes + + // or + + bin/cake i18n extract --extract-core no + +.. meta:: + :title lang=es: Herramienta de internacionalización (i18n) + :keywords lang=es: pot files,locale default,translation tools,message string,app locale,php class,validation,i18n,translations,command,models diff --git a/es/console-commands/input-output.rst b/es/console-commands/input-output.rst new file mode 100644 index 0000000000..685bef35ab --- /dev/null +++ b/es/console-commands/input-output.rst @@ -0,0 +1,369 @@ +Comandos Entrada/Salida (Input/Output) +###################################### + +.. php:namespace:: Cake\Console +.. php:class:: ConsoleIo + +CakePHP proporciona el objeto ``ConsoleIo`` a los comandos para que puedan +leer interactivamente la información de entrada y salida del usuario. + +.. _command-helpers: + +Ayudantes de comando (Helpers) +============================== + +Se puede acceder y utilizar los ayudantes (helpers) de comandos desde cualquier comando:: + + // Generar algunos datos como una tabla.. + $io->helper('Table')->output($data); + + // Obtenga una ayuda de un complemento. + $io->helper('Plugin.HelperName')->output($data); + +También puede obtener instancias de ayudantes y llamar a cualquier método público sobre ellos:: + + // Obtenga y utilice Progress Helper. + $progress = $io->helper('Progress'); + $progress->increment(10); + $progress->draw(); + +Creando ayudantes +================= + +Si bien CakePHP viene con algunos comandos auxiliares, puedes crear más en tu +aplicación o complementos. Como ejemplo, crearemos un asistente simple para +generar encabezados sofisticados. Primero cree +**src/Command/Helper/HeadingHelper.php** y coloque lo siguiente en él:: + + _io->out($marker . ' ' . $args[0] . ' ' . $marker); + } + } + +Luego podemos usar este nuevo asistente en uno de nuestros comandos de +shell llamándolo:: + + // Con ### a cada lado + $this->helper('Heading')->output(['It works!']); + + // Con ~~~~ a cada lado + $this->helper('Heading')->output(['It works!', '~', 4]); + +Los ayudantes generalmente implementan el método ``output()`` que toma una serie +de parámetros. Sin embargo, debido a que los Console Helpers son clases básicas, +pueden implementar métodos adicionales que toman cualquier forma de argumentos. + +.. note:: + Los ayudantes también pueden vivir en ``src/Shell/Helper`` para + compatibilidad con versiones anteriores. + +Ayudantes incorporados +====================== + +Table Helper +------------ + +TableHelper ayuda a crear tablas artísticas ASCII bien formateadas. +Usarlo es bastante simple:: + + $data = [ + ['Header 1', 'Header', 'Long Header'], + ['short', 'Longish thing', 'short'], + ['Longer thing', 'short', 'Longest Value'], + ]; + $io->helper('Table')->output($data); + + // Outputs + +--------------+---------------+---------------+ + | Header 1 | Header | Long Header | + +--------------+---------------+---------------+ + | short | Longish thing | short | + | Longer thing | short | Longest Value | + +--------------+---------------+---------------+ + +Puede utilizar la etiqueta de formato ```` en las tablas para +alinear el contenido a la derecha:: + + $data = [ + ['Name', 'Total Price'], + ['Cake Mix', '1.50'], + ]; + $io->helper('Table')->output($data); + + // Outputs + +----------+-------------+ + | Name 1 | Total Price | + +----------+-------------+ + | Cake Mix | 1.50 | + +----------+-------------+ + +Progress Helper +--------------- + +ProgressHelper se puede utilizar de dos maneras diferentes. El modo simple +le permite proporcionar una devolución de llamada que se invoca hasta que +se completa el progreso:: + + $io->helper('Progress')->output(['callback' => function ($progress) { + // Funciona aqui + $progress->increment(20); + $progress->draw(); + }]); + +Puede controlar más la barra de progreso proporcionando opciones adicionales: + +- ``total`` El número total de elementos en la barra de progreso. + El valor predeterminado es 100. +- ``width`` El ancho de la barra de progreso. El valor predeterminado es 80. +- ``callback`` La devolución de llamada que se llamará en un bucle para avanzar + en la barra de progreso. + +Un ejemplo de todas las opciones en uso sería:: + + $io->helper('Progress')->output([ + 'total' => 10, + 'width' => 20, + 'callback' => function ($progress) { + $progress->increment(2); + $progress->draw(); + } + ]); + +El asistente de progreso también se puede utilizar manualmente para incrementar +y volver a representar la barra de progreso según sea necesario:: + + $progress = $io->helper('Progress'); + $progress->init([ + 'total' => 10, + 'width' => 20, + ]); + + $progress->increment(4); + $progress->draw(); + + +Obtener información del usuario +=============================== + +.. php:method:: ask($question, $choices = null, $default = null) + +Al crear aplicaciones de consola interactivas, necesitará obtener información +del usuario. CakePHP proporciona una manera de hacer esto:: + + // Obtenga texto arbitrario del usuario. + $color = $io->ask('What color do you like?'); + + // Obtenga una opción del usuario. + $selection = $io->askChoice('Red or Green?', ['R', 'G'], 'R'); + +La validación de la selección no distingue entre mayúsculas y minúsculas. + +Creando archivos +================ + +.. php:method:: createFile($path, $contents) + +La creación de archivos suele ser una parte importante de muchos comandos de +consola que ayudan a automatizar el desarrollo y la implementación. +El método ``createFile()`` le brinda una interfaz simple para crear archivos +con confirmación interactiva:: + + // Crear un archivo con confirmación de sobrescritura + $io->createFile('bower.json', $stuff); + + // Forzar sobrescritura sin preguntar + $io->createFile('bower.json', $stuff, true); + +Creando salidas (Output) +======================== + +.. php:method:out($message, $newlines, $level) +.. php:method:err($message, $newlines) + +Escribir en ``stdout`` y ``stderr`` es otra operación común en CakePHP:: + + // Escribir a stdout + $io->out('Normal message'); + + // Escribir a stderr + $io->err('Error message'); + +Además de los métodos de salida básicos, CakePHP proporciona métodos envolventes +que diseñan la salida con colores ANSI apropiados: + + // Texto verde en stdout + $io->success('Success message'); + + // Texto cian en stdout + $io->info('Informational text'); + + // Texto azul en stdout + $io->comment('Additional context'); + + // Texto rojo en stderr + $io->error('Error text'); + + // Texto amarillo en stderr + $io->warning('Warning text'); + +El formato de color se desactivará automáticamente si ``posix_isatty`` devuelve +verdadero o si se establece la variable de entorno ``NO_COLOR``. + +``ConsoleIo`` proporciona dos métodos convenientes con respecto al nivel +de salida:: + + // Solo aparecerá cuando la salida detallada esté habilitada (-v) + $io->verbose('Verbose message'); + + // Aparecería en todos los niveles. + $io->quiet('Quiet message'); + +También puedes crear líneas en blanco o dibujar líneas de guiones:: + + // Salida 2 nuevas líneas + $io->out($io->nl(2)); + + // Dibuja una línea horizontal + $io->hr(); + +Por último, puede actualizar la línea de texto actual en la pantalla:: + + $io->out('Counting down'); + $io->out('10', 0); + for ($i = 9; $i > 0; $i--) { + sleep(1); + $io->overwrite($i, 0, 2); + } + +.. note:: + Es importante recordar que no puede sobrescribir texto una vez + que se ha generado una nueva línea. + +.. _shell-output-level: + +Niveles de salida +================= + +Las aplicaciones de consola a menudo necesitan diferentes niveles de detalle. +Por ejemplo, cuando se ejecuta como una tarea cron, la mayor parte del resultado +es innecesario. Puede utilizar niveles de salida para marcar la salida de forma +adecuada. El usuario del shell puede entonces decidir qué nivel de detalle le +interesa configurando el indicador correcto al llamar al comando. Hay 3 niveles: + +* ``QUIET`` - Sólo la información absolutamente importante debe marcarse para + una salida silenciosa. +* ``NORMAL`` - El nivel predeterminado y el uso normal. +* ``VERBOSE`` - Marque los mensajes que pueden ser demasiado ruidosos para el + uso diario, pero útiles para la depuración como ``VERBOSE``. + +Puede marcar la salida de la siguiente manera:: + + // Aparecería en todos los niveles. + $io->out('Quiet message', 1, ConsoleIo::QUIET); + $io->quiet('Quiet message'); + + // No aparecería cuando se alterna la salida silenciosa. + $io->out('normal message', 1, ConsoleIo::NORMAL); + $io->out('loud message', 1, ConsoleIo::VERBOSE); + $io->verbose('Verbose output'); + + // Solo aparecería cuando la salida detallada esté habilitada. + $io->out('extra message', 1, ConsoleIo::VERBOSE); + $io->verbose('Verbose output'); + +Puede controlar el nivel de salida de los comandos utilizando las opciones +``--quiet`` y ``--verbose``. Estas opciones se agregan de forma predeterminada +y le permiten controlar consistentemente los niveles de salida dentro de sus +comandos de CakePHP. + +Las opciones ``--quiet`` y ``--verbose`` también controlan cómo se envían los +datos de registro a stdout/stderr. Normalmente, la información y los mensajes de +registro superiores se envían a stdout/stderr. Cuando se utiliza ``--verbose``, +los registros de depuración se enviarán a la salida estándar. Cuando se usa +``--quiet``, solo se enviarán a stderr mensajes de advertencia y de registro +superiores. + +Estilos en salidas +================== + +El estilo de la salida se logra incluyendo etiquetas, al igual que HTML, en la +salida. Estas etiquetas se reemplazarán con la secuencia de códigos ansi +correcta o se eliminarán si estás en una consola que no admite códigos ansi. +Hay varios estilos integrados y puedes crear más. Los incorporados son + +* ``success`` Mensajes de éxito. Texto verde. +* ``error`` Mensajes de error. Texto rojo. +* ``warning`` Mensajes de advertencia. Texto amarillo. +* ``info`` Mensajes informativos. Texto cian. +* ``comment`` Texto adicional. Texto azul. +* ``question`` Texto que es una pregunta, agregado automáticamente por shell. + +Puede crear estilos adicionales usando ``$io->setStyle()``. Para declarar un +nuevo estilo de salida que podrías hacer:: + + $io->setStyle('flashy', ['text' => 'magenta', 'blink' => true]); + +This would then allow you to use a ```` tag in your shell output, and if +ansi colours are enabled, the following would be rendered as blinking magenta +text ``$this->out('Whoooa Something went wrong');``. When +defining styles you can use the following colours for the ``text`` and +``background`` attributes: + +Esto le permitiría usar una etiqueta ```` en la salida de su shell, y si +los colores ansi están habilitados, lo siguiente se representaría como texto +magenta parpadeante ``$this->out('Whoooa Algo salió mal');``. +Al definir estilos, puede utilizar los siguientes colores para los atributos +``text`` y ``background``: + +* black +* blue +* cyan +* green +* magenta +* red +* white +* yellow + +También puede utilizar las siguientes opciones como modificadores booleanos, +configurarlas en un valor verdadero las habilita. + +* blink +* bold +* reverse +* underline + +Agregar un estilo también lo hace disponible en todas las instancias de +ConsoleOutput, por lo que no es necesario volver a declarar estilos para los +objetos stdout y stderr. + +Desactivar la coloración +========================= + +Aunque el color es bonito, puede haber ocasiones en las que desees apagarlo o forzarlo: + + $io->outputAs(ConsoleOutput::RAW); + +Lo anterior pondrá el objeto de salida en modo de salida sin formato. En el modo +de salida sin formato, no se aplica ningún estilo. Hay tres modos que puedes +usar. + +* ``ConsoleOutput::COLOR`` - Salida con códigos de escape de color implementados. +* ``ConsoleOutput::PLAIN`` - Salida de texto sin formato, las etiquetas de + estilo conocidas se eliminarán de la salida. +* ``ConsoleOutput::RAW`` - Salida sin formato, no se realizará ningún estilo ni + formato. Este es un buen modo para usar si está generando XML o desea depurar + por qué su estilo no funciona. + +De forma predeterminada, en los sistemas \*nix, los objetos ConsoleOutput tienen +una salida de color predeterminada. En los sistemas Windows, la salida simple es +la predeterminada a menos que la variable de entorno ``ANSICON`` esté presente. diff --git a/es/console-commands/option-parsers.rst b/es/console-commands/option-parsers.rst new file mode 100644 index 0000000000..9c2b96d30b --- /dev/null +++ b/es/console-commands/option-parsers.rst @@ -0,0 +1,379 @@ +Opcion de analizadores (Parsers) +################################# + +.. php:namespace:: Cake\Console +.. php:class:: ConsoleOptionParser + +Las aplicaciones de consola normalmente toman opciones y argumentos como la +forma principal de obtener información del terminal en sus comandos. + +Definición de un OptionParser +============================= + +Los comandos y shells proporcionan un método de enlace +``buildOptionParser($parser)`` que puede utilizar para definir las opciones y +argumentos de sus comandos:: + + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + // Defina sus opciones y argumentos. + + // Devolver el analizador completo + return $parser; + } + +Las clases Shell usan el método de enlace ``getOptionParser()`` para definir su analizador de opciones:: + + public function getOptionParser() + { + // Obtenga un analizador vacío del marco. + $parser = parent::getOptionParser(); + + // Defina sus opciones y argumentos. + + // Devolver el analizador completo + return $parser; + } + + +Usando argumentos +================= + +.. php:method:: addArgument($name, $params = []) + +Los argumentos posicionales se utilizan con frecuencia en herramientas de línea +de comandos, y ``ConsoleOptionParser`` le permite definir argumentos +posicionales así como hacerlos obligatorios. Puede agregar argumentos uno a la +vez con ``$parser->addArgument();`` o varios a la vez con +``$parser->addArguments();``:: + + $parser->addArgument('model', ['help' => 'The model to bake']); + +Puede utilizar las siguientes opciones al crear un argumento: + +* ``help`` El texto de ayuda que se mostrará para este argumento. +* ``required`` Si este parámetro es necesario. +* ``index`` El índice del argumento, si no se define, el argumento se colocará + al final de los argumentos. Si define el mismo índice dos veces, se + sobrescribirá la primera opción. +* ``choices`` Una serie de opciones válidas para este argumento. Si se deja + vacío, todos los valores son válidos. Se generará una excepción cuando parse() + encuentre un valor no válido. + +Los argumentos que se han marcado como obligatorios generarán una excepción al +analizar el comando si se han omitido. Entonces no tienes que manejar eso en tu +shell. + +Agregar múltiples argumentos +---------------------------- + +.. php:method:: addArguments(array $args) + +Si tiene una matriz con múltiples argumentos, puede usar +``$parser->addArguments()`` para agregar múltiples argumentos a la vez. :: + + $parser->addArguments([ + 'node' => ['help' => 'The node to create', 'required' => true], + 'parent' => ['help' => 'The parent node', 'required' => true], + ]); + +Al igual que con todos los métodos de creación de ConsoleOptionParser, +addArguments se puede utilizar como parte de una cadena de métodos fluida. + +Validación de Argumentos +------------------------ + +Al crear argumentos posicionales, puede utilizar el indicador ``required`` para +indicar que un argumento debe estar presente cuando se llama a un shell. Además, +puedes usar ``choices`` para forzar que un argumento provenga de una lista de +opciones válidas:: + + $parser->addArgument('type', [ + 'help' => 'The type of node to interact with.', + 'required' => true, + 'choices' => ['aro', 'aco'], + ]); + +Lo anterior creará un argumento que es obligatorio y tiene validación en la +entrada. Si falta el argumento o tiene un valor incorrecto, se generará una +excepción y se detendrá el shell. + +Usando opciones +=============== + +.. php:method:: addOption($name, array $options = []) + +Las opciones o indicadores se utilizan en las herramientas de línea de comandos +para proporcionar argumentos clave/valor desordenados para sus comandos. Las +opciones pueden definir alias tanto detallados como cortos. Pueden aceptar un +valor (por ejemplo, ``--connection=default``) o ser opciones booleanas(por +ejemplo, ``--verbose``). Las opciones se definen con el método ``addOption()``:: + + $parser->addOption('connection', [ + 'short' => 'c', + 'help' => 'connection', + 'default' => 'default', + ]); + +Lo anterior le permitiría usar ``cake myshell --connection=other``, +``cake myshell --connection other`` o ``cake myshell -c other`` +al invocar el shell. + +Los modificadores booleanos no aceptan ni consumen valores, y su presencia +simplemente los habilita en los parámetros analizados:: + + $parser->addOption('no-commit', ['boolean' => true]); + +Esta opción, cuando se usa como ``cake mycommand --no-commit something``, +tendría un valor de ``true`` y 'something' se trataría como un argumento posicional. + +Al crear opciones, puede utilizar las siguientes opciones para definir el +comportamiento de la opción: + +* ``short`` - La variante de una sola letra para esta opción, déjela sin definir + para ninguna. +* ``help`` - Texto de ayuda para esta opción. Se utiliza al generar ayuda para + la opción. +* ``default`` - El valor predeterminado para esta opción. Si no se define, el + valor predeterminado será ``true``. +* ``boolean`` - La opción no utiliza ningún valor, es solo un modificador booleano. + El valor predeterminado es ``false``. +* ``multiple`` - La opción se puede proporcionar varias veces. La opción + analizada será una matriz de valores cuando esta opción esté habilitada. +* ``choices`` - Una serie de opciones válidas para esta opción. Si se deja + vacío, todos los valores son válidos. Se generará una excepción cuando parse() + encuentre un valor no válido. + +Agregar múltiples opciones +-------------------------- + +.. php:method:: addOptions(array $options) + +Si tiene una matriz con múltiples opciones, puede usar ``$parser->addOptions()`` +para agregar múltiples opciones a la vez. :: + + $parser->addOptions([ + 'node' => ['short' => 'n', 'help' => 'The node to create'], + 'parent' => ['short' => 'p', 'help' => 'The parent node'], + ]); + +Al igual que con todos los métodos de creación de ConsoleOptionParser, +addOptions se puede utilizar como parte de una cadena de métodos fluida. + +Validación de Opciones +---------------------- + +Las opciones pueden contar con un conjunto de opciones de manera muy similar a +como lo pueden ser los argumentos posicionales. Cuando una opción tiene opciones +definidas, esas son las únicas opciones válidas para una opción. Todos los demás +valores generarán una ``InvalidArgumentException``:: + + $parser->addOption('accept', [ + 'help' => 'What version to accept.', + 'choices' => ['working', 'theirs', 'mine'], + ]); + +Usando opciones booleanas +------------------------- + +Las opciones se pueden definir como opciones booleanas, que son útiles cuando +necesitas crear algunas opciones de bandera. Al igual que las opciones con +valores predeterminados, las opciones booleanas siempre se incluyen en los +parámetros analizados. Cuando las banderas están presentes, se establecen +``true``; cuando están ausentes, se establecen en ``false``:: + + $parser->addOption('verbose', [ + 'help' => 'Enable verbose output.', + 'boolean' => true + ]); + +La siguiente opción siempre tendría un valor en el parámetro analizado. Cuando +no se incluye, su valor predeterminado será ``false`` y, cuando se defina, +será ``true``. + +Construyendo una consola OptionParser a partir de una matriz +------------------------------------------------------------ + +.. php:method:: buildFromArray($spec) + +Como se mencionó anteriormente, al crear analizadores de opciones de subcomando, +puede definir la especificación del analizador como una matriz para ese método. +Esto puede ayudar a facilitar la creación de analizadores de subcomandos, ya que +todo es una matriz:: + + $parser->addSubcommand('check', [ + 'help' => __('Check the permissions between an ACO and ARO.'), + 'parser' => [ + 'description' => [ + __("Use this command to grant ACL permissions. Once executed, the "), + __("ARO specified (and its children, if any) will have ALLOW access "), + __("to the specified ACO action (and the ACO's children, if any)."), + ], + 'arguments' => [ + 'aro' => ['help' => __('ARO to check.'), 'required' => true], + 'aco' => ['help' => __('ACO to check.'), 'required' => true], + 'action' => ['help' => __('Action to check')], + ], + ], + ]); + +Dentro de las especificaciones del analizador, puede definir claves para +``arguments``, ``options``, ``description`` y ``epilog``. No se pueden definir +``subcommands`` dentro de un generador de estilos de matriz. Los valores de los +argumentos y las opciones deben seguir el formato :php:func:`Cake\\Console\\ConsoleOptionParser::addArguments()` y +:php:func:`Cake\\Console\\ConsoleOptionParser::addOptions( )`. También puede +utilizar buildFromArray por sí solo para crear un analizador de opciones:: + + public function getOptionParser() + { + return ConsoleOptionParser::buildFromArray([ + 'description' => [ + __("Use this command to grant ACL permissions. Once executed, the "), + __("ARO specified (and its children, if any) will have ALLOW access "), + __("to the specified ACO action (and the ACO's children, if any).") + ], + 'arguments' => [ + 'aro' => ['help' => __('ARO to check.'), 'required' => true], + 'aco' => ['help' => __('ACO to check.'), 'required' => true], + 'action' => ['help' => __('Action to check')], + ], + ]); + } + +Fusionar analizadores de opciones +--------------------------------- + +.. php:method:: merge($spec) + +Al crear un comando de grupo, es posible que desee combinar varios analizadores +para esto:: + + $parser->merge($anotherParser); + +Tenga en cuenta que el orden de los argumentos para cada analizador debe ser el +mismo y que las opciones también deben ser compatibles para que funcione. Así +que no utilices claves para cosas diferentes. + +Obtener ayuda de comandos +========================== + +Al definir sus opciones y argumentos con el analizador de opciones, CakePHP +puede generar automáticamente información de ayuda rudimentaria y agregar +``--help`` y ``-h`` a cada uno de sus comandos. El uso de una de estas opciones +le permitirá ver el contenido de ayuda generado: + +.. code-block:: console + + bin/cake bake --help + bin/cake bake -h + +Ambos generarían la ayuda para hornear. También puede obtener ayuda para +comandos anidados: + +.. code-block:: console + + bin/cake bake model --help + bin/cake bake model -h + +Lo anterior le brindará ayuda específica para el comando ``bake model``. + +Obtener ayuda como XML +---------------------- + +Al crear herramientas automatizadas o herramientas de desarrollo que necesitan +interactuar con los comandos de CakePHP, es bueno tener ayuda disponible en un +formato que la máquina pueda analizar. Al proporcionar la opción ``xml`` al +solicitar ayuda, puede obtener el contenido de la ayuda como XML: + +.. code-block:: console + + cake bake --help xml + cake bake -h xml + +Lo anterior devolvería un documento XML con la ayuda, opciones, argumentos y +subcomandos generados para el shell seleccionado. Un documento XML de muestra +se vería así: + +.. code-block:: xml + + + + bake fixture + Generate fixtures for use with the test suite. You can use + `bake fixture all` to bake all fixtures. + + Omitting all arguments and options will enter into an interactive + mode. + + + + + + + + + + + + + + + + + +Personalización de salida de la ayuda +===================================== + +Puede enriquecer aún más el contenido de ayuda generado agregando una +descripción y un epílogo. + +Establecer la descripción +------------------------- + +.. php:method:: setDescription($text) + +La descripción se muestra encima de la información del argumento y la opción. +Al pasar una matriz o una cadena, puede establecer el valor de la descripción:: + + // Establecer varias líneas a la vez + $parser->setDescription(['line one', 'line two']); + + // Leer el valor actual + $parser->getDescription(); + +Establecer el epílogo +--------------------- + +.. php:method:: setEpilog($text) + +Obtiene o establece el epílogo del analizador de opciones. El epílogo se muestra +después de la información del argumento y la opción. Al pasar una matriz o una +cadena, puede establecer el valor del epílogo:: + + // Establecer varias líneas a la vez + $parser->setEpilog(['line one', 'line two']); + + // Leer el valor actual + $parser->getEpilog(); diff --git a/es/console-commands/plugin.rst b/es/console-commands/plugin.rst new file mode 100644 index 0000000000..8318caac95 --- /dev/null +++ b/es/console-commands/plugin.rst @@ -0,0 +1,67 @@ +.. _plugin-shell: + +Herramienta de complemento (Plugin) +################################### + +La herramienta de complemento le permite cargar y descargar complementos a +través del símbolo del sistema. Si necesita ayuda, ejecute: + +.. code-block:: console + + bin/cake plugin --help + +Cargando complementos +--------------------- + +A través de la tarea ``Load`` puedes cargar complementos en tu +**config/bootstrap.php**. Puedes hacer esto ejecutando: + +.. code-block:: console + + bin/cake plugin load MyPlugin + +Esto agregará lo siguiente a su **src/Application.php**:: + + // En el método bootstrap agregue: + $this->addPlugin('MyPlugin'); + +Descarga de complementos +------------------------ + +Puede descargar un complemento especificando su nombre: + +.. code-block:: console + + bin/cake plugin unload MyPlugin + +Esto eliminará la línea ``$this->addPlugin('MyPlugin',...)`` de +**src/Application.php**. + +Activos del complemento (Assets) +--------------------------------- + +CakePHP sirve de forma predeterminada recursos de complementos utilizando el +middleware ``AssetMiddleware``. Si bien esto es una buena conveniencia, se +recomienda vincular/copiar los activos del complemento en la raíz web de la +aplicación para que el servidor web pueda servirlos directamente sin invocar +PHP. Puedes hacer esto ejecutando: + +.. code-block:: console + + bin/cake plugin assets symlink + +La ejecución del comando anterior vinculará simbólicamente todos los recursos de +los complementos en la raíz web de la aplicación. En Windows, que no admite +enlaces simbólicos, los activos se copiarán en las carpetas respectivas en lugar +de tener enlaces simbólicos. + +Puede vincular simbólicamente los activos de un complemento en particular +especificando su nombre: + +.. code-block:: console + + bin/cake plugin assets symlink MyPlugin + +.. meta:: + :title lang=es: Herramienta de complemento (Plugin) + :keywords lang=es: plugin,assets,tool,load,unload,complemento,activos diff --git a/es/console-commands/repl.rst b/es/console-commands/repl.rst new file mode 100644 index 0000000000..89e17276be --- /dev/null +++ b/es/console-commands/repl.rst @@ -0,0 +1,46 @@ +Consola interactiva (REPL) +########################## + +CakePHP ofrece el complemento +`REPL (Read Eval Print Loop) `__ para +permitirle explorar algo de CakePHP y su aplicación en una consola interactiva. + +Puede iniciar la consola interactiva usando: + +.. code-block:: console + + bin/cake console + +Esto iniciará su aplicación e iniciará una consola interactiva. En este punto, +puede interactuar con el código de su aplicación y ejecutar consultas utilizando +los modelos de su aplicación: + +.. code-block:: console + + bin/cake console + + >>> $articles = Cake\Datasource\FactoryLocator::get('Table')->get('Articles'); + // object(Cake\ORM\Table)( + // + // ) + >>> $articles->find()->all(); + +Dado que su aplicación ha sido iniciada, también puede probar el enrutamiento +usando REPL:: + + >>> Cake\Routing\Router::parse('/articles/view/1'); + // [ + // 'controller' => 'Articles', + // 'action' => 'view', + // 'pass' => [ + // 0 => '1' + // ], + // 'plugin' => NULL + // ] + +También puedes probar la generación de URL:: + + >>> Cake\Routing\Router::url(['controller' => 'Articles', 'action' => 'edit', 99]); + // '/articles/edit/99' + +Para salir de REPL, puede usar ``CTRL-C`` o escribir ``exit``. diff --git a/es/console-commands/routes.rst b/es/console-commands/routes.rst new file mode 100644 index 0000000000..57540214b8 --- /dev/null +++ b/es/console-commands/routes.rst @@ -0,0 +1,39 @@ +Herramienta de enrutamiento (Routes) +#################################### + +La herramienta de rutas proporciona una interfaz CLI fácil de usar para probar +y depurar rutas. Puede usarlo para probar cómo se analizan las rutas y qué +parámetros de enrutamiento de URL generarán. + +Obtener una lista de todas las rutas +------------------------------------ + +.. code-block:: console + + bin/cake routes + +Prueba de análisis de URL +------------------------- + +Puedes ver rápidamente cómo se analizará una URL usando el método ``check``: + +.. code-block:: console + + bin/cake routes check /articles/edit/1 + +Si su ruta contiene algún parámetro de cadena de consulta, recuerde encerrar +la URL entre comillas: + +.. code-block:: console + + bin/cake routes check "/articles/?page=1&sort=title&direction=desc" + +Prueba de generación de URL +--------------------------- + +Puede ver la URL que generará :term:`arreglo de enrutamiento` usando el método ``generar``: + +.. code-block:: console + + bin/cake routes generate controller:Articles action:edit 1 + diff --git a/es/console-commands/schema-cache.rst b/es/console-commands/schema-cache.rst new file mode 100644 index 0000000000..a66620f13a --- /dev/null +++ b/es/console-commands/schema-cache.rst @@ -0,0 +1,30 @@ +Herramienta de esquemas de caché (Schema Cache) +################################################ + +SchemaCacheShell proporciona una herramienta CLI sencilla para administrar las +cachés de metadatos de su aplicación. En situaciones de implementación, resulta +útil reconstruir la caché de metadatos in situ sin borrar los datos de la caché +existente. Puedes hacer esto ejecutando: + +.. code-block:: console + + bin/cake schema_cache build --connection default + +Esto reconstruirá el caché de metadatos para todas las tablas en la conexión +``default``. Si solo necesita reconstruir una única tabla, puede hacerlo +proporcionando su nombre: + +.. code-block:: console + + bin/cake schema_cache build --connection default articles + +Además de crear datos almacenados en caché, también puede utilizar +SchemaCacheShell para eliminar metadatos almacenados en caché: + +.. code-block:: console + + # Borrar todos los metadatos + bin/cake schema_cache clear + + # Limpiar una sola tabla + bin/cake schema_cache clear articles diff --git a/es/console-commands/server.rst b/es/console-commands/server.rst new file mode 100644 index 0000000000..ce9b051f33 --- /dev/null +++ b/es/console-commands/server.rst @@ -0,0 +1,30 @@ +Herramienta de servidor +####################### + +El ``ServerCommand`` te permite crear un servidor web simple utilizando el +servidor web PHP integrado. Si bien este servidor *no* está diseñado para uso +en producción, puede ser útil en el desarrollo cuando desea probar rápidamente +una idea y no quiere perder tiempo configurando Apache o Nginx. Puedes iniciar +el servidor con el comando: + +.. code-block:: console + + bin/cake server + +Deberías ver que el servidor arranca y se conecta al puerto 8765. Puedes visitar +el servidor CLI visitando ``http://localhost:8765`` en su navegador web. +Para cerrar el servidor presiona ``CTRL-C`` en tu terminal. + +.. note:: + + Prueba ``bin/cake server -H 0.0.0.0`` si no se puede acceder al servidor + desde otros hosts. + +Cambiar el puerto y la raíz del documento +========================================= + +Puedes personalizar el puerto y el directorio raíz usando las opciones: + +.. code-block:: console + + bin/cake server --port 8080 --document_root path/to/app diff --git a/es/contents.rst b/es/contents.rst index 973e108a3e..58d174f95b 100644 --- a/es/contents.rst +++ b/es/contents.rst @@ -2,24 +2,27 @@ Contenidos ########## .. toctree:: - :hidden: + :hidden: - index + index .. toctree:: - :caption: Prólogo + :caption: Prefacio intro quickstart - appendices/4-0-migration-guide + appendices/migration-guides tutorials-and-examples contributing + release-policy .. toctree:: :caption: Comenzando installation development/configuration + development/application + development/dependency-injection development/routing controllers/request-response controllers/middleware @@ -30,9 +33,8 @@ Contenidos .. toctree:: :caption: Usando CakePHP - controllers/components/authentication core-libraries/caching - console-and-shells + console-commands development/debugging deployment core-libraries/email @@ -41,7 +43,7 @@ Contenidos core-libraries/internationalization-and-localization core-libraries/logging core-libraries/form - controllers/components/pagination + controllers/pagination plugins development/rest security @@ -50,28 +52,33 @@ Contenidos core-libraries/validation .. toctree:: - :caption: Clases de utilidad + :caption: Clases de Utilidad core-libraries/app core-libraries/collections - core-libraries/file-folder core-libraries/hash core-libraries/httpclient core-libraries/inflector core-libraries/number + core-libraries/plugin core-libraries/registry-objects core-libraries/text core-libraries/time core-libraries/xml .. toctree:: - :caption: Plugins - - Bake - Chronos - Debug Kit - migrations - elasticsearch + :caption: Plugins & Paquetes + + standalone-packages + Authentication + Authorization + Bake + Debug Kit + Migrations + Elasticsearch + Phinx + Chronos + Queue .. toctree:: :caption: Otros @@ -85,9 +92,12 @@ Contenidos topics chronos debug-kit + elasticsearch bake - bake/usage bake/development + bake/usage + migrations + phinx .. todolist:: diff --git a/es/contributing/backwards-compatibility.rst b/es/contributing/backwards-compatibility.rst index 2de0448513..f63cfe12c9 100644 --- a/es/contributing/backwards-compatibility.rst +++ b/es/contributing/backwards-compatibility.rst @@ -8,7 +8,7 @@ versiones ``major``. Puedes familiarizarte con el `versionado semántico de CakePHP. Pero resumiendo, el versionado semántico significa que sólo las liberaciones de versiones ``major`` (tales como 2.0, 3.0, 4.0) pueden romper la compatibilidad hacia atrás. Las liberaciones ``minor`` (tales como 2.1, 3.1, 3.2) -pueden introducir nuevas funcionalidades pero no pueden romper la compatibilidad. +pueden introducir nuevas funcionalidades, pero no pueden romper la compatibilidad. Los lanzamientos de correcciones de errores (tales como 3.0.1) no añaden nuevas funcionaliades, sólo correcciones de errores o mejoras de rendimiento. @@ -42,7 +42,7 @@ Interfaces Con excepción de las liberaciones ``major``, las interfaces que provee CakePHP **no** tendrán ningún cambio en los métodos existentes. Podrán añadirse nuevos -métodos pero no habrá cambios en los ya existentes. +métodos, pero no habrá cambios en los ya existentes. Clases ------ @@ -62,7 +62,7 @@ defecto, pero si sobreescribes métodos con una firma diferente puedes encontrar ``fatal errors``. Los métodos con nuevos argumentos estarán documentados en las guías de migración.. -La siguiente tabla esboza varios casos de uso y que compatibilidad puedes esperar +La siguiente tabla esboza varios casos de uso y qué compatibilidad puedes esperar de CakePHP: +---------------------------------------+--------------------------+ diff --git a/es/contributing/cakephp-coding-conventions.rst b/es/contributing/cakephp-coding-conventions.rst index be350ac561..d6118b2b17 100644 --- a/es/contributing/cakephp-coding-conventions.rst +++ b/es/contributing/cakephp-coding-conventions.rst @@ -23,7 +23,7 @@ Configuración del *IDE* Asegúrate de que tu *IDE* haga *trim* por la derecha para que no haya espacios al final de las líneas. -La mayoría de los *IDEs* modernos soportan archivos ``.editorconfig``. El +La mayoría de los *IDE* modernos soportan archivos ``.editorconfig``. El esqueleto de aplicación de CakePHP viene con él por defecto y contiene las mejores prácticas de forma predeterminada. @@ -51,7 +51,7 @@ O también:: } } -En los casos donde utilices llamadas de funciones que ocupen más de un línea +En los casos donde utilices llamadas de funciones que ocupen más de una línea usa las siguientes guías: * El paréntesis de abertura de la llamada de la función deberá ser lo @@ -138,9 +138,9 @@ Operador ternario ----------------- Los operadores ternarios están permitidos cuando toda su declaración cabe en una -sola línea. Operadores más largos deberán ir dentro dentro de una declaración +sola línea. Operadores más largos deberán ir dentro de una declaración ``if else``. Los operadores ternarios no deberían ir nunca anidados y opcionalmente -pueden utilizarse paréntesis entorno a las condiciones para dar claridad:: +pueden utilizarse paréntesis alrededor de las condiciones para dar claridad:: // Correcto, sencillo y legible $variable = isset($options['variable']) ? $options['variable'] : true; @@ -172,7 +172,7 @@ Intenta ser siempre lo más estricto posible. Si una comparación no es estricta de forma deliberada, puede ser inteligente añadir un comentario para evitar confundirla con un error. -Para comprobar si una variables es ``null`` se recomienda utilizar comprobación +Para comprobar si una variable es ``null`` se recomienda utilizar la comprobación estricta:: if ($value === null) { @@ -378,7 +378,7 @@ object resource Tipo resource (devuelto por ejemplo por mysql\_connect()). Recuerda que cuando especificas el tipo como mixed deberás indicar - si es desconocido o cuales son los tipos posibles. + si es desconocido o cuáles son los tipos posibles. callable Función Callable. @@ -388,7 +388,7 @@ Puedes combinar tipos usando el caracter ``|``:: Para más de dos tipos normalmente lo mejor es utilizar ``mixed``. -Cuando se devuelva el propio objeto, p.ej. para encadenar, deberás utilizar +Cuando se devuelva el propio objeto, por ejemplo para encadenar, deberás utilizar ``$this`` en su lugar:: /** @@ -467,7 +467,7 @@ Los nombres de las clases deberán escribirse en ``CamelCase``, por ejemplo:: Variables --------- -Los nombres de variables deberán ser todo lo descriptibles que puedan pero +Los nombres de variables deberán ser todo lo descriptibles que puedan, pero también lo más corto posible. Se escribirán en minúscula salvo que estén compuestos por múltiples palabras, en cuyo caso irán en ``camelBack``. Los nombres de las variables que referencien objetos deberán ir asociados de algún modo a la clase @@ -516,15 +516,15 @@ Tipo (bool) Cast a boolean. (int) - Cast a integer. + Cast an integer. (float) Cast a float. (string) Cast a string. (array) - Cast a array. + Cast an array. (object) - Cast a object. + Cast an object. Por favor utiliza ``(int)$var`` en lugar de ``intval($var)`` y ``(float)$var`` en lugar de ``floatval($var)`` cuando aplique. @@ -611,4 +611,4 @@ por defecto puedes asegurarte de que las claves necesarias están definidas:: .. meta:: :title lang=es: Estándares de codificación - :keywords lang=es: llaves, nivel de tabulación, errores logicos, estructuras de control,expr,estándares de codificación,paréntesis,foreach, legibilidad,moose,nuevas funcionalidades,repositorio,desarrolladores + :keywords lang=es: llaves, nivel de tabulación, errores logicos, estructuras de control, expr, estándares de codificación, paréntesis, foreach, legibilidad,moose,nuevas funcionalidades,repositorio,desarrolladores diff --git a/es/contributing/code.rst b/es/contributing/code.rst index a001325ee1..fd11eb2bc8 100644 --- a/es/contributing/code.rst +++ b/es/contributing/code.rst @@ -32,7 +32,7 @@ Clona el código fuente de CakePHP desde GitHub: * Si no tienes una cuenta de `GitHub `_ créate una. * Haz un *fork* del `repositorio CakePHP `_ - haciendo click en el botón **Fork**. + haciendo clic en el botón **Fork**. Después de haber hecho el fork, clónalo en tu equipo local:: diff --git a/es/contributing/documentation.rst b/es/contributing/documentation.rst index 309b116277..2773f7e0d0 100644 --- a/es/contributing/documentation.rst +++ b/es/contributing/documentation.rst @@ -22,7 +22,7 @@ Nueva traducción ---------------- Nos gustaría poder disponer de traducciones que estén todo lo completas posible. -Sin embargo hay ocasiones donde un archivo de traducción no está al día, por lo +Sin embargo, hay ocasiones donde un archivo de traducción no está al día, por lo que debes considerar siempre la versión en inglés como la versión acreditada. Si tu idioma no está entre los disponibles, por favor, contacta con nosotros a @@ -30,7 +30,7 @@ través de Github y estudiaremos la posibilidad de crear la estructura de archiv para ello. Las siguientes secciones son las primeras que deberías considerar -traducir ya que estos archivos no cambian a menudo: +traducir, ya que estos archivos no cambian a menudo: - index.rst - intro.rst @@ -48,7 +48,7 @@ dichos cambios en los demás idiomas. Por ejemplo, si se crea un nuevo archivo en inglés en **en/file.rst** tendremos que: -- Añadir el archivo en todos los idiomas: **fr/file.rst**, **zh/file.rst**,... +- Añadir el archivo en todos los idiomas: **fr/file.rst**, **zh/file.rst**, ... - Borrar el contenido pero manteniendo el ``title``, ``meta`` información y ``toc-tree`` que pueda haber. Se añadirá la siguiente nota mientras nadie traduzca el archivo:: @@ -87,7 +87,7 @@ Consejos para traductores - Usa la `Forma informal `_. - Traduce el título y el contenido a la vez. - Compara con la versión en inglés antes de subir una corrección (si corriges - algo pero no indicas una referencia tu subida no será aceptada). + algo, pero no indicas una referencia tu subida no será aceptada). - Si necesitas escribir un término en inglés envuélvelo en etiquetas ````. E.g. "asdf asdf *Controller* asdf" o "asdf asdf Kontroller (*Controller*) asfd" como proceda. @@ -114,7 +114,7 @@ Tamaño de línea --------------- Las líneas de texto deberían medir como máximo 40 caracteres. Las únicas -excepciones son URLs largas y fragmentos de código. +excepciones son URL largas y fragmentos de código. Cabeceras y secciones --------------------- @@ -154,7 +154,7 @@ Marcado en línea * Dos acentos graves (*``*): ``texto`` para ejemplos de código. Lo usaramos para nombres de opciones de métodos, columnas de tablas, objetos (excluyendo la palabra "objeto") y para nombres de métodos y funciones - (incluídos los paréntesis ) + (incluídos los paréntesis) * ````cascadeCallbacks````, ````true````, ````id````, ````PagesController````, ````config()````, etc. @@ -206,7 +206,7 @@ Pueden crearse listas de definiciones haciendo lo siguiente:: CakePHP Un framework MVC para PHP -Los términos no pueden ocupar más de una línea pero las definiciones pueden +Los términos no pueden ocupar más de una línea, pero las definiciones pueden ocupar más líneas mientras se aniden consistentemente. Enlaces diff --git a/es/controllers.rst b/es/controllers.rst index 0e993037f2..ae342a66b8 100644 --- a/es/controllers.rst +++ b/es/controllers.rst @@ -5,38 +5,38 @@ Controladores .. php:class:: Controller -Los controladores son la 'C' en MVC. Después de aplicar el enrutamiento y +Los controladores son la 'C' en MVC. Después de aplicar el enrutamiento y que el controlador ha sido encontrado, la acción de tu controlador es llamado. Tu controlador -debe manejar la interpretación de los datos de la solicitud, +debe manejar la interpretación de los datos de la solicitud, asegurándose de que se llamen -a los modelos correctos y se muestre la respuesta o vista correcta. +a los modelos correctos y se muestre la respuesta o vista correcta. Los controladores se pueden considerar como una capa intermedia entre el Modelo y la Vista. Quieres mantener -tus controladores delgados, y tus modelos gruesos. -Esto te ayudará a reutilizar tu código y lo hará mas fácil de probar. +tus controladores delgados, y tus modelos gruesos. +Esto te ayudará a reutilizar tu código y lo hará más fácil de probar. -Comúnmente, un controlador se usa para administrar la lógica en torno +Comúnmente, un controlador se usa para administrar la lógica en torno a un solo modelo. Por -ejemplo, si estuvieras construyendo un sitio online para una panadería, +ejemplo, si estuvieras construyendo un sitio online para una panadería, podrías tener un -RecipesController que gestiona tus recetas y un IngredientsController +RecipesController que gestiona tus recetas y un IngredientsController que gestiona tus -ingredientes. Sin embargo, es posible hacer que los controladores trabajen -con más de +ingredientes. Sin embargo, es posible hacer que los controladores trabajen +con más de un modelo. En CakePHP, un controlador es nombrado a raíz del modelo que maneja. Los controladores de tu aplicación extienden de la clase ``AppController``, que a su vez extiende de la clase principal :php:class:`Controller`. La clase ``AppController`` puede ser definida en **src/Controller/AppController.php** -y debería contener los métodos que se comparten entre todos los controladores +y debería contener los métodos que se comparten entre todos los controladores de tu aplicación. Los controladores proveen una serie de métodos que manejan las peticiones. Estos son llamadas *acciones*. Por defecto, cada método público en un controlador es -una acción, y es accesible mediante una URL. Una acción es responsable de +una acción, y es accesible mediante una URL. Una acción es responsable de interpretar la petición y crear la respuesta. Por lo general, las respuestas -son de la forma de una vista renderizada, pero también, hay otras maneras de crear +son de la forma de una vista renderizada, pero también, hay otras maneras de crear respuestas. .. _app-controller: @@ -44,10 +44,10 @@ respuestas. El App Controller ================== -Como se indicó en la introducción, la clase ``AppController`` es clase padre de +Como se indicó en la introducción, la clase ``AppController`` es clase padre de todos los controladores de tu aplicación. ``AppController`` extiende de la clase :php:class:`Cake\\Controller\\Controller` que está incluida en CakePHP. -``AppController`` se define en **src/Controller/AppController.php** como se +``AppController`` se define en **src/Controller/AppController.php** como se muestra a continuación:: namespace App\Controller; @@ -58,7 +58,7 @@ muestra a continuación:: { } -Los atributos y métodos del controlador creados en tu ``AppController`` van a +Los atributos y métodos del controlador creados en tu ``AppController`` van a estar disponibles en todos los controladores que extiendan de este. Los componentes (que aprenderás más adelante) son mejor usados para código que se encuentra en muchos (pero no necesariamente en todos) los componentes. @@ -66,7 +66,7 @@ encuentra en muchos (pero no necesariamente en todos) los componentes. Puedes usar tu ``AppController`` para cargar componentes que van a ser utilizados en cada controlador de tu aplicación. CakePHP proporciona un método ``initialize()`` que es llamado al final del constructor de un controlador para este tipo de uso:: - + namespace App\Controller; use Cake\Controller\Controller; @@ -75,19 +75,20 @@ que es llamado al final del constructor de un controlador para este tipo de uso: { public function initialize(): void { - // Siempre habilita el componente CSRF. - $this->loadComponent('Csrf'); + // Always enable the FormProtection component. + $this->loadComponent('FormProtection'); } } + Flujo de solicitud ================== -Cuando se realiza una solicitud a una aplicación CakePHP, las clases CakePHP +Cuando se realiza una solicitud a una aplicación CakePHP, las clases CakePHP :php:class:`Cake\\Routing\\Router` y :php:class:`Cake\\Routing\\Dispatcher` -usan :ref:`routes-configuration` para encontrar y crear la instancia correcta -del controlador. Los datos de la solicitud son encapsulados en un objeto de -solicitud. CakePHP pone toda la información importante de la solicitud en la +usan :ref:`routes-configuration` para encontrar y crear la instancia correcta +del controlador. Los datos de la solicitud son encapsulados en un objeto de +solicitud. CakePHP pone toda la información importante de la solicitud en la propiedad ``$this->request``. Consulta la sección sobre :ref:`cake-request` para obtener más información sobre el objeto de solicitud de CakePHP. @@ -95,14 +96,14 @@ Acciones del controlador ======================== Las acciones del controlador son las responsables de convertir los parámetros -de la solicitud en una respuesta para el navegador/usuario que realiza la +de la solicitud en una respuesta para el navegador/usuario que realiza la petición. CakePHP usa convenciones para automatizar este proceso y eliminar algunos códigos repetitivos que de otro modo se necesitaría escribir. -Por convención, CakePHP renderiza una vista con una versión en infinitivo del nombre -de la acción. Volviendo a nuestro ejemplo de la panadería online, nuestro -RecipesController podría contener las acciones ``view()``, ``share()``, y -``search()``. El controlador sería encontrado en +Por convención, CakePHP renderiza una vista con una versión en infinitivo del nombre +de la acción. Volviendo a nuestro ejemplo de la panadería online, nuestro +RecipesController podría contener las acciones ``view()``, ``share()``, y +``search()``. El controlador sería encontrado en **src/Controller/RecipesController.php** y contiene:: // src/Controller/RecipesController.php @@ -139,7 +140,7 @@ CakePHP se encargará de renderizar y entregar la vista. Si por algún motivo deseas omitir el comportamiento predeterminado, puedes retornar un objeto :php:class:`Cake\\Http\\Response` de la acción con la respuesta creada. -Para que puedas usar un controlador de manera efectiva en tu aplicación, +Para que puedas usar un controlador de manera efectiva en tu aplicación, cubriremos algunos de los atributos y métodos principales proporcionados por los controladores de CakePHP. @@ -148,7 +149,7 @@ Interactuando con vistas Los controladores interactúan con las vistas de muchas maneras. Primero, los controladores son capaces de pasar información a las vistas, usando ``Controller::set()``. -También puedes decidir qué clase de vista usar, y qué archivo de vista debería +También puedes decidir qué clase de vista usar, y qué archivo de vista debería ser renderizado desde el controlador. .. _setting-view_variables: @@ -158,7 +159,7 @@ Configuración de variables de vista .. php:method:: set(string $var, mixed $value) -El método ``Controller::set()`` es la manera principal de mandar información +El método ``Controller::set()`` es la manera principal de mandar información desde el controlador a la vista. Una vez que hayas utilizado ``Controller::set()``, la variable puede ser accedida en tu vista:: @@ -172,13 +173,13 @@ la variable puede ser accedida en tu vista:: Has seleccionado cubierta para la tarta. El método ``Controller::set()`` también toma un array asociativo como su primer -parámetro. A menudo, esto puede ser una forma rápida de asignar un conjunto de +parámetro. A menudo, esto puede ser una forma rápida de asignar un conjunto de información a la vista:: $data = [ 'color' => 'pink', 'type' => 'sugar', - 'base_price' => 23.95 + 'base_price' => 23.95, ]; // Hace $color, $type, y $base_price @@ -218,7 +219,7 @@ coloca la vista dentro de su ``View::$layout``, y lo devuelve al usuario final. El archivo de vista por defecto utilizado para el renderizado es definido por convención. -Si la acción ``search()`` de RecipesController es solicitada, el archivo vista en +Si la acción ``search()`` de RecipesController es solicitada, el archivo vista en **templates/Recipes/search.php** será renderizado:: namespace App\Controller; @@ -234,8 +235,8 @@ Si la acción ``search()`` de RecipesController es solicitada, el archivo vista // ... } -Aunque CakePHP va a llamarlo automáticamente después de cada acción de lógica -(a menos que llames a ``$this->disableAutoRender()``), puedes usarlo para +Aunque CakePHP va a llamarlo automáticamente después de cada acción de lógica +(a menos que llames a ``$this->disableAutoRender()``), puedes usarlo para especificar un archivo de vista alternativo especificando el nombre de este como primer argumento del método ``Controller::render()``. @@ -254,7 +255,7 @@ Renderizando una plantilla específica En tu controlador, puede que quieras renderizar una vista diferente a la que es convencional. Puedes hacer esto llamando a ``Controller::render()`` directamente. -Una vez que hayas llamado a ``Controller::render()``, CakePHP no tratará de +Una vez que hayas llamado a ``Controller::render()``, CakePHP no tratará de re-renderizar la vista:: namespace App\Controller; @@ -267,7 +268,7 @@ re-renderizar la vista:: } } -Esto renderizará **templates/Posts/custom_file.php** en vez de +Esto renderizará **templates/Posts/custom_file.php** en vez de **templates/Posts/my_action.php**. También puedes renderizar vistas dentro de plugins usando la siguiente sintaxis: @@ -292,10 +293,10 @@ Negociación del tipo de contenido .. php:method:: viewClasses() -Los controladores pueden definir una lista de clases de vistas que soportan. -Después de que la acción del controlador este completa, CakePHP usará la lista de +Los controladores pueden definir una lista de clases de vistas que soportan. +Después de que la acción del controlador este completa, CakePHP usará la lista de vista para realizar negociación del tipo de contenido. Esto permite a tu aplicación -rehusar la misma acción del controlador para renderizar una vista HTML o +rehusar la misma acción del controlador para renderizar una vista HTML o renderizar una respuesta JSON o XML. Para definir la lista de clases de vista que soporta un controlador se utiliza el método ``viewClasses()``:: @@ -314,9 +315,33 @@ soporta un controlador se utiliza el método ``viewClasses()``:: La clase ``View`` de la aplicación se usa automáticamente como respaldo cuando no se puede seleccionar otra vista en función del encabezado de la petición ``Accept`` -o de la extensión del enrutamiento. Si tu aplicación necesita realizar una lógica -diferente para diferente formatos de respuesta puedes usar ``$this->request->is()`` -para construir la lógica condicional requerida. +o de la extensión del enrutamiento. Si tu aplicación sólo soporta tipos de contenido +para una acción específica, puedes definir esa lógica dentro de ``viewClasses()``:: + + public function viewClasses(): array + { + if ($this->request->getParam('action') === 'export') { + // Usa una vista CSV personalizada para exportación de datos + return [CsvView::class]; + } + + return [JsonView::class]; + } + +Si dentro de las acciones de tu controlador necesitas procesar la petición o cargar datos +de forma diferente dependiendo del tipo de contenido puedes usar +:ref:`check-the-request`:: + + // En la acción de un controlador + + // Carga información adicional cuando se preparan respuestas JSON + if ($this->request->is('json')) { + $query->contain('Authors'); + } + +También puedes definir las clases View soportadas por tu controlador usando +el método ``addViewClasses()`` que unirá la vista proporcionada con +aquellas que están actualmente en la propiedad ``viewClasses``. .. note:: Las clases de vista deben implementar el método estático ``contentType()`` @@ -352,12 +377,9 @@ de vista alternativa:: } -Es importante recordar que las vistas coincidentes se aplican sólo *después* de +Es importante recordar que las vistas coincidentes se aplican sólo *después* de intentar la negociación del tipo de contenido. -.. versionadded:: 4.4.0 - Anterior a 4.4 debes usar :doc:`/controllers/components/request-handling` - en vez de ``viewClasses()``. Redirigiendo a otras páginas ============================ @@ -416,16 +438,30 @@ nombrada acción:: Cargando modelos adicionales ============================ +.. php:method:: fetchModel(string $alias, array $config = []) + +La función ``fetchModel()`` es útil cuando se necesita cargar un modelo o tabla del ORM que +no es la predeterminada por el controlador. Modelos obtenidos de ésta manera no serán seteados +como propiedades en el controlador:: + + // Obtiene un modelo de ElasticSearch + $articles = $this->fetchModel('Articles', 'Elastic'); + + // Obtiene un modelo de webservice + $github = $this->fetchModel('GitHub', 'Webservice'); + +.. versionadded:: 4.5.0 + .. php:method:: fetchTable(string $alias, array $config = []) -La función ``fetchTable()`` es útil cuando se necesita usar una tabla que no es +La función ``fetchTable()`` es útil cuando se necesita usar una tabla del ORM que no es la predeterminada por el controlador:: // En un método del controlador. - $recentArticles = $this->fetchTable('Articles')->find('all', [ - 'limit' => 5, - 'order' => 'Articles.created DESC' - ]) + $recentArticles = $this->fetchTable('Articles')->find('all', + limit: 5, + order: 'Articles.created DESC' + ) ->all(); .. versionadded:: 4.3.0 @@ -438,16 +474,18 @@ Paginación de un modelo Este método se utiliza para paginar los resultados obtenidos por tus modelos. Puedes especificar tamaño de páginas, condiciones de búsqueda del modelo y más. +Ve a la sección :doc:`pagination ` para más detalles +sobre como usar ``paginate()``. -El atributo ``$paginate`` te da una manera de personalizar cómo ``paginate()`` +El atributo ``$paginate`` te da una manera de personalizar cómo ``paginate()`` se comporta:: class ArticlesController extends AppController { - public $paginate = [ + protected array $paginate = [ 'Articles' => [ - 'conditions' => ['published' => 1] - ] + 'conditions' => ['published' => 1], + ], ]; } @@ -462,7 +500,7 @@ que deseas cargar, y cualquier dato de configuración para ellos:: public function initialize(): void { parent::initialize(); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); $this->loadComponent('Comments', Configure::read('Comments')); } @@ -486,24 +524,24 @@ Lista de eventos Métodos de callback del controlador ================================================ -Por defecto, los siguientes métodos de callback están conectados a +Por defecto, los siguientes métodos de callback están conectados a eventos relacionados si los métodos son implementados por tus controladores. .. php:method:: beforeFilter(EventInterface $event) Llamado durante el evento ``Controller.initialize`` que ocurre antes de cada - acción en el controlador. Es un lugar útil para comprobar si hay una sesión + acción en el controlador. Es un lugar útil para comprobar si hay una sesión activa o inspeccionar los permisos del usuario. .. note:: El método beforeFilter() será llamado por acciones faltantes. - + Devolver una respuesta del método ``beforeFilter`` no evitará que otros oyentes del mismo evento sean llamados. Debes explícitamente parar el evento. .. php:method:: beforeRender(EventInterface $event) - Llamado durante el evento ``Controller.beforeRender`` que ocurre después + Llamado durante el evento ``Controller.beforeRender`` que ocurre después de la lógica de acción del controlador, pero antes de que la vista sea renderizada. Este callback no se usa con frecuencia, pero puede ser necesaria si estas llamando :php:meth:`~Cake\\Controller\\Controller::render()` de forma @@ -515,7 +553,7 @@ eventos relacionados si los métodos son implementados por tus controladores. de cada acción del controlador, y después de que se complete el renderizado. Este es el último método del controlador para ejecutar. -Además de las devoluciones de llamada del ciclo de vida del controlador, +Además de las devoluciones de llamada del ciclo de vida del controlador, :doc:`/controllers/components` también proporciona un conjunto similar de devoluciones de llamada. @@ -535,9 +573,9 @@ Middleware del controlador .. php:method:: middleware($middleware, array $options = []) -:doc:`Middleware ` puede ser definido globalmente, en +:doc:`Middleware ` puede ser definido globalmente, en un ámbito de enrutamiento o dentro de un controlador. Para definir el middleware -para un controlador en específico usa el método ``middleware()`` de tu método +para un controlador en específico usa el método ``middleware()`` de tu método ``initialize()`` del controlador:: public function initialize(): void @@ -555,9 +593,6 @@ para un controlador en específico usa el método ``middleware()`` de tu métod El middleware definido por un controlador será llamado **antes** ``beforeFilter()`` y se llamarán a los métodos de acción. -.. versionadded:: 4.3.0 - ``Controller::middleware()`` fue agregado. - Más sobre controladores ======================= diff --git a/es/controllers/components.rst b/es/controllers/components.rst index 6f3a53ada1..c4af604468 100644 --- a/es/controllers/components.rst +++ b/es/controllers/components.rst @@ -2,24 +2,20 @@ Componentes ########### Los componentes son paquetes de lógica que se comparten entre los controladores. -CakePHP viene un con fantástico conjunto de componentes básicos que puedes usar +CakePHP viene un con fantástico conjunto de componentes básicos que puedes usar para ayudar en varias tareas comunes. También puedes crear tus propios componentes. Si te encuentras queriendo copiar y pegar cosas entre componentes, deberías considerar crear tu propio componente que contenga la funcionalidad. Crear componentes mantiene el código del controlador limpio y te permite rehusar código entre los diferentes controladores. -Para más información sobre componentes incluidos en CakePHP, consulte el capítulo +Para más información sobre componentes incluidos en CakePHP, consulte el capítulo de cada componente: .. toctree:: :maxdepth: 1 - /controllers/components/authentication /controllers/components/flash - /controllers/components/security - /controllers/components/pagination - /controllers/components/request-handling /controllers/components/form-protection /controllers/components/check-http-cache @@ -28,9 +24,8 @@ de cada componente: Configurando componentes ======================== -Muchos de los componentes principales requieren configuración. Algunos ejemplos -de componentes que requieren configuración son :doc:`/controllers/components/security` -y :doc:`/controllers/components/form-protection`. La configuración para estos +Muchos de los componentes principales requieren configuración. Un ejemplo sería +:doc:`/controllers/components/form-protection`. La configuración para estos componentes, y para los componentes en general, es usualmente hecho a través ``loadComponent()`` en el método ``initialize()`` del controlador o a través del array ``$components``:: @@ -42,9 +37,8 @@ en el método ``initialize()`` del controlador o a través del array ``$componen $this->loadComponent('FormProtection', [ 'unlockedActions' => ['index'], ]); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } - } También puedes configurar los componentes en tiempo de ejecución usando el método @@ -56,14 +50,14 @@ Lo anterior podría ser también expresado como:: $this->FormProtection->setConfig('unlockedActions', ['index']); } -Al igual que los helpers, componentes implementan los métodos ``getConfig()`` y +Al igual que los helpers, los componentes implementan los métodos ``getConfig()`` y ``setConfig()`` para leer y escribir los datos de configuración:: // Lee los datos de configuración. $this->FormProtection->getConfig('unlockedActions'); // Escribe los datos de configuración - $this->Csrf->setConfig('cookieName', 'token'); + $this->Flash->setConfig('key', 'myFlash'); Al igual que con los helpers, los componentes fusionarán automáticamente su propiedad ``$_defaultConfig`` con la configuración del controlador para crear la propiedad ``$_config`` que es @@ -73,7 +67,7 @@ Componentes de alias -------------------- Una configuración común para usar es la opción ``className``, que te permite utilizar -componentes de alias. Esta característica es útil cuando quieres reemplazar ``$this->Auth`` +componentes de alias. Esta característica es útil cuando quieres reemplazar ``$this->Flash`` u otra referencia común de componente con una implementación personalizada:: // src/Controller/PostsController.php @@ -81,21 +75,21 @@ u otra referencia común de componente con una implementación personalizada:: { public function initialize(): void { - $this->loadComponent('Auth', [ - 'className' => 'MyAuth' + $this->loadComponent('Flash', [ + 'className' => 'MyFlash' ]); } } - // src/Controller/Component/MyAuthComponent.php - use Cake\Controller\Component\AuthComponent; + // src/Controller/Component/MyFlashComponent.php + use Cake\Controller\Component\FlashComponent; - class MyAuthComponent extends AuthComponent + class MyFlashComponent extends FlashComponent { - // Agrega tu código para sobreescribir el AuthComponent principal + // Agrega tu código para sobreescribir el FlashComponent principal } -Lo de arriba haría *alias* ``MyAuthComponent`` a ``$this->Auth`` en tus controladores. +Lo de arriba haría *alias* ``MyFlashComponent`` a ``$this->Flash`` en tus controladores. .. note:: El alias de un componente reemplaza esa instancia en cualquier lugar donde se @@ -105,7 +99,7 @@ Carga de componentes sobre la marcha ------------------------------------ Es posible que no necesites todos tus componentes disponibles en cada acción del -controlador. En situaciones como estas, puedes cargar un componente en tiempo de +controlador. En situaciones como estas, puedes cargar un componente en tiempo de ejecución usando el método ``loadComponent()`` en tu controlador:: // En una acción del controlador @@ -115,7 +109,7 @@ ejecución usando el método ``loadComponent()`` en tu controlador:: .. note:: Ten en cuenta que los componentes cargados sobre la marcha no perderán devoluciones - de llamadas. Si te basas en que las devoluciones de llamada ``beforeFilter`` o + de llamadas. Si te basas en que las devoluciones de llamada ``beforeFilter`` o ``startup`` serán llamadas, necesitarás llamarlas manualmente dependiendo de cuándo cargas tu componente. @@ -123,7 +117,7 @@ Uso de componentes ================== Una vez que hayas incluido algunos componentes a tu controlador, usarlos es bastante -simple. Cada componente que uses se exponen como una propiedad en tu controlador. +simple. Cada componente que uses se exponen como una propiedad en tu controlador. Si cargaste el :php:class:`Cake\\Controller\\Component\\FlashComponent` en tu controlador, puedes acceder a él de esta forma:: @@ -150,6 +144,12 @@ puedes acceder a él de esta forma:: como propiedades, comparten el mismo 'espacio de nombres'. Asegúrate de no dar a un componente y un modelo el mismo nombre. +.. warning:: + + Los métodos de un componente **no** tienen acceso a :doc:`/development/dependency-injection` + como lo tienen los controladores. Usa una clase de servicio dentro de las acciones de tu controlador + en lugar de en el componente si necesitas ésta funcionalidad. + .. _creating-a-component: Creando un componente @@ -159,7 +159,7 @@ Supongamos que nuestra aplicación necesita realizar una operación matemática en muchas partes diferentes de la aplicación. Podríamos crear un componente para albergar esta lógica compartida para su uso en muchos controladores diferentes. -El primer paso es crear un nuevo archivo de componente y clase. Crea el archivo en +El primer paso es crear un nuevo archivo de componente y clase. Crea el archivo en **src/Controller/Component/MathComponent.php**. La estructura básica para el componente debería verse algo como esto:: @@ -184,21 +184,21 @@ Incluyendo tu componente en tus controladores --------------------------------------------- Una vez que nuestro componente está terminado, podemos usarlo en los controladores -de la aplicación cargándolo durante el método ``initialize()`` del controlador. -Una vez cargado, el controlador recibirá un nuevo atributo con el nombre del +de la aplicación cargándolo durante el método ``initialize()`` del controlador. +Una vez cargado, el controlador recibirá un nuevo atributo con el nombre del componente, a través del cual podemos acceder a una instancia del mismo:: // En un controlador // Haz que el nuevo componente esté disponible en $this->Math, - // así como el estándar $this->Csrf + // así como el estándar $this->Flash public function initialize(): void { parent::initialize(); $this->loadComponent('Math'); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } -Al incluir componentes en un controlador, también puedes declarar un conjunto de +Al incluir componentes en un controlador, también puedes declarar un conjunto de parámetros que se pasarán al constructor del componente. Estos parámetros pueden ser manejados por el componente:: @@ -210,10 +210,10 @@ ser manejados por el componente:: 'precision' => 2, 'randomGenerator' => 'srand' ]); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } -Lo anterior pasaría el array que contiene precision y randomGenerator a ``MathComponent::initialize()`` +Lo anterior pasaría el array que contiene precision y randomGenerator a ``MathComponent::initialize()`` en el parámetro ``$config``. Usando otros componentes en tu componente @@ -259,13 +259,13 @@ componentes agregándolos a la propiedad `$components`:: .. note:: - A diferencia de un componente incluido en un controlador, no se activarán + A diferencia de un componente incluido en un controlador, no se activarán devoluciones de llamada en el componente de un componente. Accediendo al controlador de un componente ------------------------------------------ -Desde dentro de un componente, puedes acceder al controlador actual a través del +Desde dentro de un componente, puedes acceder al controlador actual a través del registro:: $controller = $this->getController(); @@ -278,22 +278,22 @@ de las solicitudes que les permiten aumentar el ciclo de solicitud. .. php:method:: beforeFilter(EventInterface $event) - Es llamado antes que el método beforeFilter del controlador, pero *después* + Es llamado antes que el método beforeFilter() del controlador, pero *después* del método initialize() del controlador. .. php:method:: startup(EventInterface $event) - Es llamado después del método beforeFilter del controlador, pero antes de que - el controlador ejecute la acción actual del manejador. + Es llamado después del método beforeFilter() del controlador, pero antes de que + el controlador ejecute el "handler" de la acción actual .. php:method:: beforeRender(EventInterface $event) - Es llamado después de que el controlador ejecute la lógica de la acción + Es llamado después de que el controlador ejecute la lógica de la acción solicitada, pero antes de que el controlador renderize las vistas y el diseño. -.. php:method:: shutdown(EventInterface $event) +.. php:method:: afterFilter(EventInterface $event) - Es llamado antes de enviar la salida al navegador. + Es llamado durante el evento ``Controller.shutdown``, antes de enviar la salida al navegador. .. php:method:: beforeRedirect(EventInterface $event, $url, Response $response) @@ -341,5 +341,4 @@ actual. Al generar una ``RedirectException`` puedes incluir encabezados adiciona .. meta:: :title lang=es: Componentes - :keywords lang=es: array controller,core libraries,authentication request,array name,access control lists,public components,controller code,core components,cookiemonster,login cookie,configuration settings,functionality,logic,sessions,cakephp,doc - + :keywords lang=en: array controller,core libraries,array name,access control lists,public components,controller code,core components,cookiemonster,login cookie,configuration settings,functionality,logic,sessions,cakephp,doc diff --git a/es/controllers/components/authentication.rst b/es/controllers/components/authentication.rst deleted file mode 100644 index 552273efa1..0000000000 --- a/es/controllers/components/authentication.rst +++ /dev/null @@ -1,15 +0,0 @@ -Authentication -############## - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Authentication - :keywords lang=es: authentication handlers,array php,basic authentication,web application,different ways,credentials,exceptions,cakephp,logging diff --git a/es/controllers/components/csrf.rst b/es/controllers/components/csrf.rst deleted file mode 100644 index 2055c79598..0000000000 --- a/es/controllers/components/csrf.rst +++ /dev/null @@ -1,15 +0,0 @@ -Cross Site Request Forgery -########################## - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Csrf - :keywords lang=es: configurable parameters,security component,configuration parameters,invalid request,csrf,submission diff --git a/es/controllers/components/pagination.rst b/es/controllers/components/pagination.rst deleted file mode 100644 index 138fccaaa0..0000000000 --- a/es/controllers/components/pagination.rst +++ /dev/null @@ -1,324 +0,0 @@ -Paginación -########## - -.. php:namespace:: Cake\Controller\Component - -.. php:class:: PaginatorComponent - -Uno de los mayores obstaculos para crear aplicaciones web flexibles y -amigables para el usuario es diseñar una interfaz de usuario intuitiva. -Muchas aplicaciones tienen a crecer en tamaño y complejidad rapidamente, -y los diseñadores y programadores por igual no pueden hacer frente -a la visualización de cientos o miles de registros. Refactorizar lleva -tiempo, y el rendimiento y la satisfación del usuario pueden verse afectados. - -Mostrar un número razonable de registros por página siempre ha sido una parte crítica -para cada aplicación y suele causar muchos dolores de cabeza a los desarrolladores. -CakePHP alivia la carga del desarrollador al proporcionar una manera rapida y fácil -de paginar datos. - -La paginación en CakePHP es ofrecida por un componente de un controlador. Puedes utilizar -:php:class:`~Cake\\View\\Helper\\PaginatorHelper` en la vista de tu plantilla para generar -los controles de paginación. - -Uso Básico -========== - -Para paginar una consulta primero debemos cargar el ``PaginatorComponent``:: - - class ArticlesController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Paginator'); - } - } - -Una vez cargado podemos paginar una tabla de clase ORM o un objeto ``Query``:: - - public function index() - { - // Paginate the ORM table. - $this->set('articles', $this->paginate($this->Articles)); - - // Paginate a partially completed query - $query = $this->Articles->find('published'); - $this->set('articles', $this->paginate($query)); - } - -Uso Avanzado -============ - -El componente ``PaginatorComponent`` admite casos de uso más complejos mediante la -configuración de la propiedad del controlador ``$paginate`` o como el argumento ``$settings`` -para ``paginate()``. Estas condiciones sirven como base para tus consultas de paginación. -Son aumentados por los parametros ``sort``, ``direction``, ``limit``, y ``page`` pasados -dentro de la URL:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'limit' => 25, - 'order' => [ - 'Articles.title' => 'asc' - ] - ]; - } - -.. tip:: - Las opciones predeterminadas de ``order`` deben definirse como un array. - -Si bien puedes incluir cualquiera de las opciones soportadas por :php:meth:`~Cake\\ORM\\Table::find()` -como ``fields`` en tus ajustes de paginación. Es más limpio y sencillo agrupar tus opciones de -paginación dentro de :ref:`custom-find-methods`. Puedes usar tu buscador en la paginación utilizando la -opción ``finder`` :: - - class ArticlesController extends AppController - { - public $paginate = [ - 'finder' => 'published', - ]; - } - -Si tu metodo de busqueda requiere opciones adicionales, puedes pasarlas como como valores -para el buscador:: - - class ArticlesController extends AppController - { - // find articles by tag - public function tags() - { - $tags = $this->request->getParam('pass'); - - $customFinderOptions = [ - 'tags' => $tags - ]; - // Estamos utilizando el argumento $settings para paginate() aqui. - // Pero la misma estructura puede ser utilizada para $this->paginate - // - // Nuestro buscador personalizado se llama findTagged dentro ArticlesTable.php - // por eso estamos usando `tagged` como clave. - // Nuestro buscador deberia verse como: - // public function findTagged(Query $query, array $options) { - $settings = [ - 'finder' => [ - 'tagged' => $customFinderOptions - ] - ]; - $articles = $this->paginate($this->Articles, $settings); - $this->set(compact('articles', 'tags')); - } - } - -Además de definir valores generales de paginación, puedes definir mas de un conjunto de -valores predeterminados para la paginación en el controlador. El nombre de cada modelo -puede ser usado como clave en la propiedad ``$paginate``:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'Articles' => [], - 'Authors' => [], - ]; - } - -Los valores de las claves de ``Articles`` y ``Authors`` podrían contener todas las -propiedades que tendría una matriz básica ``$paginate``. - -Una vez que hayas utilizado ``paginate()`` para crear resultados. La solicitud del -controlador se actualizará con los parámetros de paginación. Puedes acceder a los -metadatos de paginación en ``$this->request->getParam('paging')``. - -Paginación Simple -================= - -Por defecto, la paginación utiliza una consulta ``count()`` para calcular el tamaño -del conjunto de resultados para que puedan ser renderizados los enlaces de número de -página. En conjuntos de datos muy grandes, esta consulta de conteo puede ser muy costosa. -En situaciones donde solo quieres mostrar los enlaces "Siguiente" y "Anterior" puedes -utilizar el paginador 'simple' que realiza una consulta de conteo:: - - public function initialize(): void - { - parent::initialize(); - - // Load the paginator component with the simple paginator strategy. - $this->loadComponent('Paginator', [ - 'paginator' => new \Cake\Datasource\SimplePaginator(), - ]); - } - -Cuando se utilice el ``SimplePaginator`` no se podra generar los números de pagina, -datos de contador, enlaces a la ultima pagina, o controles de recuento total de registros. - -Utilizando Directamente PaginatorComponent -========================================== - -Si necesitas paginar datos de otro componente, puedes utilizar el ``PaginatorComponent`` directamente. -Cuenta con una API similar al método controlador:: - - $articles = $this->Paginator->paginate($articleTable->find(), $config); - - // Or - $articles = $this->Paginator->paginate($articleTable, $config); - -El primer parámetro debe ser el objeto de consulta a encontrar en la tabla de objetos de la que se -desea paginar los resultados. Opcionalmente, puedes pasar el tabla de objetos y dejar la consulta -se construirá para usted. El segundo parametro deberia ser el array de los ajustes para usar en la -paginación. Este array deberia tener la misma estructura que la propiedad ``$paginate`` en el -controlador. Al paginar un objeto ``Query``, la opción ``finder`` sera ignorada. Se da por asumido -que se esta pasando la consulta que desas que sea paginada. - -.. _paginating-multiple-queries: - -Paginando Multiples Consultas -============================= - -Puedes paginar multiples modelos en una sola accion del controlador, usando la opción ``scope`` -tanto en la propiedad ``$paginate`` del controlador y en la llamada al metodo ``paginate()``:: - - // Propiedad paginado - public $paginate = [ - 'Articles' => ['scope' => 'article'], - 'Tags' => ['scope' => 'tag'] - ]; - - // En una acción del controlador - $articles = $this->paginate($this->Articles, ['scope' => 'article']); - $tags = $this->paginate($this->Tags, ['scope' => 'tag']); - $this->set(compact('articles', 'tags')); - -La opción ``scope`` dará como resultado el aspecto de ``PaginatorComponent`` en parámetros de cadena -de consulta con ámbito. Por ejemplo, el siguiente URL podría ser utilizado para paginar tags y articles -al mismo tiempo:: - - /dashboard?article[page]=1&tag[page]=3 - -Consulte la sección `paginator-helper-multiple` para saber como generar elementos HTML con ambito -y URLs para paginación. - -Paginar el Mismo Modelo Varias Veces ------------------------------------- - -Para paginar el mismo modelo multiples veces dentro de una sola acción del controlador necesitas definir -un alias para el modelo. Consulte `table-registry-usage` para detalles adicionales sobre como -utilizar la tabla de registros:: - - // En una acción del controlador - $this->paginate = [ - 'ArticlesTable' => [ - 'scope' => 'published_articles', - 'limit' => 10, - 'order' => [ - 'id' => 'desc', - ], - ], - 'UnpublishedArticlesTable' => [ - 'scope' => 'unpublished_articles', - 'limit' => 10, - 'order' => [ - 'id' => 'desc', - ], - ], - ]; - - // Registrar una tabla de objetos adicional para permitir la diferenciación en el componente de paginación - TableRegistry::getTableLocator()->setConfig('UnpublishedArticles', [ - 'className' => 'App\Model\Table\ArticlesTable', - 'table' => 'articles', - 'entityClass' => 'App\Model\Entity\Article', - ]); - - $publishedArticles = $this->paginate( - $this->Articles->find('all', [ - 'scope' => 'published_articles' - ])->where(['published' => true]) - ); - - $unpublishedArticles = $this->paginate( - TableRegistry::getTableLocator()->get('UnpublishedArticles')->find('all', [ - 'scope' => 'unpublished_articles' - ])->where(['published' => false]) - ); - -.. _control-which-fields-used-for-ordering: - -Controlar que Campos se utilizan para Ordenar -============================================= - -Por defecto, el ordenamiento se puede realizar en cualquier columna no virtual que la tabla tenga. -Esto es, a veces no deseable ya que permite a los usuarios ordenar por columnas no indexadas que pueden -provocar gran trabajo para ser ordenadas. Puedes establecer una lista blanca de campos que se pueden -ordenar utilando la opción ``sortWhitelist``. Esta opción es necesaria cuando quieres ordenar datos asociados -o campos calculados que pueden formar parte de la consulta de paginación:: - - public $paginate = [ - 'sortWhitelist' => [ - 'id', 'title', 'Users.username', 'created' - ] - ]; - -Cualquier solicitud que intente ordenar campos que no se encuentren en el lista blanca será ignorada. - -Limitar el Número Máximo de Filas por Página -============================================ - -El número de resultados que se obtienen por página se expone al usuario como el parametro ``limit``. -Generalmente no es deseable permitir que los usuarios obtengan todas las filas en un conjunto paginado. -La opción ``maxLimit`` establece que nadie puede configurar este límite demasiado alto desde afuera. -Por defecto, CakePHP limita el número maximo de filas que pueden ser obtenidas a 100. Si este limite por -defecto no es apropiado para tu aplicación, puedes ajustarlo en las opciones de paginación, por ejemplo, -reduciendolo a ``10``:: - - public $paginate = [ - // Other keys here. - 'maxLimit' => 10 - ]; - -Si el parametro de la solictud es mayor a este valor, se reducirá al valor de ``maxLimit``. - -Uniendo Asociaciones Adicionales -================================ - -Se pueden cargar asociaciones adicionales en la tabla paginada utilizando el parametro ``contain``:: - - public function index() - { - $this->paginate = [ - 'contain' => ['Authors', 'Comments'] - ]; - - $this->set('articles', $this->paginate($this->Articles)); - } - -Solicitudes de Página Fuera de Rango -==================================== - -El PaginatorComponent lanzará un ``NotFoundException`` cuando trate de acceder a una página no existente, -es decir, cuando el número de página solicitado sea mayor al número de páginas. - -Por lo tanto, puedes dejar que se muestre la página de error normal o utilizar un bloque try catch y -tomar las medidas apropiadas cuando se detecta un ``NotFoundException``:: - - use Cake\Http\Exception\NotFoundException; - - public function index() - { - try { - $this->paginate(); - } catch (NotFoundException $e) { - // Has algo aquí como redirigir a la primera página o a la ultima página. - // $this->request->getAttribute('paging') te dara la información requerida. - } - } - -Paginación en la Vista -====================== - -Consulte la documentación :php:class:`~Cake\\View\\Helper\\PaginatorHelper` para saber como crear -enlaces para la navegación de paginación. - -.. meta:: - :title lang=es: Paginación - :keywords lang=es: array ordenado,condiciones de consulta,clase php,aplicaciones web,dolores de cabeza,obstaculos,complejidad,programadores,parametros,paginado,diseñadores,cakephp,satisfacción,desarrolladores diff --git a/es/controllers/components/request-handling.rst b/es/controllers/components/request-handling.rst deleted file mode 100644 index c219ab6f61..0000000000 --- a/es/controllers/components/request-handling.rst +++ /dev/null @@ -1,15 +0,0 @@ -Request Handling -################ - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Request Handling - :keywords lang=es: handler component,javascript libraries,public components,null returns,model data,request data,content types,file extensions,ajax,meth,content type,array,conjunction,cakephp,insight,php diff --git a/es/controllers/components/security.rst b/es/controllers/components/security.rst deleted file mode 100644 index 261c8bd3d4..0000000000 --- a/es/controllers/components/security.rst +++ /dev/null @@ -1,191 +0,0 @@ -Seguridad -######### - -.. php:class:: SecurityComponent(ComponentCollection $collection, array $config = []) - -El componente de seguridad crea una forma de integrar seguridad de forma más estricta -en tu aplicación. Proporciona métodos para diversas tareas como: - -* Restringir qué métodos HTTP acepta tu aplicación. -* Protección contra manipulación de formularios. -* Requerir que se utilice SSL. -* Limitación de la comunicación entre controladores. - -Como todos los componentes, se configura a través de varios parámetros configurables. -Todas estas propiedades se pueden establecer directamente o a través de métodos setter -del mismo nombre en ``beforeFilter()`` de tu controlador. - -Al usar el componente de seguridad, obtienes automáticamente protección contra la -manipulación de formularios. Los campos token ocultos se insertarán automáticamente -en los formularios y serán verificados por el componente de seguridad. - -Si estas utilizando las funciones de protección de formularios del componente de -seguridad y otros componentes que procesan datos de formularios en tus devoluciones -de llamada ``startup()``, asegúrate de colocar el componente de seguridad antes de -esos componentes en tu método ``initialize()``. - -.. note:: - - Al usar el componente de seguridad, **debes** usar FormHelper para crear tus - formularios. Además, **no** debes reescribir ninguno de los "nombres" de los - campos. El componente de seguridad busca ciertos indicadores que son creados - y manejados por FormHelper (especialmente esos creados en - :php:meth:`~Cake\\View\\Helper\\FormHelper::create()` y - :php:meth:`~Cake\\View\\Helper\\FormHelper::end()`). Es probable que la modificación - dinámica de los campos que se envían en una solicitud POST, como deshabilitar, - borrar o crear nuevos campos a través de JavaScript, haga que la solicitud se - envíe a la devolución de llamada de blackhole. - - Siempre debes verificar el método HTTP que se utiliza antes de ejecutarlo para - evitar efectos secundarios. Debes verificar el método HTTP - o usar :php:meth:`Cake\\Http\\ServerRequest::allowMethod()` para asegurarte - de que se utiliza el método HTTP correcto. - -Manejo de devoluciones de llamada blackhole -=========================================== - -.. php:method:: blackHole(Controller $controller, string $error = '', ?SecurityException $exception = null) - -Si una acción está restringida por el componente de seguridad, es 'black-holed' -como una solicitud no válida que dará como resultado un error 400 por defecto. -Puedes configurar este comportamiento seteando la opción de configuración -``blackHoleCallback`` para una función de devolución de llamada en el controlador. - -Al configurar un método de devolución de llamada, puedes personalizar como el -proceso blackhole funciona:: - - public function beforeFilter(EventInterface $event) - { - parent::beforeFilter($event); - - $this->Security->setConfig('blackHoleCallback', 'blackhole'); - } - - public function blackhole($type, SecurityException $exception) - { - if ($exception->getMessage() === 'Request is not SSL and the action is required to be secure') { - // Reformule el mensaje de excepción con un string traducible. - $exception->setMessage(__('Please access the requested page through HTTPS')); - } - - // Vuelve a lanzar la excepción reformulada condicionalmente. - throw $exception; - - // Alternativamente, maneja el error. Por ejemplo, configura un mensaje flash & - // redirige a la versión HTTPS de la página solicitada. - } - -El parámetro ``$type`` puede tener los siguientes valores: - -* 'auth' Indica un error de validación de formulario o un error de discrepancia - entre controlador y acción. -* 'secure' Indica un error de restricción del método SSL. - -Prevención de manipulación de formularios -========================================= - -Por defecto, ``SecurityComponent`` evita que los usuarios alteren los formularios -de formas específicas. El ``SecurityComponent`` evitará las siguientes cosas: - -* Los campos desconocidos no podrán ser agregados al formulario. -* Los campos no pueden ser eliminados del formulario. -* Los valores en las entradas ocultas no podrán ser modificadas. - -La prevención de este tipo de manipulación se logra trabajando con ``FormHelper`` -y rastreando qué campos hay en un formulario. También se realiza un seguimiento -de los valores de los campos ocultos. Todos estos datos se combinan y se convierten -en un hash. Cuando un formulario es enviado, ``SecurityComponent`` usará los datos -POST para construir la misma estructura y comparar el hash. - -.. note:: - - SecurityComponent **no** evitará que se agreguen/cambien opciones seleccionadas. - Tampoco impedirá que se agreguen/cambien opciones de radio. - -unlockedFields - Establecer en una lista de campos de formulario para excluir de la validación - POST. Los campos se pueden desbloquear en el componente o con :php:meth:`FormHelper::unlockField()`. - Los campos que han sido desbloqueados no están obligados a ser parte del POST - y los campos desbloqueados ocultos no tienen su valores verificados. - -validatePost - Establece en ``false`` para omitir por completo la validación de las solicitudes - POST, esencialmente desactivando las validaciones de los formularios. - -Uso -=== - -La configuración del componente de seguridad generalmente se realiza en las -devoluciones de llamada ``initialize`` o ``beforeFilter()`` del controlador:: - - namespace App\Controller; - - use App\Controller\AppController; - use Cake\Event\EventInterface; - - class WidgetsController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Security'); - } - - public function beforeFilter(EventInterface $event) - { - parent::beforeFilter($event); - - if ($this->request->getParam('prefix') === 'Admin') { - $this->Security->setConfig('validatePost', false); - } - } - } - -El ejemplo anterior deshabilitaría la prevención de manipulación de formularios -para rutas con prefijo de administrador. - -.. _security-csrf: - -Protección CSRF -=============== - -CSRF o Cross Site Request Forgery es una vulnerabilidad común en las aplicaciones -web. Permite a un atacante capturar y reproducir una solicitud anterior, y a veces, -enviar solicitudes de datos utilizando etiquetas de imagen o recursos en otros -dominios. Para habilitar las funciones de protección CSRF. - -Deshabilitar la manipulación de formularios para acciones específicas -===================================================================== - -Hay muchos casos en los que querrías deshabilitar la prevención de manipulación -de formularios para una acción (por ejemplo, solicitudes AJAX). Puedes "desbloquear" -estas acciones enumerándolas en ``$this->Security->unlockedActions`` en tu -``beforeFilter()``:: - - namespace App\Controller; - - use App\Controller\AppController; - use Cake\Event\EventInterface; - - class WidgetController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Security'); - } - - public function beforeFilter(EventInterface $event) - { - parent::beforeFilter($event); - - $this->Security->setConfig('unlockedActions', ['edit']); - } - } - -Este ejemplo deshabilitaría todas las comprobaciones de seguridad para las acciones -de edición. - -.. meta:: - :title lang=es: Seguridad - :keywords lang=es: parámetros configurables, componente de seguridad, parámetros de configuración, solicitud no válida, funciones de protección, seguridad más estricta, holing, clase php, meth, error 404, período de inactividad, csrf, array, envío, clase de seguridad, deshabilitar seguridad, desbloquear acciones diff --git a/es/controllers/middleware.rst b/es/controllers/middleware.rst index f0ae2d006c..cefa1954e0 100644 --- a/es/controllers/middleware.rst +++ b/es/controllers/middleware.rst @@ -34,33 +34,263 @@ Middleware en CakePHP CakePHP proporciona varios middleware para manejar tareas comunes en aplicaciones web: * ``Cake\Error\Middleware\ErrorHandlerMiddleware`` atrapa las excepciones del middleware envuelto - y presenta una página de error usando el controlador de + y presenta una página de error usando el controlador de :doc:`/development/errors` excepciones de manejo de errores y excepciones. -* ``Cake\Routing\AssetMiddleware`` comprueba si la solicitud se refiere a un archivo - de tema o complemento, como un archivo CSS, JavaScript o de imagen almacenado en +* ``Cake\Routing\AssetMiddleware`` comprueba si la solicitud se refiere a un archivo + de tema o complemento, como un archivo CSS, JavaScript o de imagen almacenado en la carpeta webroot de un complemento o la correspondiente para un Tema. -* ``Cake\Routing\Middleware\RoutingMiddleware`` utiliza ``Router`` para analizar la URL entrante +* ``Cake\Routing\Middleware\RoutingMiddleware`` utiliza ``Router`` para analizar la URL entrante y asignar parámetros de enrutamiento a la solicitud. -* ``Cake\I18n\Middleware\LocaleSelectorMiddleware`` permite el cambio automático de idioma desde +* ``Cake\I18n\Middleware\LocaleSelectorMiddleware`` permite el cambio automático de idioma desde el ``Accept-Languageencabezado`` enviado por el navegador. * ``Cake\Http\Middleware\SecurityHeadersMiddleware`` facilita la adición de encabezados relacionados con la seguridad ``X-Frame-Options`` a las respuestas. -* ``Cake\Http\Middleware\EncryptedCookieMiddleware`` le brinda la capacidad de manipular cookies +* ``Cake\Http\Middleware\EncryptedCookieMiddleware`` le brinda la capacidad de manipular cookies encriptadas en caso de que necesite manipular las cookies con datos confusos. * ``Cake\Http\Middleware\CsrfProtectionMiddleware`` agrega protección CSRF a su aplicación. -* ``Cake\Http\Middleware\BodyParserMiddleware`` le permite decodificar JSON, XML y otros cuerpos +* ``Cake\Http\Middleware\BodyParserMiddleware`` le permite decodificar JSON, XML y otros cuerpos de solicitud codificados basados ​​en ``Content-Type`` del encabezado. +.. _using-middleware: + +Usando Middlewares +================== + +Los Middlewares pueden ser agregados a tu aplicación globalmente, a rutas específicas o incluso controladores. + +Para agregar un middleware a todos los requests, usa el método ``middleware`` de tu +clase ``App\Application``. Este método se llama al inicio del procesamiento del request. +Puedes usar el objeto ``MiddlewareQueue`` para agregar el middleware:: + + namespace App; + + use Cake\Http\BaseApplication; + use Cake\Http\MiddlewareQueue; + use Cake\Error\Middleware\ErrorHandlerMiddleware; + + class Application extends BaseApplication + { + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + // Agregar el manejador de errores en la cola de middlewares. + $middlewareQueue->add(new ErrorHandlerMiddleware()); + + // Agregar middleware por clase. + // Desde la versión 4.5.0 el nombre de la clase del middleware es + // resuelto opcionalmente usando el contener DI. Si la clase no es encontrada + // en el contenedor, entonces una instancia es creada por la cola de middlewares + $middlewareQueue->add(UserRateLimiting::class); + + return $middlewareQueue; + } + } + +Adicionalmente, aparte de agregarlo al final de la ``MiddlewareQueue``, puedes realizar distintas +operaciones:: + + $layer = new \App\Middleware\CustomMiddleware; + + // El middleware agregado será el último de la cola. + $middlewareQueue->add($layer); + + // El middleware agregado será el primero de la cola. + $middlewareQueue->prepend($layer); + + // Inserta el middleware en un lugar especifico. Si el lugar no existe, + // será agregado al final + $middlewareQueue->insertAt(2, $layer); + + // Inserta el middleware antes de otro. + // Si el middleware especificado no existe, + // se lanzará una excepción + $middlewareQueue->insertBefore( + 'Cake\Error\Middleware\ErrorHandlerMiddleware', + $layer + ); + + // Inserta después que otro middleware + // Si el middleware especificado no existe, + // el middleware will added to the end. + $middlewareQueue->insertAfter( + 'Cake\Error\Middleware\ErrorHandlerMiddleware', + $layer + ); + + +Si tu middleware solo es aplicable a un subconjunto de rutas o controladores especificos puedes usar +:ref:`Middleware por Rutas `, o :ref:`Middleware por Controlador `. + +Agregando Middleware desde un Plugin +------------------------------------- + +Los Plugins pueden usar su propio método ``middleware`` para agregar cualquier middleware que +implementen a la cola de middlewares de la aplicación:: + + // en plugins/ContactManager/src/Plugin.php + namespace ContactManager; + + use Cake\Core\BasePlugin; + use Cake\Http\MiddlewareQueue; + use ContactManager\Middleware\ContactManagerContextMiddleware; + + class Plugin extends BasePlugin + { + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue->add(new ContactManagerContextMiddleware()); + + return $middlewareQueue; + } + } + +Creaando un Middleware +====================== + +Un Middleware puede ser implementado mediante funciones anónimas (Closures), o clases que extiendan +a ``Psr\Http\Server\MiddlewareInterface``. Mientras que los Closures son apropiados para +tareas pequeñas, las pruebas se vuelven complicadas y puedes complicar aún más la clase +``Application``. Las clases middleware en CakePHP tienen algunasconvenciones: + +* Los archivos deben ubicarse en **src/Middleware**. Por ejemplo: + **src/Middleware/CorsMiddleware.php** +* Deben tener ``Middleware`` como sufijo. Por ejemplo: + ``LinkMiddleware``. +* Deben implementar la interfaz ``Psr\Http\Server\MiddlewareInterface``. + +Un Middleware puede devolver la respuesta llamando a ``$handler->handle()`` o +creando su propia respuesta. Podemos ver ambas opciones en el siguiente ejemplo:: + + // En src/Middleware/TrackingCookieMiddleware.php + namespace App\Middleware; + + use Cake\Http\Cookie\Cookie; + use Cake\I18n\Time; + use Psr\Http\Message\ResponseInterface; + use Psr\Http\Message\ServerRequestInterface; + use Psr\Http\Server\RequestHandlerInterface; + use Psr\Http\Server\MiddlewareInterface; + + class TrackingCookieMiddleware implements MiddlewareInterface + { + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface + { + // Llamando $handler->handle() delega el control al siguiente middleware + // en la cola de tu aplicación. + $response = $handler->handle($request); + + if (!$request->getCookie('landing_page')) { + $expiry = new Time('+ 1 year'); + $response = $response->withCookie(new Cookie( + 'landing_page', + $request->getRequestTarget(), + $expiry + )); + } + + return $response; + } + } + +Ahora que hemos hecho un middleware bastante simple, agreguémoslo a nuestra aplicación:: + + // En src/Application.php + namespace App; + + use App\Middleware\TrackingCookieMiddleware; + use Cake\Http\MiddlewareQueue; + + class Application + { + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + // Agrega tu middleware a la cola + $middlewareQueue->add(new TrackingCookieMiddleware()); + + // Agrega más middlewares a la cola si lo deseas + + return $middlewareQueue; + } + } + + +.. _routing-middleware: + +Middleware Routing +================== + +El middleware de enrutamiento es responsable de procesar las rutas de tu aplicación e +identificar el plugin, controlador, y acción hacia la cual va un request:: + + // En Application.php + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + // ... + $middlewareQueue->add(new RoutingMiddleware($this)); + } + +.. _encrypted-cookie-middleware: + +Middleware EncryptedCookie +=========================== + +Si tu aplicación tiene cookies que contienen información que +quieres ofuscar y proteger, puedes usar el middleware de Cookies encriptadas +de CakePHP para encriptar y desencriptar de manera transparente la información +vía middleware. La información del Cookie es encriptada vía OpenSSL using AES:: + + use Cake\Http\Middleware\EncryptedCookieMiddleware; + + $cookies = new EncryptedCookieMiddleware( + // Names of cookies to protect + ['secrets', 'protected'], + Configure::read('Security.cookieKey') + ); + + $middlewareQueue->add($cookies); .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. + Se recomienda que la clave de encriptación que se utiliza para la información + del Cookie sea **exclusivamente** para esto. + +Los algoritmos de encriptación y el estilo de relleno usado por el middleware son retrocompatibles +con el ``CookieComponent`` de versiones anteriores de CakePHP. + +.. _body-parser-middleware: + +Middleware BodyParser +====================== - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +Si tu aplicación acepta JSON, XML o algún `request` de este tipo, el +``BodyParserMiddleware`` te permitirá decodificar esos `requests` en un arreglo que +estará disponible via ``$request->getParsedData()`` y ``$request->getData()``. Por defecto sólo +``json`` será procesado, pero el procesamiento XML puede ser activado como opción. +También puedes definir tus propios procesadores:: - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + use Cake\Http\Middleware\BodyParserMiddleware; + + // solo JSON será procesado + $bodies = new BodyParserMiddleware(); + + // Activar procesamiento XML + $bodies = new BodyParserMiddleware(['xml' => true]); + + // Desactivar procesamiento JSON + $bodies = new BodyParserMiddleware(['json' => false]); + + // Agregar tu propio procesador aplicándolo a un content-type + // específico y asignandole una funcion de procesamiento + $bodies = new BodyParserMiddleware(); + $bodies->addParser(['text/csv'], function ($body, $request) { + // Use a CSV parsing library. + return Csv::parse($body); + }); .. meta:: :title lang=es: Http Middleware :keywords lang=es: http, middleware, psr-7, request, response, wsgi, application, baseapplication + + + diff --git a/es/controllers/pagination.rst b/es/controllers/pagination.rst new file mode 100644 index 0000000000..63c3a4a4ea --- /dev/null +++ b/es/controllers/pagination.rst @@ -0,0 +1,13 @@ +Paginación +########## + +.. note:: + La documentación no es compatible actualmente con el idioma español en esta + página. + + Por favor, siéntase libre de enviarnos un pull request en + `Github `_ o utilizar el botón + **Improve this Doc** para proponer directamente los cambios. + + Usted puede hacer referencia a la versión en Inglés en el menú de selección + superior para obtener información sobre el tema de esta página. diff --git a/es/controllers/request-response.rst b/es/controllers/request-response.rst index ea6cc2dad7..ff035b378d 100644 --- a/es/controllers/request-response.rst +++ b/es/controllers/request-response.rst @@ -1,20 +1,1118 @@ -Request & Response Objects -########################## +Objetos de Solicitud y Respuesta +################################ + +.. php:namespace:: Cake\Http + +Los objetos de solicitud y respuesta proporcionan una abstracción en torno a las solicitudes y respuestas HTTP. El objeto +de solicitud en CakePHP le permite realizar una introspección de una solicitud entrante, mientras que el objeto de +respuesta le permite crear respuestas HTTP sin esfuerzo desde sus controladores. + +.. index:: $this->request +.. _cake-request: + +Solicitud (Request) +=================== + +.. php:class:: ServerRequest + +``ServerRequest`` es el objeto de solicitud predeterminado utilizado en CakePHP. Centraliza una serie de funciones para +interrogar e interactuar con los datos de la solicitud. En cada solicitud, se crea un Request y luego se pasa por +referencia a las distintas capas de una aplicación que utiliza datos de solicitud. De forma predeterminada, la solicitud +se asigna a ``$this->request`` y está disponible en Controladores, Celdas, Vistas y Ayudantes. También puede acceder a él +en Componentes usando la referencia del controlador. + +Algunas de las tareas que realiza ``ServerRequest`` incluyen: + +* Procesar los arreglos GET, POST y FILES en las estructuras de datos con las que está familiarizado. +* Proporcionar una introspección del entorno correspondiente a la solicitud. Información como los encabezados enviados, + la dirección IP del cliente y los nombres de subdominio/dominio en el servidor en el que se ejecuta su aplicación. +* Proporcionar acceso a los parámetros de solicitud tanto como índices de matriz como propiedades de objetos. + +El objeto de la solicitud de CakePHP implementa `PSR-7 ServerRequestInterface `_, lo que +facilita el uso de bibliotecas desde fuera de CakePHP. + +.. _request-parameters: + +Parámetros de la solicitud +-------------------------- + +La solicitud expone los parámetros de enrutamiento a través del método ``getParam()``:: + + $controllerName = $this->request->getParam('controller'); + +Para obtener todos los parámetros de enrutamiento como una matriz, use ``getAttribute()``:: + + $parameters = $this->request->getAttribute('params'); + +Se accede a todos los :ref:`route-elements` a través de esta interfaz. + +Además de :ref:`route-elements`, a menudo también necesita acceso a :ref:`passed-arguments`. Ambos también están +disponibles en el objeto de solicitud: + + // Argumentos pasados + $passedArgs = $this->request->getParam('pass'); + +Todos le proporcionarán acceso a los argumentos pasados. Hay varios parámetros importantes/útiles que CakePHP usa +internamente, y todos ellos también se encuentran en los parámetros de enrutamiento: + +* ``plugin`` El complemento que maneja la solicitud. Será nulo cuando no haya ningún complemento. +* ``controller`` El controlador que maneja la solicitud actual. +* ``action`` La acción que maneja la solicitud actual. +* ``prefix`` El prefijo de la acción actual. Consulte :ref:`prefix-routing` para obtener más información. + +Parámetros de cadena de consulta +-------------------------------- + +.. php:method:: getQuery($name, $default = null) + +Los parámetros de la cadena de consulta se pueden leer usando el método ``getQuery()``:: + + // URL es /posts/index?page=1&sort=title + $page = $this->request->getQuery('page'); + +Puede acceder directamente a la propiedad de consulta o puede utilizar el método ``getQuery()`` para leer la matriz de +consulta de URL sin errores. Cualquier clave que no exista devolverá ``null``:: + + $foo = $this->request->getQuery('value_that_does_not_exist'); + // $foo === null + + // También puede proporcionar valores predeterminados + $foo = $this->request->getQuery('does_not_exist', 'default val'); + +Si desea acceder a todos los parámetros de consulta, puede utilizar ``getQueryParams()``: + + $query = $this->request->getQueryParams(); + +Datos del cuerpo de la solicitud +-------------------------------- + +.. php:method:: getData($name, $default = null) + +Se puede acceder a todos los datos POST normalmente disponibles a través de la variable global ``$_POST`` de PHP usando +:php:meth:`Cake\\Http\\ServerRequest::getData()`. Por ejemplo:: + + // Se puede acceder a una entrada con un atributo de nombre 'título' + $title = $this->request->getData('title'); + +Puede utilizar nombres separados por puntos para acceder a datos anidados. Por ejemplo:: + + $value = $this->request->getData('address.street_name'); + +Para nombres inexistentes se devolverá el valor ``$default``:: + + $foo = $this->request->getData('value.that.does.not.exist'); + // $foo == null + +También puede utilizar :ref:`body-parser-middleware` para analizar el cuerpo de la solicitud de diferentes tipos de +contenido en una matriz, de modo que sea accesible a través de ``ServerRequest::getData()``. + +Si desea acceder a todos los parámetros de datos, puede utilizar +``getParsedBody()``:: + + $data = $this->request->getParsedBody(); + +.. _request-file-uploads: + +Cargas de archivos +------------------ + +Se puede acceder a los archivos cargados a través de los datos del cuerpo de la solicitud, utilizando el método +:php:meth:`Cake\\Http\\ServerRequest::getData()` descrito anteriormente. Por ejemplo, se puede acceder a un archivo desde +un elemento de entrada con un atributo de nombre ``attachment`` de esta manera:: + + $attachment = $this->request->getData('attachment'); + +De forma predeterminada, las cargas de archivos se representan en los datos de la solicitud como objetos que implementan +`\\Psr\\Http\\Message\\UploadedFileInterface `__. En la +implementación actual, la variable ``$attachment`` en el ejemplo anterior contendría de forma predeterminada una +instancia de ``\Laminas\Diactoros\UploadedFile``. + +Acceder a los detalles del archivo cargado es bastante simple, así es como puede obtener los mismos datos que proporciona +la matriz de carga de archivos de estilo antiguo: + + $name = $attachment->getClientFilename(); + $type = $attachment->getClientMediaType(); + $size = $attachment->getSize(); + $tmpName = $attachment->getStream()->getMetadata('uri'); + $error = $attachment->getError(); + +Mover el archivo cargado desde su ubicación temporal a la ubicación de destino deseada no requiere acceder manualmente al +archivo temporal, sino que se puede hacer fácilmente usando el método ``moveTo()`` del objeto:: + + $attachment->moveTo($targetPath); + +En un entorno HTTP, el método ``moveTo()`` validará automáticamente si el archivo es un archivo cargado real y generará +una excepción en caso de que sea necesario. En un entorno CLI, donde no existe el concepto de cargar archivos, permitirá +mover el archivo al que ha hecho referencia independientemente de sus orígenes, lo que hace posible probar la carga de +archivos. + +.. php:method:: getUploadedFile($path) + +Devuelve el archivo cargado en una ruta específica. La ruta utiliza la misma sintaxis de puntos que el método +:php:meth:`Cake\\Http\\ServerRequest::getData()`:: + + $attachment = $this->request->getUploadedFile('attachment'); + +A diferencia de :php:meth:`Cake\\Http\\ServerRequest::getData()`, :php:meth:`Cake\\Http\\ServerRequest::getUploadedFile()` +solo devolvería datos cuando exista una carga de archivo real para la ruta dada, si hay datos regulares del cuerpo de la +solicitud que no son archivos presentes en la ruta dada, entonces este método devolverá "nulo", tal como lo haría para +cualquier ruta inexistente. + +.. php:method:: getUploadedFiles() + +Devuelve todos los archivos cargados en una estructura de matriz normalizada. Para el ejemplo anterior con el nombre de +entrada del archivo ``attachment``, la estructura se vería así:: + + [ + 'attachment' => object(Laminas\Diactoros\UploadedFile) { + // ... + } + ] + +.. php:method:: withUploadedFiles(array $files) + +Este método establece los archivos cargados del objeto de solicitud, acepta una matriz de objetos que implementan +`\\Psr\\Http\\Message\\UploadedFileInterface `__. Reemplazará +todos los archivos cargados posiblemente existentes:: + + $files = [ + 'MyModel' => [ + 'attachment' => new \Laminas\Diactoros\UploadedFile( + $streamOrFile, + $size, + $errorStatus, + $clientFilename, + $clientMediaType + ), + 'anotherAttachment' => new \Laminas\Diactoros\UploadedFile( + '/tmp/hfz6dbn.tmp', + 123, + \UPLOAD_ERR_OK, + 'attachment.txt', + 'text/plain' + ), + ], + ]; + + $this->request = $this->request->withUploadedFiles($files); .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + Los archivos cargados que se agregaron a la solicitud a través de este método *no* estarán disponibles en los datos + del cuerpo de la solicitud, es decir, no puede recuperarlos a través de + :php:meth:`Cake\\Http\\ServerRequest::getData()` ! Si los necesita en los datos de la solicitud (también), entonces + debe configurarlos mediante :php:meth:`Cake\\Http\\ServerRequest::withData()` o + :php:meth:`Cake\\Http\ \ServerRequest::withParsedBody()`. - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +PUT, PATCH o DELETE Datos +------------------------- -.. _cake-request: +.. php:method:: input($callback, [$options]) + +Al crear servicios REST, a menudo se aceptan datos de solicitud en solicitudes ``PUT`` y ``DELETE``. Cualquier dato del +cuerpo de solicitud ``application/x-www-form-urlencoded`` se analizará automáticamente y se establecerá en +``$this->data`` para las solicitudes ``PUT`` y ``DELETE``. Si acepta datos JSON o XML, consulte a continuación cómo puede +acceder a esos cuerpos de solicitud. + +Al acceder a los datos de entrada, puede decodificarlos con una función opcional. Esto resulta útil al interactuar con el +contenido del cuerpo de la solicitud XML o JSON. Se pueden pasar parámetros adicionales para la función de decodificación +como argumentos a ``input()``:: + + $jsonData = $this->request->input('json_decode'); + +Variables de entorno (de $_SERVER y $_ENV) +------------------------------------------ + +.. php:method:: putenv($key, $value = null) + +``ServerRequest::getEnv()`` es un contenedor para la función global ``getenv()`` y actúa como un captador/establecedor de +variables de entorno sin tener que modificar los globales ``$_SERVER`` y ``$_ENV``:: + + // Obtener el host + $host = $this->request->getEnv('HTTP_HOST'); + + // Establecer un valor, generalmente útil en las pruebas. + $this->request->withEnv('REQUEST_METHOD', 'POST'); + +Para acceder a todas las variables de entorno en una solicitud, utilice ``getServerParams()``:: + + $env = $this->request->getServerParams(); + +Datos XML o JSON +---------------- + +Las aplicaciones que emplean :doc:`/development/rest` a menudo intercambian datos en cuerpos de publicaciones sin +codificación URL. Puede leer datos de entrada en cualquier formato usando :php:meth:`~Cake\\Http\\ServerRequest::input()`. +Al proporcionar una función de decodificación, puede recibir el contenido en un formato deserializado:: + + // Obtenga datos codificados en JSON enviados a una acción PUT/POST + $jsonData = $this->request->input('json_decode'); + +Algunos métodos de deserialización requieren parámetros adicionales cuando se llaman, como el parámetro 'as array' en +``json_decode``. Si desea convertir XML en un objeto DOMDocument, :php:meth:`~Cake\\Http\\ServerRequest::input()` también +admite el paso de parámetros adicionales:: + + // Obtener datos codificados en XML enviados a una acción PUT/POST + $data = $this->request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']); + +Información de ruta +------------------- + +El objeto de solicitud también proporciona información útil sobre las rutas de su aplicación. Los atributos ``base`` y +``webroot`` son útiles para generar URL y determinar si su aplicación está o no en un subdirectorio. Los atributos que +puedes utilizar son: + + // Supongamos que la URL de solicitud actual es /subdir/articles/edit/1?page=1 + + // Contiene /subdir/articles/edit/1?page=1 + $here = $request->getRequestTarget(); + + // Contiene /subdir + $base = $request->getAttribute('base'); + + // Contiene /subdir/ + $base = $request->getAttribute('webroot'); + +.. _check-the-request: + +Comprobación de las condiciones de la solicitud +----------------------------------------------- + +.. php:method:: is($type, $args...) + +El objeto de solicitud proporciona una forma de inspeccionar ciertas condiciones en una solicitud determinada. Al +utilizar el método ``is()``, puede comprobar una serie de condiciones comunes, así como inspeccionar otros criterios de +solicitud específicos de la aplicación: + + $isPost = $this->request->is('post'); + +También puede ampliar los detectores de solicitudes que están disponibles, utilizando +:php:meth:`Cake\\Http\\ServerRequest::addDetector()` para crear nuevos tipos de detectores. Hay diferentes tipos de +detectores que puedes crear: + +* Comparación de valores del entorno: compara un valor obtenido de :php:func:`env()` para determinar su igualdad con el + valor proporcionado. +* Comparación del valor del encabezado: si el encabezado especificado existe con el valor especificado o si el invocable + devuelve verdadero. +* Comparación de valores de patrón: la comparación de valores de patrón le permite comparar un valor obtenido de + :php:func:`env()` con una expresión regular. +* Comparación basada en opciones: las comparaciones basadas en opciones utilizan una lista de opciones para crear una + expresión regular. Las llamadas posteriores para agregar un detector de opciones ya definido fusionarán las opciones. +* Detectores de devolución de llamada: los detectores de devolución de llamada le permiten proporcionar un tipo de + "callback" para manejar la verificación. La devolución de llamada recibirá el objeto de solicitud como único parámetro. + +.. php:method:: addDetector($name, $options) + +Algunos ejemplos serían:: + + // Agregue un detector de entorno. + $this->request->addDetector( + 'post', + ['env' => 'REQUEST_METHOD', 'value' => 'POST'] + ); + + // Agregue un detector de valor de patrón. + $this->request->addDetector( + 'iphone', + ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'] + ); + + // Agregar un detector de opciones + $this->request->addDetector('internalIp', [ + 'env' => 'CLIENT_IP', + 'options' => ['192.168.0.101', '192.168.0.100'] + ]); + + + // Agregue un detector de encabezado con comparación de valores + $this->request->addDetector('fancy', [ + 'env' => 'CLIENT_IP', + 'header' => ['X-Fancy' => 1] + ]); + + // Agregue un detector de encabezado con comparación invocable + $this->request->addDetector('fancy', [ + 'env' => 'CLIENT_IP', + 'header' => ['X-Fancy' => function ($value, $header) { + return in_array($value, ['1', '0', 'yes', 'no'], true); + }] + ]); + + // Agregue un detector de devolución de llamada. Debe ser un invocable válido. + $this->request->addDetector( + 'awesome', + function ($request) { + return $request->getParam('awesome'); + } + ); + + // Agregue un detector que use argumentos adicionales. + $this->request->addDetector( + 'csv', + [ + 'accept' => ['text/csv'], + 'param' => '_ext', + 'value' => 'csv', + ] + ); + +Hay varios detectores integrados que puedes utilizar: + +* ``is('get')`` Verifique si la solicitud actual es un GET. +* ``is('put')`` Verifique si la solicitud actual es un PUT. +* ``is('patch')`` Verifique si la solicitud actual es un PATCH. +* ``is('post')`` Verifique si la solicitud actual es una POST. +* ``is('delete')`` Verifique si la solicitud actual es DELETE. +* ``is('head')`` Verifique si la solicitud actual es HEAD. +* ``is('options')`` Verifique si la solicitud actual es OPTIONS. +* ``is('ajax')`` Verifique si la solicitud actual vino con X-Requested-With = XMLHttpRequest. +* ``is('ssl')`` Compruebe si la solicitud se realiza a través de SSL. +* ``is('flash')`` Verifique si la solicitud tiene un User-Agent de Flash. +* ``is('json')`` Verifique si la solicitud tiene la extensión 'json' y acepte el tipo mime 'application/json'. +* ``is('xml')`` Verifique si la solicitud tiene la extensión 'xml' y acepte el tipo mime 'application/xml' o 'text/xml'. + +``ServerRequest`` También incluye métodos como :php:meth:`Cake\\Http\\ServerRequest::domain()`, +:php:meth:`Cake\\Http\\ServerRequest::subdomains()` y :php:meth:`Cake\\Http\\ServerRequest::host()` para simplificar las +aplicaciones que utilizan subdominios. + +Datos de sesión +--------------- + +Para acceder a la sesión para una solicitud determinada utilice el método ``getSession()`` o utilice el atributo +``session``:: + + $session = $this->request->getSession(); + $session = $this->request->getAttribute('session'); + + $data = $session->read('sessionKey'); + +Para obtener más información, consulte la documentación :doc:`/development/sessions` sobre cómo utilizar el objeto de +sesión. + +Host y nombre de dominio +------------------------ + +.. php:method:: domain($tldLength = 1) + +Devuelve el nombre de dominio en el que se ejecuta su aplicación:: + + // Muestra 'example.org' + echo $request->domain(); + +.. php:method:: subdomains($tldLength = 1) + +Devuelve los subdominios en los que se ejecuta su aplicación como una matriz:: + + // Regresa ['my', 'dev'] de 'my.dev.example.org' + $subdomains = $request->subdomains(); + +.. php:method:: host() + +Devuelve el host en el que se encuentra su aplicación:: + + // Muestra 'my.dev.example.org' + echo $request->host(); + +Leyendo el método HTTP +---------------------- + +.. php:method:: getMethod() + +Devuelve el método HTTP con el que se realizó la solicitud:: + + // Salida POST + echo $request->getMethod(); + +Restringir qué método HTTP acepta una acción +-------------------------------------------- + +.. php:method:: allowMethod($methods) + +Establecer métodos HTTP permitidos. Si no coincide, arrojará ``MethodNotAllowedException``. La respuesta 405 incluirá el +encabezado ``Allow`` requerido con los métodos pasados:: + + public function delete() + { + // Solo acepte solicitudes POST y DELETE + $this->request->allowMethod(['post', 'delete']); + ... + } + +Lectura de encabezados HTTP +--------------------------- + +Le permite acceder a cualquiera de los encabezados ``HTTP_*`` que se utilizaron para la solicitud. Por ejemplo:: + + // Obtener el encabezado como una cadena + $userAgent = $this->request->getHeaderLine('User-Agent'); + + // Obtenga una matriz de todos los valores. + $acceptHeader = $this->request->getHeader('Accept'); + + // Comprobar si existe un encabezado + $hasAcceptHeader = $this->request->hasHeader('Accept'); + +Si bien algunas instalaciones de Apache no hacen que el encabezado ``Authorization`` sea accesible, CakePHP lo hará +disponible a través de métodos específicos de Apache según sea necesario. + +.. php:method:: referer($local = true) + +Devuelve la dirección de referencia de la solicitud. + +.. php:method:: clientIp() + +Devuelve la dirección IP del visitante actual. + +Confiar en los encabezados de proxy +----------------------------------- + +Si su aplicación está detrás de un balanceador de carga o se ejecuta en un servicio en la nube, a menudo obtendrá el +host, el puerto y el esquema del balanceador de carga en sus solicitudes. A menudo, los balanceadores de carga también +enviarán encabezados ``HTTP-X-Forwarded-*`` con los valores originales. CakePHP no utilizará los encabezados reenviados +de fábrica. Para que el objeto de solicitud utilice estos encabezados, establezca la propiedad ``trustProxy`` en ``true``:: + + $this->request->trustProxy = true; + + // Estos métodos ahora utilizarán los encabezados proxy. + $port = $this->request->port(); + $host = $this->request->host(); + $scheme = $this->request->scheme(); + $clientIp = $this->request->clientIp(); + +Una vez que se confía en los servidores proxy, el método ``clientIp()`` utilizará la *última* dirección IP en el +encabezado ``X-Forwarded-For``. Si su aplicación está detrás de varios servidores proxy, puede usar +``setTrustedProxies()`` para definir las direcciones IP de los servidores proxy bajo su control:: + + $request->setTrustedProxies(['127.1.1.1', '127.8.1.3']); + +Después de que los servidores proxy sean confiables, ``clientIp()`` usará la primera dirección IP en el encabezado +``X-Forwarded-For`` siempre que sea el único valor que no provenga de un proxy confiable. + +Comprobando encabezados aceptados +--------------------------------- + +.. php:method:: accepts($type = null) + +Descubra qué tipos de contenido acepta el cliente o compruebe si acepta un tipo de contenido en particular. + +Consigue todos los tipos:: + + $accepts = $this->request->accepts(); + +Consulta por un solo tipo:: + + $acceptsJson = $this->request->accepts('application/json'); + +.. php:method:: acceptLanguage($language = null) + +Obtenga todos los idiomas aceptados por el cliente o verifique si se acepta un idioma específico. + +Obtenga la lista de idiomas aceptados:: + + $acceptsLanguages = $this->request->acceptLanguage(); + +Compruebe si se acepta un idioma específico:: + + $acceptsSpanish = $this->request->acceptLanguage('es-es'); + +.. _request-cookies: + +Leyendo Cookies +--------------- + +Las cookies de solicitud se pueden leer a través de varios métodos: + + // Obtenga el valor de la cookie, o nulo si falta la cookie. + $rememberMe = $this->request->getCookie('remember_me'); + + // Lea el valor u obtenga el valor predeterminado de 0 + $rememberMe = $this->request->getCookie('remember_me', 0); + + // Obtener todas las cookies como hash + $cookies = $this->request->getCookieParams(); + + // Obtener una instancia de CookieCollection + $cookies = $this->request->getCookieCollection() + +Consulte la documentación :php:class:`Cake\\Http\\Cookie\\CookieCollection` para saber cómo trabajar con la recopilación +de cookies. + +Archivos cargados +----------------- + +Las solicitudes exponen los datos del archivo cargado en ``getData()`` o ``getUploadedFiles()`` como objetos +``UploadedFileInterface``:: + + // Obtener una lista de objetos UploadedFile + $files = $request->getUploadedFiles(); + + // Lea los datos del archivo. + $files[0]->getStream(); + $files[0]->getSize(); + $files[0]->getClientFileName(); + + // Mover el archivo + $files[0]->moveTo($targetPath); + +Manipulación de URI +------------------- + +Las solicitudes contienen un objeto URI, que contiene métodos para interactuar con el URI solicitado:: + + // Obtener la URI + $uri = $request->getUri(); + + // Leer datos de la URI. + $path = $uri->getPath(); + $query = $uri->getQuery(); + $host = $uri->getHost(); + + +.. index:: $this->response + +Respueta (Response) +=================== + +.. php:class:: Response + +:php:class:`Cake\\Http\\Response` es la clase de respuesta predeterminada en CakePHP. Encapsula una serie de +características y funcionalidades para generar respuestas HTTP en su aplicación. También ayuda en las pruebas, ya que se +puede simular o eliminar, lo que le permite inspeccionar los encabezados que se enviarán. + +``Response`` proporciona una interfaz para envolver las tareas comunes relacionadas con la respuesta, como por ejemplo: + +* Envío de encabezados para redireccionamientos. +* Envío de encabezados de tipo de contenido. +* Envío de cualquier encabezado. +* Envío del cuerpo de la respuesta. + +Tratar con tipos de contenido +----------------------------- + +.. php:method:: withType($contentType = null) + +Puede controlar el tipo de contenido de las respuestas de su aplicación con :php:meth:`Cake\\Http\\Response::withType()`. +Si su aplicación necesita manejar tipos de contenido que no están integrados en Response, también puede asignarlos con +``setTypeMap()``:: + + // Agregar un tipo de vCard + $this->response->setTypeMap('vcf', ['text/v-card']); + + // Establezca el tipo de contenido de respuesta en vcard + $this->response = $this->response->withType('vcf'); + +Por lo general, querrás asignar tipos de contenido adicionales en la devolución de llamada de tu controlador +:php:meth:`~Controller::beforeFilter()`, para poder aprovechar las funciones de cambio automático de vista de +:php:class:`RequestHandlerComponent` si lo están usando. + +.. _cake-response-file: + +Enviando arhivos +---------------- + +.. php:method:: withFile(string $path, array $options = []) + +Hay ocasiones en las que desea enviar archivos como respuesta a sus solicitudes. Puedes lograrlo usando +:php:meth:`Cake\\Http\\Response::withFile()`:: + + public function sendFile($id) + { + $file = $this->Attachments->getFile($id); + $response = $this->response->withFile($file['path']); + // Devuelve la respuesta para evitar que el controlador intente representar una vista. + return $response; + } + +Como se muestra en el ejemplo anterior, debe pasar la ruta del archivo al método. CakePHP enviará un encabezado de tipo +de contenido adecuado si es un tipo de archivo conocido que figura en `Cake\\Http\\Response::$_mimeTypes`. Puede agregar +nuevos tipos antes de llamar a :php:meth:`Cake\\Http\\Response::withFile()` usando el método +:php:meth:`Cake\\Http\\Response::withType()` . + +Si lo desea, también puede forzar la descarga de un archivo en lugar de mostrarlo en el navegador especificando las +opciones:: + + $response = $this->response->withFile( + $file['path'], + ['download' => true, 'name' => 'foo'] + ); + +Las opciones admitidas son: + +name + El nombre le permite especificar un nombre de archivo alternativo para enviarlo al usuario. +download + Un valor booleano que indica si los encabezados deben configurarse para forzar la descarga. + +Enviar una cadena como archivo +------------------------------ + +Puedes responder con un archivo que no existe en el disco, como un pdf o un ics generado sobre la marcha a partir de una +cadena:: + + public function sendIcs() + { + $icsString = $this->Calendars->generateIcs(); + $response = $this->response; + + // Inyectar contenido de cadena en el cuerpo de la respuesta + $response = $response->withStringBody($icsString); + + $response = $response->withType('ics'); + + // Opcionalmente forzar la descarga de archivos + $response = $response->withDownload('filename_for_download.ics'); + + // Devuelve un objeto de respuesta para evitar que el controlador intente representar una vista. + return $response; + } + +Configuración de encabezados +---------------------------- + +.. php:method:: withHeader($header, $value) + +La configuración de los encabezados se realiza con el método :php:meth:`Cake\\Http\\Response::withHeader()`. Como todos +los métodos de la interfaz PSR-7, este método devuelve una instancia *nueva* con el nuevo encabezado:: + + // Agregar/reemplazar un encabezado + $response = $response->withHeader('X-Extra', 'My header'); + + // Establecer múltiples encabezados + $response = $response->withHeader('X-Extra', 'My header') + ->withHeader('Location', 'http://example.com'); + + // Agregar un valor a un encabezado existente + $response = $response->withAddedHeader('Set-Cookie', 'remember_me=1'); + +Los encabezados no se envían cuando se configuran. En cambio, se retienen hasta que ``Cake\Http\Server`` emite la +respuesta. + +Ahora puede utilizar el método conveniente :php:meth:`Cake\\Http\\Response::withLocation()` para configurar u obtener +directamente el encabezado de ubicación de redireccionamiento. + +Configurando el cuerpo +---------------------- + +.. php:method:: withStringBody($string) + +Para establecer una cadena como cuerpo de respuesta, haga lo siguiente: + + // Coloca una cadena en el cuerpo. + $response = $response->withStringBody('My Body'); + + // Si quieres una respuesta json + $response = $response->withType('application/json')->withStringBody(json_encode(['Foo' => 'bar'])); + +.. php:method:: withBody($body) + +Para configurar el cuerpo de la respuesta, use el método ``withBody()``, que es proporcionado por +:php:class:`Laminas\\Diactoros\\MessageTrait`:: + + $response = $response->withBody($stream); + +Asegúrese de que ``$stream`` sea un objeto :php:class:`Psr\\Http\\Message\\StreamInterface`. Vea a continuación cómo +crear un nuevo stream. + +También puedes transmitir respuestas desde archivos usando :php:class:`Laminas\\Diactoros\\Stream` streams:: + + // Para transmitir desde un archivo + use Laminas\Diactoros\Stream; + + $stream = new Stream('/path/to/file', 'rb'); + $response = $response->withBody($stream); + +También puedes transmitir respuestas desde una devolución de llamada usando ``CallbackStream``. Esto es útil cuando tiene +recursos como imágenes, archivos CSV o PDF que necesita transmitir al cliente:: + + // Transmisión desde una devolución de llamada + use Cake\Http\CallbackStream; + + // Crea una imagen. + $img = imagecreate(100, 100); + // ... + + $stream = new CallbackStream(function () use ($img) { + imagepng($img); + }); + $response = $response->withBody($stream); + +Configuración del juego de caracteres +------------------------------------- + +.. php:method:: withCharset($charset) + +Establece el juego de caracteres que se utilizará en la respuesta:: + + $this->response = $this->response->withCharset('UTF-8'); + +Interactuar con el almacenamiento en caché del navegador +-------------------------------------------------------- + +.. php:method:: withDisabledCache() + +A veces es necesario obligar a los navegadores a no almacenar en caché los resultados de una acción del controlador. +:php:meth:`Cake\\Http\\Response::withDisabledCache()` está destinado precisamente a eso:: + + public function index() + { + // Deshabilitar el almacenamiento en caché + $this->response = $this->response->withDisabledCache(); + } + +.. warning:: + + Deshabilitar el almacenamiento en caché de dominios SSL al intentar enviar archivos a Internet Explorer puede + generar errores. + +.. php:method:: withCache($since, $time = '+1 day') + +También puede decirles a los clientes que desea que almacenen en caché las respuestas. Usando +:php:meth:`Cake\\Http\\Response::withCache()`:: + + public function index() + { + // Habilitar el almacenamiento en caché + $this->response = $this->response->withCache('-1 minute', '+5 days'); + } + +Lo anterior les indicaría a los clientes que guarden en caché la respuesta resultante durante 5 días, con la esperanza +de acelerar la experiencia de sus visitantes. El método ``withCache()`` establece el valor ``Última modificación`` en el +primer argumento. El encabezado ``Expires`` y la directiva ``max-age`` se establecen en función del segundo parámetro. +La directiva "pública" de Cache-Control también está configurada. + +.. _cake-response-caching: + +Ajuste fino de la caché HTTP +---------------------------- + +Una de las mejores y más sencillas formas de acelerar su aplicación es utilizar la caché HTTP. Según este modelo de +almacenamiento en caché, solo debe ayudar a los clientes a decidir si deben usar una copia en caché de la respuesta +configurando algunos encabezados, como la hora de modificación y la etiqueta de entidad de respuesta. + +En lugar de obligarlo a codificar la lógica para el almacenamiento en caché y para invalidarla (actualizarla) una vez que +los datos han cambiado, HTTP utiliza dos modelos, caducidad y validación, que generalmente son mucho más simples de usar. + +Además de usar :php:meth:`Cake\\Http\\Response::withCache()`, también puedes usar muchos otros métodos para ajustar los +encabezados de caché HTTP para aprovechar el almacenamiento en caché del navegador o del proxy inverso. + +El encabezado de control de caché +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: withSharable($public, $time = null) + +Utilizado bajo el modelo de vencimiento, este encabezado contiene múltiples indicadores que pueden cambiar la forma en +que los navegadores o servidores proxy usan el contenido almacenado en caché. Un encabezado ``Cache-Control`` puede +verse así:: + + Cache-Control: private, max-age=3600, must-revalidate + +La clase ``Response`` le ayuda a configurar este encabezado con algunos métodos de utilidad que producirán un encabezado +``Cache-Control`` final válido. El primero es el método ``withSharable()``, que indica si una respuesta debe considerarse +compartible entre diferentes usuarios o clientes. Este método en realidad controla la parte "pública" o "privada" de este +encabezado. Establecer una respuesta como privada indica que toda o parte de ella está destinada a un solo usuario. Para +aprovechar las cachés compartidas, la directiva de control debe configurarse como pública. + +El segundo parámetro de este método se utiliza para especificar una ``max-age`` para el caché, que es el número de +segundos después de los cuales la respuesta ya no se considera nueva:: + + public function view() + { + // ... + // Configure Cache-Control como público durante 3600 segundos + $this->response = $this->response->withSharable(true, 3600); + } + + public function my_data() + { + // ... + // Configure Cache-Control como privado durante 3600 segundos + $this->response = $this->response->withSharable(false, 3600); + } + +``Response`` expone métodos separados para configurar cada una de las directivas en el encabezado ``Cache-Control``. + +El encabezado de vencimiento +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: withExpires($time) + +Puede configurar el encabezado ``Expires`` en una fecha y hora después de la cual la respuesta ya no se considera nueva. +Este encabezado se puede configurar usando el método ``withExpires()``:: + + public function view() + { + $this->response = $this->response->withExpires('+5 days'); + } + +Este método también acepta una instancia :php:class:`DateTime` o cualquier cadena que pueda ser analizada por la clase +:php:class:`DateTime`. + +El encabezado de la etiqueta electrónica +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: withEtag($tag, $weak = false) + +La validación de caché en HTTP se usa a menudo cuando el contenido cambia constantemente y le pide a la aplicación que +solo genere el contenido de la respuesta si el caché ya no está actualizado. Bajo este modelo, el cliente continúa +almacenando páginas en el caché, pero pregunta a la aplicación cada vez si el recurso ha cambiado, en lugar de usarlo +directamente. Esto se usa comúnmente con recursos estáticos como imágenes y otros activos. + +El método ``withEtag()`` (llamado etiqueta de entidad) es una cadena que identifica de forma única el recurso solicitado, +como lo hace una suma de comprobación para un archivo, para determinar si coincide con un recurso almacenado en caché. + +Para aprovechar este encabezado, debe llamar al método ``checkNotModified()`` manualmente o incluir +:doc:`/controllers/components/check-http-cache` en su controlador:: + + public function index() + { + $articles = $this->Articles->find('all')->all(); + + // Suma de comprobación simple del contenido del artículo. + // Debería utilizar una implementación más eficiente en una aplicación del mundo real. + $checksum = md5(json_encode($articles)); + + $response = $this->response->withEtag($checksum); + if ($response->checkNotModified($this->request)) { + return $response; + } + + $this->response = $response; + // ... + } + +.. note:: + + La mayoría de los usuarios de proxy probablemente deberían considerar usar el encabezado de última modificación en + lugar de Etags por razones de rendimiento y compatibilidad. + +El último encabezado modificado +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: withModified($time) + +Además, bajo el modelo de validación de caché HTTP, puede configurar el encabezado ``Last-Modified`` para indicar la +fecha y hora en la que se modificó el recurso por última vez. Configurar este encabezado ayuda a CakePHP a decirle a los +clientes de almacenamiento en caché si la respuesta se modificó o no según su caché. + +Para aprovechar este encabezado, debe llamar al método ``checkNotModified()`` manualmente o incluir +:doc:`/controllers/components/check-http-cache` en su controlador:: + + public function view() + { + $article = $this->Articles->find()->first(); + $response = $this->response->withModified($article->modified); + if ($response->checkNotModified($this->request)) { + return $response; + } + $this->response; + // ... + } + +El encabezado variable +~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: withVary($header) + +En algunos casos, es posible que desee publicar contenido diferente utilizando la misma URL. Este suele ser el caso si +tiene una página multilingüe o responde con HTML diferente según el navegador. En tales circunstancias, puede utilizar +el encabezado ``Vary``:: + + $response = $this->response->withVary('User-Agent'); + $response = $this->response->withVary('Accept-Encoding', 'User-Agent'); + $response = $this->response->withVary('Accept-Language'); + +Envío de respuestas no modificadas +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. php:method:: checkNotModified(Request $request) + +Compara los encabezados de la caché del objeto de solicitud con el encabezado de la caché de la respuesta y determina +sitodavía se puede considerar nuevo. Si es así, elimina el contenido de la respuesta y envía el encabezado +`304 Not Modified`:: + + // En una acción del controlador. + if ($this->response->checkNotModified($this->request)) { + return $this->response; + } + +.. _response-cookies: + +Configuración de cookies +------------------------ + +Las cookies se pueden agregar a la respuesta usando una matriz o un objeto :php:class:`Cake\\Http\\Cookie\\Cookie`:: + + use Cake\Http\Cookie\Cookie; + use DateTime; + + // Agregar una cookie + $this->response = $this->response->withCookie(Cookie::create( + 'remember_me', + 'yes', + // Todas las claves son opcionales. + [ + 'expires' => new DateTime('+1 year'), + 'path' => '', + 'domain' => '', + 'secure' => false, + 'httponly' => false, + 'samesite' => null // O una de las constantes CookieInterface::SAMESITE_* + ] + )); + +Consulte la sección :ref:`creating-cookies` para saber cómo utilizar el objeto cookie. Puede utilizar +``withExpiredCookie()`` para enviar una cookie caducada en la respuesta. Esto hará que el navegador elimine su cookie +local:: + + $this->response = $this->response->withExpiredCookie(new Cookie('remember_me')); + +.. _cors-headers: + +Configuración de encabezados de solicitud de origen cruzado (CORS) +================================================================== + +El método ``cors()`` se utiliza para definir `Control de acceso HTTP +`__ encabezados relacionados con una interfaz fluida:: + + $this->response = $this->response->cors($this->request) + ->allowOrigin(['*.cakephp.org']) + ->allowMethods(['GET', 'POST']) + ->allowHeaders(['X-CSRF-Token']) + ->allowCredentials() + ->exposeHeaders(['Link']) + ->maxAge(300) + ->build(); + +Los encabezados relacionados con CORS solo se aplicarán a la respuesta si se cumplen los siguientes criterios: + +#. The request has an ``Origin`` header. +#. The request's ``Origin`` value matches one of the allowed Origin values. + +.. tip:: + + CakePHP no tiene middleware CORS incorporado porque manejar solicitudes CORS es muy específico de la aplicación. + Le recomendamos que cree su propio ``CORSMiddleware`` si lo necesita y ajuste el objeto de respuesta como desee. + +Errores comunes con respuestas inmutables +========================================= + +Los objetos de respuesta ofrecen varios métodos que tratan las respuestas como objetos inmutables. Los objetos inmutables +ayudan a prevenir efectos secundarios accidentales difíciles de rastrear y reducen los errores causados por llamadas a +métodos causadas por la refactorización que cambia el orden. Si bien ofrecen una serie de beneficios, es posible que sea +necesario algo de tiempo para acostumbrarse a los objetos inmutables. Cualquier método que comience con ``with`` opera +en la respuesta de forma inmutable y **siempre** devolverá una **nueva** instancia. Olvidar conservar la instancia +modificada es el error más frecuente que cometen las personas cuando trabajan con objetos inmutables: + + $this->response->withHeader('X-CakePHP', 'yes!'); + +En el código anterior, a la respuesta le faltará el encabezado ``X-CakePHP``, ya que el valor de retorno del método +``withHeader()`` no se retuvo. Para corregir el código anterior escribirías:: + + $this->response = $this->response->withHeader('X-CakePHP', 'yes!'); + +.. php:namespace:: Cake\Http\Cookie + +Colección de Cookies +===================== + +.. php:class:: CookieCollection + +Se puede acceder a los objetos ``CookieCollection`` desde los objetos de solicitud y respuesta. Le permiten interactuar +con grupos de cookies utilizando patrones inmutables, que permiten preservar la inmutabilidad de la solicitud y la +respuesta. + +.. _creating-cookies: + +Creando cookies +--------------- + +.. php:class:: Cookie + +Los objetos ``Cookie`` se pueden definir a través de objetos constructores o utilizando la interfaz fluida que sigue +patrones inmutables:: + + use Cake\Http\Cookie\Cookie; + + // Todos los argumentos en el constructor. + $cookie = new Cookie( + 'remember_me', // nombre + 1, // valor + new DateTime('+1 year'), // tiempo de vencimiento, si corresponde + '/', // ruta, si corresponde + 'example.com', // dominio, si corresponde + false, // ¿Solo seguro? + true // ¿Solo http? + ); + + // Usando los métodos constructores + $cookie = (new Cookie('remember_me')) + ->withValue('1') + ->withExpiry(new DateTime('+1 year')) + ->withPath('/') + ->withDomain('example.com') + ->withSecure(false) + ->withHttpOnly(true); + +Una vez que haya creado una cookie, puede agregarla a una ``CookieCollection`` nueva o existente:: + + use Cake\Http\Cookie\CookieCollection; + + // Crear una nueva colección + $cookies = new CookieCollection([$cookie]); + + // Agregar a una colección existente + $cookies = $cookies->add($cookie); + + // Eliminar una cookie por nombre + $cookies = $cookies->remove('remember_me'); + +.. note:: + Recuerde que las colecciones son inmutables y agregar o eliminar cookies de una colección crea un *nuevo* objeto + de colección. + +Se pueden agregar objetos cookie a las respuestas:: + + // Agregar una cookie + $response = $this->response->withCookie($cookie); + + // Reemplazar toda la colección de cookies + $response = $this->response->withCookieCollection($cookies); + +Las cookies configuradas para las respuestas se pueden cifrar utilizando :ref:`encrypted-cookie-middleware`. + +Leyendo Cookies +--------------- + +Una vez que tenga una instancia ``CookieCollection``, podrá acceder a las cookies que contiene:: + + // Comprobar si existe una cookie + $cookies->has('remember_me'); + + // Obtener el número de cookies de la colección. + count($cookies); + + // Obtener una instancia de cookie. Lanzará un error si no se encuentra la cookie. + $cookie = $cookies->get('remember_me'); + + // Obtener una cookie o nulo + $cookie = $cookies->remember_me; + + // Comprobar si existe una cookie + $exists = isset($cookies->remember_me) + +Una vez que tenga un objeto ``Cookie``, puede interactuar con su estado y modificarlo. Tenga en cuenta que las cookies +son inmutables, por lo que deberá actualizar la colección si modifica una cookie:: + + // Obtener el valor + $value = $cookie->getValue() + + // Acceder a datos dentro de un valor JSON + $id = $cookie->read('User.id'); -Request -======= + // Comprobar estado + $cookie->isHttpOnly(); + $cookie->isSecure(); .. meta:: - :title lang=es: Request and Response objects - :keywords lang=es: request controller,request parameters,array indexes,purpose index,response objects,domain information,request object,request data,interrogating,params,parameters,previous versions,introspection,dispatcher,rout,data structures,arrays,ip address,migration,indexes,cakephp + :title lang=es: Objetos Request y Response + :keywords lang=en: request controller,request parameters,array indexes,purpose index,response objects,domain information,request object,request data,interrogating,params,parameters,previous versions,introspection,dispatcher,rout,data structures,arrays,ip address,migration,indexes,cakephp,PSR-7,immutable diff --git a/es/core-libraries/caching.rst b/es/core-libraries/caching.rst index 6bb1c5fa93..ffa7ea2568 100644 --- a/es/core-libraries/caching.rst +++ b/es/core-libraries/caching.rst @@ -1,15 +1,584 @@ Caching ####### +.. php:namespace:: Cake\Cache + +.. php:class:: Cache + +El almacenamiento en caché se puede utilizar para acelerar la lectura de recursos caros o lentos, manteniendo una segunda copia de los datos requeridos en un sistema de almacenamiento más rápido o más cercano. Por ejemplo, puedes almacenar los resultados de consultas costosas o el acceso a servicios web remotos que no cambian con frecuencia en una caché. Una vez que los datos están en la caché, leerlos desde la caché es mucho más económico que acceder al recurso remoto. + +En CakePHP, el almacenamiento en caché se facilita mediante la clase ``Cache``. Esta clase proporciona una interfaz estática y uniforme para interactuar con diversas implementaciones de almacenamiento en caché. CakePHP proporciona varios motores de caché y ofrece una interfaz sencilla si necesitas construir tu propio backend. Los motores de almacenamiento en caché integrados son: + +- ``File``: el almacenamiento en caché de archivos es una caché simple que utiliza archivos locales. Es el motor de caché más lento y no proporciona muchas características para operaciones atómicas. Sin embargo, dado que el almacenamiento en disco a menudo es bastante económico, almacenar objetos grandes o elementos que rara vez se escriben funciona bien en archivos. +- ``Memcached``: utiliza la extensión `Memcached `_. +- ``Redis``: utiliza la extensión `phpredis `_. Redis proporciona un sistema de caché rápido y persistente similar a Memcached y también ofrece operaciones atómicas. +- ``Apcu``: la caché de APCu utiliza la extensión PHP `APCu `_. Esta extensión utiliza memoria compartida en el servidor web para almacenar objetos. Esto lo hace muy rápido y capaz de proporcionar funciones de lectura/escritura atómicas. +- ``Array``: almacena todos los datos en una matriz. Este motor no proporciona almacenamiento persistente y está destinado a su uso en suites de pruebas de aplicaciones. +- ``Null``: el motor nulo en realidad no almacena nada y falla en todas las operaciones de lectura. + +Independientemente del motor de caché que elijas usar, tu aplicación interactúa con :php:class:`Cake\\Cache\\Cache`. + +.. _cache-configuration: + +Configuración de los Motores de Caché +====================================== + +.. php:staticmethod:: setConfig($clave, $configuracion = null) + +Tu aplicación puede configurar cualquier número de 'motores' durante su proceso de inicio. Las configuraciones del motor de caché se definen en **config/app.php**. + +Para un rendimiento óptimo, CakePHP requiere que se definan dos motores de caché. + +- ``_cake_core_`` se utiliza para almacenar mapas de archivos y resultados analizados de archivos de :doc:`/core-libraries/internationalization-and-localization`. +- ``_cake_model_`` se utiliza para almacenar descripciones de esquemas para los modelos de tu aplicación. + +Usar múltiples configuraciones de motores también te permite cambiar incrementalmente el almacenamiento según sea necesario. Por ejemplo, en tu **config/app.php** podrías poner lo siguiente:: + + // ... + 'Cache' => [ + 'short' => [ + 'className' => 'File', + 'duration' => '+1 hours', + 'path' => CACHE, + 'prefix' => 'cake_short_', + ], + // Usando un nombre completamente calificado. + 'long' => [ + 'className' => 'Cake\Cache\Engine\FileEngine', + 'duration' => '+1 week', + 'probability' => 100, + 'path' => CACHE . 'long' . DS, + ], + ] + // ... + +Las opciones de configuración también se pueden proporcionar como una cadena :term:`DSN`. Esto es útil cuando se trabaja con variables de entorno o proveedores de :term:`PaaS`:: + + Cache::setConfig('short', [ + 'url' => 'memcached://user:password@cache-host/?timeout=3600&prefix=myapp_', + ]); + +Cuando usas una cadena DSN, puedes definir cualquier parámetro/opción adicional como argumentos de cadena de consulta. + +También puedes configurar los motores de caché en tiempo de ejecución:: + + // Usando un nombre corto + Cache::setConfig('short', [ + 'className' => 'File', + 'duration' => '+1 hours', + 'path' => CACHE, + 'prefix' => 'cake_short_' + ]); + + // Usando un nombre completamente calificado. + Cache::setConfig('long', [ + 'className' => 'Cake\Cache\Engine\FileEngine', + 'duration' => '+1 week', + 'probability' => 100, + 'path' => CACHE . 'long' . DS, + ]); + + // Usando un objeto construido. + $objeto = new FileEngine($configuracion); + Cache::setConfig('otro', $objeto); + +Los nombres de estas configuraciones de motor ('short' y 'long') se utilizan como el parámetro ``$config`` +para :php:meth:`Cake\\Cache\\Cache::write()` y +:php:meth:`Cake\\Cache\\Cache::read()`. Al configurar los motores de caché, puedes +referenciar el nombre de la clase utilizando las siguientes sintaxis:: + + // Nombre corto (en App\ o en los espacios de nombres de Cake) + Cache::setConfig('long', ['className' => 'File']); + + // Nombre corto del plugin + Cache::setConfig('long', ['className' => 'MyPlugin.SuperCache']); + + // Espacio de nombres completo + Cache::setConfig('long', ['className' => 'Cake\Cache\Engine\FileEngine']); + + // Un objeto que implementa CacheEngineInterface + Cache::setConfig('long', ['className' => $miCache]); + +.. note:: + + Al utilizar FileEngine, es posible que necesites usar la opción ``mask`` para + asegurarte de que los archivos de caché se creen con los permisos correctos. + +Opciones del Motor +------------------ + +Cada motor acepta las siguientes opciones: + +- ``duration``: especifica cuánto tiempo duran los elementos en esta configuración de caché. Se especifica como una expresión compatible con ``strtotime()``. +- ``groups``: lista de grupos o 'etiquetas' asociados a cada clave almacenada en esta configuración. Útil cuando necesitas eliminar un subconjunto de datos de una caché. +- ``prefix``: se antepone a todas las entradas. Bueno cuando necesitas compartir + un espacio de claves con otra configuración de caché o con otra aplicación. +- ``probability``: probabilidad de activar una limpieza de la caché. Establecerlo en 0 deshabilitará automáticamente la llamada a ``Cache::gc()`` + +Opciones del Motor de FileEngine +-------------------------------- + +FileEngine utiliza las siguientes opciones específicas del motor: + +- ``isWindows``: se rellena automáticamente con si el host es Windows o no. +- ``lock``: ¿deberían bloquearse los archivos antes de escribir en ellos? +- ``mask``: la máscara utilizada para los archivos creados. +- ``path``: ruta donde deben guardarse los archivos de caché. Por defecto, es el directorio temporal del sistema. + +.. _caching-redisengine: + +Opciones del Motor RedisEngine +------------------------------ + +RedisEngine utiliza las siguientes opciones específicas del motor: + +- ``port``: el puerto en el que se está ejecutando tu servidor Redis. +- ``host``: el host en el que se está ejecutando tu servidor Redis. +- ``database``: el número de base de datos a usar para la conexión. +- ``password``: contraseña del servidor Redis. +- ``persistent``: ¿se debe realizar una conexión persistente a Redis? +- ``timeout``: tiempo de espera de conexión para Redis. +- ``unix_socket``: ruta a un socket Unix para Redis. + +Opciones del Motor MemcacheEngine +--------------------------------- + +- ``compress``: si comprimir datos o no. +- ``username``: usuario para acceder al servidor Memcache. +- ``password``: contraseña para acceder al servidor Memcache. +- ``persistent``: el nombre de la conexión persistente. Todas las configuraciones que usan + el mismo valor persistente compartirán una única conexión subyacente. +- ``serialize``: el motor de serialización utilizado para serializar datos. Los motores disponibles son php, + igbinary y json. Además de php, la extensión memcached debe estar compilada con el + soporte adecuado para el serializador correspondiente. +- ``servers``: cadena o array de servidores memcached. Si es un array, MemcacheEngine los usará + como un grupo. +- ``duration``: ten en cuenta que cualquier duración mayor de 30 días se tratará como un valor de tiempo Unix real + en lugar de un desfase desde el tiempo actual. +- ``options``: opciones adicionales para el cliente memcached. Debe ser un array de opción => valor. + Usa las constantes ``\Memcached::OPT_*`` como claves. + +.. _configuracion-fallback-caché: + +Configuración de la Caída de Caché +---------------------------------- + +En caso de que un motor no esté disponible, como el ``FileEngine`` que intenta +escribir en una carpeta no escribible o el ``RedisEngine`` que no puede conectarse a +Redis, el motor volverá al ``NullEngine`` y generará un error que se puede registrar. +Esto evita que la aplicación genere una excepción no capturada debido a un error de caché. + +Puedes configurar las configuraciones de la caché para que vuelvan a una configuración especificada usando la clave de configuración ``fallback``:: + + Cache::setConfig('redis', [ + 'className' => 'Redis', + 'duration' => '+1 hours', + 'prefix' => 'cake_redis_', + 'host' => '127.0.0.1', + 'port' => 6379, + 'fallback' => 'default', + ]); + +Si falla la inicialización de la instancia ``RedisEngine``, la configuración de caché ``redis`` +volverá a usar la configuración de caché ``default``. Si también falla la inicialización del +motor para la configuración de caché ``default`` en este escenario, el motor volvería nuevamente al ``NullEngine`` +y evitaría que la aplicación genere una excepción no capturada. + +Puedes desactivar las caídas de caché con ``false``:: + + Cache::setConfig('redis', [ + 'className' => 'Redis', + 'duration' => '+1 hours', + 'prefix' => 'cake_redis_', + 'host' => '127.0.0.1', + 'port' => 6379, + 'fallback' => false + ]); + +Cuando no hay una caída, los errores de caché se generarán como excepciones. + +Eliminación de Motores de Caché Configurados +--------------------------------------------- + +.. php:staticmethod:: drop($clave) + +Una vez que se crea una configuración, no puedes cambiarla. En su lugar, debes eliminar +la configuración y volver a crearla usando :php:meth:`Cake\\Cache\\Cache::drop()` y +:php:meth:`Cake\\Cache\\Cache::setConfig()`. Eliminar un motor de caché eliminará +la configuración y destruirá el adaptador si se construyó. + +Escritura en Caché +================== + +.. php:staticmethod:: write($clave, $valor, $configuracion = 'default') + +``Cache::write()`` escribirá un $valor en la caché. Puedes leer o +eliminar este valor más tarde refiriéndote a él por ``$clave``. Puedes +especificar una configuración opcional para almacenar la caché también. Si +no se especifica ninguna ``$configuración``, se usará la predeterminada. ``Cache::write()`` +puede almacenar cualquier tipo de objeto y es ideal para almacenar resultados de +búsquedas de modelos:: + + $entradas = Cache::read('entradas'); + if ($entradas === null) { + $entradas = $servicio->obtenerTodasLasEntradas(); + Cache::write('entradas', $entradas); + } + +Usar ``Cache::write()`` y ``Cache::read()`` para reducir el número +de consultas realizadas a la base de datos para obtener las entradas. + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + Si planeas almacenar en caché el resultado de las consultas realizadas con el ORM de CakePHP, + es mejor utilizar las capacidades de almacenamiento en caché integradas del objeto de consulta + como se describe en la sección de :ref:`caching-query-results` + +Escritura de Múltiples Claves a la Vez +-------------------------------------- + +.. php:staticmethod:: writeMany($datos, $configuracion = 'default') + +Puede que necesites escribir múltiples claves de caché a la vez. Aunque podrías usar múltiples llamadas a ``write()``, ``writeMany()`` permite a CakePHP utilizar +API de almacenamiento más eficientes cuando están disponibles. Por ejemplo, usando ``writeMany()`` +ahorras múltiples conexiones de red cuando usas Memcached:: + + $resultado = Cache::writeMany([ + 'articulo-' . $slug => $articulo, + 'articulo-' . $slug . '-comentarios' => $comentarios + ]); + + // $resultado contendrá + ['articulo-primer-post' => true, 'articulo-primer-post-comentarios' => true] + +Escrituras Atómicas +------------------- + +.. php:staticmethod:: add($clave, $valor, $configuracion = 'default') + +Usar ``Cache::add()`` te permitirá establecer atómicamente una clave en un valor si la clave +aún no existe en la caché. Si la clave ya existe en el backend de la caché o la escritura falla, ``add()`` devolverá ``false``:: + + // Establecer una clave para actuar como bloqueo + $resultado = Cache::add($claveBloqueo, true); + if (!$resultado) { + return; + } + // Realizar una acción donde solo puede haber un proceso activo a la vez. + + // Eliminar la clave de bloqueo. + Cache::delete($claveBloqueo); + +.. warning:: + + La caché basada en archivos no admite escrituras atómicas. + +Caché de Lectura Directa +------------------------ + +.. php:staticmethod:: remember($clave, $callable, $configuracion = 'default') + +La caché ayuda con la caché de lectura directa. Si la clave de caché nombrada existe, +se devolverá. Si la clave no existe, se invocará la función de llamada +y los resultados se almacenarán en la caché en la clave proporcionada. + +Por ejemplo, a menudo quieres cachear los resultados de las llamadas a servicios remotos. Puedes usar +``remember()`` para hacerlo simple:: + + class ServicioDeAsunto + { + public function todasLasTemas($repositorio) + { + return Cache::remember($repositorio . '-temas', function () use ($repositorio) { + return $this->obtenerTodos($repositorio); + }); + } + } + +Lectura Desde la Caché +====================== + +.. php:staticmethod:: read($clave, $configuracion = 'default') + +``Cache::read()`` se usa para leer el valor en caché almacenado bajo +``$clave`` desde la ``$configuración``. Si ``$configuración`` es nulo, se usará la configuración predeterminada +configuración. ``Cache::read()`` devolverá el valor en caché +si es una caché válida o ``null`` si la caché ha caducado o +no existe. Utiliza los operadores de comparación estricta ``===`` o ``!==`` +para comprobar el éxito de la operación ``Cache::read()``. + +Por ejemplo:: + + $nube = Cache::read('nube'); + if ($nube !== null) { + return $nube; + } + + // Generar datos de la nube + // ... + + // Almacenar datos en la caché + Cache::write('nube', $nube); + + return $nube; + +O si estás usando otra configuración de caché llamada ``corta``, puedes +especificarlo en las llamadas a ``Cache::read()`` y ``Cache::write()`` de la siguiente manera:: + + // Leer la clave "nube", pero de la configuración corta en lugar de la predeterminada + $nube = Cache::read('nube', 'corta'); + if ($nube === null) { + // Generar datos de la nube + // ... + + // Almacenar datos en la caché, usando la configuración de caché corta en lugar de la predeterminada + Cache::write('nube', $nube, 'corta'); + } + + return $nube; + +Lectura de Múltiples Claves a la Vez +------------------------------------- + +.. php:staticmethod:: readMany($claves, $configuracion = 'default') + +Después de haber escrito múltiples claves a la vez, probablemente querrás leerlas también. Aunque podrías usar múltiples llamadas a ``read()``, ``readMany()`` permite +a CakePHP utilizar API de almacenamiento más eficientes donde estén disponibles. Por ejemplo, usando +``readMany()`` ahorras múltiples conexiones de red cuando usas Memcached:: + + $resultado = Cache::readMany([ + 'articulo-' . $slug, + 'articulo-' . $slug . '-comentarios' + ]); + // $resultado contendrá + ['articulo-primer-post' => '...', 'articulo-primer-post-comentarios' => '...'] + +Eliminación de la Caché +======================= + +.. php:staticmethod:: delete($clave, $configuracion = 'default') + +``Cache::delete()`` te permitirá eliminar completamente un objeto en caché +del almacén:: + + // Eliminar una clave + Cache::delete('mi_clave'); + +A partir de la versión 4.4.0, el ``RedisEngine`` también proporciona un método ``deleteAsync()`` que utiliza la operación ``UNLINK`` para eliminar las claves de caché:: + + Cache::pool('redis')->deleteAsync('mi_clave'); + +Eliminación de Múltiples Claves a la Vez +---------------------------------------- + +.. php:staticmethod:: deleteMany($claves, $configuracion = 'default') + +Después de haber escrito múltiples claves a la vez, es posible que desees eliminarlas. Aunque +podrías usar múltiples llamadas a ``delete()``, ``deleteMany()`` permite a CakePHP utilizar +API de almacenamiento más eficientes donde estén disponibles. Por ejemplo, usando ``deleteMany()`` +ahorras múltiples conexiones de red cuando usas Memcached:: + + $resultado = Cache::deleteMany([ + 'articulo-' . $slug, + 'articulo-' . $slug . '-comentarios' + ]); + // $resultado contendrá + ['articulo-primer-post' => true, 'articulo-primer-post-comentarios' => true] + +Limpieza de Datos en Caché +========================== + +.. php:staticmethod:: clear($configuracion = 'default') + +Elimina todos los valores en caché para una configuración de caché. En motores como: Apcu, +Memcached, se utiliza el prefijo de la configuración de caché para eliminar +entradas de caché. Asegúrate de que las diferentes configuraciones de caché tengan diferentes +prefijos:: + + // Eliminará todas las claves. + Cache::clear(); + +A partir de la versión 4.4.0, el ``RedisEngine`` también proporciona un método ``clearBlocking()`` que utiliza la operación ``UNLINK`` para eliminar las claves de caché:: + + Cache::pool('redis')->clearBlocking(); + +.. note:: + + Debido a que APCu utiliza cachés aisladas para el servidor web y la interfaz de línea de comandos, + deben ser limpiadas por separado (la CLI no puede limpiar el servidor web y viceversa). + +Uso de Caché para Almacenar Contadores +======================================= + +.. php:staticmethod:: increment($key, $offset = 1, $config = 'default') + +.. php:staticmethod:: decrement($key, $offset = 1, $config = 'default') + +Los contadores en tu aplicación son buenos candidatos para ser almacenados en caché. Por ejemplo, +una contador de días para un evento puede ser guardado en la caché. La clase Cache +expone formas de incrementar y decrementar los valores del contador. El hecho de que estas +operaciones sean atómicas es importante para que se reduzca el riesgo de contención y la abilidad de que +dos usuarios simultaneamente incrementen o decrementen el mismo valor. + +Después de guardar un valor entero en la caché, puedes manipularlo usando ``increment()`` y +``decrement()``:: + + Cache::write('initial_count', 10); + + // Later on + Cache::decrement('initial_count'); + + // Or + Cache::increment('initial_count'); + + +.. note:: + + Recuerda que las operaciones de incremento y decremento no están disponibles en FileEngine. Debes usar APCu, Redis o Memcached. + +.. _caching-query-results: + +Utilizando la Caché para Almacenar Resultados Comunes de Consultas +=================================================================== + +Puedes mejorar significativamente el rendimiento de tu aplicación almacenando en caché los resultados +que rara vez cambian o que están sujetos a lecturas frecuentes. Un ejemplo perfecto de esto son los resultados de +:php:meth:`Cake\\ORM\\Table::find()`. El objeto de consulta te permite almacenar en caché +los resultados utilizando el método ``cache()``. Consulta la sección :ref:`caching-query-results` +para obtener más información. + +.. _cache-groups: + +Uso de Grupos +============= + +A veces querrás marcar varias entradas en caché para que pertenezcan a cierto grupo o espacio de nombres. Esta es una necesidad común para invalidar masivamente claves cada vez que cambia alguna información que se comparte entre todas las entradas en el mismo grupo. Esto es posible declarando los grupos en la configuración de la caché:: + + Cache::setConfig('site_home', [ + 'className' => 'Redis', + 'duration' => '+999 days', + 'groups' => ['comment', 'article'], + ]); + +.. php:method:: clearGroup($group, $config = 'default') + +Digamos que quieres almacenar en caché el HTML generado para tu página de inicio, pero también quieres invalidar automáticamente esta caché cada vez que se agrega un comentario o una publicación a tu base de datos. Al agregar los grupos ``comment`` y ``article``, hemos etiquetado efectivamente cualquier clave almacenada en esta configuración de caché con ambos nombres de grupo. + +Por ejemplo, cada vez que se añade una nueva publicación, podríamos decirle al motor de caché que elimine todas las entradas asociadas al grupo ``article``:: + + // src/Model/Table/ArticlesTable.php + public function afterSave($event, $entity, $options = []) + { + if ($entity->isNew()) { + Cache::clearGroup('article', 'site_home'); + } + } + +.. php:staticmethod:: groupConfigs($group = null) + +``groupConfigs()`` se puede utilizar para recuperar la asignación entre el grupo y las configuraciones, es decir, tener el mismo grupo:: + + // src/Model/Table/ArticlesTable.php + + /** + * Una variación del ejemplo anterior que limpia todas las configuraciones de caché + * que tienen el mismo grupo + */ + public function afterSave($event, $entity, $options = []) + { + if ($entity->isNew()) { + $configs = Cache::groupConfigs('article'); + foreach ($configs['article'] as $config) { + Cache::clearGroup('article', $config); + } + } + } + +Los grupos se comparten en todas las configuraciones de caché que utilizan el mismo motor y el mismo prefijo. Si estás usando grupos y quieres aprovechar la eliminación de grupos, elige un prefijo común para todas tus configuraciones. + +Habilitar o Deshabilitar Globalmente la Caché +============================================= + +.. php:staticmethod:: disable() + +Puede que necesites deshabilitar todas las lecturas y escrituras en la caché cuando intentas resolver problemas relacionados con la expiración de la caché. Puedes hacerlo usando ``enable()`` y ``disable()``:: + + // Deshabilitar todas las lecturas y escrituras en la caché. + Cache::disable(); + +Una vez deshabilitada, todas las lecturas y escrituras devolverán ``null``. + +.. php:staticmethod:: enable() + +Una vez deshabilitada, puedes usar ``enable()`` para habilitar nuevamente la caché:: + + // Habilitar nuevamente todas las lecturas y escrituras en la caché. + Cache::enable(); + +.. php:staticmethod:: enabled() + +Si necesitas verificar el estado de la caché, puedes usar ``enabled()``. + +Creación de un Motor de Caché +============================= + +Puedes proporcionar motores de ``Cache`` personalizados en ``App\Cache\Engine``, así como en plugins usando ``$plugin\Cache\Engine``. Los motores de caché deben estar en un directorio de caché. Si tuvieras un motor de caché llamado ``MyCustomCacheEngine``, se colocaría en **src/Cache/Engine/MyCustomCacheEngine.php**. O en **plugins/MyPlugin/src/Cache/Engine/MyCustomCacheEngine.php** como parte de un plugin. Las configuraciones de caché de los plugins deben utilizar la sintaxis de puntos del plugin:: + + Cache::setConfig('custom', [ + 'className' => 'MyPlugin.MyCustomCache', + // ... + ]); + +Los motores de caché personalizados deben extender :php:class:`Cake\\Cache\\CacheEngine`, que define varios métodos abstractos y también proporciona algunos métodos de inicialización. + +La API requerida para un CacheEngine es + +.. php:class:: CacheEngine + + La clase base para todos los motores de caché utilizados con Cache. + +.. php:method:: write($key, $value) + + :return: booleano para indicar el éxito. + + Escribe el valor de una clave en la caché, devuelve ``true`` si los datos se almacenaron correctamente, ``false`` en caso de fallo. + +.. php:method:: read($key) + + :return: El valor en caché o ``null`` en caso de fallo. + + Lee una clave de la caché. Devuelve ``null`` para indicar que la entrada ha caducado o no existe. + +.. php:method:: delete($key) + + :return: Booleano ``true`` en caso de éxito. + + Elimina una clave de la caché. Devuelve ``false`` para indicar que la entrada no existía o no se pudo eliminar. + +.. php:method:: clear($check) + + :return: Booleano ``true`` en caso de éxito. + + Elimina todas las claves de la caché. Si $check es ``true``, debes validar que cada valor realmente ha caducado. + +.. php:method:: clearGroup($group) + + :return: Booleano ``true`` en caso de éxito. + + Elimina todas las claves de la caché pertenecientes al mismo grupo. + +.. php:method:: decrement($key, $offset = 1) + + :return: Booleano ``true`` en caso de éxito. + + Decrementa un número bajo la clave y devuelve el valor decrecido. + +.. php:method:: increment($key, $offset = 1) + + :return: Booleano ``true`` en caso de éxito. - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + Incrementa un número bajo la clave y devuelve el valor incrementado. .. meta:: - :title lang=es: Caching - :keywords lang=es: uniform api,xcache,cache engine,cache system,atomic operations,php class,disk storage,static methods,php extension,consistent manner,similar features,apc,memcache,queries,cakephp,elements,servers,memory + :title lang=es: Almacenamiento en Caché + :keywords lang=en: uniform api,cache engine,cache system,atomic operations,php class,disk storage,static methods,php extension,consistent manner,similar features,apcu,apc,memcache,queries,cakephp,elements,servers,memory diff --git a/es/core-libraries/email.rst b/es/core-libraries/email.rst index aceaad7e25..338b640ac3 100644 --- a/es/core-libraries/email.rst +++ b/es/core-libraries/email.rst @@ -1,15 +1,613 @@ -Email -##### +Mailer +###### + +.. php:namespace:: Cake\Mailer + +.. php:class:: Mailer(string|array|null $profile = null) + +``Mailer`` es una clase de conveniencia para enviar correos electrónicos. Con esta clase, puedes enviar correos electrónicos desde cualquier lugar dentro de tu aplicación. + +Uso Básico +========== + +Primero, asegúrate de que la clase esté cargada:: + + use Cake\Mailer\Mailer; + +Después de cargar ``Mailer``, puedes enviar un correo electrónico de la siguiente manera:: + + $mailer = new Mailer('default'); + $mailer->setFrom(['me@example.com' => 'Mi Sitio']) + ->setTo('you@example.com') + ->setSubject('Acerca de') + ->deliver('Mi mensaje'); + +Dado que los métodos setter de ``Mailer`` devuelven una instancia de la clase, puedes configurar sus propiedades encadenando los métodos. + +``Mailer`` tiene varios métodos para definir destinatarios: ``setTo()``, ``setCc()``, ``setBcc()``, ``addTo()``, ``addCc()`` y ``addBcc()``. +La principal diferencia es que los primeros tres sobrescribirán lo que ya se haya establecido, mientras que los últimos simplemente +agregarán más destinatarios a su campo respectivo:: + + $mailer = new Mailer(); + $mailer->setTo('to@example.com', 'Destinatario Ejemplo'); + $mailer->addTo('to2@example.com', 'Destinatario2 Ejemplo'); + // Los destinatarios del correo electrónico son: to@example.com y to2@example.com + $mailer->setTo('test@example.com', 'DestinatarioPrueba Ejemplo'); + // El destinatario del correo electrónico es: test@example.com + +Elección del Remitente +---------------------- + +Cuando envíes correos electrónicos en nombre de otras personas, suele ser una buena idea definir el remitente original usando el encabezado del remitente (Sender header). Puedes hacerlo usando ``setSender()``:: + + $mailer = new Mailer(); + $mailer->setSender('app@example.com', 'Mi aplicación de correo'); + +.. note:: + + También es una buena idea establecer el remitente del sobre (envelope sender) al enviar correos electrónicos en nombre de otra persona. Esto evita que reciban mensajes sobre la entregabilidad. + +.. _email-configuration: + +Configuración +============= + +Los perfiles de Mailer y las configuraciones de transporte de correo electrónico se definen en los archivos de configuración de tu aplicación. Las claves ``'Email'`` y ``'EmailTransport'`` definen perfiles de Mailer y configuraciones de transporte de correo electrónico respectivamente. Durante el inicio de la aplicación, los valores de configuración se pasan desde ``Configure`` a las clases ``Mailer`` y ``TransportFactory`` utilizando ``setConfig()``. Al definir perfiles y transportes, puedes mantener el código de tu aplicación libre de datos de configuración y evitar la duplicación que complica el mantenimiento y el despliegue. + +Para cargar una configuración predefinida, puedes usar el método ``setProfile()`` o pasarlo al constructor de ``Mailer``:: + + $mailer = new Mailer(); + $mailer->setProfile('default'); + + // O en el constructor + $mailer = new Mailer('default'); + +En lugar de pasar una cadena que coincida con un nombre de configuración preestablecido, también puedes cargar simplemente un array de opciones:: + + $mailer = new Mailer(); + $mailer->setProfile(['from' => 'me@example.org', 'transport' => 'my_custom']); + + // O en el constructor + $mailer = new Mailer(['from' => 'me@example.org', 'transport' => 'my_custom']); + +.. _email-configurations: + +Perfiles de Configuración +------------------------- + +Definir perfiles de entrega te permite consolidar la configuración común del correo electrónico en perfiles reutilizables. Tu aplicación puede tener tantos perfiles como sea necesario. Se utilizan las siguientes claves de configuración: + +- ``'from'``: Mailer o array del remitente. Ver ``Mailer::setFrom()``. +- ``'sender'``: Mailer o array del remitente real. Ver ``Mailer::setSender()``. +- ``'to'``: Mailer o array del destino. Ver ``Mailer::setTo()``. +- ``'cc'``: Mailer o array de copia carbono. Ver ``Mailer::setCc()``. +- ``'bcc'``: Mailer o array de copia carbono oculta. Ver ``Mailer::setBcc()``. +- ``'replyTo'``: Mailer o array para responder al correo electrónico. Ver ``Mailer::setReplyTo()``. +- ``'readReceipt'``: Dirección del Mailer o un array de direcciones para recibir + el recibo de lectura. Ver ``Mailer::setReadReceipt()``. +- ``'returnPath'``: Dirección del Mailer o un array de direcciones para devolver si hay + algún error. Ver ``Mailer::setReturnPath()``. +- ``'messageId'``: ID del mensaje del correo electrónico. Ver ``Mailer::setMessageId()``. +- ``'subject'``: Asunto del mensaje. Ver ``Mailer::setSubject()``. +- ``'message'``: Contenido del mensaje. No establezcas este campo si estás usando contenido renderizado. +- ``'priority'``: Prioridad del correo electrónico como valor numérico (generalmente de 1 a 5, siendo 1 el más alto). +- ``'headers'``: Cabeceras a incluir. Ver ``Mailer::setHeaders()``. +- ``'viewRenderer'``: Si estás usando contenido renderizado, establece el nombre de la clase de vista. + Ver ``ViewBuilder::setClassName()``. +- ``'template'``: Si estás usando contenido renderizado, establece el nombre de la plantilla. Ver + ``ViewBuilder::setTemplate()``. +- ``'theme'``: Tema utilizado al renderizar la plantilla. Ver ``ViewBuilder::setTheme()``. +- ``'layout'``: Si estás usando contenido renderizado, establece el diseño a renderizar. Ver + ``ViewBuilder::setTemplate()``. +- ``'autoLayout'``: Si quieres renderizar una plantilla sin diseño, establece este campo en + ``false``. Ver ``ViewBuilder::disableAutoLayout()``. +- ``'viewVars'``: Si estás usando contenido renderizado, establece el array con + variables que se utilizarán en la vista. Ver ``Mailer::setViewVars()``. +- ``'attachments'``: Lista de archivos para adjuntar. Ver ``Mailer::setAttachments()``. +- ``'emailFormat'``: Formato del correo electrónico (html, texto o ambos). Ver ``Mailer::setEmailFormat()``. +- ``'transport'``: Nombre de la configuración del transporte. Ver :ref:`email-transport`. +- ``'log'``: Nivel de registro para registrar las cabeceras y el mensaje del correo electrónico. ``true`` utilizará + LOG_DEBUG. Ver :ref:`logging-levels`. Ten en cuenta que los registros se emitirán bajo el ámbito denominado ``email``. + Ver también :ref:`logging-scopes`. +- ``'helpers'``: Array de helpers utilizados en la plantilla del correo electrónico. + ``ViewBuilder::setHelpers()``/``ViewBuilder::addHelpers()``. + +.. note:: + + Los valores de las claves mencionadas anteriormente que usan Mailer o array, como from, to, cc, etc., se pasarán + como el primer parámetro de los métodos correspondientes. El equivalente a: + ``$mailer->setFrom('mi@example.com', 'Mi Sitio')`` + se definiría como ``'from' => ['mi@example.com' => 'Mi Sitio']`` en tu configuración. + +Configurando Cabeceras +====================== + +En ``Mailer``, eres libre de establecer las cabeceras que desees. No olvides agregar el prefijo ``X-`` a tus cabeceras personalizadas. + +Consulta ``Mailer::setHeaders()`` y ``Mailer::addHeaders()`` + +Envío de Correos Electrónicos con Plantillas +============================================= + +Los correos electrónicos a menudo son mucho más que un simple mensaje de texto. Para facilitar eso, CakePHP proporciona una forma de enviar correos electrónicos utilizando la :doc:`capa de vista ` de CakePHP. + +Las plantillas para correos electrónicos residen en una carpeta especial ``templates/email`` de tu aplicación. Las vistas del Mailer también pueden utilizar diseños y elementos al igual que las vistas normales:: + + $mailer = new Mailer(); + $mailer + ->setEmailFormat('html') + ->setTo('bob@example.com') + ->setFrom('app@domain.com') + ->viewBuilder() + ->setTemplate('bienvenida') + ->setLayout('elegante'); + + $mailer->deliver(); + +Lo anterior utilizará **templates/email/html/bienvenida.php** para la vista +y **templates/layout/email/html/elegante.php** para el diseño. También puedes +enviar mensajes de correo electrónico con varias partes de plantilla:: + + $mailer = new Mailer(); + $mailer + ->setEmailFormat('both') + ->setTo('bob@example.com') + ->setFrom('app@domain.com') + ->viewBuilder() + ->setTemplate('bienvenida') + ->setLayout('elegante'); + + $mailer->deliver(); + +Esto utilizará los siguientes archivos de plantilla: + +* **templates/email/text/bienvenida.php** +* **templates/layout/email/text/elegante.php** +* **templates/email/html/bienvenida.php** +* **templates/layout/email/html/elegante.php** + +Cuando envíes correos electrónicos con plantillas, tienes la opción de enviar ``texto``, ``html`` o ``ambos``. + +Puedes configurar toda la configuración relacionada con la vista usando la instancia de creador de vistas ``Mailer::viewBuilder()`` de manera similar a como lo haces en el controlador. + +Puedes establecer variables de vista con ``Mailer::setViewVars()``:: + + $mailer = new Mailer('plantilla'); + $mailer->setViewVars(['valor' => 12345]); + +O puedes usar los métodos del creador de vistas ``ViewBuilder::setVar()`` y ``ViewBuilder::setVars()``. + +En tus plantillas de correo electrónico, puedes usarlos de la siguiente manera:: + +

Aquí está tu valor:

+ +También puedes usar ayudantes en los correos electrónicos, al igual que en los archivos de plantilla normales. De forma predeterminada, solo se carga el ``HtmlHelper``. Puedes cargar ayudantes adicionales utilizando el método ``ViewBuilder::addHelpers()``:: + + $mailer->viewBuilder()->addHelpers(['Html', 'Custom', 'Text']); + +Cuando agregues ayudantes, asegúrate de incluir 'Html' o se eliminará de los ayudantes cargados en tu plantilla de correo electrónico. .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. + En versiones anteriores a 4.3.0, deberás usar ``setHelpers()`` en su lugar. + +Si deseas enviar correos electrónicos utilizando plantillas en un plugin, puedes usar la familiar :term:`Sintaxis de plugin` para hacerlo:: + + $mailer = new Mailer(); + $mailer->viewBuilder()->setTemplate('Blog.new_comment'); + +Lo anterior utilizará la plantilla y el diseño del plugin Blog como ejemplo. + +En algunos casos, es posible que necesites anular la plantilla predeterminada proporcionada por los complementos. +Puedes hacer esto usando temas:: + + $mailer->viewBuilder() + ->setTemplate('Blog.new_comment') + ->setLayout('Blog.auto_message') + ->setTheme('MiTema'); + +Esto te permite anular la plantilla "new_comment" en tu tema sin modificar el complemento Blog. El archivo de plantilla debe crearse en la siguiente ruta: +**templates/plugin/MiTema/plugin/Blog/email/text/new_comment.php**. + +Envío de Archivos Adjuntos +=========================== + +.. php:method:: setAttachments($adjuntos) + +También puedes adjuntar archivos a los mensajes de correo electrónico. Hay algunos formatos diferentes dependiendo del tipo de archivos que tengas y de cómo quieras que aparezcan los nombres de archivo en el cliente de correo del destinatario: + +1. Array: ``$mailer->setAttachments(['/ruta/completa/archivo.png'])`` adjuntará este archivo con el nombre archivo.png.. +2. Array con clave: + ``$mailer->setAttachments(['foto.png' => '/ruta/completa/algun_hash.png'])`` adjuntará some_hash.png con el nombre foto.png. El destinatario verá + foto.png, no some_hash.png. +3. Arrays anidados:: + + $mailer->setAttachments([ + 'foto.png' => [ + 'archivo' => '/ruta/completa/algun_hash.png', + 'mimetype' => 'image/png', + 'contentId' => 'mi-id-unico', + ], + ]); + + Lo anterior adjuntará el archivo con un tipo MIME diferente y con un ID de contenido personalizado (cuando se establece el ID de contenido, el archivo adjunto se convierte en incrustado). + El tipo MIME y contentId son opcionales en esta forma. + + 3.1. Cuando estás usando el ``contentId``, puedes usar el archivo en el cuerpo HTML + como ````. + + 3.2. Puedes usar la opción ``contentDisposition`` para desactivar el encabezado ``Content-Disposition`` para un archivo adjunto. Esto es útil cuando + envías invitaciones ical a clientes que usan Outlook. + + 3.3 En lugar de la opción ``archivo``, puedes proporcionar el contenido del archivo como + una cadena utilizando la opción ``datos``. Esto te permite adjuntar archivos sin + necesidad de tener rutas de archivo para ellos. + +Relajando las Reglas de Validación de Direcciones +-------------------------------------------------- + +.. php:method:: setEmailPattern($patrón) + +Si tienes problemas de validación al enviar a direcciones no conformes, puedes relajar el patrón utilizado para validar direcciones de correo electrónico. Esto es a veces +necesario al tratar con algunos proveedores de servicios de Internet:: + + $mailer = new Mailer('predeterminado'); + + // Relaja el patrón de correo electrónico, para que puedas enviar + // a direcciones no conformes. + $mailer->setEmailPattern($nuevoPatrón); + +Envío de Correos Electrónicos desde la CLI +=========================================== + +Cuando envíes correos electrónicos dentro de un script de CLI (Shells, Tasks, ...), debes establecer manualmente +el nombre de dominio que Mailer utilizará. Servirá como el nombre de host para el +ID del mensaje (ya que no hay un nombre de host en un entorno CLI):: + + $mailer->setDomain('www.ejemplo.org'); + // Da como resultado IDs de mensajes como ```` (válidos) + // En lugar de ```` (inválidos) + +Un ID de mensaje válido puede ayudar a evitar que los correos electrónicos terminen en carpetas de spam. + +Creación de Correos Electrónicos Reutilizables +=============================================== + +Hasta ahora hemos visto cómo usar directamente la clase ``Mailer`` para crear y +enviar un correo electrónico. Pero la característica principal del mailer es permitir la creación de correos electrónicos reutilizables +en toda tu aplicación. También se pueden usar para contener múltiples +configuraciones de correo electrónico en un solo lugar. Esto ayuda a mantener tu código DRY y a evitar la configuración de correo electrónico +en otras áreas de tu aplicación. + +En este ejemplo, crearemos un ``Mailer`` que contiene correos electrónicos relacionados con el usuario. +Para crear nuestro ``UserMailer``, crea el archivo +**src/Mailer/UserMailer.php**. El contenido del archivo debería verse así:: + + namespace App\Mailer; + + use Cake\Mailer\Mailer; + + class UserMailer extends Mailer + { + public function welcome($user) + { + $this + ->setTo($user->email) + ->setSubject(sprintf('Welcome %s', $user->name)) + ->viewBuilder() + ->setTemplate('welcome_mail'); // Por defecto, se utiliza la plantilla con el mismo nombre que el nombre del método. + } + + public function resetPassword($user) + { + $this + ->setTo($user->email) + ->setSubject('Reset password') + ->setViewVars(['token' => $user->token]); + } + } + + +En nuestro ejemplo, hemos creado dos métodos, uno para enviar un correo electrónico de bienvenida y +otro para enviar un correo electrónico de restablecimiento de contraseña. Cada uno de estos métodos espera una entidad de usuario +y utiliza sus propiedades para configurar cada correo electrónico. + +Ahora podemos usar nuestro ``UserMailer`` para enviar nuestros correos electrónicos relacionados con el usuario +desde cualquier parte de nuestra aplicación. Por ejemplo, si queremos enviar nuestro correo de bienvenida +podríamos hacer lo siguiente:: + + namespace App\Controller; + + use Cake\Mailer\MailerAwareTrait; + + class UsersController extends AppController + { + use MailerAwareTrait; + + public function register() + { + $user = + + $this->Users->newEmptyEntity(); + if ($this->request->is('post')) { + $user = $this->Users->patchEntity($user, $this->request->getData()); + if ($this->Users->save($user)) { + // Enviar correo electrónico de bienvenida. + $this->getMailer('User')->send('welcome', [$user]); + // Redirigir a la página de inicio de sesión u otra página de destino. + return $this->redirect(['controller' => 'Users', 'action' => 'login']); + } + $this->Flash->error(__('Unable to register user. Please try again.')); + } + $this->set(compact('user')); + } + } + +Si quisiéramos separar por completo el envío del correo de bienvenida del usuario de nuestro código de aplicación, podemos hacer que nuestro +`UserMailer` se suscriba al evento `Model.afterSave`. Al suscribirse a un evento, podemos mantener nuestras clases relacionadas con el +usuario completamente libres de lógica e instrucciones relacionadas con el correo electrónico de nuestra aplicación. Por ejemplo, +podríamos agregar lo siguiente a nuestro `UserMailer`:: + + public function implementedEvents() + { + return [ + 'Model.afterSave' => 'onRegistration', + ]; + } + + public function onRegistration(EventInterface $event, EntityInterface $entity, ArrayObject $options) + { + if ($entity->isNew()) { + $this->send('welcome', [$entity]); + } + } + +Ahora puedes registrar el mailer como un oyente de eventos y el método `onRegistration()` se invocará cada vez que se dispare el evento `Model.afterSave`:: + + // Adjuntar al gestor de eventos de Usuarios + $this->Users->getEventManager()->on($this->getMailer('User')); + +.. _email-transport: + +Configuración de Transportes +============================ + +Los mensajes de correo electrónico se entregan mediante transportes. Diferentes transportes te permiten enviar mensajes a través de la función `mail()` +de PHP, servidores SMTP o no enviarlos en absoluto, lo cual es útil para depurar. Configurar transportes te permite mantener los datos de configuración +fuera del código de tu aplicación y simplifica la implementación, ya que simplemente puedes cambiar los datos de configuración. Una configuración de +transporte de ejemplo se ve así:: + + // En config/app.php + 'EmailTransport' => [ + // Configuración de ejemplo para correo + 'default' => [ + 'className' => 'Mail', + ], + // Configuración de ejemplo para SMTP + 'gmail' => [ + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'username' => 'mi@gmail.com', + 'password' => 'secreto', + 'className' => 'Smtp', + 'tls' => true, + ], + ], + +Los transportes también se pueden configurar en tiempo de ejecución utilizando `TransportFactory::setConfig()`:: + + use Cake\Mailer\TransportFactory; + + // Definir un transporte SMTP + TransportFactory::setConfig('gmail', [ + 'host' => 'ssl://smtp.gmail.com', + 'port' => 465, + 'username' => 'mi@gmail.com', + 'password' => 'secreto', + 'className' => 'Smtp' + ]); + +Puedes configurar servidores SMTP SSL, como Gmail. Para hacerlo, coloca el prefijo `ssl://` en el host y configura el valor del puerto en consecuencia. También puedes habilitar SMTP TLS usando la opción `tls`:: + + use Cake\Mailer\TransportFactory; + + TransportFactory::setConfig('gmail', [ + 'host' => 'smtp.gmail.com', + 'port' => 587, + 'username' => 'mi@gmail.com', + 'password' => 'secreto', + 'className' => 'Smtp', + 'tls' => true + ]); + +La configuración anterior habilitaría la comunicación TLS para los mensajes de correo electrónico. + +Para configurar tu mailer para usar un transporte específico, puedes usar el método :php:meth:`Cake\\Mailer\\Mailer::setTransport()` o tener el transporte en tu configuración:: + + + // Usa un transporte con nombre ya configurado usando TransportFactory::setConfig() + $mailer->setTransport('gmail'); + + // Usa un objeto construido. + $mailer->setTransport(new \Cake\Mailer\Transport\DebugTransport()); + +.. warning :: + + Deberás tener habilitado el acceso para aplicaciones menos seguras en tu cuenta de Google para que funcione: + `Permitir que aplicaciones menos seguras accedan a tu cuenta `__. + +.. note :: +   `Configuración SMTP de Gmail `__. + +.. note :: + Para usar SSL + SMTP, necesitarás tener SSL configurado en tu instalación de PHP. + +También se pueden proporcionar opciones de configuración como una cadena :term:`DSN`. Esto es útil cuando trabajas con variables de entorno o proveedores de :term:`PaaS`:: + + TransportFactory::setConfig('default', [ + 'url' => 'smtp://mi@gmail.com:secreto@smtp.gmail.com:587?tls=true', + ]); + +Cuando usas una cadena DSN, puedes definir cualquier parámetro / opción adicional como argumentos de cadena de consulta. + +.. php:staticmethod:: drop($key) + +Una vez configurados, los transportes no se pueden modificar. Para modificar un transporte, primero debes eliminarlo y luego reconfigurarlo. + +Creación de Transportes Personalizados +-------------------------------------- + +Puedes crear tus propios transportes para situaciones como enviar correos electrónicos utilizando servicios como SendGrid, MailGun +o Postmark. Para crear tu transporte, primero crea el archivo **src/Mailer/Transport/ExampleTransport.php** (donde Example es el +nombre de tu transporte). Para empezar, tu archivo debería verse así:: + + namespace App\Mailer\Transport; + + use Cake\Mailer\AbstractTransport; + use Cake\Mailer\Message; + + class ExampleTransport extends AbstractTransport + { + public function send(Message $message): array + { + // Haz algo. + } + } + +Debes implementar el método ``send(Message $message)`` con tu lógica personalizada. + +Envío de correos electrónicos sin usar Mailer +============================================= + +El ``Mailer`` es una clase de abstracción de nivel superior que actúa como un puente entre las clases ``Cake\Mailer\Message``, ``Cake\Mailer\Renderer`` y ``Cake\Mailer\AbstractTransport`` para configurar correos electrónicos con una interfaz fluida. + +Si lo deseas, también puedes usar estas clases directamente con el ``Mailer``. + +Por ejemplo:: + + $render = new \Cake\Mailer\Renderer(); + $render->viewBuilder() + ->setTemplate('custom') + ->setLayout('sparkly'); + + $message = new \Cake\Mailer\Message(); + $message + ->setFrom('admin@cakephp.org') + ->setTo('user@foo.com') + ->setBody($render->render()); + + $transport = new \Cake\Mailer\Transport\MailTransport(); + $result = $transport->send($message); + +Incluso puedes omitir el uso del ``Renderer`` y establecer el cuerpo del mensaje directamente +usando los métodos ``Message::setBodyText()`` y ``Message::setBodyHtml()``. + +.. _email-testing: + +Pruebas de Mailers +================== + +Para probar mailers, agrega ``Cake\TestSuite\EmailTrait`` a tu caso de prueba.El ``MailerTrait`` +utiliza ganchos de PHPUnit para reemplazar los transportes de correo electrónico de tu aplicación +con un proxy que intercepta los mensajes de correo electrónico y te permite hacer afirmaciones +sobre el correo que se enviaría. + +Agrega el trait a tu caso de prueba para comenzar a probar correos electrónicos, y carga rutas si tus +correos electrónicos necesitan generar URL:: + + namespace App\Test\TestCase\Mailer; + + use App\Mailer\WelcomeMailer; + use App\Model\Entity\User; + + use Cake\TestSuite\EmailTrait; + use Cake\TestSuite\TestCase; + + class WelcomeMailerTestCase extends TestCase + { + use EmailTrait; + + public function setUp(): void + { + parent::setUp(); + $this->loadRoutes(); + } + } + +Supongamos que tenemos un mailer que envía correos electrónicos de bienvenida cuando un nuevo usuario +se registra. Queremos comprobar que el asunto y el cuerpo contienen el nombre del usuario:: + + // en nuestra clase WelcomeMailerTestCase. + public function testName() + { + $user = new User([ + 'name' => 'Alice Alittea', + 'email' => 'alice@example.org', + ]); + $mailer = new WelcomeMailer(); + $mailer->send('welcome', [$user]); + + $this->assertMailSentTo($user->email); + $this->assertMailContainsText('Hola ' . $user->name); + $this->assertMailContainsText('¡Bienvenido a CakePHP!'); + } + +Métodos de afirmación +---------------------- + +El trait ``Cake\TestSuite\EmailTrait`` proporciona las siguientes afirmaciones:: + + // Asegura que se enviaron un número esperado de correos electrónicos + $this->assertMailCount($count); + + // Asegura que no se enviaron correos electrónicos + $this->assertNoMailSent(); + + // Asegura que se envió un correo electrónico a una dirección + $this->assertMailSentTo($address); + + // Asegura que se envió un correo electrónico desde una dirección + $this->assertMailSentFrom($emailAddress); + $this->assertMailSentFrom([$emailAddress => $displayName]); + + // Asegura que un correo electrónico contiene los contenidos esperados + $this->assertMailContains($contents); + + // Asegura que un correo electrónico contiene los contenidos HTML esperados + $this->assertMailContainsHtml($contents); + + // Asegura que un correo electrónico contiene los contenidos de texto esperados + $this->assertMailContainsText($contents); + + // Asegura que un correo electrónico contiene el valor esperado dentro de un getter de Message (por ejemplo, "subject") + $this->assertMailSentWith($expected, $parameter); + + // Asegura que un correo electrónico en un índice específico se envió a una dirección + $this->assertMailSentToAt($at, $address); + + // Asegura que un correo electrónico en un índice específico se envió desde una dirección + $this->assertMailSentFromAt($at, $address); + + // Asegura que un correo electrónico en un índice específico contiene los contenidos esperados + $this->assertMailContainsAt($at, $contents); + + // Asegura que un correo electrónico en un índice específico contiene los contenidos HTML esperados + $this->assertMailContainsHtmlAt($at, $contents); + + // Asegura que un correo electrónico en un índice específico contiene los contenidos de texto esperados + $this->assertMailContainsTextAt($at, $contents); + + // Asegura que un correo electrónico contiene un archivo adjunto + $this->assertMailContainsAttachment('test.png'); + + // Asegura que un correo electrónico en un índice específico contiene el valor esperado dentro de un getter de Message (por ejemplo, "cc") + $this->assertMailSentWithAt($at, $expected, $parameter); - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + // Asegura que un correo electrónico contiene una subcadena en el asunto. + $this->assertMailSubjectContains('Oferta Gratuita'); - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + // Asegura que un correo electrónico en un índice específico contiene una subcadena en el asunto. + $this->assertMailSubjectContainsAt(1, 'Oferta Gratuita'); .. meta:: - :title lang=es: Email - :keywords lang=es: sending mail,email sender,envelope sender,php class,database configuration,sending emails,meth,shells,smtp,transports,attributes,array,config,flexibility,php email,new email,sending email,models + :title lang=es: Correo Electrónico + :keywords lang=en: sending mail,email sender,envelope sender,php class,database configuration,sending emails,commands,smtp,transports,attributes,array,config,flexibility,php email,new email,sending email,models diff --git a/es/core-libraries/file-folder.rst b/es/core-libraries/file-folder.rst deleted file mode 100644 index 5ed8e5803b..0000000000 --- a/es/core-libraries/file-folder.rst +++ /dev/null @@ -1,16 +0,0 @@ -Folder & File -############# - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Folder & File - :description lang=es: The Folder and File utilities are convenience classes to help you read, write, and append to files; list files within a folder and other common directory related tasks. - :keywords lang=es: file,folder,cakephp utility,read file,write file,append file,recursively copy,copy options,folder path,class folder,file php,php files,change directory,file utilities,new folder,directory structure,delete file diff --git a/es/core-libraries/inflector.rst b/es/core-libraries/inflector.rst index 2f857aa8aa..1bb5d1af2b 100644 --- a/es/core-libraries/inflector.rst +++ b/es/core-libraries/inflector.rst @@ -5,15 +5,177 @@ Inflector .. php:class:: Inflector +La clase `Inflector` toma una cadena y puede manipularla para manejar variaciones de palabras como +pluralización o conversión a formato camello (camelCase). Por lo general, se accede a esta clase de +manera estática. Por ejemplo: + +``Inflector::pluralize('example')`` devuelve "examples". + +Puedes probar las inflecciones en línea en `inflector.cakephp.org +`_ or `sandbox.dereuromark.de +`_. + +.. _inflector-methods-summary: + +Métodos integrados en Inflector y su resultado +============================================== + +Los métodos integrados en el Inflector y los resultados que generan al proporcionarles un argumento compuesto por varias palabras: + ++-------------------+---------------+---------------+ +| Método | Argumento | Resultado | ++===================+===============+===============+ +| ``pluralize()`` | BigApple | BigApples | ++ +---------------+---------------+ +| | big_apple | big_apples | ++-------------------+---------------+---------------+ +| ``singularize()`` | BigApples | BigApple | ++ +---------------+---------------+ +| | big_apples | big_apple | ++-------------------+---------------+---------------+ +| ``camelize()`` | big_apples | BigApples | ++ +---------------+---------------+ +| | big apple | BigApple | ++-------------------+---------------+---------------+ +| ``underscore()`` | BigApples | big_apples | ++ +---------------+---------------+ +| | Big Apples | big apples | ++-------------------+---------------+---------------+ +| ``humanize()`` | big_apples | Big Apples | ++ +---------------+---------------+ +| | bigApple | BigApple | ++-------------------+---------------+---------------+ +| ``classify()`` | big_apples | BigApple | ++ +---------------+---------------+ +| | big apple | BigApple | ++-------------------+---------------+---------------+ +| ``dasherize()`` | BigApples | big-apples | ++ +---------------+---------------+ +| | big apple | big apple | ++-------------------+---------------+---------------+ +| ``tableize()`` | BigApple | big_apples | ++ +---------------+---------------+ +| | Big Apple | big apples | ++-------------------+---------------+---------------+ +| ``variable()`` | big_apple | bigApple | ++ +---------------+---------------+ +| | big apples | bigApples | ++-------------------+---------------+---------------+ + +Generando formas Plural y Singular +================================== + +.. php:staticmethod:: singularize($singular) +.. php:staticmethod:: pluralize($singular) + +Both ``pluralize`` and ``singularize()`` work on most English nouns. If you need +to support other languages, you can use :ref:`inflection-configuration` to +customize the rules used:: + + // Apples + echo Inflector::pluralize('Apple'); + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + ``pluralize()`` no debería ser usado en un nombre que ya está en su forma plural. + +.. code-block:: php + + // Person + echo Inflector::singularize('People'); + +.. note:: + + ``singularize()`` no debería ser usado en un nombre que ya está en su forma singular. + +Generando formas CamelCase y under_scored +========================================= + +.. php:staticmethod:: camelize($underscored) +.. php:staticmethod:: underscore($camelCase) + +Estos métodos son útiles cuando creas nombres de clases o de propiedades:: + + // ApplePie + Inflector::camelize('Apple_pie') + + // apple_pie + Inflector::underscore('ApplePie'); + +Nótese que el método *underscore* sólo convertirá palabras en formato *CamelCase*. +Palabras que contengan espacios serán transformadas a minúscula pero no contendrán un guión bajo. + +Generando formas legibles por humanos +===================================== + +.. php:staticmethod:: humanize($underscored) + +Este método es útil cuando se quiere convertir una palabra de la forma *under_scored* al formato "Título" para que sea legible por un ser humano:: + + // Apple Pie + Inflector::humanize('apple_pie'); + +Generando formas de tabla y nombre de clase +=========================================== + +.. php:staticmethod:: classify($underscored) +.. php:staticmethod:: dasherize($dashed) +.. php:staticmethod:: tableize($camelCase) + +Cuando se genera código, o usando las convenciones de CakePHP, puedes necesitar generar inflecciones para los nombres de tabla o de clase:: + + // UserProfileSetting + Inflector::classify('user_profile_settings'); + + // user-profile-setting + Inflector::dasherize('UserProfileSetting'); + + // user_profile_settings + Inflector::tableize('UserProfileSetting'); + +Generando Nombres de Variables +============================== + +.. php:staticmethod:: variable($underscored) + +Los nombres de variable son a menudo útiles cuando se hacen tareas de meta-programación que involucran generar código o hacer trabajo basado en convenciones:: + + // applePie + Inflector::variable('apple_pie'); + + +.. _inflection-configuration: + +Configurando las Inflecciones +============================= + +Las convenciones de nomenclatura de CakePHP pueden ser muy útiles: puedes nombrar tu +tabla de base de datos como ``big_boxes``, tu modelo como ``BigBoxes``, tu controlador +como ``BigBoxesController``, y todo funcionará automáticamente juntos. La forma en que +CakePHP sabe cómo vincular las cosas es *inflectando* las palabras entre sus formas +singular y plural. + +Existen ocasiones (especialmente para nuestros amigos que no hablan inglés) en las que +podrías encontrarte con situaciones donde el inflector de CakePHP (la clase que pluraliza, +singulariza, utiliza notación camello y subrayados) puede no funcionar como deseas. Si +CakePHP no reconoce tus "Foci" o "Fish", puedes indicarle a CakePHP acerca de tus casos especiales. + +Cargando Inflecciones Personalizadas +------------------------------------ + +.. php:staticmethod:: rules($type, $rules, $reset = false) + +Define nuevas reglas de inflexión y transliteración para que Inflector las utilice. A menudo, este método se utiliza +en tu archivo **config/bootstrap.php**:: + + Inflector::rules('singular', ['/^(bil)er$/i' => '\1', '/^(inflec|contribu)tors$/i' => '\1ta']); + Inflector::rules('uninflected', ['singulars']); + Inflector::rules('irregular', ['phylum' => 'phyla']); // The key is singular form, value is plural form - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Las reglas suministradas se fusionarán en los conjuntos de inflexión respectivos definidos en ``Cake/Utility/Inflector``, +y las reglas añadidas tendrán prioridad sobre las reglas principales del núcleo. Puedes usar ``Inflector::reset()`` +para eliminar las reglas y restaurar el estado original del Inflector. .. meta:: - :title lang=es: Inflector - :keywords lang=es: apple orange,word variations,apple pie,person man,latin versions,profile settings,php class,initial state,puree,slug,apples,oranges,user profile,underscore + :title lang=es: Objeto Inflector + :keywords lang=en: apple orange,word variations,apple pie,person man,latin versions,profile settings,php class,initial state,puree,slug,apples,oranges,user profile,underscore diff --git a/es/core-libraries/logging.rst b/es/core-libraries/logging.rst index 9b2206f53e..1729ff0119 100644 --- a/es/core-libraries/logging.rst +++ b/es/core-libraries/logging.rst @@ -1,16 +1,500 @@ Logging ####### +Si bien la configuración de la clase Configure de CakePHP puede ayudarte a ver +lo que está sucediendo en el sistema, hay momentos en los que necesitarás registrar +datos en el disco para averiguar lo que está ocurriendo. Con tecnologías como SOAP, AJAX y API REST, +la depuración puede ser bastante difícil. + +Logging también puede ser una forma de averiguar lo que ha estado ocurriendo +en tu aplicación con el tiempo. ¿Qué términos de búsqueda se están utilizando? +¿Qué tipos de errores están viendo mis usuarios? ¿Con qué frecuencia se ejecuta +una consulta en particular? + +Logging data in CakePHP is done with the ``log()`` function. It is provided by the +``LogTrait``, which is the common ancestor for many CakePHP classes. If the +context is a CakePHP class (Controller, Component, View,...), you can log your +data. You can also use ``Log::write()`` directly. See :ref:`writing-to-logs`. + +El registro de datos en CakePHP se realiza con la función "log()". Esta función es proporcionada por el +"LogTrait", que es el ancestro común de muchas clases de CakePHP. Si el contexto es una clase de CakePHP +(Controlador, Componente, Vista, etc.), puedes registrar tus datos. También puedes usar "Log::write()" +directamente. Consulta la sección :ref:`writing-to-logs` para obtener más información. + +.. _log-configuration: + +Logging Configuration +===================== + +La configuración de ``Log`` debe realizarse durante la fase de arranque de tu aplicación. +El archivo **config/app.php** está diseñado precisamente para esto. Puedes definir tantos +``loggers`` como necesite tu aplicación. Los ``loggers`` deben configurarse utilizando la clase +:php:class:`Cake\\Log\\Log`. Un ejemplo sería:: + + use Cake\Log\Engine\FileLog; + use Cake\Log\Log; + + // Nombre de la clase utilizando la constante 'class' del logger. + Log::setConfig('info', [ + 'className' => FileLog::class, + 'path' => LOGS, + 'levels' => ['info'], + 'file' => 'info', + ]); + + // Nombre de clase corto + Log::setConfig('debug', [ + 'className' => 'File', + 'path' => LOGS, + 'levels' => ['notice', 'debug'], + 'file' => 'debug', + ]); + + // Fully namespaced name. + Log::setConfig('error', [ + 'className' => 'Cake\Log\Engine\FileLog', + 'path' => LOGS, + 'levels' => ['warning', 'error', 'critical', 'alert', 'emergency'], + 'file' => 'error', + ]); + +Lo anterior crea tres loggers, llamados ``info``, ``debug`` and ``error``. +Cada uno está configurado para manejar diferentes niveles de mensajes. +También almacenan sus mensajes de registro en archivos separados, de esta manera, +podemos separar los registros de depuración/aviso/información de los errores más graves. +Consulta la sección sobr :ref:`logging-levels` para obtener más información sobre +los diferentes niveles y lo que significan. + +Una vez que se crea una configuración, no se puede cambiar. En su lugar, debes eliminar +la configuración y volver a crearla utilizando :php:meth:`Cake\\Log\\Log::drop()` y +:php:meth:`Cake\\Log\\Log::setConfig()`. + +También es posible crear loggers proporcionando un cierre (closure). Esto es útil +cuando necesitas un control completo sobre cómo se construye el objeto del logger. El cierre +debe devolver la instancia del logger. Por ejemplo:: + + Log::setConfig('special', function () { + return new \Cake\Log\Engine\FileLog(['path' => LOGS, 'file' => 'log']); + }); + + +Las opciones de configuración también se pueden proporcionar como una cadena :term:`DSN`. Esto es +útil cuando se trabaja con variables de entorno o proveedores :term:`PaaS`:: + + Log::setConfig('error', [ + 'url' => 'file:///full/path/to/logs/?levels[]=warning&levels[]=error&file=error', + ]); + +.. warning:: + Si no configuras motores de registro (logging), los mensajes de log no se almacenarán. + +Registro de Errores y Excepciones +================================= + +Los errores y excepciones también pueden registrarse configurando los valores correspondientes en tu archivo **config/app.php**. +Los errores se mostrarán cuando el modo de depuración esté en ``true`` y se registrarán en los archivos de log cuando el modo de depuración esté en ``false``. +Para registrar excepciones no capturadas, configura la opción ``log`` como ``true``. +Consulta ::doc:`/development/configuration` para obtener más información. + +.. _writing-to-logs: + +Escribiendo en los archivos de Log +=================================== + +Escribir en los archivos de registro se puede hacer de dos maneras diferentes. La primera es +utilizando el método estático ::php:meth:`Cake\\Log\\Log::write()`:: + + Log::write('debug', 'Something did not work'); + +La segunda opción es utilizar la función de acceso directo ``log()`` disponible en cualquier clase +que utilice el ``LogTrait``. Llamar a``log()`` llamará internamente a``Log::write()``:: + + // Ejecutando esto dentro de una clase que utiliza LogTrait + $this->log('Something did not work!', 'debug'); + +Todos los ``log`` configurados se escriben secuencialmente cada vez que se llama a +:php:meth:`Cake\\Log\\Log::write()`. Si no has configurado ningún motor de registro, +``log()`` devolverá "false" y no se escribirán mensajes de registro. + +Usando marcadores de posición (placeholders) en mensajes +--------------------------------------------------------- + +Si necesitas registrar datos definidos dinámicamente, puedes utilizar marcadores de posición en tus +mensajes de registro y proporcionar un array de pares clave/valor en el parámetro ``$context`` +como sigue:: + + + // Se registrará `No se pudo procesar para el usuario id = 1` + Log::write('error', 'No se pudo procesar para el usuario id ={user}', ['user' => $user->id]); + +Los marcadores (placeholders) que no tienen claves definidas no serán reemplazados. +Si necesitas utilizar una palabra entre llaves de forma literal, debes escapar el marcador:: + + + // Se registrará `No {replace}` + Log::write('error', 'No \\{replace}', ['replace' => 'no']); + +Si incluyes objetos en los marcadores, esos objetos deben implementar +uno de los siguientes métodos: + +* ``__toString()`` +* ``toArray()`` +* ``__debugInfo()`` + +.. _logging-levels: + +Usando Niveles +--------------- + +CakePHP admite el conjunto estándar de niveles de registro POSIX. Cada nivel representa un aumento +en el nivel de gravedad: + +* Emergency: el sistema no es utilizable +* Alert: se debe tomar una acción inmediata +* Critical: condiciones críticas +* Error: condiciones de error +* Warning: condiciones de advertencia +* Notice: condiciones normales pero significativas +* Info: mensajes informativos +* Debug: mensajes de depuración + +Puedes hacer referencia a estos niveles por nombre al configurar lo ``loggers`` y al escribir +mensajes de registro. Alternativamente, puedes utilizar métodos de conveniencia como : +:php:meth:`Cake\\Log\\Log::error()` para indicar claramente el nivel de registro. +Utilizar un nivel que no esté en la lista de niveles anteriores resultará en una excepción. + +.. note:: + Cuando ``levels`` se establece en un valor vacío en la configuración de un ``logger``, + aceptará mensajes de cualquier nivel. + +.. _logging-scopes: + +Ámbitos de Registro (scope) +---------------------------- + +En muchas ocasiones, querrás configurar diferentes comportamientos de registro para diferentes +subsistemas o partes de tu aplicación. Tomemos como ejemplo una tienda en línea. +Probablemente, quieras manejar el registro de pedidos y pagos de manera diferente a como lo haces +con otros registros menos críticos. + +CakePHP expone este concepto como ámbitos de registro. Cuando se escriben mensajes de registro, +puedes incluir un nombre de ámbito ``scope``. Si hay un registrador configurado para ese ámbito, +los mensajes de registro se dirigirán a esos ``loggers``. Por ejemplo:: + + use Cake\Log\Engine\FileLog; + + // Configura logs/shops.log para recibir todos los niveles, pero solo aquellos con ``scope`` + // `orders` y `payments`. + Log::setConfig('shops', [ + 'className' => FileLog::class, + 'path' => LOGS, + 'levels' => [], + 'scopes' => ['orders', 'payments'], + 'file' => 'shops.log', + ]); + + // Configura logs/payments.log para recibir todos los niveles, pero solo aquellos con ``scope`` + // `payments`. + Log::setConfig('payments', [ + 'className' => FileLog::class, + 'path' => LOGS, + 'levels' => [], + 'scopes' => ['payments'], + 'file' => 'payments.log', + ]); + + Log::warning('this gets written only to shops.log', ['scope' => ['orders']]); + Log::warning('this gets written to both shops.log and payments.log', ['scope' => ['payments']]); + +Los ``scopes`` también se pueden pasar como una cadena única o como una matriz indexada numéricamente. +Ten en cuenta que al usar esta forma, se limitará la capacidad de pasar más datos como contexto:: + + Log::warning('This is a warning', ['orders']); + Log::warning('This is a warning', 'payments'); + +.. note:: + Cuando ``scopes`` se establece como un arreglo vacío o null en la configuración de un ``logger``, + aceptará mensajes de cualquier ``scope``. Establecerlo como false solo coincidirá con mensajes sin ``scope``. + +.. _file-log: + +Guardando logs en Archivos +=========================== + +Como su nombre indica, ``FileLog`` escribe mensajes de registro en archivos. El nivel del mensaje +de registro que se está escribiendo determina el nombre del archivo en el que se almacena el mensaje. +Si no se proporciona un nivel, se utiliza :php:const:`LOG_ERR`, que escribe en el registro de errores. +La ubicación de registro predeterminada es **logs/$level.log**:: + + // Es ejecutado asi dentro de una clase CakePHP + $this->log("Something didn't work!"); + + // Se añadirá lo siguiente al archivo logs/error.log. + // 2007-11-02 10:22:02 Error: Something didn't work! + +El directorio configurado debe tener permisos de escritura por el usuario del servidor web para +que el registro funcione correctamente. + +Puedes configurar ubicaciones adicionales o alternativas para FileLog al configurar un registrador. +FileLog acepta un "path" que permite utilizar rutas personalizadas:: + + Log::setConfig('custom_path', [ + 'className' => 'File', + 'path' => '/path/to/custom/place/' + ]); + +El motor de ``FileLog`` toma las siguientes opciones: + +* ``size`` Se utiliza para implementar una rotación básica de archivos de registro. Si el tamaño + del archivo de registro alcanza el tamaño especificado, el archivo existente se renombra agregando + una marca de tiempo al nombre de archivo y se crea un nuevo archivo de registro. Puede ser un valor + entero en bytes o valores como '10MB', '100KB', etc. El valor predeterminado es 10MB. +* ``rotate`` Los archivos de registro se rotan un número especificado de veces antes de ser eliminados. + Si el valor es 0, se eliminan las versiones antiguas en lugar de rotarlas. El valor predeterminado es 10. +* ``mask`` Establece los permisos de archivo para los archivos creados. Si se deja vacío, se utilizan + los permisos predeterminados. + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + Los directorios faltantes se crearán automáticamente para evitar errores innecesarios + cuando se utiliza FileEngine. + +.. _syslog-log: + +Guardando logs en Syslog +========================= + +En entornos de producción, se recomienda encarecidamente configurar tu sistema para utilizar el +syslog en lugar del guardar los logs en archivos. Esto mejorará el rendimiento, ya que cualquier +escritura se realizará de manera (casi) no bloqueante y el ``logger`` del sistema operativo se +puede configurar de forma independiente para rotar archivos, preprocesar escrituras o +utilizar un almacenamiento completamente diferente para tus registros. + +Usar syslog es prácticamente como usar el motor de registro de archivos predeterminado, simplemente +necesitas especificar ``Syslog`` como el motor a utilizar para el registro de logs. El siguiente +fragmento de configuración reemplazará el ``logger`` predeterminado con syslog, esto se debe hacer +en el archivo **config/bootstrap.php**:: + + Log::setConfig('default', [ + 'engine' => 'Syslog' + ]); + +El arreglo de configuración aceptado para el motor de registro Syslog comprende +las siguientes claves: + +* ``format``: Una cadena de plantilla sprintf con dos marcadores de posición (placeholdes), + el primero para el nivel de error y el segundo para el mensaje en sí. Esta clave es + útil para agregar información adicional sobre el servidor o el proceso en el mensaje + registrado. Por ejemplo: ``%s -Servidor web 1 - %s`` se verá como + ``error - Servidor web 1 - Ocurrió un error en esta solicitud`` después de reemplazar + los placeholders. Esta opción está obsoleta. Deberías usar :ref:`logging-formatters` en su lugar. +* ``prefix``: Una cadena que se utilizará como prefijo para cada mensaje registrado. +* ``flag``: Una bandera tipo ``int`` que se usará para abrir la conexión al registro, + por defecto se usará ``LOG_ODELAY```. Consulta la documentación de ``openlog`` para ver más opciones. +* ``facility``: El espacio de registro a utilizar en syslog. Por defecto se utiliza ``LOG_USER``. + Consulta la documentación de ``syslog`` para ver más opciones. + +Creación de Motores de Logs +================================= + +Los motores de registro pueden formar parte de tu aplicación o de plugins. Por ejemplo, +si tuvieras un registro en base de datos llamado ``DatabaseLog``, como parte de tu aplicación +se colocaría en **src/Log/Engine/DatabaseLog.php**. Como parte de un plugin se colocaría en +**plugins/LoggingPack/src/Log/Engine/DatabaseLog.php**. Para configurar el motor de registro, +debes usar :php:meth:`Cake\\Log\\Log::setConfig()`. Por ejemplo, la configuración de nuestro +DatabaseLog se vería así:: + + // Para src/Log + Log::setConfig('otherFile', [ + 'className' => 'Database', + 'model' => 'LogEntry', + // ... + ]); + + // Para el plugin llamado LoggingPack + Log::setConfig('otherFile', [ + 'className' => 'LoggingPack.Database', + 'model' => 'LogEntry', + // ... + ]); + +Al configurar un motor de registro, el parámetro ``className`` se utiliza para localizar +y cargar el controlador de registro. Todas las demás propiedades de configuración se pasan +al constructor del motor de registro como un array.:: + + namespace App\Log\Engine; + use Cake\Log\Engine\BaseLog; + + class DatabaseLog extends BaseLog + { + public function __construct(array $config = []) + { + parent::__construct($config); + // ... + } + + public function log($level, string $message, array $context = []) + { + // Write to the database. + } + } + +CakePHP requiere que todos los motores de registro implementen Psr\Log\LoggerInterface. +La clase :php:class:`Cake\Log\Engine\BaseLog` es una forma sencilla de cumplir con la interfaz, +ya que solo requiere que implementes el método log(). + +.. _logging-formatters: + + +Formateadores de Logs +--------------------------- +Los formateadores de registro te permiten controlar cómo se formatean los mensajes de registro +de forma independiente al motor de almacenamiento. Cada motor de registro proporcionado por +defecto viene con un formateador configurado para mantener una salida compatible con versiones +anteriores. Sin embargo, puedes ajustar los formateadores para satisfacer tus requisitos. +Los formateadores se configuran junto al motor de registro:: + + use Cake\Log\Engine\SyslogLog; + use App\Log\Formatter\CustomFormatter; + + // Configuración de formato simple sin opciones. + Log::setConfig('error', [ + 'className' => SyslogLog::class, + 'formatter' => CustomFormatter::class, + ]); + + // Configurar un formateador con algunas opciones. + Log::setConfig('error', [ + 'className' => SyslogLog::class, + 'formatter' => [ + 'className' => CustomFormatter::class, + 'key' => 'value', + ], + ]); + + +Para implementar tu propio formateador de registro, necesitas extender +``Cake\Log\Format\AbstractFormatter`` o una de sus subclases. El método principal que +debes implementar es ``format($level, $message, $context)`` que es responsable de +formatear los mensajes de log. + + +Log API +======= + +.. php:namespace:: Cake\Log + +.. php:class:: Log + +Una clase sencilla para escribir logs. + +.. php:staticmethod:: setConfig($key, $config) + + :param string $name: Nombre para el registro al que se está conectando, utilizado para + eliminar un registro más adelante. + :param array $config: Arreglo de configuración y argumentos del constructor para el ``logger``. + + Devuelve o establece la configuración de un ``logger``. Para mas información ver :ref:`log-configuration`. + +.. php:staticmethod:: configured() + + :returns: Arreglo de los ``loggers`` configurados + + Devuelve los nombres de los ``loggers`` configurados. + +.. php:staticmethod:: drop($name) + + :param string $name: Nombre del ``logger`` del que ya no deseas recibir mensajes. + +.. php:staticmethod:: write($level, $message, $scope = []) + + Escribe un mensaje en todos los ``loggers`` configurados + ``$level`` indica el nivel del mensaje de registro que se está creando. + ``$message`` es el mensaje de la entrada del registro que se está escribiendo. + ``$scope`` es el(los) ámbito(s) en el que se está creando un mensaje de registro. + +.. php:staticmethod:: levels() + + +Llama a este método sin argumentos, por ejemplo: `Log::levels()` para obtener +la configuración actual del nivel. + + +Métodos de conveniencia +------------------------ + +Se agregaron los siguientes métodos útiles para registrar `$message` con el nivel +de registro apropiado. + +.. php:staticmethod:: emergency($message, $scope = []) +.. php:staticmethod:: alert($message, $scope = []) +.. php:staticmethod:: critical($message, $scope = []) +.. php:staticmethod:: error($message, $scope = []) +.. php:staticmethod:: warning($message, $scope = []) +.. php:staticmethod:: notice($message, $scope = []) +.. php:staticmethod:: info($message, $scope = []) +.. php:staticmethod:: debug($message, $scope = []) + +Logging Trait +============== + +.. php:trait:: LogTrait + + Un ``trait`` que proporciona métodos abreviados para el registro de mensajes. + +.. php:method:: log($msg, $level = LOG_ERR) + + Agregar un mensaje al log. De forma predeterminada, los mensajes se registran + como mensajes de ERROR. + + +Usando Monolog +================ + +Monolog es una librería de logging popular en PHP. Dado que implementa las mismas interfaces +que los ``loggers`` de CakePHP, puedes usarlos en tu aplicación como el ``logger`` predeterminado. + +Una vez instalado Monolog utilizando composer, configura el ``logger`` usando el método +``Log::setConfig()``:: + + // config/bootstrap.php + + use Monolog\Logger; + use Monolog\Handler\StreamHandler; + + Log::setConfig('default', function () { + $log = new Logger('app'); + $log->pushHandler(new StreamHandler('ruta/a/tu/combined.log')); + return $log; + }); + + // Opcionalmente deja de usar los ``loggers`` predeterminados que ahora son redundantes. + Log::drop('debug'); + Log::drop('error'); + +Utiliza métodos similares si deseas configurar un ``logger`` diferente para tu consola:: + + // config/bootstrap_cli.php + + use Monolog\Logger; + use Monolog\Handler\StreamHandler; + + Log::setConfig('default', function () { + $log = new Logger('cli'); + $log->pushHandler(new StreamHandler('ruta/a/tu/combined-cli.log')); + return $log; + }); + + // Opcionalmente deja de usar los ``logger`` predeterminados redundantes para la línea de comando. + Configure::delete('Log.debug'); + Configure::delete('Log.error'); + +.. note:: - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + Cuando uses un ``logger`` específico para la consola, asegúrate de configurar condicionalmente tu ``logger`` de aplicación. + Esto evitará entradas de registro duplicadas. .. meta:: :title lang=es: Logging - :description lang=es: Log CakePHP data to the disk to help debug your application over longer periods of time. - :keywords lang=es: cakephp logging,log errors,debug,logging data,cakelog class,ajax logging,soap logging,debugging,logs + :description lang=en: Registra datos de CakePHP a disco para ayudar a depurar la aplicación a lo largo de largos períodos de tiempo + :keywords lang=en: cakephp logging,log errors,debug,logging data,cakelog class,ajax logging,soap logging,debugging,logs, bitácora de eventos, registro de datos, registro, depuración diff --git a/es/core-libraries/number.rst b/es/core-libraries/number.rst index e28d122311..5edea0903e 100644 --- a/es/core-libraries/number.rst +++ b/es/core-libraries/number.rst @@ -1,20 +1,354 @@ -Number -###### +Clase Number +############ .. php:namespace:: Cake\I18n .. php:class:: Number -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +Si necesitas las funcionalidades de :php:class:`NumberHelper` fuera de una vista, utiliza la clase ``Number``:: - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + namespace App\Controller; + + use Cake\I18n\Number; + + class UsersController extends AppController + { + public function initialize(): void + { + parent::initialize(); + $this->loadComponent('Authentication.Authentication'); + } + + public function afterLogin() + { + $identity = $this->Authentication->getIdentity(); + $storageUsed = $identity->storage_used; + if ($storageUsed > 5000000) { + // Notificar a los usuarios de su cuota + $this->Flash->success(__('Estás usando {0} de almacenamiento', Number::toReadableSize($storageUsed))); + } + } + } + +.. start-cakenumber + +Todas estas funciones devuelven el número formateado; no imprimen automáticamente la salida en la vista. + +Formato de Valores Monetarios +============================= + +.. php:method:: currency(mixed $value, string $currency = null, array $options = []) + +Este método se utiliza para mostrar un número en formatos de moneda comunes (EUR, GBP, USD), basándose en el código de moneda de tres letras ISO 4217. Su uso en una vista se ve así:: + + // Llamado como NumberHelper + echo $this->Number->currency($value, $currency); + + // Llamado como Number + echo Number::currency($value, $currency); + +El primer parámetro, ``$value``, debería ser un número de punto flotante que representa la cantidad de dinero que estás expresando. El segundo parámetro es una cadena utilizada para elegir un esquema de formato de moneda predefinido: + ++---------------------+----------------------------------------------------+ +| $currency | 1234,56, formateado por tipo de moneda | ++=====================+====================================================+ +| EUR | €1.234,56 | ++---------------------+----------------------------------------------------+ +| GBP | £1.234,56 | ++---------------------+----------------------------------------------------+ +| USD | $1.234,56 | ++---------------------+----------------------------------------------------+ + +El tercer parámetro es un arreglo de opciones para definir aún más la salida. Las siguientes opciones están disponibles: + ++---------------------+----------------------------------------------------+ +| Opción | Descripción | ++=====================+====================================================+ +| before | Texto para mostrar antes del número formateado. | ++---------------------+----------------------------------------------------+ +| after | Texto para mostrar después del número formateado. | ++---------------------+----------------------------------------------------+ +| zero | El texto a usar para los valores cero; puede ser | +| | una cadena o un número, por ejemplo, 0, '¡Gratis!'.| ++---------------------+----------------------------------------------------+ +| places | Número de lugares decimales a usar, por ejemplo, 2 | ++---------------------+----------------------------------------------------+ +| precision | Número máximo de lugares decimales a usar, | +| | por ejemplo, 2. | ++---------------------+----------------------------------------------------+ +| locale | El nombre de la localidad a usar para formatear | +| | el número, por ejemplo, "es_ES". | ++---------------------+----------------------------------------------------+ +| fractionSymbol | Cadena a usar para números fraccionarios, por | +| | ejemplo, 'centavos'. | ++---------------------+----------------------------------------------------+ +| fractionPosition | Ya sea 'antes' o 'después' para colocar el símbolo | +| | fraccionario. | ++---------------------+----------------------------------------------------+ +| pattern | Un patrón de número ICU para usar para formatear el| +| | número, por ejemplo, #,###.00. | ++---------------------+----------------------------------------------------+ +| useIntlCode | Establecer en ``true`` para reemplazar el símbolo | +| | de moneda con el código de moneda internacional. | ++---------------------+----------------------------------------------------+ + +Si el valor de ``$currency`` es ``null``, la moneda predeterminada se recuperará de +:php:meth:`Cake\\I18n\\Number::defaultCurrency()`. Para formatear monedas en un +formato de contabilidad, debes establecer el formato de la moneda:: + + Number::setDefaultCurrencyFormat(Number::FORMAT_CURRENCY_ACCOUNTING); + +Configurar la Moneda Predeterminada +=================================== + +.. php:method:: setDefaultCurrency($currency) + +Configura la moneda predeterminada. Esto evita la necesidad de pasar siempre la +moneda a :php:meth:`Cake\\I18n\\Number::currency()` y cambiar todas las +salidas de moneda configurando otro valor predeterminado. Si ``$currency`` se establece en ``null``, +se eliminará el valor almacenado actualmente. + +Obtener la Moneda Predeterminada +================================ + +.. php:method:: getDefaultCurrency() + +Obtén la moneda predeterminada. Si la moneda predeterminada se configuró anteriormente utilizando +``setDefaultCurrency()``, se devolverá ese valor. De forma predeterminada, recuperará el valor de la ini de ``intl.default_locale`` si está configurado y ``'en_US'`` si no lo está. + +Formato de Números de Punto Flotante +==================================== + +.. php:method:: precision(float $value, int $precision = 3, array $options = []) + +Este método muestra un número con la cantidad especificada de precisión (lugares decimales). Se redondeará para mantener el +nivel de precisión definido. :: + + // Llamado como NumberHelper + echo $this->Number->precision(456.91873645, 2); + + // Salida + 456.92 + + // Llamado como Number + echo Number::precision(456.91873645, 2); + +Formato de Porcentajes +====================== + +.. php:method:: toPercentage(mixed $value, int $precision = 2, array $options = []) + ++---------------------+----------------------------------------------------+ +| Opción | Descripción | ++=====================+====================================================+ +| multiply | Booleano para indicar si el valor debe ser | +| | multiplicado por 100. Útil para porcentajes | +| | decimales. | ++---------------------+----------------------------------------------------+ + +Al igual que :php:meth:`Cake\\I18n\\Number::precision()`, este método formatea un número +según la precisión proporcionada (donde los números se redondean para cumplir con la +precisión dada). Adicionalmente, también expresa el número como un porcentaje +y agrega un signo de porcentaje a la salida. :: + + // Llamado como NumberHelper. Salida: 45.69% + echo $this->Number->toPercentage(45.691873645); + + // Llamado como Number. Salida: 45.69% + echo Number::toPercentage(45.691873645); + + // Llamado con multiplicar. Salida: 45.7% + echo Number::toPercentage(0.45691, 1, [ + 'multiply' => true + ]); + +Interactuar con Valores Legibles para Humanos +============================================= + +.. php:method:: toReadableSize(string $size) + +Este método formatea tamaños de datos en formas legibles para humanos. Proporciona +una forma abreviada de convertir bytes a KB, MB, GB y TB. El tamaño se +muestra con un nivel de precisión de dos dígitos, de acuerdo con el tamaño +de los datos suministrados (es decir, los tamaños más altos se expresan en términos más grandes):: + + // Llamado como NumberHelper + echo $this->Number->toReadableSize(0); // 0 Byte + echo $this->Number->toReadableSize(1024); // 1 KB + echo $this->Number->toReadableSize(1321205.76); // 1.26 MB + echo $this->Number->toReadableSize(5368709120); // 5 GB + + // Llamado como Number + echo Number::toReadableSize(0); // 0 Byte + echo Number::toReadableSize(1024); // 1 KB + echo Number::toReadableSize(1321205.76); // 1.26 MB + echo Number::toReadableSize(5368709120); // 5 GB + +Formato de Números +================== + +.. php:method:: format(mixed $value, array $options = []) + +Este método te brinda mucho más control sobre el formato de +números para usar en tus vistas (y se utiliza como el método principal por +la mayoría de los otros métodos de NumberHelper). Usar este método puede +verse así:: + + // Llamado como NumberHelper + $this->Number->format($value, $options); + + // Llamado como Number + Number::format($value, $options); + +El parámetro ``$value`` es el número que estás planeando +formatear para la salida. Sin opciones proporcionadas, el número +1236.334 se mostraría como 1,236. Ten en cuenta que la precisión predeterminada es +cero decimales. + +El parámetro ``$options`` es donde reside la verdadera magia para este método. + +- Si pasas un entero, este se convierte en la cantidad de precisión + o lugares para la función. +- Si pasas un arreglo asociado, puedes usar las siguientes claves: + ++---------------------+----------------------------------------------------+ +| Opción | Descripción | ++=====================+====================================================+ +| places | Número de lugares decimales a usar, por ejemplo, 2 | ++---------------------+----------------------------------------------------+ +| precision | Número máximo de lugares decimales a usar, por | +| | ejemplo, 2. | ++---------------------+----------------------------------------------------+ +| pattern | Un patrón de número ICU para usar para formatear el| +| | número, por ejemplo, #,###.00. | ++---------------------+----------------------------------------------------+ +| locale | El nombre de la localidad a usar para formatear el | +| | número, por ejemplo, "es_ES". | ++---------------------+----------------------------------------------------+ +| before | Texto para mostrar antes del número formateado. | ++---------------------+----------------------------------------------------+ +| after | Texto para mostrar después del número formateado. | ++---------------------+----------------------------------------------------+ + +Ejemplo:: + + // Llamado como NumberHelper + echo $this->Number->format('123456.7890', [ + 'places' => 2, + 'before' => '¥ ', + 'after' => ' !' + ]); + // Salida '¥ 123,456.79 !' + + echo $this->Number->format('123456.7890', [ + 'locale' => 'fr_FR' + ]); + // Salida '123 456,79 !' + + // Llamado como Number + echo Number::format('123456.7890', [ + 'places' => 2, + 'before' => '¥ ', + 'after' => ' !' + ]); + // Salida '¥ 123,456.79 !' + + echo Number::format('123456.7890', [ + 'locale' => 'fr_FR' + ]); + // Salida '123 456,79 !' + +.. php:method:: ordinal(mixed $value, array $options = []) + +Este método mostrará un número ordinal. + +Ejemplos:: + + echo Number::ordinal(1); + // Salida '1st' + + echo Number::ordinal(2); + // Salida '2nd' + + echo Number::ordinal(2, [ + 'locale' => 'fr_FR' + ]); + // Salida '2e' + + echo Number::ordinal(410); + // Salida '410th' + +Diferencias en el Formato +========================= + +.. php:method:: formatDelta(mixed $value, array $options = []) + +Este método muestra diferencias en el valor como un número con signo:: + + // Llamado como NumberHelper + $this->Number->formatDelta($value, $options); + + // Llamado como Number + Number::formatDelta($value, $options); + +El parámetro ``$value`` es el número que estás planeando +formatear para la salida. Sin opciones proporcionadas, el número +1236.334 se mostraría como 1,236. Ten en cuenta que la precisión predeterminada es +cero decimales. + +El parámetro ``$options`` toma las mismas claves que :php:meth:`Number::format()` en sí: + ++---------------------+----------------------------------------------------+ +| Opción | Descripción | ++=====================+====================================================+ +| places | Número de lugares decimales a usar, por ejemplo, 2 | ++---------------------+----------------------------------------------------+ +| precision | Número máximo de lugares decimales a usar, por | +| | ejemplo, 2. | ++---------------------+----------------------------------------------------+ +| locale | El nombre de la localidad a usar para formatear el | +| | número, por ejemplo, "es_ES". | ++---------------------+----------------------------------------------------+ +| before | Texto para mostrar antes del número formateado. | ++---------------------+----------------------------------------------------+ +| after | Texto para mostrar después del número formateado. | ++---------------------+----------------------------------------------------+ + +Ejemplo:: + + // Llamado como NumberHelper + echo $this->Number->formatDelta('123456.7890', [ + 'places' => 2, + 'before' => '[', + 'after' => ']' + ]); + // Salida '[+123,456.79]' + + // Llamado como Number + echo Number::formatDelta('123456.7890', [ + 'places' => 2, + 'before' => '[', + 'after' => ']' + ]); + // Salida '[+123,456.79]' + +.. end-cakenumber + +Configurar Formateadores +======================== + +.. php:method:: config(string $locale, int $type = NumberFormatter::DECIMAL, array $options = []) + +Este método te permite configurar valores predeterminados del formateador que persisten en llamadas +a varios métodos. + +Ejemplo:: + + Number::config('es_ES', \NumberFormatter::CURRENCY, [ + 'pattern' => '#,##,##0' + ]); - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. .. meta:: - :title lang=es: NumberHelper - :description lang=es: The Number Helper contains convenience methods that enable display numbers in common formats in your views. - :keywords lang=es: number helper,currency,number format,number precision,format file size,format numbers + :title lang=es: Clase Number + :keywords lang=es: number,currency,number format,number precision,format file size,format numbers diff --git a/es/console-and-shells/plugin-shell.rst b/es/core-libraries/plugin.rst similarity index 66% rename from es/console-and-shells/plugin-shell.rst rename to es/core-libraries/plugin.rst index cb8566496e..64f10b13d0 100644 --- a/es/console-and-shells/plugin-shell.rst +++ b/es/core-libraries/plugin.rst @@ -1,6 +1,4 @@ -.. _plugin-shell: - -Plugin Shell +Clase Plugin ############ .. note:: @@ -8,12 +6,8 @@ Plugin Shell página. Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve - this Doc** para proponer directamente los cambios. + `Github `_ o utilizar el botón + **Improve this Doc** para proponer directamente los cambios. Usted puede hacer referencia a la versión en Inglés en el menú de selección superior para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Plugin Shell - :keywords lang=es: api docs,shell,plugin,load,unload diff --git a/es/deployment.rst b/es/deployment.rst index 4f71474555..1421dfa20f 100644 --- a/es/deployment.rst +++ b/es/deployment.rst @@ -1,15 +1,140 @@ -ES - Deployment -############### +Despliegue +########## -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +Una vez que tu aplicación esté lista para ser desplegada, hay algunas cosas que debes hacer. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +Mover archivos +============== - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Puedes clonar tu repositorio en tu servidor de producción y luego seleccionar la +revisión/etiqueta que deseas ejecutar. Luego, ejecuta ``composer install``. Aunque esto requiere +un cierto conocimiento sobre git y una instalación existente de ``git`` y ``composer``, +este proceso se encargará de las dependencias de las bibliotecas y los permisos de archivos y carpetas. + +Ten en cuenta que al desplegar a través de FTP deberás corregir los permisos de archivo y +carpeta. + +También puedes utilizar esta técnica de despliegue para configurar un servidor de pruebas o demostración +(preproducción) y mantenerlo sincronizado con tu entorno local. + +Ajustar la configuración +======================== + +Querrás hacer algunos ajustes en la configuración de tu aplicación para +un entorno de producción. El valor de ``debug`` es extremadamente importante. +Al desactivar debug = ``false`` se deshabilitan una serie de características de desarrollo que no deberían +ser expuestas a Internet en general. Deshabilitar debug cambia las siguientes +características: + +* Los mensajes de depuración, creados con :php:func:`pr()`, :php:func:`debug()` y :php:func:`dd()`, + están deshabilitados. +* La duración de las cachés básicas de CakePHP se establece en 365 días, en lugar de 10 segundos, + como en desarrollo. +* Las vistas de errores son menos informativas y se muestran páginas de error genéricas + en lugar de mensajes de error detallados con trazas de pila. +* Los avisos y errores de PHP no se muestran. + +Además de lo anterior, muchos complementos y extensiones de la aplicación usan ``debug`` +para modificar su comportamiento. + +Puedes utilizar una variable de entorno para establecer dinámicamente el nivel de depuración +entre entornos. Esto evitará desplegar una aplicación con debug +``true`` y también te ahorrará tener que cambiar el nivel de depuración cada vez +antes de desplegar en un entorno de producción. + +Por ejemplo, puedes establecer una variable de entorno en tu configuración de Apache:: + + SetEnv CAKEPHP_DEBUG 1 + +Y luego puedes establecer dinámicamente el nivel de depuración en **app_local.php**:: + + $debug = (bool)getenv('CAKEPHP_DEBUG'); + + return [ + 'debug' => $debug, + ..... + ]; + +Se recomienda que coloques la configuración que se comparte en todas +los entornos de tu aplicación en **config/app.php**. Para la configuración que +varía entre entornos, utiliza **config/app_local.php** o variables de entorno. + +Verificar tu Seguridad +====================== + +Si estás lanzando tu aplicación al mundo, es una buena idea asegurarte de que no tenga ningun problema de seguridad obvio: + +* Asegúrate de estar usando el componente o middleware :ref:`csrf-middleware`. +* Puedes habilitar el componente :doc:`/controllers/components/form-protection`. + Puede ayudar a prevenir varios tipos de manipulación de formularios y reducir la posibilidad + de problemas de asignación masiva. +* Asegúrate de que tus modelos tengan las reglas de :doc:`/core-libraries/validation` correctas + habilitadas. +* Verifica que solo tu directorio ``webroot`` sea públicamente visible y que tus + secretos (como tu sal de aplicación y cualquier clave de seguridad) sean privados y únicos + también. + +Establecer la Raíz (Document Root) +================================== + +Establecer correctamente la raíz en tu aplicación es un paso importante para +mantener tanto tu código como tu aplicación seguros. Las aplicaciones de CakePHP +deben tener la raíz establecida en el ``webroot`` de la aplicación. Esto +hace que los archivos de aplicación y configuración sean inaccesibles a través de una URL. +Establecer la raíz es diferente para diferentes servidores web. Consulta la +documentación de :ref:`url-rewriting` para obtener información específica del servidor web. + +En todos los casos, querrás establecer la raiz del host virtual/dominio en +``webroot/``. Esto elimina la posibilidad de que se ejecuten archivos fuera del directorio raíz. + +.. _symlink-assets: + +Mejora el Rendimiento de tu Aplicación +======================================= + +La carga de clases puede llevarse una gran parte del tiempo de procesamiento de tu aplicación. +Para evitar este problema, se recomienda que ejecutes este comando en tu servidor de producción una vez que la aplicación esté implementada:: + + php composer.phar dumpautoload -o + +Dado que manejar los archivos estáticos, como imágenes, archivos JavaScript y CSS de +los complementos, a través del ``Dispatcher`` es increíblemente ineficiente, se recomienda encarecidamente crear enlaces simbólicos para producción. Esto se puede hacer usando +el comando ``plugin``:: + + bin/cake plugin assets symlink + +El comando anterior creará enlaces simbólicos del directorio ``webroot`` de todos los complementos cargados +a la ruta adecuada en el directorio ``webroot`` de la aplicación. + +Si tu sistema de archivos no permite crear enlaces simbólicos, los directorios se copiarán en lugar de enlazarse. También puedes copiar explícitamente los directorios usando:: + + bin/cake plugin assets copy + +CakePHP utiliza internamente ``assert()`` para proporcionar comprobación de tipos en tiempo de ejecución y +proporcionar mejores mensajes de error durante el desarrollo. Puedes hacer que PHP omita estas +comprobaciones ``assert()`` actualizando tu ``php.ini`` para incluir: + +.. code-block:: ini + + ; Desactivar la generación de código assert(). + zend.assertions = -1 + +Omitir la generación de código para ``assert()`` proporcionará un rendimiento de ejecución más rápido, +y se recomienda para aplicaciones que tienen una buena cobertura de pruebas o que están +usando un analizador estático. + +Desplegar una actualización +============================ + +En cada implementación es probable que tengas algunas tareas para coordinar en tu servidor web. Algunas tareas típicas son: + +1. Instalar dependencias con ``composer install``. Evita usar ``composer + update`` al hacer implementaciones, ya que podrías obtener versiones inesperadas de paquetes. +2. Ejecutar `migraciones de base de datos `__ con el complemento Migrations + u otra herramienta. +3. Limpiar la caché del esquema del modelo con ``bin/cake schema_cache clear``. La página :doc:`/console-commands/schema-cache` + tiene más información sobre este comando. .. meta:: - :title lang=es: Deployment - :keywords lang=es: stack traces,application extensions,set document,installation documentation,development features,generic error,document root,func,debug,caches,error messages,configuration files,webroot,deployment,cakephp,applications + :title lang=es: Despliegue + :keywords lang=en: stack traces,application extensions,set document,installation documentation,development features,generic error,document root,func,debug,caches,error messages,configuration files,webroot,deployment,cakephp,applications diff --git a/es/development/application.rst b/es/development/application.rst new file mode 100644 index 0000000000..17dfbd0ad3 --- /dev/null +++ b/es/development/application.rst @@ -0,0 +1,13 @@ +Clase Application +################# + +.. note:: + La documentación no es compatible actualmente con el idioma español en esta + página. + + Por favor, siéntase libre de enviarnos un pull request en + `Github `_ o utilizar el botón + **Improve this Doc** para proponer directamente los cambios. + + Usted puede hacer referencia a la versión en Inglés en el menú de selección + superior para obtener información sobre el tema de esta página. diff --git a/es/development/configuration.rst b/es/development/configuration.rst index 322d2a7453..a607285155 100644 --- a/es/development/configuration.rst +++ b/es/development/configuration.rst @@ -1,15 +1,504 @@ -Configuration +Configuración ############# +Aunque las convenciones eliminan la necesidad de configurar todas las partes de CakePHP, +todavía necesitarás configurar algunas cosas, como las credenciales de tu base de datos. + +Además, existen opciones de configuración opcionales que te permiten cambiar los valores +y las implementaciones predeterminadas por otros personalizados para tu aplicación. + +.. index:: app.php, app_local.example.php + +.. index:: configuration + +Configurando tu Aplicación +============================ + +La configuración generalmente se almacena en archivos PHP o INI, y se carga durante el inicio +de la aplicación. CakePHP viene con un archivo de configuración por defecto, pero si es necesario, +puedes agregar archivos de configuración adicionales y cargarlos en el código de inicio de tu +aplicación. La clase :php:class:`Cake\\Core\\Configure` se utiliza para la configuración global, +y clases como ``Cache`` proporcionan métodos como ``setConfig()`` para hacer que la configuración +sea simple y transparente. + +El esqueleto de la aplicación incluye un archivo **config/app.php** que debería contener configuraciones +que no varían en los diversos entornos en los que se despliega tu aplicación. El archivo **config/app_local.php** +debería contener datos de configuración que varían entre los entornos y deben ser gestionados por +herramientas de gestión de configuración o tus herramientas de implementación. Ambos archivos hacen +referencia a variables de entorno a través de la función ``env()`` que permite establecer valores +de configuración a través del entorno del servidor. + +Cargar Archivos de Configuración Adicionales +--------------------------------------------- + +Si tu aplicación tiene muchas opciones de configuración, puede ser útil dividir la configuración +en varios archivos. Después de crear cada uno de los archivos en tu directorio **config/**, puedes +cargarlos en **bootstrap.php**:: + + use Cake\Core\Configure; + use Cake\Core\Configure\Engine\PhpConfig; + + Configure::setConfig('default', new PhpConfig()); + Configure::load('app', 'default', false); + Configure::load('other_config', 'default'); + +.. _environment-variables: + +Variables de Entorno +===================== + +Muchos proveedores de servicios en la nube modernos, como Heroku, te permiten definir variables de +entorno para datos de configuración. Puedes configurar tu aplicación CakePHP a través de variables +de entorno en el estilo de aplicación `12factor `. Las variables de entorno +permiten que tu aplicación sea fácil de gestionar cuando se implementa en varios entornos. + +Como puedes ver en tu archivo **app.php**, la función ``env()`` se utiliza para leer la configuración +del entorno y construir la configuración de la aplicación. CakePHP utiliza cadenas de conexión :term:`DSN` +para bases de datos, registros, transportes de correo electrónico y configuraciones de caché, lo que +te permite variar fácilmente estas bibliotecas en cada entorno. + +Para el desarrollo local, CakePHP utiliza `dotenv `_ para +recargar automáticamente las variables de entorno locales. Utiliza Composer para requerir esta biblioteca +y luego hay un bloque de código en ``bootstrap.php`` que debe descomentarse para aprovecharla. + +Encontrarás un archivo ``config/.env.example`` en tu aplicación. Al copiar este archivo en ``config/.env`` +y personalizar los valores, puedes configurar tu aplicación. + +Debes evitar incluir el archivo ``config/.env`` en tu repositorio y, en su lugar, utilizar +``config/.env.example`` como una plantilla con valores predeterminados para que todos +en tu equipo sepan qué variables de entorno se están utilizando y qué debe ir en cada una. + +Una vez que se hayan establecido tus variables de entorno, puedes usar ``env()`` para leer datos del entorno:: + + $debug = env('APP_DEBUG', false); + +El segundo valor pasado a la función env es el valor predeterminado. Este valor se utilizará si no existe una +variable de entorno para la clave dada. + +.. _general-configuration: + +Configuración General +---------------------- + +A continuación se muestra una descripción de las variables y cómo afectan a tu aplicación CakePHP. + +- **debug** + Cambia la salida de depuración de CakePHP. ``false`` = Modo de producción. No se muestran mensajes de error o advertencias. ``true`` = Se muestran errores y advertencias. + +- **App.namespace** + El espacio de nombres para encontrar las clases de la aplicación. + + .. note:: + + Al cambiar el espacio de nombres en tu configuración, también deberás actualizar tu archivo **composer.json** para usar este espacio de nombres. Además, crea un nuevo autoloader ejecutando ``php composer.phar dumpautoload``. + +- **App.baseUrl** + Descomenta esta definición si **no** planeas usar mod_rewrite de Apache con CakePHP. No olvides eliminar tus archivos .htaccess también. + +- **App.base** + El directorio base en el que reside la aplicación. Si es ``false``, se detectará automáticamente. Si no es ``false``, asegúrate de que tu cadena comience con un `/` y **NO** termine con un `/`. Por ejemplo, `/basedir` es un valor válido para App.base. + +- **App.encoding** + Define qué codificación utiliza tu aplicación. Esta codificación se utiliza para definir la codificación en las vistas y codificar entidades. Debería coincidir con los valores de codificación especificados para tu base de datos. + +- **App.webroot** + El directorio webroot. + +- **App.wwwRoot** + La ruta de archivo al directorio webroot. + +- **App.fullBaseUrl** + El nombre de dominio completamente cualificado (incluyendo el protocolo) hasta la raíz de tu aplicación. Se utiliza al generar URLs absolutas. Por defecto, este valor se genera utilizando la variable ``$_SERVER`` del entorno. Sin embargo, debes definirlo manualmente para optimizar el rendimiento o si te preocupa que las personas manipulen el encabezado "Host". En un contexto CLI (desde la línea de comandos), el `fullBaseUrl` no se puede leer de $_SERVER, ya que no hay un servidor web involucrado. Debes especificarlo tú mismo si necesitas generar URLs desde una terminal (por ejemplo, al enviar correos electrónicos). + +- **App.imageBaseUrl** + Ruta web al directorio público de imágenes dentro del webroot. Si estás utilizando un :term:`CDN`, debes configurar este valor con la ubicación del CDN. + +- **App.cssBaseUrl** + Ruta web al directorio público de CSS dentro del webroot. Si estás utilizando un :term:`CDN`, debes configurar este valor con la ubicación del CDN. + +- **App.jsBaseUrl** + Ruta web al directorio público de JavaScript dentro del webroot. Si estás utilizando un :term:`CDN`, debes configurar este valor con la ubicación del CDN. + +- **App.paths** + Configura rutas para recursos que no son de clase. Admite las subclaves ``plugins``, ``templates``, ``locales``, que permiten la definición de rutas para los archivos de plugins, plantillas de vista y archivos de traducción, respectivamente. + +- **App.uploadedFilesAsObjects** + Define si los archivos cargados se representan como objetos (``true``) o como arrays (``false``). Esta opción está habilitada de forma predeterminada. Consulta la sección :ref:`File Uploads ` en el capítulo de Objetos de Request & Response para obtener más información. + +- **Security.salt** + Una cadena aleatoria utilizada en el cifrado. Esta cadena también se utiliza como la sal de HMAC al hacer cifrado simétrico. + +- **Asset.timestamp** + Añade una marca de tiempo, que es la última vez que se modificó el archivo en particular, al final de las URLs de los archivos de activos (CSS, JavaScript, Imagen) cuando se utilizan los ayudantes adecuados. Valores válidos: + + - (bool) ``false`` - No hace nada (predeterminado) + - (bool) ``true`` - Añade la marca de tiempo cuando el modo de depuración es ``true`` + - (string) 'force' - Siempre añade la marca de tiempo. + +- **Asset.cacheTime** + Establece el tiempo de caché del archivo de activo. Esto determina el encabezado ``Cache-Control``, ``max-age`` y el tiempo de ``Expire`` del encabezado de HTTP para los activos. Esto puede tomar cualquier valor que la función `strtotime `_ tu versión de PHP pueda tomar. El valor predeterminado es ``+1 día``. + +Usar un CDN +----------- + +Para utilizar un CDN para cargar tus activos estáticos, cambia las variables ``App.imageBaseUrl``, ``App.cssBaseUrl``, ``App.jsBaseUrl`` para que apunten a la URI del CDN, por ejemplo: ``https://micdn.ejemplo.com/`` (nota la barra diagonal al final ``/``). + +Todas las imágenes, scripts y estilos cargados a través de HtmlHelper agregarán la ruta absoluta del CDN, coincidiendo con la misma ruta relativa utilizada en la aplicación. Ten en cuenta que hay un caso de uso específico cuando se utilizan activos basados en plugins: los plugins no utilizarán el prefijo del plugin cuando se utiliza una URI absoluta ``...BaseUrl``, por ejemplo, por defecto: + +* ``$this->Helper->assetUrl('TestPlugin.logo.png')`` resuelve a ``test_plugin/logo.png`` + +Si configuras ``App.imageBaseUrl`` como ``https://micdn.ejemplo.com/``: + +* ``$this->Helper->assetUrl('TestPlugin.logo.png')`` se resuelve a ``https://micdn.ejemplo.com/logo.png``. + +Configuración de la Base de Datos +--------------------------------- + +Consulta la :ref:`Configuración de la Base de Datos ` para obtener información sobre cómo configurar las conexiones a tu base de datos. + +Configuración de Caché +----------------------- + +Consulta la :ref:`Configuración de Caché ` para obtener información sobre cómo configurar la caché en CakePHP. + +Configuración de Manejo de Errores y Excepciones +------------------------------------------------ + +Consulta la :ref:`Configuración de Errores y Excepciones ` para obtener información sobre cómo configurar los manejadores de errores y excepciones. + +Configuración de Registro (Logs) +-------------------------------- + +Consulta la :ref:`Configuración de Registro ` para obtener información sobre cómo configurar el registro (logs) en CakePHP. + +Configuración de Correo Electrónico +------------------------------------ + +Consulta la :ref:`Configuración de Correo Electrónico ` para obtener información sobre cómo configurar preajustes de correo electrónico en CakePHP. + +Configuración de Sesión +------------------------ + +Consulta la :ref:`Configuración de Sesión ` para obtener información sobre cómo configurar el manejo de sesiones en CakePHP. + +Configuración de Enrutamiento +------------------------------ + +Consulta la :ref:`Configuración de Rutas ` para obtener más información sobre cómo configurar el enrutamiento y crear rutas para tu aplicación. + +.. _additional-class-paths: + +Rutas de Clases Adicionales +============================ + +Las rutas de clases adicionales se configuran a través de los cargadores automáticos que utiliza tu aplicación. Cuando utilizas `composer` para generar tu cargador automático, puedes hacer lo siguiente para proporcionar rutas alternativas para los controladores en tu aplicación:: + + "autoload": { + "psr-4": { + "App\\Controller\\": "/ruta/a/directorio/con/carpetas/de/controladores/", + "App\\": "src/" + } + } + +El ejemplo anterior establecería rutas para los espacios de nombres `App` y `App\Controller`. Se buscará la primera clave y, si esa ruta no contiene la clase/archivo, se buscará la segunda clave. También puedes asignar un solo espacio de nombres a múltiples directorios de la siguiente manera:: + + "autoload": { + "psr-4": { + "App\\": ["src/", "/ruta/a/directorio/"] + } + } + +Rutas de Plugins, Plantillas de Vista y Localizaciones +----------------------------------------------------------- + +Dado que los plugins, las plantillas de vista y las localizaciones no son clases, no pueden tener un cargador automático configurado. CakePHP proporciona tres variables de configuración para establecer rutas adicionales para estos recursos. En tu **config/app.php**, puedes configurar estas variables:: + + return [ + // Otras configuraciones + 'App' => [ + 'paths' => [ + 'plugins' => [ + ROOT . DS . 'plugins' . DS, + '/ruta/a/otros/plugins/', + ], + 'templates' => [ + ROOT . DS . 'templates' . DS, + ROOT . DS . 'templates2' . DS, + ], + 'locales' => [ + ROOT . DS . 'resources' . DS . 'locales' . DS, + ], + ], + ], + ] + +Las rutas deben terminar con un separador de directorio, o no funcionarán correctamente. + +Configuración de Inflexión +============================== + +Consulta la documentación de :ref:`inflection-configuration` para obtener más información. + +Clase Configure +=================== + +.. php:namespace:: Cake\Core + +.. php:class:: Configure + +La clase Configure de CakePHP se puede utilizar para almacenar y recuperar valores específicos de la aplicación o en +tiempo de ejecución. Sin embargo, debes tener cuidado, ya que esta clase te permite almacenar cualquier cosa y usarla +en cualquier parte de tu código, lo que puede ser una tentación para romper el patrón MVC para el que CakePHP fue diseñado. +El principal objetivo de la clase Configure es mantener variables centralizadas que puedan compartirse entre varios objetos. +Recuerda intentar seguir el principio "convención sobre configuración" para no terminar rompiendo la estructura MVC que +CakePHP proporciona. + +Escritura de Datos de Configuración +----------------------------------- + +.. php:staticmethod:: write($clave, $valor) + +Utiliza ``write()`` para almacenar datos en la configuración de la aplicación:: + + Configure::write('Company.name', 'Pizza, Inc.'); + Configure::write('Company.slogan', 'Pizza for your body and soul'); + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + La :term:`notación de punto` utilizada en el parámetro ``$clave`` se puede utilizar para organizar tus configuraciones en grupos lógicos. + +El ejemplo anterior también se podría escribir en una sola llamada:: + + Configure::write('Company', [ + 'name' => 'Pizza, Inc.', + 'slogan' => 'Pizza for your body and soul' + ]); + +Puedes utilizar ``Configure::write('debug', $boolean)`` para alternar entre los modos de depuración y producción sobre la marcha. + +.. note:: + + Cualquier cambio en la configuración realizado mediante ``Configure::write()`` se mantiene en memoria y no persistirá entre solicitudes. + +Lectura de Datos de Configuración +--------------------------------- + +.. php:staticmethod:: read($clave = null, $predeterminado = null) + +Se utiliza para leer datos de configuración de la aplicación. Si se proporciona una clave, se devolverán los datos. Usando nuestros ejemplos anteriores de ``write()``, podemos leer esos datos de la siguiente manera:: + + # Devuelve 'Pizza, Inc.' + Configure::read('Company.name'); + + # Devuelve 'Pizza for your body and soul' + Configure::read('Company.slogan'); + + Configure::read('Company'); + # Devuelve: + ['name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul']; + + # Devuelve 'fallback' ya que Company.nope no está definido. + Configure::read('Company.nope', 'fallback'); + +Si se deja el parámetro ``$clave`` como nulo, se devolverán todos los valores en Configure. + +.. php:staticmethod:: readOrFail($clave) + +Lee datos de configuración igual que :meth:`Cake\\Core\\Configure::read`, pero espera encontrar un par clave/valor. Si el par solicitado no existe, se lanzará una :class:`RuntimeException`. + + Configure::readOrFail('Company.name'); # Devuelve: 'Pizza, Inc.' + Configure::readOrFail('Company.geolocation'); # Lanzará una excepción + + Configure::readOrFail('Company'); + + # Devuelve: + ['name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul']; + +Comprobación para ver si los Datos de Configuración están Definidos +-------------------------------------------------------------------- + +.. php:staticmethod:: check($clave) + +Se utiliza para comprobar si una clave/ruta existe y tiene un valor distinto de nulo:: + + $existe = Configure::check('Company.name'); + +Eliminación de Datos de Configuración +------------------------------------- + +.. php:staticmethod:: delete($clave) + +Se utiliza para eliminar información de la configuración de la aplicación:: + + Configure::delete('Company.name'); + +Lectura y Eliminación de Datos de Configuración +------------------------------------------------ + +.. php:staticmethod:: consume($clave) + +Lee y elimina una clave de Configure. Esto es útil cuando deseas combinar la lectura y eliminación de valores en una sola operación. + +.. php:staticmethod:: consumeOrFail($clave) + +Consume datos de configuración de la misma manera que :meth:`Cake\\Core\\Configure::consume`, pero espera encontrar un par clave/valor. Si el par solicitado no existe, se lanzará una :class:`RuntimeException`. + + Configure::consumeOrFail('Company.name'); # Devuelve: 'Pizza, Inc.' + Configure::consumeOrFail('Company.geolocation'); # Lanzará una excepción + + Configure::consumeOrFail('Company'); + + # Devuelve: + ['name' => 'Pizza, Inc.', 'slogan' => 'Pizza for your body and soul'] + +Lectura y Escritura de Archivos de Configuración +------------------------------------------------ + +.. php:staticmethod:: setConfig($nombre, $motor) + +CakePHP viene con dos motores de archivos de configuración integrados. +:php:class:`Cake\\Core\\Configure\\Engine\\PhpConfig` es capaz de leer archivos +de configuración PHP, en el mismo formato que Configure ha leído históricamente. +:php:class:`Cake\\Core\\Configure\\Engine\\IniConfig` es capaz de leer archivos +de configuración ini. Consulta la `documentación de PHP `_ para +obtener más información sobre los detalles de los archivos ini. Para utilizar un +motor de configuración central, debes adjuntarlo a Configure utilizando :php::meth:`Configure::config()`:: + + use Cake\Core\Configure\Engine\PhpConfig; + + # Leer archivos de configuración desde config + Configure::config('default', new PhpConfig()); + + # Leer archivos de configuración desde otra ruta. + Configure::config('default', new PhpConfig('/ruta/a/tus/archivos/de/configuración/')); + +Puedes tener varios motores adjuntos a Configure, cada uno leyendo diferentes tipos o fuentes de archivos de configuración. Puedes interactuar con los motores adjuntos usando los métodos definidos en Configure. Para verificar qué alias de motor están adjuntos, puedes usar :meth:`Configure::configured()`:: + + # Obtén el array de alias para los motores adjuntos. + Configure::configured(); + + # Comprueba si un motor específico está adjunto + Configure::configured('default'); + +.. php:staticmethod:: drop($nombre) + +También puedes eliminar motores adjuntos. ``Configure::drop('default')`` eliminaría el alias del motor predeterminado. Cualquier intento futuro de cargar archivos de configuración con ese motor fallaría:: + + Configure::drop('default'); + +Carga de Archivos de Configuración +---------------------------------- + +.. php:staticmethod:: load($clave, $config = 'default', $merge = true) + +Una vez que hayas adjuntado un motor de configuración a Configure, puedes cargar archivos de configuración:: + + # Cargar my_file.php usando el objeto de motor 'default'. + Configure::load('my_file', 'default'); + +Los archivos de configuración cargados fusionan sus datos con la configuración en tiempo de ejecución existente en Configure. Esto te permite sobrescribir y agregar nuevos valores a la configuración en tiempo de ejecución existente. Al establecer ``$merge`` en ``true``, los valores nunca sobrescribirán la configuración existente. + +.. warning:: + Al fusionar archivos de configuración con `$merge = true`, la notación de puntos en las claves no se expande:: + + # config1.php + 'Clave1' => [ + 'Clave2' => [ + 'Clave3' => ['ClaveAnidada1' => 'Valor'], + ], + ], + + # config2.php + 'Clave1.Clave2' => [ + 'Clave3' => ['ClaveAnidada2' => 'Valor2'], + ] + + Configure::load('config1', 'default'); + Configure::load('config2', 'default', true); + + # Ahora Clave1.Clave2.Clave3 tiene el valor ['ClaveAnidada2' => 'Valor2'] + # en lugar de ['ClaveAnidada1' => 'Valor', 'ClaveAnidada2' => 'Valor2'] + +Creación o Modificación de Archivos de Configuración +----------------------------------------------------- + +.. php:staticmethod:: dump($clave, $config = 'default', $claves = []) + +Vuelca todos o algunos de los datos en Configure en un archivo o sistema de almacenamiento compatible con un motor de configuración. El formato de serialización lo decide el motor de configuración adjunto como $config. Por ejemplo, si el motor 'default' es una :class:`Cake\\Core\\Configure\\Engine\\PhpConfig`, el archivo generado será un archivo de configuración PHP que se puede cargar mediante el :class:`Cake\\Core\\Configure\\Engine\\PhpConfig` + +Dado que el motor 'default' es una instancia de PhpConfig. Guarda todos los datos en Configure en el archivo `mi_configuracion.php`:: + + Configure::dump('mi_configuracion', 'default'); + +Guarda solo la configuración de manejo de errores:: + + Configure::dump('error', 'default', ['Error', 'Exception']); + +``Configure::dump()`` se puede utilizar para modificar o sobrescribir archivos de configuración que se pueden leer con :meth:`Configure::load()` + +Almacenamiento de Configuración en Tiempo de Ejecución +------------------------------------------------------ + +.. php:staticmethod:: store($nombre, $configuracionCache = 'default', $datos = null) + +También puedes almacenar valores de configuración en tiempo de ejecución para usarlos en solicitudes futuras. Dado que configure solo recuerda valores para la solicitud actual, deberás almacenar cualquier información de configuración modificada si deseas usarla en solicitudes posteriores:: + + # Almacena la configuración actual en la clave 'usuario_1234' en la caché 'default'. + Configure::store('usuario_1234', 'default'); + +Los datos de configuración almacenados persisten en la configuración de caché con el nombre especificado. Consulta la documentación sobre :doc:`/core-libraries/caching` para obtener más información sobre el almacenamiento en caché. + +Restauración de Configuración en Tiempo de Ejecución +----------------------------------------------------- + +.. php:staticmethod:: restore($nombre, $configuracionCache = 'default') + +Una vez que hayas almacenado la configuración en tiempo de ejecución, probablemente necesitarás restaurarla para poder acceder a ella nuevamente. ``Configure::restore()`` hace precisamente eso:: + + # Restaura la configuración en tiempo de ejecución desde la caché. + Configure::restore('usuario_1234', 'default'); + +Al restaurar información de configuración, es importante restaurarla con la misma clave y configuración de caché que se usó para almacenarla. La información restaurada se fusiona con la configuración en tiempo de ejecución existente. + +Motores de Configuración +------------------------ + +CakePHP proporciona la capacidad de cargar archivos de configuración desde varias fuentes diferentes y cuenta con un sistema plugable para `crear tus propios motores de configuración +`__. Los motores de configuración integrados son: + +* `JsonConfig `__ +* `IniConfig `__ +* `PhpConfig `__ + +Por defecto, tu aplicación utilizará ``PhpConfig``. + +Desactivación de Tablas Genéricas +================================== + +Aunque utilizar clases de tabla genéricas, también llamadas auto-tablas, al crear rápidamente nuevas aplicaciones y hornear +modelos es útil, las clases de tabla genéricas pueden dificultar la depuración en algunos escenarios. + +Puedes verificar si se emitió alguna consulta desde una clase de tabla genérica a través del panel SQL de DebugKit. Si +aún tienes problemas para diagnosticar un problema que podría ser causado por las auto-tablas, puedes lanzar una excepción +cuando CakePHP utiliza implícitamente una ``Cake\ORM\Table`` genérica en lugar de tu clase concreta de la siguiente manera:: + + # En tu bootstrap.php + use Cake\Event\EventManager; + use Cake\Http\Exception\InternalErrorException; - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + $seEjecutaCakeBakeShell = (PHP_SAPI === 'cli' && isset($argv[1]) && $argv[1] === 'bake'); + if (!$seEjecutaCakeBakeShell) { + EventManager::instance()->on('Model.initialize', function($event) { + $subject = $event->getSubject(); + if (get_class($subject) === 'Cake\ORM\Table') { + $mensaje = sprintf( + 'Clase de tabla faltante o alias incorrecto al registrar la clase de tabla para la tabla de base de datos %s.', + $subject->getTable()); + throw new InternalErrorException($mensaje); + } + }); + } .. meta:: - :title lang=es: Configuration + :title lang=es: Configuración :keywords lang=es: finished configuration,legacy database,database configuration,value pairs,default connection,optional configuration,example database,php class,configuration database,default database,configuration steps,index database,configuration details,class database,host localhost,inflections,key value,database connection,piece of cake,basic web diff --git a/es/development/debugging.rst b/es/development/debugging.rst index fabadb0a3b..8314c84dc2 100644 --- a/es/development/debugging.rst +++ b/es/development/debugging.rst @@ -3,7 +3,7 @@ Depuración La depuración es una parte inevitable y necesaria de cualquier ciclo de desarrollo. Aunque CakePHP no ofrece ninguna herramienta que se conecte directamente -con algun IDE o editor, CakePHP proporciona varias herramientas para +con algún IDE o editor, CakePHP proporciona varias herramientas para asistirte en la depuración y exponer lo que se está ejecutando bajo el capó de tu aplicación. @@ -113,7 +113,7 @@ Generando seguimientos de pila .. php:staticmethod:: trace($options) Devuelve el seguimiento de pila actual. Cada línea de la pila incluye -cual método llama, incluyendo el archivo y la línea en la que se originó +cuál método llama, incluyendo el archivo y la línea en la que se originó la llamada:: // En PostsController::index() @@ -185,7 +185,7 @@ Kit De Depuración ================= DebugKit es un complemento que proporciona una serie de buenas herramientas de depuración. -Principalmente provee una barra de herramientas en el HTML +Principalmente, provee una barra de herramientas en el HTML renderizado, que porporciona una gran cantidad de información sobre tu aplicación y la solicitud actual. Ver el capítulo :doc:`/debug-kit` para saber cómo instalar y usar DebugKit. diff --git a/es/console-and-shells/repl.rst b/es/development/dependency-injection.rst similarity index 81% rename from es/console-and-shells/repl.rst rename to es/development/dependency-injection.rst index 0953970dfb..dbfb3a740a 100644 --- a/es/console-and-shells/repl.rst +++ b/es/development/dependency-injection.rst @@ -1,4 +1,4 @@ -Interactive Console (REPL) +Inyección de Dependencias ########################## .. note:: @@ -6,8 +6,8 @@ Interactive Console (REPL) página. Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve - this Doc** para proponer directamente los cambios. + `Github `_ o utilizar el botón + **Improve this Doc** para proponer directamente los cambios. Usted puede hacer referencia a la versión en Inglés en el menú de selección superior para obtener información sobre el tema de esta página. diff --git a/es/development/dispatch-filters.rst b/es/development/dispatch-filters.rst deleted file mode 100644 index 06f9e8df0c..0000000000 --- a/es/development/dispatch-filters.rst +++ /dev/null @@ -1,16 +0,0 @@ -Dispatcher Filters -################## - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: Dispatcher Filters - :description lang=es: Dispatcher filters are a middleware layer for CakePHP allowing to alter the request or response before it is sent - :keywords lang=es: middleware, filters, dispatcher, request, response, rack, application stack, events, beforeDispatch, afterDispatch, router diff --git a/es/development/errors.rst b/es/development/errors.rst index e0e93e9c99..113fe078b2 100644 --- a/es/development/errors.rst +++ b/es/development/errors.rst @@ -1,15 +1,641 @@ -Error & Exception Handling -########################## +Manejo de Errores y Excepciones +################################ + +Las aplicaciones de CakePHP vienen con la configuración predeterminada de manejo de errores y excepciones. +Los errores de PHP son capturados y mostrados o registrados. Las excepciones no capturadas se representan +automáticamente en páginas de error. + +.. _error-configuration: + +Configuración +============= + +La configuración de errores se realiza en el archivo **config/app.php** de tu aplicación. Por defecto, CakePHP +utiliza ``Cake\Error\ErrorTrap`` y ``Cake\Error\ExceptionTrap`` para manejar tanto errores de PHP como excepciones, +respectivamente. La configuración de errores te permite personalizar el manejo de errores para tu aplicación. +Las siguientes opciones son compatibles: + +* ``errorLevel`` - int - El nivel de errores que te interesa capturar. Usa las constantes de error de PHP integradas + y las máscaras de bits para seleccionar el nivel de error que te interesa. Consulta :ref:`deprecation-warnings` + para deshabilitar advertencias de obsolescencia. +* ``trace`` - bool - Incluir trazas para errores en los archivos de registro. Las trazas se incluirán en el + registro después de cada error. Esto es útil para encontrar dónde/cuándo se están generando los errores. +* ``exceptionRenderer`` - string - La clase responsable de representar excepciones no capturadas. Si eliges + una clase personalizada, debes colocar el archivo para esa clase en **src/Error**. Esta clase debe implementar + un método ``render()``. +* ``log`` - bool - Cuando es ``true``, las excepciones y sus trazas se registrarán en :php:class:`Cake\\Log\\Log`. +* ``skipLog`` - array - Un array de nombres de clases de excepción que no deben ser registrados. Esto es útil para + eliminar mensajes de registro comunes pero poco interesantes, como NotFoundExceptions. +* ``extraFatalErrorMemory`` - int - Establece el número de megabytes para aumentar el límite de memoria cuando + se encuentra un error fatal. Esto permite espacio para completar el registro o el manejo de errores. +* ``logger`` (antes de la versión 4.4.0, usa ``errorLogger``) - ``Cake\Error\ErrorLoggerInterface`` - La clase + responsable de registrar errores y excepciones no controladas. Por defecto, es ``Cake\Error\ErrorLogger``. +* ``errorRenderer`` - ``Cake\Error\ErrorRendererInterface`` - La clase responsable de representar errores. Se + elige automáticamente en función del SAPI de PHP. +* ``ignoredDeprecationPaths`` - array - Una lista de rutas compatibles con la sintaxis Glob que deben ignorar errores de + obsolescencia. Añadido en la versión 4.2.0 + +Por defecto, los errores de PHP se muestran cuando ``debug`` es ``true``, y se registran cuando debug es ``false``. +El manejador de errores fatal se llamará independientemente del nivel de ``debug`` o la configuración de ``errorLevel``, +pero el resultado será diferente según el nivel de ``debug``. El comportamiento predeterminado para errores fatales es +mostrar una página de error interno del servidor (``debug`` deshabilitado) o una página con el mensaje, archivo y línea (``debug`` habilitado). .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + Si utilizas un manejador de errores personalizado, las opciones compatibles dependerán de tu manejador. + +.. _deprecation-warnings: + +Advertencias de Obsolescencia +============================== + +CakePHP utiliza advertencias de obsolescencia para indicar cuándo se ha marcado como obsoleta alguna característica. También +recomendamos este sistema para su uso en tus plugins y código de aplicación cuando sea útil. Puedes activar advertencias de +obsolescencia con ``deprecationWarning()``:: + + deprecationWarning('5.0', 'El método example() está obsoleto. Usa getExample() en su lugar.'); + +Al actualizar CakePHP o plugins, es posible que te encuentres con nuevas advertencias de obsolescencia. Puedes desactivar +temporalmente las advertencias de obsolescencia de varias formas: + +#. Usar la configuración ``Error.errorLevel`` con ``E_ALL ^ E_USER_DEPRECATED`` para ignorar *todas* las advertencias de + obsolescencia. +#. Usar la opción de configuración ``Error.ignoredDeprecationPaths`` para ignorar advertencias con expresiones compatibles + con la sintaxis Glob. Por ejemplo:: + + 'Error' => [ + 'ignoredDeprecationPaths' => [ + 'vendors/company/contacts/*', + 'src/Models/*', + ], + ], + + Ignoraría todas las advertencias de obsolescencia de tu directorio ``Models`` y el plugin ``Contacts`` en tu aplicación. + +Cambiar el Manejo de Excepciones +================================= + +El manejo de excepciones en CakePHP ofrece varias formas de personalizar cómo se manejan las excepciones. Cada enfoque te brinda +diferentes niveles de control sobre el proceso de manejo de excepciones. + +#. *Escucha eventos* Esto te permite recibir notificaciones a través de eventos de CakePHP cuando se han manejado errores y excepciones. +#. *Plantillas personalizadas* Esto te permite cambiar las plantillas de vista renderizadas como lo harías con cualquier otra + plantilla en tu aplicación. +#. *Controlador personalizado* Esto te permite controlar cómo se renderizan las páginas de excepción. +#. *ExceptionRenderer personalizado* Esto te permite controlar cómo se realizan las páginas de excepción y el registro. +#. *Crea y registra tus propios manejadores* Esto te brinda control total sobre cómo se manejan, registran y representan los errores y + excepciones. Utiliza ``Cake\Error\ExceptionTrap`` y ``Cake\Error\ErrorTrap`` como referencia cuando implementes tus manejadores. + +Escuchar Eventos +================ + +Los manejadores ``ErrorTrap`` y ``ExceptionTrap`` activarán eventos de CakePHP cuando manejan errores. Puedes escuchar el evento ``Error.beforeRender`` para ser notificado de los errores de PHP. El evento ``Exception.beforeRender`` se desencadena cuando se maneja una excepción:: + + $errorTrap = new ErrorTrap(Configure::read('Error')); + $errorTrap->getEventManager()->on( + 'Error.beforeRender', + function (EventInterface $event, PhpError $error) { + // haz lo que necesites + } + ); + +Dentro de un manejador ``Error.beforeRender``, tienes algunas opciones: + +* Detener el evento para evitar la representación. +* Devolver una cadena para omitir la representación y usar la cadena proporcionada en su lugar. + +Dentro de un manejador ``Exception.beforeRender``, también tienes algunas opciones: + +* Detener el evento para evitar la representación. +* Establecer el atributo de datos ``exception`` con ``setData('exception', $err)`` + para reemplazar la excepción que se está representando. +* Devolver una respuesta desde el evento para omitir la representación y usar + la respuesta proporcionada en su lugar. + +.. _error-views: + +Plantillas Personalizadas +========================== + +El atrapador de excepciones predeterminado representa todas las excepciones no capturadas que tu aplicación genera con la ayuda de ``Cake\Error\WebExceptionRenderer`` y tu ``ErrorController`` de la aplicación. + +Las vistas de página de error están ubicadas en **templates/Error/**. Todos los errores 4xx usan la plantilla **error400.php**, y los errores 5xx usan la plantilla **error500.php**. Tus plantillas de error tendrán las siguientes variables disponibles: + +* ``message`` El mensaje de la excepción. +* ``code`` El código de la excepción. +* ``url`` La URL de la solicitud. +* ``error`` El objeto de la excepción. + +En modo de depuración, si tu error se extiende de ``Cake\Core\Exception\CakeException``, los datos devueltos por ``getAttributes()`` se expondrán también como variables de vista. + +.. note:: + Necesitarás establecer ``debug`` en falso para ver tus plantillas **error404** y **error500**. En modo de depuración, verás la página de error de desarrollo de CakePHP. + +Diseño Personalizado para la Página de Error +-------------------------------------------- + +Por defecto, las plantillas de error usan **templates/layout/error.php** para un diseño. Puedes usar la propiedad ``layout`` para elegir un diseño diferente:: + + // dentro de templates/Error/error400.php + $this->layout = 'mi_error'; + +Lo anterior usaría **templates/layout/mi_error.php** como el diseño para tus páginas de error. + +Muchas excepciones generadas por CakePHP representarán plantillas de vista específicas en modo de depuración. Con la depuración desactivada, todas las excepciones generadas por CakePHP usarán **error400.php** o **error500.php** según su código de estado. + +Controlador Personalizado +========================= + +La clase ``App\Controller\ErrorController`` se utiliza para la representación de excepciones de CakePHP para renderizar la vista de la página de error y recibe todos los eventos estándar del ciclo de vida de la solicitud. Al modificar esta clase, puedes controlar qué componentes se utilizan y qué plantillas se representan. + +Si tu aplicación utiliza :ref:`rutas con prefijo `, puedes crear controladores de error personalizados para cada prefijo de enrutamiento. Por ejemplo, si tienes un prefijo ``Admin``, podrías crear la siguiente clase:: + + namespace App\Controller\Admin; + + use App\Controller\AppController; + use Cake\Event\EventInterface; + + class ErrorController extends AppController + { + /** + * Callback beforeRender. + * + * @param \Cake\Event\EventInterface $event Evento. + * @return void + */ + public function beforeRender(EventInterface $event) + { + $this->viewBuilder()->setTemplatePath('Error'); + } + } + +Este controlador solo se utilizaría cuando se encuentra un error en un controlador con prefijo y te permite definir lógica/plantillas específicas del prefijo según sea necesario. + +.. _custom-exceptionrenderer: + +ExceptionRenderer Personalizado +================================ + +Si deseas controlar todo el proceso de representación y registro de excepciones, puedes utilizar la opción ``Error.exceptionRenderer`` en **config/app.php** para elegir una clase que representará las páginas de excepciones. Cambiar el ExceptionRenderer es útil cuando quieres cambiar la lógica utilizada para crear un controlador de error, elegir la plantilla o controlar el proceso general de representación. + +Tu clase personalizada de ExceptionRenderer debe colocarse en **src/Error**. Supongamos que nuestra aplicación usa ``App\Exception\MissingWidgetException`` para indicar un widget faltante. Podríamos crear un ExceptionRenderer que represente páginas de error específicas cuando se maneja este error:: + + // En src/Error/AppExceptionRenderer.php + namespace App\Error; + + use Cake\Error\WebExceptionRenderer; + + class AppExceptionRenderer extends WebExceptionRenderer + { + public function missingWidget($error) + { + $response = $this->controller->getResponse(); + + return $response->withStringBody('Oops, ese widget está perdido.'); + } + } + + // En config/app.php + 'Error' => [ + 'exceptionRenderer' => 'App\Error\AppExceptionRenderer', + // ... + ], + // ... + +Lo anterior manejaría nuestro ``MissingWidgetException``, +y nos permitiría proporcionar lógica de visualización/manejo personalizado para esas excepciones de aplicación. + +Los métodos de representación de excepciones reciben la excepción manejada como argumento y deben devolver un objeto ``Response``. También puedes implementar métodos para agregar lógica adicional al manejar errores de CakePHP:: + + // En src/Error/AppExceptionRenderer.php + namespace App\Error; + + use Cake\Error\WebExceptionRenderer; + + class AppExceptionRenderer extends WebExceptionRenderer + { + public function notFound($error) + { + // Haz algo con objetos NotFoundException. + } + } + +Cambiar la Clase ErrorController +---------------------------------- + +El ExceptionRenderer dicta qué controlador se utiliza para la representación de excepciones. Si quieres cambiar qué controlador se utiliza para representar excepciones, puedes anular el método ``_getController()`` en tu ExceptionRenderer:: + + // en src/Error/AppExceptionRenderer + namespace App\Error; + + use App\Controller\SuperCustomErrorController; + use Cake\Controller\Controller; + use Cake\Error\WebExceptionRenderer; + + class AppExceptionRenderer extends WebExceptionRenderer + { + protected function _getController(): Controller + { + return new SuperCustomErrorController(); + } + } + + // en config/app.php + 'Error' => [ + 'exceptionRenderer' => 'App\Error\AppExceptionRenderer', + // ... + ], + + + // ... + +.. index:: excepciones de aplicación + +Crear tus Propias Excepciones de Aplicación +============================================ + +Puedes crear tus propias excepciones de aplicación utilizando cualquiera de las `excepciones SPL incorporadas `_, ``Exception`` +en sí, o :php:exc:`Cake\\Core\\Exception\\Exception`. +Si tu aplicación contiene la siguiente excepción:: + + use Cake\Core\Exception\CakeException; + + class MissingWidgetException extends CakeException + { + } + +Podrías proporcionar errores de desarrollo detallados, creando +**templates/Error/missing_widget.php**. Cuando estás en modo de producción, el error anterior se trataría como un error 500 y usaría la plantilla **error500**. + +Las excepciones que son subclases de ``Cake\Http\Exception\HttpException``, usarán su código de error como código de estado HTTP si el código de error está entre ``400`` y ``506``. + +El constructor para :php:exc:`Cake\\Core\\Exception\\CakeException` te permite pasar datos adicionales. Estos datos adicionales se interpolan en el ``_messageTemplate``. Esto te permite crear excepciones ricas en datos que proporcionen más contexto sobre tus errores:: + + use Cake\Core\Exception\CakeException; + + class MissingWidgetException extends Exception + { + // Los datos del contexto se interpolan en esta cadena de formato. + protected $_messageTemplate = 'Parece que falta %s.'; + + // También puedes establecer un código de excepción predeterminado. + protected $_defaultCode = 404; + } + + throw new MissingWidgetException(['widget' => 'Puntiagudo']); + +Cuando se representa, tu plantilla de vista tendría una variable ``$widget`` establecida. Si lanzas la excepción como una cadena o usas su método ``getMessage()``, obtendrás ``Parece que falta Puntiagudo.``. + +.. note:: + + Antes de CakePHP 4.2.0, usa la clase ``Cake\Core\Exception\Exception`` en lugar de ``Cake\Core\Exception\CakeException`` + +Registro de Excepciones +------------------------ + +Usando el manejo de excepciones incorporado, puedes registrar todas las excepciones que son tratadas por ErrorTrap configurando la opción ``log`` en ``true`` en tu **config/app.php**. Al habilitar esto, se registrarán todas las excepciones en :php:class:`Cake\\Log\\Log` y en los registradores configurados. + +.. note:: + + Si estás utilizando un manejador de excepciones personalizado, esta configuración no tendrá + ningún efecto, a menos que la referencies dentro de tu implementación. + +.. php:namespace:: Cake\Http\Exception + +.. _built-in-exceptions: + +Excepciones Incorporadas para CakePHP +====================================== + +Excepciones HTTP +----------------- + +Hay varias excepciones incorporadas en CakePHP, además de las excepciones internas del framework, hay varias +excepciones para métodos HTTP. + +.. php:exception:: BadRequestException + :nocontentsentry: + + Usado para el error 400 Bad Request. + +.. php:exception:: UnauthorizedException + :nocontentsentry: + + Usado para el error 401 Unauthorized. + +.. php:exception:: ForbiddenException + :nocontentsentry: + + Usado para el error 403 Forbidden. + +.. php:exception:: InvalidCsrfTokenException + :nocontentsentry: + + Usado para el error 403 causado por un token CSRF inválido. + +.. php:exception:: NotFoundException + :nocontentsentry: + + Usado para el error 404 Not found. + +.. php:exception:: MethodNotAllowedException + :nocontentsentry: + + Usado para el error 405 Method Not Allowed. + +.. php:exception:: NotAcceptableException + :nocontentsentry: + + Usado para el error 406 Not Acceptable. + +.. php:exception:: ConflictException + :nocontentsentry: + + Usado para el error 409 Conflict. + +.. php:exception:: GoneException + :nocontentsentry: + + Usado para el error 410 Gone. + +Para más detalles sobre los códigos de estado 4xx del protocolo HTTP, consulta :rfc:`2616#section-10.4`. + +.. php:exception:: InternalErrorException + :nocontentsentry: + + Usado para el error 500 Internal Server Error. + +.. php:exception:: NotImplementedException + :nocontentsentry: + + Usado para el error 501 Not Implemented Errors. + +.. php:exception:: ServiceUnavailableException + :nocontentsentry: + + Usado para el error 503 Service Unavailable. + +Para más detalles sobre los códigos de estado 5xx del protocolo HTTP, consulta :rfc:`2616#section-10.5`. + +Puedes lanzar estas excepciones desde tus controladores para indicar estados de error o errores HTTP. Un ejemplo de uso de las excepciones HTTP podría ser renderizar páginas 404 para los elementos que no se han encontrado:: + + use Cake\Http\Exception\NotFoundException; + + public function ver($id = null) + { + $articulo = $this->Articulos->findById($id)->first(); + if (empty($articulo)) { + throw new NotFoundException(__('Artículo no encontrado')); + } + $this->set('articulo', $articulo); + $this->viewBuilder()->setOption('serialize', [' + +articulo']); + } + +Usar excepciones para errores HTTP te permite mantener tu código limpio y dar respuestas RESTful a aplicaciones de clientes y usuarios. + +Uso de Excepciones HTTP en tus Controladores +-------------------------------------------- + +Puedes lanzar cualquiera de las excepciones relacionadas con HTTP desde las acciones de tu controlador para indicar estados de error. Por ejemplo:: + + use Cake\Network\Exception\NotFoundException; + + public function ver($id = null) + { + $articulo = $this->Articulos->findById($id)->first(); + if (empty($articulo)) { + throw new NotFoundException(__('Artículo no encontrado')); + } + $this->set('articulo', 'articulo'); + $this->viewBuilder()->setOption('serialize', ['articulo']); + } + +Lo anterior causaría que el manejador de excepciones configurado capture y +procese la :php:exc:`NotFoundException`. Por defecto, esto creará una página de error +y registrará la excepción. + +Otras Excepciones Incorporadas +------------------------------ + +Además, CakePHP utiliza las siguientes excepciones: + +.. php:namespace:: Cake\View\Exception + +.. php:exception:: MissingViewException + :nocontentsentry: + + No se pudo encontrar la clase de vista elegida. + +.. php:exception:: MissingTemplateException + :nocontentsentry: + + No se pudo encontrar el archivo de plantilla elegido. + +.. php:exception:: MissingLayoutException + :nocontentsentry: + + No se pudo encontrar el diseño elegido. + +.. php:exception:: MissingHelperException + :nocontentsentry: + + No se pudo encontrar el ayudante elegido. + +.. php:exception:: MissingElementException + :nocontentsentry: + + No se pudo encontrar el archivo de elemento elegido. + +.. php:exception:: MissingCellException + :nocontentsentry: + + No se pudo encontrar la clase de celda elegida. + +.. php:exception:: MissingCellViewException + :nocontentsentry: + + No se pudo encontrar el archivo de vista de celda elegido. + +.. php:namespace:: Cake\Controller\Exception + +.. php:exception:: MissingComponentException + :nocontentsentry: + + No se pudo encontrar el componente configurado. + +.. php:exception:: MissingActionException + :nocontentsentry: + + No se pudo encontrar la acción del controlador solicitada. + +.. php:exception:: PrivateActionException + :nocontentsentry: + + Acceder a acciones con prefijos privados/protegidos/_. + +.. php:namespace:: Cake\Console\Exception + +.. php:exception:: ConsoleException + :nocontentsentry: + + Una clase de biblioteca de consola encontró un error. + +.. php:namespace:: Cake\Database\Exception + +.. php:exception:: MissingConnectionException + :nocontentsentry: + + Falta una conexión de modelo. + +.. php:exception:: MissingDriverException + :nocontentsentry: + + No se pudo encontrar un controlador de base de datos. + +.. php:exception:: MissingExtensionException + :nocontentsentry: + + Falta una extensión de PHP para el controlador de base de datos. + +.. php:namespace:: Cake\ORM\Exception + +.. php:exception:: MissingTableException + :nocontentsentry: + + No se pudo encontrar la tabla de un modelo. + +.. php:exception:: MissingEntityException + :nocontentsentry: + + No se pudo encontrar la entidad de un modelo. + +.. php:exception:: MissingBehaviorException + :nocontentsentry: + + No se pudo encontrar el comportamiento de un modelo. + +.. php:exception:: PersistenceFailedException + :nocontentsentry: + + No se pudo guardar/eliminar una entidad al usar :php:meth:`Cake\\ORM\\Table::saveOrFail()` o + :php:meth:`Cake\\ORM\\Table::deleteOrFail()`. + +.. php:namespace:: Cake\Datasource\Exception + +.. php:exception:: RecordNotFoundException + :nocontentsentry: + + No se pudo encontrar el registro solicitado. Esto también establecerá las cabeceras de respuesta HTTP en 404. + +.. php:namespace:: Cake\Routing\Exception + +.. php:exception:: MissingControllerException + :nocontentsentry: + + No se pudo encontrar el controlador solicitado. + +.. php:exception:: MissingRouteException + :nocontentsentry: + + No se pudo hacer coincidir la URL solicitada o no se pudo analizar. + +.. php:namespace:: Cake\Core\Exception + +.. php:exception:: Exception + :nocontentsentry: + + Clase base de excepción en CakePHP. Todas las excepciones de capa de framework lanzadas por + CakePHP extenderán esta clase. + +Estas clases de excepción se extienden de :php:exc:`Exception`. +Al extender Exception, puedes crear tus propios errores de 'framework'. + +.. php:method:: responseHeader($header = null, $value = null) + :nocontentsentry: + + Consulta :php:func:`Cake\\Network\\Request::header()` + +Todas las excepciones Http y Cake extienden la clase Exception, que tiene un método +para agregar encabezados a la respuesta. Por ejemplo, al lanzar un 405 +MethodNotAllowedException, el rfc2616 dice:: + + "La respuesta DEBE incluir un encabezado Allow que contenga una lista de métodos válidos + para el recurso solicitado." + +Manejo Personalizado de Errores de PHP +====================================== + +Por defecto, los errores de PHP se representan en la consola o en la salida HTML, y también se registran. +Si es necesario, puedes cambiar la lógica de manejo de errores de CakePHP con la tuya propia. + +Registro de Errores Personalizado +--------------------------------- + +Los manejadores de errores utilizan instancias de ``Cake\Error\ErrorLoggingInterface`` para crear +mensajes de registro y registrarlos en el lugar apropiado. Puedes reemplazar el +registrador de errores utilizando el valor de configuración ``Error.errorLogger``. Un ejemplo de registrador de errores:: + + namespace App\Error; + + use Cake\Error\ErrorLoggerInterface; + use Cake\Error\PhpError; + use Psr\Http\Message\ServerRequestInterface; + use Throwable; + + /** + * Registra errores y excepciones no manejadas en `Cake\Log\Log` + */ + class ErrorLogger implements ErrorLoggerInterface + { + /** + * @inheritDoc + */ + public function logError( + PhpError $error, + ?ServerRequestInterface $request, + bool $includeTrace = false + ): void { + // Registra errores de PHP + } + + /** + * @inheritDoc + */ + public function logException( + ?ServerRequestInterface $request, + bool $includeTrace = false + ): void { + // Registra excepciones. + } + } + +**Renderizado Personalizado de Errores** + +CakePHP incluye renderizadores de errores tanto para entornos web como de consola. Sin embargo, si deseas reemplazar la lógica que renderiza los errores, puedes crear una clase personalizada:: + + // src/Error/CustomErrorRenderer.php + namespace App\Error; + + use Cake\Error\ErrorRendererInterface; + use Cake\Error\PhpError; + + class CustomErrorRenderer implements ErrorRendererInterface + { + public function write(string $out): void + { + // enviar el error renderizado al flujo de salida apropiado + } + + public function render(PhpError $error, bool $debug): string + { + // Convertir el error en una cadena de salida. + } + } - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +El constructor de tu renderizador recibirá un array con la configuración almacenada en `Error`. Conecta tu renderizador de errores personalizado a CakePHP a través del valor de configuración `Error.errorRenderer`. Al reemplazar el manejo de errores, deberás tener en cuenta tanto los entornos web como los de línea de comandos. .. meta:: - :title lang=es: Error & Exception Handling - :keywords lang=es: stack traces,error constants,error array,default displays,anonymous functions,error handlers,default error,error level,exception handler,php error,error handler,write error,core classes,exception handling,configuration error,application code,callback,custom error,exceptions,bitmasks,fatal error + :title lang=es: Manejo de Errores y Excepciones + :keywords lang=en: stack traces,error constants,error array,default displays,anonymous functions,error handlers,default error,error level,exception handler,php error,error handler,write error,core classes,exception handling,configuration error,application code,callback,custom error,exceptions,bitmasks,fatal error, http status codes diff --git a/es/development/rest.rst b/es/development/rest.rst index 48ad6e2cbd..59f11a72b4 100644 --- a/es/development/rest.rst +++ b/es/development/rest.rst @@ -1,9 +1,9 @@ REST #### -Muchos de los nuevos programadores de aplicaciones se estan dando cuenta -de la necesidad de abrir el núcleo de la funcionalidad a un mayor publico. -Proporcionando acceso fácil y sin restricciones al núcleo de su API puede +Muchos de los nuevos programadores de aplicaciones se están dando cuenta +de la necesidad de abrir el núcleo de la funcionalidad a un mayor público. +Proporcionando acceso fácil y sin restricciones al núcleo de su API puede ayudar a que su plataforma sea aceptada, y permite realizar mashups y fácil integración con otros sistemas. @@ -16,10 +16,10 @@ Exponer una API utilizando REST en CakePHP es simple. La Configuración Simple ======================= -La forma más rapida para empezar a utilizar REST es agregar unas lineas para -configurar la `resource routes ` en su archivo **config/routes.php** . +La forma más rapida para empezar a utilizar REST es agregar unas líneas para +configurar la `resource routes ` en su archivo **config/routes.php**. -Una vez que la ruta se ha configurado para mapear las solicitudes REST a +Una vez que la ruta se ha configurado para mapear las solicitudes REST a cierto controlador de acciones, se puede proceder a crear la lógica de nuestro controlador de acciones. Un controlador básico podría visualizarse de la siguiente forma:: @@ -92,21 +92,21 @@ controlador de acciones. Un controlador básico podría visualizarse de la sigui } } -Los controladores RESTful a menudo usan extensiones parseadas para mostrar diferentes vistas +Los controladores RESTful a menudo usan extensiones parseadas para mostrar diferentes vistas, basado en diferentes tipos de solicitudes. Como estamos tratando con solicitudes REST, estaremos haciendo vistas XML. Puedes realizar vistas en JSON usando el CakePHP :doc:`/views/json-and-xml-views`. Mediante el uso de :php:class:`XmlView` se puede -definir una opción de ``serialize``. Esta opción se usa para definir qué variables de vistas +definir una opción de ``serialize``. Esta opción se usa para definir qué variables de vistas `` XmlView`` deben serializarse en XML. -Si se quiere modificar los datos antes de convertirlos en XML, no se debería definir la +Si se quiere modificar los datos antes de convertirlos en XML, no se debería definir la opción ``serialize``, y en lugar de eso, se debería usar archivos plantilla. Colocaremos las vistas REST de nuestro RecipesController dentro de **templates/Recipes/xml**. también podemos utilizar el :php:class:`Xml` para una salida XML rápida y fácil en esas vistas. De esta forma, así podría verse nuestra vista de índice:: // templates/Recipes/xml/index.php - // Realizar un formateo y manipulacion en + // Realizar un formateo y manipulacion en // $recipes array. $xml = Xml::fromArray(['response' => $recipes]); echo $xml->asXML(); @@ -114,7 +114,7 @@ De esta forma, así podría verse nuestra vista de índice:: Al entregar un tipo de contenido específico usando :php:meth:`Cake\\Routing\\Router::extensions()`, CakePHP busca automáticamente un asistente de vista que coincida con el tipo. Como estamos utilizando -XML como tipo de contenido, no hay un asistente incorporado, sin embargo, si creara uno, se cargaría +XML como tipo de contenido, no hay un asistente incorporado, sin embargo, si creara uno, se cargaría automáticamente para nuestro uso en esas vistas. El XML procesado terminará pareciéndose a esto:: @@ -138,12 +138,12 @@ El XML procesado terminará pareciéndose a esto:: Crear la lógica para la acción de edición es un poco más complicado, pero no mucho. -Ya que se está proporcionando una API que genera XML como salida, es una opción natural +Ya que se está proporcionando una API que genera XML como salida, es una opción natural recibir XML como entrada. No te preocupes, las clases :php:class:`Cake\\Controller\\Component\\RequestHandler` -y :php:class:`Cake\\Routing\\Router` hacen las cosas mucho mas fáciles. Si un POST o +y :php:class:`Cake\\Routing\\Router` hacen las cosas mucho más fáciles. Si un POST o una solicitud PUT tiene un tipo de contenido XML, entonces la entrada se ejecuta a través de la clase de CakePHP :php:class:`Xml`, y la representación del arreglo de los datos se asigna a ``$this->request->getData()``. -Debido a esta característica, el manejo de datos XML y POST se hace en continuamente en paralelo: no se +Debido a esta característica, el manejo de datos XML y POST se hace en continuamente en paralelo: no se requieren cambios en el controlador o el código del modelo. Todo lo que necesita debe terminar en ``$this->request->getData()``. @@ -152,15 +152,15 @@ Aceptando Entradas en otros formatos Por lo general, las aplicaciones REST no solo generan contenido en formatos de datos alternativos, sino que también acepta datos en diferentes formatos. En CakePHP, el :php:class:`RequestHandlerComponent` ayuda a fácilitar esto. -Por defecto, decodificará cualquier entrada de datos en JSON / XML para solicitudes POST / PUT y proporcionar una -versión del arreglo de esos datos en ``$this->request->getData()``. También puedes conectar deserializadores +Por defecto, decodificará cualquier entrada de datos en JSON / XML para solicitudes POST / PUT y proporcionar una +versión del arreglo de esos datos en ``$this->request->getData()``. También puedes conectar deserializadores adicionales para formatos alternativos si los necesitas, usando: :php:meth:`RequestHandler::addInputType()`. Enrutamiento RESTful ==================== El enrutador de CakePHP fácilita la conexión de rutas de recursos RESTful. Ver la sección -`resource-routes` para más información. +`resource-routes` para más información. .. meta:: :title lang=es: REST diff --git a/es/development/routing.rst b/es/development/routing.rst index 122dddbee1..8db2d5e089 100644 --- a/es/development/routing.rst +++ b/es/development/routing.rst @@ -3,33 +3,1749 @@ Routing .. php:namespace:: Cake\Routing -.. php:class:: Router +.. php:class:: RouterBuilder -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +El enrutamiento te provee de herramientas que permiten mapear URLs a acciones +de un controlador. Al definir rutas, puedes separar cómo está implementada tu +aplicación y cómo están estructuradas sus URLs. + +El enrutamiento en CakePHP también abarca la idea de enrutamiento inverso, donde +una matriz de parámetros se puede transformar en una cadena URL. Al utilizar el +enrutamiento inverso, puedes refactorizar la estructura de tus URLs sin necesidad +de actualizar todo tu código. + +.. index:: routes.php + +Vistazo rápido +============== + +Esta sección te enseñará los usos más comunes del enrutamiento en CakePHP con +ejemplos. Normalmente, deseas mostrar algo como una página de destino, por lo que +tendrás que añadir esto a tu archivo **config/routes.php**:: + + /** @var \Cake\Routing\RouteBuilder $routes */ + $routes->connect('/', ['controller' => 'Articles', 'action' => 'index']); + +Esto ejecutará el método index que se encuentra en ``ArticlesController`` cuando +se visite la página principal de tu sitio. A veces necesitas rutas dinámicas que +aceptarán múltiples parámetos, por ejemplo cuando necesites una ruta para ver +el contenido de un artículo:: + + $routes->connect('/articles/*', ['controller' => 'Articles', 'action' => 'view']); + +La ruta anterior aceptará cualquier URL que se parezca a ``/article/15`` e invocará +el método ``view(15)`` de ``ArticlesController``. Esto no prevendrá que las personas +intenten acceder a URLs como ``/articles/foobar``. Si quieres, puedes restringir +algunos parámetros que se ajusten a una expresión regular:: + + // Utilizando una interfaz fluida + $routes->connect( + '/articles/{id}', + ['controller' => 'Articles', 'action' => 'view'], + ) + ->setPatterns(['id' => '\d+']) + ->setPass(['id']); + + // Utilizando una matriz de opciones + $routes->connect( + '/articles/{id}', + ['controller' => 'Articles', 'action' => 'view'], + ['id' => '\d+', 'pass' => ['id']] + ); + +En el ejemplo anterior se cambió el comparador asterisco por un nuevo marcador de +posición ``{id}``. Utilizar marcadores de posición nos permite valiadr partes de +la URL, en este caso utilizamos la expresión regular ``\d+`` por lo que sólo los +dígitos son comparados. Finalmente, le indicamos al enrutador que trate el marcador +de posición ``id`` como un argumento de función para el método ``view()`` +especificando la opción ``pass``. +Hablaremos más sobre el uso de esta opción más adelante. + +El enrutador de CakePHP también puede revertir rutas de coincidencia. Esto quiere +decir que desde una matriz que contiene parámetros de coincidencia es capaz de generar +una cadena de URL:: + + use Cake\Routing\Router; - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + echo Router::url(['controller' => 'Articles', 'action' => 'view', 'id' => 15]); + // Obtendrás como salida + /articles/15 - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Las rutas también pueden etiquetarse con un nombre único, esto te permite referenciarlas +rápidamente cuando creas enlaces en lugar de especificar cada uno de los parámetros de +la ruta:: + // En routes.php + $routes->connect( + '/upgrade', + ['controller' => 'Subscriptions', 'action' => 'create'], + ['_name' => 'upgrade'] + ); + + use Cake\Routing\Router; + + echo Router::url(['_name' => 'upgrade']); + // Obtendrás como salida + /upgrade + +Para ayudar a mantener tu código de enrutamiento :term:`DRY`, el Enrutador tiene el concepto +de 'ámbitos'. +Un ámbito define un segmento de ruta común y, opcionalmente, rutas predeterminadas. +Cualquier ruta conectada dentro de un ámbito heredará la ruta y valores por defecto +de su ámbito:: + + $routes->scope('/blog', ['plugin' => 'Blog'], function (RouteBuilder $routes) { + $routes->connect('/', ['controller' => 'Articles']); + }); + +La rua anterior coincidiría con ``/blog/`` y la enviaría a +``Blog\Controller\ArticlesController::index()``. + +El esqueleto de la aplicación viene con algunas rutas de inicio. Una vez has añadido +tus tuyas propias, puedes eliminar las rutas por defecto si no las necesitas. + +.. index:: {controller}, {action}, {plugin} +.. index:: greedy star, trailing star +.. _connecting-routes: .. _routes-configuration: -Connecting Routes -================= +Conectando Rutas +================ + +Para mantener tu código :term:`DRY` debes utilizar 'ámbitos de ruta'. Los ámbitos +de ruta no sólo te facilitan mantener tu código DRY, sino que ayudan al Enrutador a +optimizar sus operaciones. Este método se aplica por defecto al ámbito ``/``. Para +crear un ámbito y conectar algunas rutas utilizarás el método ``scope()``:: + + // En config/routes.php + use Cake\Routing\RouteBuilder; + use Cake\Routing\Route\DashedRoute; + + $routes->scope('/', function (RouteBuilder $routes) { + // Conecta las rutas alternativas genéricas. + $routes->fallbacks(DashedRoute::class); + }); + +El método ``connect()`` acepta hasta tres parámetros: la plantilla de URL para la que +deseas conincidencias, los valores predeterminados para los elementos de tu ruta. +Las opciones frecuentemente incluyen reglas de expresión regular que para ayudar al +enrutador a coincidir con elementos de la URL. + +El formato básico para la definición de una ruta es:: + + $routes->connect( + '/url/template', + ['targetKey' => 'targetValue'], + ['option' => 'matchingRegex'] + ); + +El primer parámetro se utiliza para indicarle al enrutador qué tipo de URL se +está intentando controlar. La URL es una cadena normal delimitada por barras +diagonales, pero también puede contener un comodín (\*) o :ref:`route-elements`. +El uso de un comodín le indica al enrutador que puede aceptar cualquier argumento +adicional que se le proporcione. Las rutas sin \* sólo coincidirán con el patrón +de plantilla exacto proporcionado. + +Una vez que hayas especificado una URL, utiliza los dos últimos parámetros de +``connect()`` para indicar a CakePHP qué debe hacer con la solicitud cuando +haya coincidencia. El segundo parámetro define la ruta 'objetivo'. Esto se puede +definir como una matriz o como una cadena de destino. Algunos ejemplos de ruta +objetivo son:: + + // Matriz de destino a un controlador de aplicación + $routes->connect( + '/users/view/*', + ['controller' => 'Users', 'action' => 'view'] + ); + $routes->connect('/users/view/*', 'Users::view'); + + // Matriz de destino a un controlador de plugin con prefijo + $routes->connect( + '/admin/cms/articles', + ['prefix' => 'Admin', 'plugin' => 'Cms', 'controller' => 'Articles', 'action' => 'index'] + ); + $routes->connect('/admin/cms/articles', 'Cms.Admin/Articles::index'); + +La primera ruta que conectamos coincide con las URL que comienzan con ``/users/view`` +y asigna esas solucitudes al ``UsersController->view()``. El ``/*`` indica al enrutador +para pasar cualquier segmento adicional como argumentos del método. Por ejemplo, +``/users/view/123`` se asignaría a ``UsersController->view(123)``. + +El ejemplo anterior también ilustra los destinos de cadena. Los destinos de cadena +proporcionan una forma compacta de definir el destino de una ruta. Los destinos de +cadena tienen la siguiente sintaxis:: + + [Plugin].[Prefix]/[Controller]::[action] + +Algunos ejemplos de destino de cadena son:: + + // Controlador de aplicación + 'Articles::view' + + // Controlador de aplicación con prefijo + Admin/Articles::view + + // Controlador de plugin + Cms.Articles::edit + + // Controlador de plugin con prefijo + Vendor/Cms.Management/Admin/Articles::view + +Anteriormente, usamos el asterisco final (``/*``) para capturar segmentos de ruta adicionales, +también está el doble asterisco final (``/**``). Utilizando el doble asterisco final, +capturará el resto de una URL como un único argumento. Esto es útil cuando se desea +utilizar un argumento que incluye ``/``:: + + $routes->connect( + '/pages/**', + ['controller' => 'Pages', 'action' => 'show'] + ); + +La URL entrante ``/pages/the-example-/-and-proof`` daría como resultado el paso de un +único argumento ``the-example-/-and-proof``. + +El segundo parámetro de ``connect()`` puede definir cualquier parámetro para componer +los parámetros de ruta po predeterminado:: + + $routes->connect( + '/government', + ['controller' => 'Pages', 'action' => 'display', 5] + ); + +Este ejemplo utiliza el segundo parámetro de ``connect()`` para definir los parámetros +predeterminados. Si creas una aplicación que presenta productos para diferentes categorías +de clientes, podrías considerar crear una ruta. Esto permite enlazar ``/government`` en +lugar de ``/pages/display/5``. + +Un uso común del enrutamiento es renombrar los controladores y sus acciones. En lugar de +acceder a nuestro controlador de usuarios como ``/users/some-action/5``, nos gustaría acceder +a él a través de ``/cooks/some-action/5``. La siguiente ruta se encarga de eso:: + + $routes->connect( + '/cooks/{action}/*', ['controller' => 'Users'] + ); + +Esto indica al enrutador que cualquier URL que empieze por ``/cooks/`` deberá ser +enviada al ``UsersController``. La acción invocada dependerá del valor del parámetro ``{action}``. +Utilizando :ref:`route-elements`, puedes crear rutas variables que aceptan entradas del usuario +o variables. La ruta anterior también utiliza el asteristo final. El asterisco final indica que +esta ruta debe aceptar cualquier argumento posicional adicional dado. Estos argumentos estarán +disponibles en la matriz :ref:`passed-arguments`. + +Al generar URL también se utilizan rutas. Utilizando +``['controller' => 'Users', 'action' => 'some-action', 5]`` como una URL, generará +``/cooks/some-action/5`` si la ruta anterior es la primera coincidencia encontrada. + +Las ruts que hemos conectado hasta ahora coincidirán con cualquier tipo de petición HTTP. Si estás +contruyendo un API REST, a menudo querrás asignar acciones HTTP a diferentes métodos de +controlador. El ``RouteBuilder`` proporciona métodos auxiliares que facilitan la definición +de rutas para tipos de peticiones HTTP específicas más simples:: + + // Crea una ruta que sólo responde a peticiones GET. + $routes->get( + '/cooks/{id}', + ['controller' => 'Users', 'action' => 'view'], + 'users:view' + ); + + // Crea una ruta que sólo responde a peticiones PUT + $routes->put( + '/cooks/{id}', + ['controller' => 'Users', 'action' => 'update'], + 'users:update' + ); + +Las rutas anteriores asignan la misma URL a diferentes acciones del controlador según +el tipo de petición HTTP utilizada. Las solicitudes GET irán a la acción 'view', mientras +que las solicitudes PUT irán a la acción UPDATE. Existen métodos auxiliares HTTP para: + +* GET +* POST +* PUT +* PATCH +* DELETE +* OPTIONS +* HEAD + +Todos estos métodos devuelven una instancia de ruta, lo que permite aprovechar la +:ref:`fluent setterts ` para configurar aún más la ruta. + +.. _route-elements: + +Elementos de ruta +----------------- + +Puedes especificar tus propios elementos de ruta y al hacerlo podrás definir los +lugares de la URL donde los parámetros para las acciones del controlador deben estar. +Cuando se realiza una solicitud, los valores para estos elementos de ruta se encuentran +en ``$this->request->getParam()`` en el controlador. +Cuando defines un elemento de ruta personalizado, opcionalmente puedes especificar +una expresión regular; esto le dice a CakePHP como saber si la URL está formada +correctamente o no. Si eliges no proporcionar una expresión regular, cualquier caracter +que no sea ``/`` será tratado como parte del parámetro:: + + $routes->connect( + '/{controller}/{id}', + ['action' => 'view'] + )->setPatterns(['id' => '[0-9]+']); + + $routes->connect( + '/{controller}/{id}', + ['action' => 'view'], + ['id' => '[0-9]+'] + ); + +El ejemplo anterior ilustra cómo crear una forma rápida de ver modelos desde cualquier +controlador creando una URL que se parezca a ``/controllername/{id}``. La URL proporcionada +a ``connect()`` especifica dos elementos de ruta: ``{controller}`` y ``{id}``. El elemento +``{controller}`` es un elemento de ruta predeterminado de CakePHP, por lo que el enrutador +conoce cómo identificar y emparejar los nombres de controladores en la URL. El elemento +``{id}`` es un elemento de ruta personalizado y debe aclararse especificando una expresión +regular en el tercer parámetro de ``connect()``. + +CakePHP no produce automáticamente URL en minúsculas y con guiones cuando utiliza el +parámetro ``{controller}``. Si necesitas hacer esto, el ejemplo anterior podría ser +reescrito así:: + + use Cake\Routing\Route\DashedRoute; + + // Crea un constructor con una clase de ruta diferente + $routes->scope('/', function (RouteBuilder $routes) { + $routes->setRouteClass(DashedRoute::class); + $routes->connect('/{controller}/{id}', ['action' => 'view']) + ->setPatterns(['id' => '[0-9]+']); + + $routes->connect( + '/{controller}/{id}', + ['action' => 'view'], + ['id' => '[0-9]+'] + ); + }); + +La clase ``DashedRoute`` se asegurará de que los parámetros ``{controller}`` y +``{plugin}`` están correctamente en minúsculas y con guiones. + +.. note:: + + Los patrones utilizados por los elementos de ruta no deben contener + ningún grupo de captura. Si lo hacen, el enrutador no funcionará + correctamente. + +Una vez que se ha definido esta ruta, al solicitar ``/apples/5`` se llamará al método +``view()`` de ApplesController. Dento del método ``view()``, necesitarás acceder al ID +pasado en ``$this->request->getParam('id')``. + +Si tienes un único controlador en tu aplicación y no quieres que el nombre del +controlador aparezca en la URL, puedes asignar todas las URL a acciones en tu +controlador. Por ejemplo, para asignar todas las URL a acciones del contolador +``home``, para tener una URL como ``/demo`` en lugar de ``/home/demo``, puedes +hacer lo siguiente:: + + $routes->connect('/{action}', ['controller' => 'Home']); + +Si quieres proporcionar una URL que no distinga entre mayúsculas y minúsculas, +puedes utilizar modificadores en línea de expresiones regulares:: + + $routes->connect( + '/{userShortcut}', + ['controller' => 'Teachers', 'action' => 'profile', 1], + )->setPatterns(['userShortcut' => '(?i:principal)']); + +Un ejemplo más y serás un profesional del enrutamiento:: + + $routes->connect( + '/{controller}/{year}/{month}/{day}', + ['action' => 'index'] + )->setPatterns([ + 'year' => '[12][0-9]{3}', + 'month' => '0[1-9]|1[012]', + 'day' => '0[1-9]|[12][0-9]|3[01]' + ]); + +Esto es bastante complicado, pero muestra cuán poderosas pueden ser las rutas. La URL +proporcionada tiene cuatro elementos de ruta. El primero nos resulta familiar: es +un elemento de ruta por defecto que incica a CakePHP que espere un nombre de controlador. + +A continuación, especificamos algunos valores predeterminados. Independientemente +del controlador, queremos que se llame a la acción ``index()``. + +Finalmente, especificamos algunas expresiones regulares que coincidirán con año, mes +y día en forma numérica. Ten en cuenta que los paréntesis (captura de grupos) no se +admiten en expresiones regulares. Aún podemos especificar alternativas, como se +indicó anteriormente, pero no agrupadas entre paréntesis. + +Una vez definida, esta ruta coincidirá con ``/articles/2007/02/01``, +``/articles/2004/11/16``, entregando las solicitudes a la acción ``index()`` +de sus respectivos controladores, con los parámetros de fecha en +``$this->request->getParams()``. + +Elementos de Ruta Reservados +---------------------------- + +Hay varios elementos de ruta que tienen un significado especial en CakePHP, +y no deben usarse a menos que desee un significado especial + +* ``controller`` Se utiliza para nombrar el controlador de una ruta. +* ``action`` Se utiliza para nombrar la acción del controlador para una ruta. +* ``plugin`` Se utiliza para nombrar el complemento en el que se encuentra un controlador. +* ``prefix`` Usado para :ref:`prefix-routing` +* ``_ext`` Usado para :ref:`File extentions routing `. +* ``_base`` Se establece a ``false`` para eliminar la ruta base de la URL generada. Si + su aplicación no está en el directorio raíz, esto puede utilizarse para generar URL + que son 'cake relative'. +* ``_scheme`` Configurado para crear enlaces en diferentes esquemas como `webcal` o `ftp`. + El valor predeterminado es el esquema actual. +* ``_host`` Establece el host que se utilizará para el enlace. El valor predeterminado + es el host actual. +* ``_port`` Establece el puerto si necesitamos crear enlaces en puertos no estándar. +* ``_full`` Si es ``true`` el valor de ``App.fullBaseUrl`` mencionado en + :ref:`general-configuration` se atepondrá a la URL generada. +* ``#`` Permite configurar fragmentos de hash de URL. +* ``_https`` Establecerlo en ``true`` para convertir la URL generada a https o``false`` + para forzar http. Antes de 4.5.0 utilizar ``_ssl``. +* ``_method`` Define el tipo de petición/método a utilizar. Útil cuando trabajamos con + :ref:`resource-routes`. +* ``_name`` Nombre de la ruta. Si has configurado rutas con nombre, puedes utilizar + esta clave para especificarlo. + +.. _route-fluent-methods: + +Configurando Opciones de Ruta +----------------------------- + +Hay varias opciones de ruta que se pueden configurar en cada ruta. Después de +conectar una ruta, puedes utilizar sus métodos de creación fluidos para configurar +aún más la ruta. Estos métodos reemplazan muchas de las claves en el parámetro +``$options`` de ``connect()``:: + + $routes->connect( + '/{lang}/articles/{slug}', + ['controller' => 'Articles', 'action' => 'view'], + ) + // Permite peticiones GET y POST. + ->setMethods(['GET', 'POST']) + + // Sólo coincide con el subdominio del blog. + ->setHost('blog.example.com') + + // Establecer los elementos de ruta que deben convertirse en argumentos pasados + ->setPass(['slug']) + + // Establecer los patrones de coincidencia para los elementos de ruta + ->setPatterns([ + 'slug' => '[a-z0-9-_]+', + 'lang' => 'en|fr|es', + ]) + + // También permite archivos con extensión JSON + ->setExtensions(['json']) + + // Establecer lang para que sea un parámetro persistente + ->setPersist(['lang']); + +Pasar Parámetros a una Acción +----------------------------- + +Cuando conectamos rutas utilizando ::ref:`route-elements` es posible que desees +que los elementos enrutados se pasen como argumentos. La opción ``pass`` indica +qué elementos de ruta también deben estaar disponibles como argumentos pasados +a las funciones del controlador:: + + // src/Controller/BlogsController.php + public function view($articleId = null, $slug = null) + { + // Algún código aquí... + } + + // routes.php + $routes->scope('/', function (RouteBuilder $routes) { + $routes->connect( + '/blog/{id}-{slug}', // For example, /blog/3-CakePHP_Rocks + ['controller' => 'Blogs', 'action' => 'view'] + ) + // Definir los elementos de ruta en la plantilla de ruta + // para anteponerlos como argumentos de la función. El orden + // es importante ya que esto pasará los elementos `$id` y `$slug` + // como primer y segundo parámetro. Cualquier otro parámetro + // adicional pasado en tu ruta se agregará después de los + // argumentos de setPass(). + ->setPass(['id', 'slug']) + // Definir un patrón con el que `id` debe coincidir. + ->setPatterns([ + 'id' => '[0-9]+', + ]); + }); + +Ahora, gracias a las capacidades de enturamiento inverso, puedes pasar la matriz +de URL como se muestra a continuación y CakePHP sabrá cómo formar la URL como se +define en las rutas:: + + // view.php + // Esto devolverá un enlace a /blog/3-CakePHP_Rocks + echo $this->Html->link('CakePHP Rocks', [ + 'controller' => 'Blog', + 'action' => 'view', + 'id' => 3, + 'slug' => 'CakePHP_Rocks' + ]); + + // También podemos utilizar índices numéricos como parámetros. + echo $this->Html->link('CakePHP Rocks', [ + 'controller' => 'Blog', + 'action' => 'view', + 3, + 'CakePHP_Rocks' + ]); + +.. _path-routing: + +Uso del Enrutamiento de Ruta +---------------------------- + +Hablamos de objetivos de cadena anteriormente. Lo mismo también funciona para la +generación de URL usando ``Router::pathUrl()``:: + + echo Router::pathUrl('Articles::index'); + // salida: /articles + + echo Router::pathUrl('MyBackend.Admin/Articles::view', [3]); + // salida: /admin/my-backend/articles/view/3 + +.. tip:: + + La compatibilidad del IDE para el autocompletado del enrutamiento de ruta se puede habilitar con `CakePHP IdeHelper Plugin `_. .. _named-routes: -Using Named Routes ------------------- +Usar Rutas con Nombre +--------------------- + +A veces encontrarás que escribir todos los parámetros de la URL para una ruta es +demasiado detallado, o le gustaría aprovechar las mejoras de rendimiento que tienen +las rutas con nombre. Al conectar rutas, puedes especificar una opción ``_name``, +esta opción se puede utilizar en rutas inversas para identificar la ruta que deseas +utilizar:: + + // Conectar una ruta con nombre. + $routes->connect( + '/login', + ['controller' => 'Users', 'action' => 'login'], + ['_name' => 'login'] + ); + + // Nombrar una ruta específica según el tipo de petición + $routes->post( + '/logout', + ['controller' => 'Users', 'action' => 'logout'], + 'logout' + ); + + // Generar una URL utilizando una ruta con nombre. + $url = Router::url(['_name' => 'logout']); + + // Generar una URL utilizando una ruta con nombre, + // con algunos argumentos de cadena en la consulta. + $url = Router::url(['_name' => 'login', 'username' => 'jimmy']); + +Si tu plantilla de ruta contienen elementos de ruta como ``{controller}`` deberás +proporcionarlos como parte de las opciones de ``Router::url()``. + +.. note:: + + Los nombres de las rutas deben ser únicos en toda la aplicación. El mismo + ``_name`` no se puede utilizar dos veces, incluso si los nombres aparecen + dentro de un alcance de enrutamiento diferente. + +Al crear rutas con nombre, probablemente querrás ceñirte a algunas convenciones +para los nombres de las rutas. CakePHP facilita la creación de nombres de rutas +al permitir definir prefijos de nombres en cada ámbito:: + + $routes->scope('/api', ['_namePrefix' => 'api:'], function (RouteBuilder $routes) { + // El nombre de esta ruta será `api:ping` + $routes->get('/ping', ['controller' => 'Pings'], 'ping'); + }); + // Generar una URL para la ruta de ping + Router::url(['_name' => 'api:ping']); + + // Utilizar namePrefix con plugin() + $routes->plugin('Contacts', ['_namePrefix' => 'contacts:'], function (RouteBuilder $routes) { + // Conectar rutas. + }); + + // O con prefix() + $routes->prefix('Admin', ['_namePrefix' => 'admin:'], function (RouteBuilder $routes) { + // Conectar rutas. + }); + +También puedes utilizar la opción ``_namePrefix`` dentro de ámbitos anidados y +funciona como se esperaba:: + + $routes->plugin('Contacts', ['_namePrefix' => 'contacts:'], function (RouteBuilder $routes) { + $routes->scope('/api', ['_namePrefix' => 'api:'], function (RouteBuilder $routes) { + // Este nombre de ruta será `contacts:api:ping` + $routes->get('/ping', ['controller' => 'Pings'], 'ping'); + }); + }); + + // Generar una URL para la ruta de ping + Router::url(['_name' => 'contacts:api:ping']); + +Las rutas conectadas en ámbitos con nombre sólo se les agregarán nombres si la +ruta también tiene nombre. A las rutas sin nombre no se les aplicará el ``_namePrefix``. +Routes connected in named scopes will only have names added if the route is also +named. Nameless routes will not have the ``_namePrefix`` applied to them. + +.. index:: admin routing, prefix routing +.. _prefix-routing: + +Enrutamiento de Prefijo +----------------------- + +.. php:staticmethod:: prefix($name, $callback) + +Muchas aplicaciones requieren una sección de adminitración donde +los usuarios con privilegios puedan realizar cambios. Esto se hace +a menudo a través de una URL especial como ``/admin/users/edit/5``. +En CakePHP, el enrutamiento de prefijo puede ser habilitado utilizando +el método de ámbito ``prefix``:: + + use Cake\Routing\Route\DashedRoute; + + $routes->prefix('Admin', function (RouteBuilder $routes) { + // Todas las rutas aquí tendrán el prefijo `/admin`, y + // tendrán el elemento de ruta `'prefix' => 'Admin'` agregado que + // será necesario para generar URL para estas rutas + $routes->fallbacks(DashedRoute::class); + }); + +Los prefijos se asignan a subespacios de nombres en el espacio de nombres +``Controller`` en tu aplicación. Al tener prefijos como controladores separados, +puedes crear controladores más pequeños y simples. El comportamiento que es común +a los controladores con y sin prefijo se puede encapsular mediante herencia, +:doc:`/controllers/components`, o traits. Utilizando nuestro ejemplo de usuarios, +acceder a la URL ``/admin/users/edit/5`` llamaría al médito ``edit()`` de nuestro +**src/Controller/Admin/UsersController.php** pasando 5 como primer parámetro. +El archivo de vista utilizado sería **templates/Admin/Users/edit.php** + +Puedes asignar la URL /admin a tu acción ``index()`` del controlador pages utilizando +la siguiente ruta:: + + $routes->prefix('Admin', function (RouteBuilder $routes) { + // Dado que te encuentras en el ámbito de admin, + // no necesitas incluir el prefijo /admin ni el + // elemento de ruta Admin. + $routes->connect('/', ['controller' => 'Pages', 'action' => 'index']); + }); + +Al crear rutas de prefijo, puedes establecer parámetros de ruta adicionales +utilizando el argumento ``$options``:: + + $routes->prefix('Admin', ['param' => 'value'], function (RouteBuilder $routes) { + // Las rutas conectadas aquí tienen el prefijo '/admin' y + // tienen configurada la clave de enrutamiento 'param'. + $routes->connect('/{controller}'); + }); + +Los prefijos con varias palabras se convierten de forma predeterminada utilizando la +inflexión dasherize, es decir, ``MyPrefix`` se asignará a ``my-prefix`` en la URL. +Asegúrate de establecer una ruta para dichos prefijos si deseas utilizar un formato +diferente como, por ejemplo, subrayado:: + + $routes->prefix('MyPrefix', ['path' => '/my_prefix'], function (RouteBuilder $routes) { + // Las rutas conectadas aquí tiene el prefijo '/my_prefix' + $routes->connect('/{controller}'); + }); + +También puedes definir prefijos dentro del alcance de un plugin:: + + $routes->plugin('DebugKit', function (RouteBuilder $routes) { + $routes->prefix('Admin', function (RouteBuilder $routes) { + $routes->connect('/{controller}'); + }); + }); + +Lo anterior crearía una plantilla de ruta como ``/debug-kit/admin/{controller}``. +La ruta conectada tendría establecidos los elementos de ruta ``plugin`` y ``prefix``. + +Al definir prefijos, puedes anidar varios prefijos si es necesario:: + + $routes->prefix('Manager', function (RouteBuilder $routes) { + $routes->prefix('Admin', function (RouteBuilder $routes) { + $routes->connect('/{controller}/{action}'); + }); + }); + +Lo anterior crearía una plantilla de ruta como ``/manager/admin/{controller}/{action}``. +La ruta conectada tendría establecido el elemento de ruta ``prefix`` a ``Manager/Admin``. + +El prefijo actual estará disponible desde los métodos del controlador a través de +``$this->request->getParam('prefix')`` + +Cuando usamos rutas de prefijo es importante configurar la opción ``prefix`` y +utilizar el mismo formato CamelCased que se utiliza in el método ``prefix()``. +A continuación se explica cómo crear este enlace utilizando el helper HTML:: + + // Ve a una ruta de prefijo + echo $this->Html->link( + 'Manage articles', + ['prefix' => 'Manager/Admin', 'controller' => 'Articles', 'action' => 'add'] + ); + + // Deja un prefijo + echo $this->Html->link( + 'View Post', + ['prefix' => false, 'controller' => 'Articles', 'action' => 'view', 5] + ); + +.. index:: plugin routing + +Crear Enlaces a Rutas de Prefijo +-------------------------------- + +Puedes crear enlaces que apunten a un prefijo añadiendo la clave del prefijo a la matriz +de URL:: + + echo $this->Html->link( + 'New admin todo', + ['prefix' => 'Admin', 'controller' => 'TodoItems', 'action' => 'create'] + ); + +Al utilizar anidamiento, es necesario encadenarlos entre sí:: + + echo $this->Html->link( + 'New todo', + ['prefix' => 'Admin/MyPrefix', 'controller' => 'TodoItems', 'action' => 'create'] + ); + +Esto se vincularía a un controlador con el espacio de nombre ``App\\Controller\\Admin\\MyPrefix`` y +la ruta de archivo ``src/Controller/Admin/MyPrefix/TodoItemsController.php``. + +.. note:: + + Aquí el prefijo siempre es CamelCased, incluso si el resultado del enrutamiento + es discontinuo. + La propia ruta hará la inflexión si es necesario. + +Enrutamiento de Plugin +---------------------- + +.. php:staticmethod:: plugin($name, $options = [], $callback) + +Las rutas para :doc:`/plugins` deben crearse utilizando el método ``plugin()``. +Este método crea un nuevo ámbito de enrutamiento para las rutas del plugin:: + + $routes->plugin('DebugKit', function (RouteBuilder $routes) { + // Las rutas conectadas aquí tienen el prefijo '/debug-kit' y + // el elemento de ruta plugin configurado a 'DebugKit'. + $routes->connect('/{controller}'); + }); + +Cuando creamos ámbitos de plugin, puedes personalizar el elemento de ruta utilizado +con la opción ``path``:: + + $routes->plugin('DebugKit', ['path' => '/debugger'], function (RouteBuilder $routes) { + // Las rutas conectadas aquí tiene el prefijo '/debugger' y + // el elemento de ruta plugin configurado a 'DebugKit'. + $routes->connect('/{controller}'); + }); + +Al utilizar ámbitos, puedes anidar ámbitos de plugin dentro de ámbitos de prefijos:: + + $routes->prefix('Admin', function (RouteBuilder $routes) { + $routes->plugin('DebugKit', function (RouteBuilder $routes) { + $routes->connect('/{controller}'); + }); + }); + +Lo anteior crearía una ruta similar a ``/admin/debug-kit/{controller}``. +Tendría configurados los elementos de ruta ``prefix`` y ``plugin``. En la sección +:ref:`plugin-routes` hay más información sobre la creación de rutas de plugin. + +Crear Enlaces a Rutas de Plugin +------------------------------- + +Puedes crear enlaces que apunten a un plugin añadiendo la clave plugin a tu +matrix de URL:: + + echo $this->Html->link( + 'New todo', + ['plugin' => 'Todo', 'controller' => 'TodoItems', 'action' => 'create'] + ); + +Por el contrario, si la solicitud activa es una solicitud de plugin y deseas crear +un enlace que no tenga plugin puedes hacer lo siguiente:: + + echo $this->Html->link( + 'New todo', + ['plugin' => null, 'controller' => 'Users', 'action' => 'profile'] + ); + +Estableciendo ``'plugin' => null`` le indicas al Enrutador que quieres +crear un enlace que no forme parte de un plugin. + +Enrutamiento SEO-Friendly +------------------------- + +Algunos desarrolladores prefieren utilizar guiones en las URL, ya que se +percibe que dan un mejor posicionamiento en los motores de búsqueda. +La clase ``DashedRoute`` se puede utilizar en tu aplicación con la capacidad +de enrutar plugin, controlador y acciones camelizadas a una URL con guiones. + +Por ejemplo, si tenemos un plugin ``ToDo``, con un controlador ``TodoItems``, y +una acción ``showItems()``, se podría acceder en ``/to-do/todo-items/show-items`` +con la siguiente conexión de enrutador:: + + use Cake\Routing\Route\DashedRoute; + + $routes->plugin('ToDo', ['path' => 'to-do'], function (RouteBuilder $routes) { + $routes->fallbacks(DashedRoute::class); + }); + +Coincidencia de Métodos HTTP Específicos +---------------------------------------- + +Las rutas pueden coincidir con métodos HTTP específicos utilizando los métodos +del helper HTTP:: + + $routes->scope('/', function (RouteBuilder $routes) { + // Esta ruta sólo coincide con peticiones POST. + $routes->post( + '/reviews/start', + ['controller' => 'Reviews', 'action' => 'start'] + ); + + // Coincide con múltiples tipos de peticiones + $routes->connect( + '/reviews/start', + [ + 'controller' => 'Reviews', + 'action' => 'start', + ] + )->setMethods(['POST', 'PUT']); + }); + +Puedes hacer coincidir múltiples métodos HTTP utilizando una matriz. Dada que el +parámetro ``_method`` es una clave de enturamiento, participa tanto en el análisis +como en la generación de URL. Para generar URLs para rutas específicas de un método +necesitarás incluir la clave ``_method`` al generar la URL:: + + $url = Router::url([ + 'controller' => 'Reviews', + 'action' => 'start', + '_method' => 'POST', + ]); + +Coincidencia con Nombres de Dominio Específicos +----------------------------------------------- + +Las rutas pueden utilizar la opción ``_host`` para coincidir sólo con dominios +específicos. Puedes utilizar el comodín ``*.`` para coincidir con cualquier +subdominio:: + + $routes->scope('/', function (RouteBuilder $routes) { + // Esta ruta sólo coincide en http://images.example.com + $routes->connect( + '/images/default-logo.png', + ['controller' => 'Images', 'action' => 'default'] + )->setHost('images.example.com'); + + // Esta ruta sólo coincide en http://*.example.com + $routes->connect( + '/images/old-log.png', + ['controller' => 'Images', 'action' => 'oldLogo'] + )->setHost('*.example.com'); + }); + +La opción ``_host`` también se utiliza en la generación de URL. Si tu opción +``_host`` especifica un dominio exacto, ese dominio se incluirá en la URL +generada. Sin embargo, si utilizas un comodín, tendrás que indicar el parámetro +``_host`` al generar la URL:: + + // Si tienes esta ruta + $routes->connect( + '/images/old-log.png', + ['controller' => 'Images', 'action' => 'oldLogo'] + )->setHost('images.example.com'); + + // Necesitas esto para generar la URL + echo Router::url([ + 'controller' => 'Images', + 'action' => 'oldLogo', + '_host' => 'images.example.com', + ]); + +.. index:: file extensions +.. _file-extensions: + +Enrutamiento de Extensiones de Archivo +-------------------------------------- +.. php:staticmethod:: extensions(string|array|null $extensions, $merge = true) + +Para manejar diferentes extensiones de archivo en tus URL, puedes definir las +extensiones utilizando el método :php:meth:`Cake\\Routing\\RouteBuilder::setExtensions()`:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->setExtensions(['json', 'xml']); + }); + +Esto habilitará ls extensiones nombradas para todas las rutas que se estén conectando +en ese ámbito **después** de la llamada a ``setExtensions()``, incluidas aquellas que +se estén conectando en ámbitos anidados. + +.. note:: + + Configurar las extensiones debe ser lo primero que hagas en un ámbito, ya que + las extensiones sólo se aplicarán a rutas conectadas **después** de configurar + las extensiones. + + También ten en cuenta que los ámbitos reabiertos **no** heredarán las extensiones + definidas en ámbitos abiertos anteriormente. + +Al utilizar extensiones, le indicas al enrutador que elimine cualquier extensión de +archivo coincidente en la URL y luego analice lo que queda. Si deseas crear una URL como +/page/title-of-page.html, crearías su ruta usando:: + + $routes->scope('/page', function (RouteBuilder $routes) { + $routes->setExtensions(['json', 'xml', 'html']); + $routes->connect( + '/{title}', + ['controller' => 'Pages', 'action' => 'view'] + )->setPass(['title']); + }); + +Luego, para crear enlaces que correspondan con las rutas, simplemente usa:: + + $this->Html->link( + 'Link title', + ['controller' => 'Pages', 'action' => 'view', 'title' => 'super-article', '_ext' => 'html'] + ); + +.. _route-scoped-middleware: + +Middleware de Ámbito de Ruta +============================ + +Si bien el Middleware se puede aplicar a toda tu aplicación, aplicar middleware +a ámbitos de enrutamiento específicos ofrece más flexibilidad, ya que puedes aplicar +middleware sólo donde sea necesario, lo que permite que tu middleware no se preocupe +por cómo y dónde se aplica. + +.. note:: + + El middleware con ámbito aplicado se ejecutará mediante :ref:`RoutingMiddleware `, + normalmente al final de la cola de middleware de tu aplicación. + +Antes de que se pueda aplicar middleware a tu acplicación, es necesario +registrarlo en la colección de rutas:: + + // en config/routes.php + use Cake\Http\Middleware\CsrfProtectionMiddleware; + use Cake\Http\Middleware\EncryptedCookieMiddleware; + + $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware()); + $routes->registerMiddleware('cookies', new EncryptedCookieMiddleware()); + +Una vez registrado, el middleware con ámbito se podrá aplicar +a ámbitos específicos:: + + $routes->scope('/cms', function (RouteBuilder $routes) { + // Activa CSRF y cookies middleware + $routes->applyMiddleware('csrf', 'cookies'); + $routes->get('/articles/{action}/*', ['controller' => 'Articles']); + }); + +En situaciones en las que tienes ámbitos anidados, los ámbitos internos +heredarán el middleware aplicado en el ámbito contenedor:: + + $routes->scope('/api', function (RouteBuilder $routes) { + $routes->applyMiddleware('ratelimit', 'auth.api'); + $routes->scope('/v1', function (RouteBuilder $routes) { + $routes->applyMiddleware('v1compat'); + // Definir las rutas aquí. + }); + }); + +En el ejemplo anterior, las rutas definidas en ``/v1`` tendrán aplicado el +middleware 'ratelimit', 'auth.api' y 'v1compat'. Si vuelves a abrir un ámbito, +el middleware aplicado a las rutas en cada ámbito quedará aislado:: + + $routes->scope('/blog', function (RouteBuilder $routes) { + $routes->applyMiddleware('auth'); + // Conecta las acciones authenticadas para el blog aquí. + }); + $routes->scope('/blog', function (RouteBuilder $routes) { + // Conecta las acciones públicas para el blog aquí. + }); + +En el ejemplo anterior, los dos usos del alcance ``/blog`` no comparten middleware. +Sin embargo, ambos ámbitos heredarán el middleware definido en los ámbitos que los +engloban. + +Agrupación de Middleware +------------------------ + +Para ayudar a mantener tu código de ruta :abbr:`DRY (Do not Repeat Yourself)` el middleware +se puede combinar en grupos. Una vez combinados, los grupos pueden aplicarse como middleware:: + + $routes->registerMiddleware('cookie', new EncryptedCookieMiddleware()); + $routes->registerMiddleware('auth', new AuthenticationMiddleware()); + $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware()); + $routes->middlewareGroup('web', ['cookie', 'auth', 'csrf']); + + // Aplicar el grupo + $routes->applyMiddleware('web'); + +.. _resource-routes: + +Enrutamiento RESTful +==================== + +El enrutador ayuda a generar rutas RESTful para tus controladores. Las rutas RESTful +son útiles cuando estás creando API endpoints para tus aplicaciones. Si quisiéramos +permitir el acceso REST a un controlador de recetas, haríamos algo como esto:: + + // En config/routes.php... + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->setExtensions(['json']); + $routes->resources('Recipes'); + }); + +La primera línea configura una serie de rutas predeterminadas para el acceso REST +donde el método especifica el formato de resultado deseado, por ejemplo, xml, json +y rss. Estas rutas son sensible al método de solicitud HTTP. + +=========== ===================== ================================ +HTTP format URL.format Acción del controlador invocada +=========== ===================== ================================ +GET /recipes.format RecipesController::index() +----------- --------------------- -------------------------------- +GET /recipes/123.format RecipesController::view(123) +----------- --------------------- -------------------------------- +POST /recipes.format RecipesController::add() +----------- --------------------- -------------------------------- +PUT /recipes/123.format RecipesController::edit(123) +----------- --------------------- -------------------------------- +PATCH /recipes/123.format RecipesController::edit(123) +----------- --------------------- -------------------------------- +DELETE /recipes/123.format RecipesController::delete(123) +=========== ===================== ================================ + +.. note:: + + El patrón predeterminado para los ID de recursos sólo coincide con números + enterors o UUID. Si tus ID son diferentes, tendrás que proporcionar un + patrón de expresión regular a través de la opción ``id``, por ejemplo + ``$builder->resources('Recipes', ['id' => '.*'])``. + +El método HTTP utilizado se detecta desde algunas fuentes diferentes. +Las fuentes en orden de preferencia son: + +#. La variable POST ``_method`` +#. El encabezado The ``X_HTTP_METHOD_OVERRIDE``. +#. El encabezado ``REQUEST_METHOD`` + +La variable POST ``_method`` es útil para utilizar un navegador como +cliente REST (o cualquier otra cosa que pueda realizar POST). +Simplemente, establece el valor de ``_method()`` con el nombre del +método de la solicitud HTTP que deseas emular. + +Crear Rutas de Recursos Anidadas +-------------------------------- + +Una vez hayas conectado recursos en un alcance, también puedes conectar rutas para +subrecursos. Las rutas de subrecursos estarán precedidas por el nombre del recurso +original y un parámetro de identificación. Por ejemplo:: + + $routes->scope('/api', function (RouteBuilder $routes) { + $routes->resources('Articles', function (RouteBuilder $routes) { + $routes->resources('Comments'); + }); + }); + +Generará rutas de recursos tanto para ``articles`` como para ``comments``. +Las rutas de comments se verán así:: + + /api/articles/{article_id}/comments + /api/articles/{article_id}/comments/{id} + +Puedes obtener el ``article_id`` en ``CommentsController`` mediante:: + + $this->request->getParam('article_id'); + +De forma predeterminada, las rutas de recursos se asignan al mismo prefijo que el +ámbito contenedor. Si tienes controladores de recursos anidados y no anidados, puedes +utilizar un controlador diferente en cada contexto mediante el uso de prefijos:: + + $routes->scope('/api', function (RouteBuilder $routes) { + $routes->resources('Articles', function (RouteBuilder $routes) { + $routes->resources('Comments', ['prefix' => 'Articles']); + }); + }); + +Lo anterior asignará el recurso 'Comments' a +``App\Controller\Articles\CommentsController``. Tener controladores separados +te permite mantener la lógica del controlador más simple. Los prefijos creados de +esta manera son compatibles con :ref:`prefix-routing`. + +.. note:: + + Si bien puedes anidar recursos con la profundidas que necesites, no se recomienda + anidar más de dos recursos juntos. + +Limitar las Rutas Creadas +------------------------- + +Por defecto, CakePHP conectará 6 rutas para cada recurso. Si deseas conectar +sólo rutas de recursos específicas podrás utilizar la opción ``only``:: + + $routes->resources('Articles', [ + 'only' => ['index', 'view'] + ]); + +Crearía rutas de recurso de sólo lectura. Los nombres de las rutas son +``create``, ``update``, ``view``, ``index``, and ``delete``. + +El **nombre de ruta y acción del controlador utilizados** predeterminados son +los siguientes: + +============== ================================= +Nombre de ruta Acción del controlador utilizada +============== ================================= +create add +-------------- --------------------------------- +update edit +-------------- --------------------------------- +view view +-------------- --------------------------------- +index index +-------------- --------------------------------- +delete delete +============== ================================= + + +Cambiar las Acciones del Controlador Utilizadas +----------------------------------------------- + +Es posible que debas cambiar los nombres de las acciones del controlador que se +utilizan al conectar rutas. Por ejemplo, si tu acción ``edit()`` se llama ``put()`` +puedes utilizar la clave ``actions`` para renombrar las acciones utilizadas:: + + $routes->resources('Articles', [ + 'actions' => ['update' => 'put', 'create' => 'add'] + ]); + +Lo anterior utilizaría ``put()`` para la acción ``edit()`` y ``add()`` +en lugar de ``create()``. + +Mapeo de Rutas de Recursos Adicionales +-------------------------------------- + +Puedes asignar métodos de recursos adicionales utilizando la opción ``map``:: + + $routes->resources('Articles', [ + 'map' => [ + 'deleteAll' => [ + 'action' => 'deleteAll', + 'method' => 'DELETE' + ] + ] + ]); + // Esto conectaría /articles/deleteAll + +Además de las rutas predeterminadas, esto también conectaría una ruta para +`/articles/delete-all`. De forma predeterminada, el segmento de ruta coincidirá +con el nombre de la clave. Puedes utilizar la clave 'path' dentro de la defición +del recurso para personalizar el nombre de la ruta:: + + $routes->resources('Articles', [ + 'map' => [ + 'updateAll' => [ + 'action' => 'updateAll', + 'method' => 'PUT', + 'path' => '/update-many', + ], + ], + ]); + // Esto conectaría /articles/update-many + +Si defines 'only' and 'map', asegúrate de que tus métodos asignados también están +en la lista 'only'. + +Enrutamiento de Recursos Prefijados +----------------------------------- + +[[Continuar]] +Las rutas de recursos pueden conectarse a los controladores en prefijos de +enrutamiento conectando rutas en un ámbito prefijado or utilizando la opción ``prefix``:: + + $routes->resources('Articles', [ + 'prefix' => 'Api', + ]); + +.. _custom-rest-routing: + +Clases de Ruta Personalizada para Rutas de Recursos +--------------------------------------------------- + +Puedes proporcionar la clave ``coneectOptions`` en la matriz ``$options`` para +``resources()`` para proporcionar la configuración personalizada utilizada por +``connect()``:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->resources('Books', [ + 'connectOptions' => [ + 'routeClass' => 'ApiRoute', + ] + ]; + }); + + +Inflexión de URL para Rutas de Recursos +--------------------------------------- + +De forma predeterminada, los fragmentos de URL de los controladores con varias +palabras están en la forma con guiones del nombre del controlador. Por ejemplo, +el fragmento de URL de ``BlogPostsController`` sería **/blog-posts**. + +Puedes especificar un tipo de inflexión alternativo utilizando la opción ``inflect``:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->resources('BlogPosts', [ + 'inflect' => 'underscore' // Will use ``Inflector::underscore()`` + ]); + }); + +Lo anterior generará una URL del tipo: **/blog_posts**. + +Cambiar el Elemento de Ruta +--------------------------- + +De forma predeterminada, las rutas de recursos utilizan una forma inflexionada +del nombre del recurso para el segmento de URL. Puedes configurar un segmento +de ruta personalizado con la opción ``path``:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->resources('BlogPosts', ['path' => 'posts']); + }); + +.. index:: passed arguments +.. _passed-arguments: + +Argumentos Pasados +================== + +Los argumentos pasados son argumentos adicionales or segmentos de ruta +que se utilizan al realizar una solicitud. A menudo se utilizan para +pasar parámetros a los métodos de tu controlador:: + + http://localhost/calendars/view/recent/mark + +En el ejemplo anterior, tanto ``recent`` como ``mark`` se pasan como argumentos a +``CalendarsController::view()``. Los argumentos pasados se entregan a tus +controladores de tres maneras. En primer lugar, como argumentos para el método de +acción llamado y, en segundo lugar, están disponibles en +``$this->request->getParams('pass')`` como una matriz indexada numéricamente. Al +utilizar rutas personalizadas, también puedes forzar que parámetros particulares +entren en los argumentos pasados. + +Si visitara la URL mencionada anteriormente, y tuviera una acción de controlador +similar a:: + + class CalendarsController extends AppController + { + public function view($arg1, $arg2) + { + debug(func_get_args()); + } + } + +Otendrías el siguiente resultado:: + + Array + ( + [0] => recent + [1] => mark + ) + +Estos mismos datos también están disponibles en ``$this->request->getParam('pass')`` +en tus controladores, vistas y helpers. Los valores en la matriz de paso están +indexados numéricamente según el orden en el que aparecen en la URL llamada:: + + debug($this->request->getParam('pass')); + +Cualquiera de los anteriores generaría:: + + Array + ( + [0] => recent + [1] => mark + ) + +Al generar URL, utilizando un :term:`arreglo de enrutamiento` agregas argumentos +pasados como valores sin claves de cadena en la matriz:: + + ['controller' => 'Articles', 'action' => 'view', 5] + +Dado que ``5`` es una clave numérica, se trata como un argumento pasado. + +Generando URL +============= + +.. php:staticmethod:: url($url = null, $full = false) +.. php:staticmethod:: reverse($params, $full = false) + +Generar URL o enrutamiento inverso es una característica de CakePHP que se +utiliza para permitirte cambiar la estructura de tu URL sin tener que modificar +todo tu código. + +Si creas URL utilizando cadenas como:: + + $this->Html->link('View', '/articles/view/' . $id); + +Y luego decides que ``/articles`` realmente debería llamarse ``posts``, +tendría que ir por toda tu aplicación renombrando las URL. Sin embargo, +si definiste tu enlace como:: + + //`link()` utiliza internamente Router::url() y acepta una matriz de enrutamiento + + $this->Html->link( + 'View', + ['controller' => 'Articles', 'action' => 'view', $id] + ); + +o:: + + //'Router::reverse()' opera en la matriz de parámetos de la petición + //y producirá una cadena de url válida para `link()` + + $requestParams = Router::getRequest()->getAttribute('params'); + $this->Html->link('View', Router::reverse($requestParams)); + +Luego, cuando decidieras cambiar tus URL, podrías hacerlo definiendo una ruta. +Esto cambiaría tanto la asignación de URL entrante como las URL generadas. + +La elección de la técnica está determinada por qué tan bien se pueden predecir +los elementos de la matriz de enrutamiento. + +Utilizando ``Router::url()`` +---------------------------- + +``Router::url()`` te permite utilizar :term:`routing arrays ` +en situaciones donde los elementos requeridos de la matriz son fijos o se deducen +fácilmente. + +Proporcionará enrutamiento inverso cuando la url de destino esté bien definida:: + + $this->Html->link( + 'View', + ['controller' => 'Articles', 'action' => 'view', $id] + ); + +También es útil cuando el destino es desconocido, pero sigue un patrón bien definido:: + + $this->Html->link( + 'View', + ['controller' => $controller, 'action' => 'view', $id] + ); + +Los elementos con claves numéricas se tratan como :ref:`passed-arguments`. + +Al utilizar matrices de enrutamiento, puedes definir tanto los parámetros de +la cadena de consulta como los fragmentos de documentos utilizando claves +especiales:: + + $routes->url([ + 'controller' => 'Articles', + 'action' => 'index', + '?' => ['page' => 1], + '#' => 'top' + ]); + + // Generará una URL del tipo. + /articles/index?page=1#top + +También puedes utilizar cualquiera de los elementos de ruta especiales al generar URL: + +* ``_ext`` Se utiliza para enrutamiento de :ref:`file-extensions`. +* ``_base`` Establecer en ``false`` para eliminar la ruta base de la URL generada. + Si tu aplicación no está en el directorio raíz, esto se puede utilizar para generar + URL relativas a cake. +* ``_scheme`` Configurado para crear enlaces en diferentes esquemas como ``webcal`` o + ``ftp``. El valor predeterminado es el esquema actual. +* ``_host`` Establece el host que se utilizará en el enlace. El valor por defecto es el + del host actual. +* ``_port`` Establece el puerto si necesitas crear enlaces a puestos no estándar. +* ``_method`` Define el verbo HTTP para el que es la URL. +* ``_full`` Si es ``true`` el valor de ``App.fullBaseUrl`` mencionado en + :ref:`general-configuration` se antepondrá a las URL generadas. +* ``_https`` Establecer en ``true`` para convertir la URL generada a https o ``false`` + para forzar http. +* ``_name`` Nombre de la ruta. Si has configurado rutas con nombre, puedes utilizar esta + clave para especificarlas. + +Utilizando ``Router::reverse()`` +-------------------------------- + +``Router::reverse()`` te permite utilizar los :ref:`request-parameters` en casos +donde la URL actual con algunas modificaciones es la base para el destino y los +elementos de la URL actual son impredecibles. + +Como ejemplo, imagina un blog que permite a los usuarios crear **Articles** y +**Comments**, y marcar ambos como *published* o *draft*. Ambas URL de la página index +pueden incluir el ID del usuario. La URL de **Comments** también puede incluir el +ID de un **Article** para identificar a qué artículo se refieren los comentarios. + +Aquí están las url para este escenario:: + + /articles/index/42 + /comments/index/42/18 + +Cuando el autor utilice estas páginas, sería conveniente incluir enlaces que +permitan mostrar la página con todos los resultados, sólo publicados o sólo +borrador. + +Para mantener el código DRY, sería mejor incluir los enlaces a través de un +elemento:: + + // element/filter_published.php + + $params = $this->getRequest()->getAttribute('params'); + + /* preparar la url para Borrador */ + $params = Hash::insert($params, '?.published', 0); + echo $this->Html->link(__('Draft'), Router::reverse($params)); + + /* Preparar la url para Publicados */ + $params = Hash::insert($params, '?.published', 1); + echo $this->Html->link(__('Published'), Router::reverse($params)); + + /* Preparar la url para Todos */ + $params = Hash::remove($params, '?.published'); + echo $this->Html->link(__('All'), Router::reverse($params)); + +Los enlaces generados por estas llamadas incluirían uno o dos parámetros +de paso dependiendo de la estructura de la URL actual. Y el código +funcionaría para cualquier URL futura, por ejemplo, si comenzara a usar +prefijos de ruta o si agregara más parámetros del paso. + +Matrices de Enrutamiento vs Parámetros de Solicitud +--------------------------------------------------- + +La diferencia significativa entre las dos matrices y su uso en estos métodos +de enrutamiento inverso está la forma en la que incluyen los parámetros +de paso. + +Las matrices de enrutamiento incluyen los parámetros de paso como valores +sin clave en la matriz:: + + $url = [ + 'controller' => 'Articles', + 'action' => 'View', + $id, //un parámetro de paso + 'page' => 3, //un argumento de consulta + ]; + +Los parámetros de consulta incluyen parámtros de paso en la clave 'pass' +de la matriz:: + + $url = [ + 'controller' => 'Articles', + 'action' => 'View', + 'pass' => [$id], //los parámetros de paso + '?' => ['page' => 3], //los parámtros de consulta + ]; + +Por lo tanto, si los deseas, es posible convertir los parámetros de solicitud +en una matriz de enrutamiento o viceversa. + +.. _asset-routing: + +Generando URL de Activos +======================== + +La clase ``Asset`` proporciona métodos para generar URL para los archivos css, +javascript, imágenes y otros archivos de activos estáticos de tu aplicación:: + + use Cake\Routing\Asset; + + // Generar una URL para APP/webroot/js/app.js + $js = Asset::scriptUrl('app.js'); + + // Generar una URL para APP/webroot/css/app.css + $css = Asset::cssUrl('app.css'); + + // Generar una URL para APP/webroot/image/logo.png + $img = Asset::imageUrl('logo.png'); + + // Generar una URL para APP/webroot/files/upload/photo.png + $file = Asset::url('files/upload/photo.png'); + +Los métodos anteriores también aceptan una matriz de opciones como segundo parámetro: + +* ``fullBase`` Agrega la URL completa con el nombre de dominio. +* ``pathPrefix`` Prefijo de ruta para URL relativas. +* ``plugin`` Puedes añdirlo como ``false`` para evitar que las rutas se traten como + un recurso de un plugin. +* ``timestamp`` Sobrescribe el valor de ``Asset.timestamp`` en Configure. Establecer + a ``false`` para omitir la generación de la marca de tiempo. Establecer a ``true`` + para aplicar marcas de tiempo cuando el debug is true. Set to ``'force'`` para habilitar + siempre la marca de tiempo independientemente del valor de debug. + +:: + + // Genera http://example.org/img/logo.png + $img = Asset::url('logo.png', ['fullBase' => true]); + + // Genera /img/logo.png?1568563625 + // Donde la marca de tiempo es la ultima hora de modificación del archivo + $img = Asset::url('logo.png', ['timestamp' => true]); + +Para generar URL de activo para archivos en un plugin utiliza +:term:`Sintaxis de plugin`:: + + // Genera `/debug_kit/img/cake.png` + $img = Asset::imageUrl('DebugKit.cake.png'); + +.. _redirect-routing: + +Redirección de Enrutamiento +=========================== + +La redirección de enrutamiento te permite emitir redirección de estado +HTTP 30x para rutas entrantes y apuntarlas a URL diferentes. Esto es útil +cuando deseas informar a las aplicaciones cliente que un recurso se ha +movido y no deseas exponer dos URL para el mismo contenido. + +Las rutas de redireccionamiento son diferentes de las rutas normales en que +realizan una redirección de encabezado real si se encuentra una coincidencia. +La redirección puede ocurrir a un destino dentro de tu aplicación o a una +ubicación externa:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->redirect( + '/home/*', + ['controller' => 'Articles', 'action' => 'view'], + ['persist' => true] + // O ['persist'=>['id']] para el enrutamiento predeterminado + // donde la acción view espera $id como argumento. + ); + }) + +Redirige ``/home/*`` a ``/articles/view`` y pasa los parámetros a +``/articles/view``. El uso de una matriz como destino de +redireccionamiento te permite utilizar otras rutas para definir +hacia dónde se debe dirigir una cadena de URL. Puedes redirigir a +ubicaciones externas utilizando una cadena de URL como destino:: + + $routes->scope('/', function (RouteBuilder $routes) { + $routes->redirect('/articles/*', 'http://google.com', ['status' => 302]); + }); + +Esto redirigiría ``/articles/*`` a ``http://google.com`` con un estado +HTTP de 302. + +.. _entity-routing: + +Enrutamiento de Entidades +========================= + +El enrutamiento de entidades te permite utilizar una entidad, una matriz o un objeto +que implemente ``ArrayAccess`` como una fuente de parámetros de enrutamiento. Esto +te permite refactorizar rutas fácilmente y generar URL con menos código. Por ejemplo, +si comienzas con una ruta similar a:: + + $routes->get( + '/view/{id}', + ['controller' => 'Articles', 'action' => 'view'], + 'articles:view' + ); + +Puedes generar URL para esta ruta utilizando:: + + // $article es una entidad en el ámbito local. + Router::url(['_name' => 'articles:view', 'id' => $article->id]); + +Más adelante, es posible que desees exponer el slug del artículo en la URL con +fines de SEO. Para hacer esto necesitarás actualizar todos los lugares donde +generes una URL a la ruta ``articles:view``, lo que podría llevar algún tiempo. +Si utilizamos rutas de entidad, pasamos toda la entidad del artículo a la +generación de URL, lo que nos permite omitir cualquier reelaboración cuando las +URL requiren más parámetros:: + + use Cake\Routing\Route\EntityRoute; + + // Crea más rutas de entidad para el resto de este ámbito. + $routes->setRouteClass(EntityRoute::class); + + // Crea la ruta como antes. + $routes->get( + '/view/{id}/{slug}', + ['controller' => 'Articles', 'action' => 'view'], + 'articles:view' + ); + +Ahora podemos generar URL utilizando la clave ``_entity``:: + + Router::url(['_name' => 'articles:view', '_entity' => $article]); + +Esto extraerá las propiedades ``id`` y ``slug`` de la entidad proporcionada. + +.. _custom-route-classes: + +Clases de Ruta Personalizadas +============================= + +Las clases de ruta personalizadas te permiten ampliar y cambiar la forma en que +las rutas individuales analizan las solicitudes y manejan el enrutamiento inverso. +Las clases de ruta tienen algunas convenciones: + +* Se espera que las clases de ruta se encuentren en el espacio de nombres ``Routing\\Route`` de tu aplicación o plugin. +* Las clases de ruta deben extender :php:class:`Cake\\Routing\\Route\\Route`. +* Las clases de ruta deben implementar uno o ambos ``match()`` y/o ``parse()``. + +El método ``parse()`` se utiliza para analizar una URL entrante. Debería generar +una matriz de parámetros de solicitud que pueda resolverse en un controlador +y acción. Este método devuelve ``null`` para indicar un error de coincidencia. + +El método ``match()`` se utiliza para hacer coincidir una matriz de parámetros +de URL y crear una cadena de URL. Si los parámertos de la URL no coinciden, se +debe devolver la ruta ``false``. + +Puedes utilizar una clase de ruta personalizada al realizar una ruta utilizando +la opción ``routeClass``:: + + $routes->connect( + '/{slug}', + ['controller' => 'Articles', 'action' => 'view'], + ['routeClass' => 'SlugRoute'] + ); + + // O configurando routeClass en su ámbito. + $routes->scope('/', function (RouteBuilder $routes) { + $routes->setRouteClass('SlugRoute'); + $routes->connect( + '/{slug}', + ['controller' => 'Articles', 'action' => 'view'] + ); + }); + +Esta ruta crearía una instancia de ``SlugRoute`` y te permitiría implementar +un manejo de parámetros personalizado. Puedes utilizar clases de ruta de plugin +utilizando el estándar :term:`Sintaxis de plugin`. + +Clase de Ruta Predeterminada +---------------------------- + +.. php:staticmethod:: setRouteClass($routeClass = null) + +Si desea utilizar una ruta de clase alternativa para tus rutas además de la +``Ruta`` predeterminada, puedes hacerlo llamando a ``RouterBuilder::setRouteClass()`` +antes de configurar cualquier ruta y evitar tener que especificar la opción +``routeClass`` para cada ruta. Por ejemplo utilizando:: + + use Cake\Routing\Route\DashedRoute; + + $routes->setRouteClass(DashedRoute::class); + +Hará que todas las rutas conectadas después de esto utilicen la clase de ruta +``DashedRoute``. Llamando al método sin un argumento devolverá la clase de ruta +predeterminada actual. + +Método de Respaldo/Alternativas +------------------------------- + +.. php:method:: fallbacks($routeClass = null) + +El método de respaldo es un atajo simple para definir rutas predeterminadas. +El método utiliza la clase de enrutamiento pasada para las reglas definidas o, +si no se proporciona ninguna clase, se utiliza la clase devuelta por +``RouterBuilder::setRouteClass()``. + +Llamar a alternativas así:: + + use Cake\Routing\Route\DashedRoute; + + $routes->fallbacks(DashedRoute::class); + +Es equivalente a las siguientes llamadas explícitas:: + + use Cake\Routing\Route\DashedRoute; + + $routes->connect('/{controller}', ['action' => 'index'], ['routeClass' => DashedRoute::class]); + $routes->connect('/{controller}/{action}/*', [], ['routeClass' => DashedRoute::class]); + +.. note:: + + El uso de la clase de ruta predeterminada (``Route``) con alternativas, + or cualquier ruta con elemento de ruta ``{plugin}`` o ``{controller}`` + dará como resultado una URL inconsistente. + +.. warning:: + Las plantillas de ruta alternativas son muy genéricas y permites generar y + analizar URL para controladore sy acciones que no existen. Las URL + alternativas también pueden introducir ambigüedad y duplicidad en tus URL. + + A medida que tu aplicaicón crece, se recomienda alejarse de las URL alternativas + y definir explícitamente las rutas en tu aplicación. + +Crear Parámetros de URL Persistentes +==================================== + +Puedes conectarte al proceso de generación de URL utilizando funciones de filtro +de URL. Las funciones de filtro se llaman *antes* de que las URL coincidan con las +rutas, esto te permite preparar las URL antes de enrutarlas. + +La devolución de llamada de las funciones de filtro deben contar con los siguientes +parámetos: + +- ``$params`` La matriz de parámetros de URL que se está procensando. +- ``$request`` La petición actual (instancia de ``Cake\Http\ServerRequest``). + +La función de filtro de URL *siempre* debería devolver los parámetros incluso si +no están modificados. + +Los filtros de URL te permiten implementar funciones como parámetros persistentes:: + + Router::addUrlFilter(function (array $params, ServerRequest $request) { + if ($request->getParam('lang') && !isset($params['lang'])) { + $params['lang'] = $request->getParam('lang'); + } + return $params; + }); + +Las funciones de filtro se aplican en el orden en que están conectadas. + +Otro caso de uso es cambiar una determinada ruta en tiempo de ejecución +(por ejemplo, rutas de plugin):: + + Router::addUrlFilter(function (array $params, ServerRequest $request) { + if (empty($params['plugin']) || $params['plugin'] !== 'MyPlugin' || empty($params['controller'])) { + return $params; + } + if ($params['controller'] === 'Languages' && $params['action'] === 'view') { + $params['controller'] = 'Locations'; + $params['action'] = 'index'; + $params['language'] = $params[0]; + unset($params[0]); + } + return $params; + }); + +Esto alterará la siguiente ruta:: + + Router::url(['plugin' => 'MyPlugin', 'controller' => 'Languages', 'action' => 'view', 'es']); + +En esto:: -.. toctree:: - :glob: - :maxdepth: 1 + Router::url(['plugin' => 'MyPlugin', 'controller' => 'Locations', 'action' => 'index', 'language' => 'es']); - /development/dispatch-filters +.. warning:: + Si estás utilizando las funcionees de almacenamiento de caché de + :ref:`routing-middleware` debes definir los filtros de URL en tu aplicación + ``bootstrap()`` ya que los filtros no son parte de los datos almacenados en + caché. .. meta:: - :title lang=es: Routing + :title lang=es: Enrutamiento :keywords lang=es: controller actions,default routes,mod rewrite,code index,string url,php class,incoming requests,dispatcher,url url,meth,maps,match,parameters,array,config,cakephp,apache,router diff --git a/es/development/sessions.rst b/es/development/sessions.rst index 671d08ecd0..464451fb73 100644 --- a/es/development/sessions.rst +++ b/es/development/sessions.rst @@ -1,15 +1,390 @@ -Sessions -######## +Sesiones +########### -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +CakePHP proporciona una envoltura y una serie de funciones de utilidad sobre la extensión nativa de sesión de PHP. Las sesiones te permiten identificar usuarios únicos a lo largo de las solicitudes y almacenar datos persistentes para usuarios específicos. A diferencia de las cookies, los datos de sesión no están disponibles en el lado del cliente. En CakePHP, se evita el uso de `$_SESSION` y se prefiere el uso de las clases de Sesión. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +.. _session-configuration: - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Configuración de Sesión +========================= + +La configuración de sesión generalmente se define en **/config/app.php**. Las opciones disponibles son: + +* ``Session.timeout`` - El número de *minutos* antes de que el manejador de sesiones de CakePHP expire la sesión. +* ``Session.defaults`` - Te permite usar las configuraciones de sesión predeterminadas incorporadas como base para tu configuración de sesión. Consulta a continuación para ver las configuraciones predeterminadas. +* ``Session.handler`` - Te permite definir un manejador de sesiones personalizado. Los manejadores de sesiones de base de datos y caché utilizan esto. Consulta a continuación para obtener información adicional sobre los manejadores de sesiones. +* ``Session.ini`` - Te permite establecer configuraciones adicionales de ini de sesión para tu configuración. Esto, combinado con ``Session.handler``, reemplaza las características de manejo de sesiones personalizadas de las versiones anteriores. +* ``Session.cookie`` - El nombre de la cookie que se utilizará. De forma predeterminada, se establece en el valor configurado para ``session.name`` en php.ini. +* ``Session.cookiePath`` - La ruta URL para la cual se establece la cookie de sesión. Se mapea a la configuración ``session.cookie_path`` de php.ini. De forma predeterminada, se establece en la ruta base de la aplicación. + +Las configuraciones predeterminadas de CakePHP establecen ``session.cookie_secure`` en ``true``, cuando tu aplicación +está en un protocolo SSL. Si tu aplicación se sirve tanto desde protocolos SSL como no SSL, podrías tener problemas +con las sesiones que se pierden. Si necesitas acceder a la sesión en dominios SSL y no SSL, deberás deshabilitar esto:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + 'session.cookie_secure' => false + ] + ]); + +A partir de la versión 4.0 de CakePHP, también se establece el atributo `SameSite `__ en ``Lax`` de forma predeterminada para las cookies de sesión, lo que ayuda a proteger contra ataques CSRF. Puedes cambiar el valor predeterminado configurando la opción ``session.cookie_samesite`` en php.ini:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + 'session.cookie_samesite' => 'Strict', + ], + ]); + + +La ruta de la cookie de sesión se establece de forma predeterminada en la ruta base de la aplicación. Para cambiar esto, puedes usar el valor de ``session.cookie_path`` en ini. Por ejemplo, si quieres que tu sesión persista en todos los subdominios, puedes hacerlo así:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + 'session.cookie_path' => '/', + 'session.cookie_domain' => '.tudominio.com', + ], + ]); + +De forma predeterminada, PHP configura la cookie de sesión para que caduque tan pronto como se cierre el navegador, independientemente del valor configurado en ``Session.timeout``. El tiempo de espera de la cookie está controlado por el valor de ``session.cookie_lifetime`` en ini y se puede configurar así:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + // Invalidar la cookie después de 30 minutos sin visitar + // ninguna página en el sitio. + 'session.cookie_lifetime' => 1800 + ] + ]); + +La diferencia entre ``Session.timeout`` y el valor de ``session.cookie_lifetime`` es que este último depende de que el cliente diga la verdad acerca de la cookie. Si necesitas una comprobación de tiempo de espera más estricta, sin depender de lo que el cliente informa, deberías usar ``Session.timeout``. + +Ten en cuenta que ``Session.timeout`` corresponde al tiempo total de inactividad para un usuario (es decir, el tiempo sin visitar ninguna página donde se utilice la sesión), y no limita el total de minutos que un usuario puede permanecer en el sitio. + +Manejadores de Sesiones Incorporados y Configuración +===================================================== + +CakePHP viene con varias configuraciones de sesión incorporadas. Puedes usar estas configuraciones como base para tu configuración de sesión o crear una solución completamente personalizada. Para usar las configuraciones predeterminadas, simplemente configura la clave 'defaults' con el nombre del predeterminado que deseas utilizar. Luego puedes anular cualquier configuración secundaria declarándola en tu configuración de Sesión:: + + Configure::write('Session', [ + 'defaults' => 'php' + ]); + +Lo anterior usará la configuración de sesión 'php' incorporada. Puedes agregar partes o la totalidad de ella haciendo lo siguiente:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'cookie' => 'mi_app', + 'timeout' => 4320 // 3 días + ]); + +Lo anterior anula el tiempo de espera y el nombre de la cookie para la configuración de sesión 'php'. Las configuraciones incorporadas son: + +* ``php`` - Guarda sesiones con las configuraciones estándar en tu archivo php.ini. +* ``cake`` - Guarda sesiones como archivos dentro de ``tmp/sessions``. Esta es una buena opción cuando estás en hosts que no te permiten escribir fuera de tu propio directorio de inicio. +* ``database`` - Utiliza las sesiones de base de datos incorporadas. Consulta a continuación para obtener más información. +* ``cache`` - Utiliza las sesiones de caché incorporadas. Consulta a continuación para obtener más información. + +Manejadores de Sesiones +--------------------------- + +Los manejadores de sesiones también se pueden definir en el array de configuración de la sesión. Al definir la clave de configuración 'handler.engine', puedes nombrar la clase o proporcionar una instancia del manejador. La clase/objeto debe implementar la interfaz nativa de PHP ``SessionHandlerInterface``. Implementar esta interfaz permitirá que ``Session`` mapee automáticamente los métodos para el manejador. Tanto los manejadores de sesiones de base de datos como de caché utilizan este método para guardar sesiones. Las configuraciones adicionales + +Para el manejador deben colocarse dentro del array del manejador. Luego puedes leer esos valores desde dentro de tu manejador:: + + 'Session' => [ + 'handler' => [ + 'engine' => 'DatabaseSession', + 'model' => 'SesionesPersonalizadas', + ], + ] + +Lo anterior muestra cómo podrías configurar el manejador de sesiones de base de datos con un modelo de aplicación. Al utilizar +nombres de clases como tu 'handler.engine', CakePHP esperará encontrar tu clase en el espacio de nombres ``Http\Session``. +Por ejemplo, si tenías una clase ``AppSessionHandler``, el archivo debería estar en **src/Http/Session/AppSessionHandler.php**, +y el nombre de la clase debería ser ``App\Http\Session\AppSessionHandler``. También puedes usar manejadores de sesiones desde +dentro de plugins, estableciendo el motor en ``MyPlugin.PluginSessionHandler``. + +Sesiones de Base de Datos +------------------------------- + +Si necesitas usar una base de datos para almacenar los datos de tu sesión, configúralo de la siguiente manera:: + + 'Session' => [ + 'defaults' => 'database' + ] + +Esta configuración requiere una tabla de base de datos con este esquema:: + + CREATE TABLE `sessions` ( + `id` char(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL, + `created` datetime DEFAULT CURRENT_TIMESTAMP, -- Opcional + `modified` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, -- Opcional + `data` blob DEFAULT NULL, -- para PostgreSQL, usa bytea en lugar de blob + `expires` int(10) unsigned DEFAULT NULL, + PRIMARY KEY (`id`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +Puedes encontrar una copia del esquema para la tabla de sesiones en el `esqueleto de la aplicación `_ en **config/schema/sessions.sql**. + +También puedes usar tu propia clase de ``Table`` para manejar el guardado de las sesiones:: + + 'Session' => [ + 'defaults' => 'database', + 'handler' => [ + 'engine' => 'DatabaseSession', + 'model' => 'SesionesPersonalizadas', + ], + ] + +Lo anterior le dirá a Session que use las configuraciones predeterminadas de 'database' y especifica que una tabla llamada ``SesionesPersonalizadas`` será la encargada de guardar la información de la sesión en la base de datos. + +.. _sessions-cache-sessions: + +Sesiones de Caché +------------------ + +La clase Cache se puede utilizar para almacenar sesiones también. Esto te permite almacenar sesiones en una caché como APCu o Memcached. Hay algunas advertencias al usar sesiones en caché, ya que si agotas el espacio de la caché, las sesiones comenzarán a caducar a medida que se eliminan registros. + +Para usar sesiones basadas en caché, puedes configurar tu configuración de Sesión así:: + + Configure::write('Session', [ + 'defaults' => 'cache', + 'handler' => [ + 'config' => 'session', + ], + ]); + +Esto configurará Session para usar la clase ``CacheSession`` como el delegado para guardar las sesiones. Puedes usar la clave 'config' para especificar qué configuración de caché usar. La configuración de caché predeterminada es ``'default'``. + +Bloqueo de Sesiones +-------------------- + +El esqueleto de la aplicación viene preconfigurado con una configuración de sesión como esta:: + + 'Session' => [ + 'defaults' => 'php', + ], + +Esto significa que CakePHP manejará las sesiones según lo que esté configurado en tu ``php.ini``. +En la mayoría de los casos, esta será la configuración predeterminada, por lo que PHP guardará +cualquier sesión recién creada como un archivo en, por ejemplo, ``/var/lib/php/session``. + +Pero esto también significa que cualquier tarea computacionalmente intensiva, como consultar un gran +conjunto de datos combinado con una sesión activa, **bloqueará ese archivo de sesión**, lo que +bloqueará a los usuarios para, por ejemplo, abrir una segunda pestaña de tu aplicación para +hacer algo más mientras tanto. + +Para evitar este comportamiento, tendrás que cambiar la forma en que CakePHP maneja las sesiones +utilizando un manejador de sesiones diferente como :ref:`sessions-cache-sessions` combinado con +el :ref:`Motor Redis ` u otro motor de caché. + +.. tip:: + + Si deseas leer más sobre el Bloqueo de Sesiones, consulta `aquí `_. + +Configuración de Directivas de ini +==================================== + +Las configuraciones predeterminadas incorporadas intentan proporcionar una base común para la configuración de sesiones. +Es posible que necesites ajustar flags de ini específicos también. CakePHP expone la capacidad de personalizar las configuraciones +de ini tanto para las configuraciones predeterminadas como para las personalizadas. La clave ``ini`` en las configuraciones +de sesión te permite especificar valores de configuración individuales. Por ejemplo, puedes usarlo para controlar configuraciones como ``session.gc_divisor``:: + + Configure::write('Session', [ + 'defaults' => 'php', + 'ini' => [ + 'session.cookie_name' => 'MiCookie', + 'session.cookie_lifetime' => 1800, // Válido por 30 minutos + 'session.gc_divisor' => 1000, + 'session.cookie_httponly' => true + ] + ]); + +Creación de un Manejador de Sesiones Personalizado +=================================================== + +Crear un manejador de sesiones personalizado es sencillo en CakePHP. En este ejemplo, crearemos un +manejador de sesiones que almacene sesiones tanto en la Caché (APC) como en la base de datos. Esto +nos brinda lo mejor de ambas opciones: la entrada/salida rápida de APC, sin tener que preocuparnos +por las sesiones que desaparecen cuando la caché se llena. + +Primero necesitamos crear nuestra clase personalizada y ponerla en **src/Http/Session/ComboSession.php**. La clase debería verse algo así:: + + namespace App\Http\Session; + + use Cake\Cache\Cache; + use Cake\Core\Configure; + use Cake\Http\Session\DatabaseSession; + + class ComboSession extends DatabaseSession + { + protected $cacheKey; + + public function __construct() + { + $this->cacheKey = Configure::read('Session.handler.cache'); + parent::__construct(); + } + + // Lee datos de la sesión. + public function read($id): string + { + $result = Cache::read($id, $this->cacheKey); + if ($result) { + return $result; + } + return parent::read($id); + + + } + + // Escribe datos en la sesión. + public function write($id, $data): bool + { + Cache::write($id, $data, $this->cacheKey); + return parent::write($id, $data); + } + + // Destruye una sesión. + public function destroy($id): bool + { + Cache::delete($id, $this->cacheKey); + return parent::destroy($id); + } + + // Elimina sesiones caducadas. + public function gc($expires = null): bool + { + return parent::gc($expires); + } + } + +Nuestra clase extiende el ``DatabaseSession`` incorporado para no tener que duplicar toda su lógica y comportamiento. Envolvemos +cada operación con una operación de :php:class:`Cake\\Cache\\Cache`. Esto nos permite obtener sesiones de la caché rápida y no +tener que preocuparnos por lo que sucede cuando llenamos la caché. En **config/app.php** haz que el bloque de sesión se vea así:: + + 'Session' => [ + 'defaults' => 'database', + 'handler' => [ + 'engine' => 'ComboSession', + 'model' => 'Session', + 'cache' => 'apc', + ], + ], + // Asegúrate de agregar una configuración de caché apc + 'Cache' => [ + 'apc' => ['engine' => 'Apc'] + ] + +Ahora nuestra aplicación comenzará a usar nuestro manejador de sesiones personalizado para leer y escribir datos de sesión. + +.. php:class:: Sesión + +.. _accessing-session-object: + +Acceso al Objeto de Sesión +=========================== + +Puedes acceder a los datos de sesión en cualquier lugar donde tengas acceso a un objeto de solicitud. Esto significa que la sesión es accesible desde: + +* Controladores +* Vistas +* Ayudantes (Helpers) +* Celdas (Cells) +* Componentes + +Un ejemplo básico de uso de sesión en controladores, vistas y celdas sería:: + + $nombre = $this->request->getSession()->read('Usuario.nombre'); + + // Si accedes a la sesión varias veces, + // probablemente querrás una variable local. + $sesion = $this->request->getSession(); + $nombre = $sesion->read('Usuario.nombre'); + +En los ayudantes, usa ``$this->getView()->getRequest()`` para obtener el objeto de solicitud; +en los componentes, usa ``$this->getController()->getRequest()``. + +Lectura y Escritura de Datos de Sesión +======================================= + +.. php:method:: read($clave, $predeterminado = null) + +Puedes leer valores de la sesión utilizando una sintaxis compatible con :php:meth:`Hash::extract()`. Ejemplo:: + + $sesion->read('Config.idioma', 'es'); + +.. php:method:: readOrFail($clave) + +Lo mismo que una envoltura de conveniencia alrededor de un valor de retorno no nulo:: + + $sesion->readOrFail('Config.idioma'); + +Esto es útil cuando sabes que esta clave debe estar configurada y no deseas tener que comprobar su existencia en el código mismo. + +.. php:method:: write($clave, $valor) + +``$clave`` debería ser la ruta separada por puntos a la que deseas escribir ``$valor``:: + + $sesion->write('Config.idioma', 'es'); + +También puedes especificar uno o varios hashes así:: + + $sesion->write([ + 'Config.tema' => 'azul', + 'Config.idioma' => 'es', + ]); + +.. php:method:: delete($clave) + +Cuando necesitas eliminar datos de la sesión, puedes usar ``delete()``:: + + $sesion->delete('Algo.valor'); + +.. php:staticmethod:: consume($clave) + +Cuando necesitas leer y eliminar datos de la sesión, puedes usar ``consume()``:: + + $sesion->consume('Algo.valor'); + +.. php:method:: check($clave) + +Si deseas ver si los datos existen en la sesión, puedes usar ``check()``:: + + if ($sesion->check('Config.idioma')) { + // Config.idioma existe y no es nulo. + } + +Destrucción de la Sesión +========================= + +.. php:method:: destroy() + +Destruir la sesión es útil cuando los usuarios cierran sesión. Para destruir una sesión, usa el método ``destroy()``:: + + $sesion->destroy(); + +Destruir una sesión eliminará todos los datos del lado del servidor en la sesión, pero **no** eliminará la cookie de la sesión. + +Rotación de Identificadores de Sesión +====================================== + +.. php:method:: renew() + +Mientras que el ``Plugin de Autenticación`` renueva automáticamente el ID de sesión cuando los usuarios inician sesión y cierran sesión, es posible que necesites rotar los ID de sesión manualmente. Para hacerlo, usa el método ``renew()``:: + + $sesion->renew(); + +Mensajes Flash +=============== + +Los mensajes flash son pequeños mensajes que se muestran a los usuarios una vez. A menudo se utilizan para presentar mensajes de error o confirmar que las acciones se realizaron con éxito. + +Para establecer y mostrar mensajes flash, debes usar el :doc:`Componente Flash ` y :doc:`Ayudante Flash `. .. meta:: - :title lang=es: Sessions - :keywords lang=es: session defaults,session classes,utility features,session timeout,session ids,persistent data,session key,session cookie,session data,last session,core database,security level,useragent,security reasons,session id,attr,countdown,regeneration,sessions,config + :title lang=es: Sesiones + :keywords lang=en: session defaults,session classes,utility features,session timeout,session ids,persistent data,session key,session cookie,session data,last session,core database,security level,useragent,security reasons,session id,attr,countdown,regeneration,sessions,config diff --git a/es/epub-contents.rst b/es/epub-contents.rst index 24a55dcc12..3d175d17ee 100644 --- a/es/epub-contents.rst +++ b/es/epub-contents.rst @@ -1,14 +1,14 @@ :orphan: -Contents -######## +Contenidos +########## .. toctree:: :maxdepth: 3 intro quickstart - appendices/4-0-migration-guide + appendices/migration-guides tutorials-and-examples contributing @@ -20,10 +20,9 @@ Contents views orm - controllers/components/authentication - bake core-libraries/caching - console-and-shells + bake + console-commands development/debugging deployment core-libraries/email @@ -32,7 +31,7 @@ Contents core-libraries/internationalization-and-localization core-libraries/logging core-libraries/form - controllers/components/pagination + controllers/pagination plugins development/rest security @@ -42,7 +41,6 @@ Contents core-libraries/app core-libraries/collections - core-libraries/file-folder core-libraries/hash core-libraries/httpclient core-libraries/inflector @@ -53,12 +51,14 @@ Contents core-libraries/xml core-libraries/global-constants-and-functions + chronos debug-kit migrations + elasticsearch appendices .. todolist:: .. meta:: - :title lang=es: Contents - :keywords lang=es: core libraries,ref search,shells,deployment,appendices,glossary,models + :title lang=es: Contenidos + :keywords lang=en: core libraries,ref search,commands,deployment,appendices,glossary,models diff --git a/es/index.rst b/es/index.rst index 41f437ee6e..4f5131b4fd 100644 --- a/es/index.rst +++ b/es/index.rst @@ -1,11 +1,11 @@ Bienvenido ########## -CakePHP 3 es un ``framework`` de desarrollo web que funciona con PHP |phpversion| (min. PHP |minphpversion|). +CakePHP 5 es un ``framework`` de desarrollo web que funciona con PHP |phpversion| (min. PHP |minphpversion|). Puedes leer :doc:`CakePHP de un Vistazo ` para introducirte en los fundamentos de CakePHP 3. El manual de CakePHP (``Cookbook``) es un proyecto de documentación abierto, -editable y mantenido por la comunidad. Fíjate en el icono del lápiz anclado en +editable y mantenido por la comunidad. Fíjate en el icono del lápiz anclado en el lado derecho de la página; te llevará al editor online de GitHub de la página en la que estés permitiéndote contribuir con cualquier añadido, borrado o corrección de la documentación. diff --git a/es/installation.rst b/es/installation.rst index a3cb5d76b9..0a8bcb64e9 100644 --- a/es/installation.rst +++ b/es/installation.rst @@ -1,7 +1,7 @@ Instalación ########### -CakePHP se instala rápida y fácilmente. Los requisitos mínimos son +CakePHP se instala rápida y fácilmente. ¡Los requisitos mínimos son un servidor web y una copia de CakePHP, y ya! Aunque este manual se enfoca principalmente en configurar Apache (ya que es el más utilizado), puedes configurar CakePHP para que corra con una variedad de servidores web @@ -16,7 +16,7 @@ Requisitos - extensión mbstring. - extensión intl. -Técnicamente una base de datos no es necesaria, pero imaginamos que la +Técnicamente, una base de datos no es necesaria, pero imaginamos que la mayoría de aplicaciones utiliza alguna. CakePHP soporta una gran variedad de sistemas de bases de datos: @@ -96,14 +96,14 @@ Permisos CakePHP utiliza el directorio **tmp** para varias operaciones. Descripciones de Modelos, el caché de las vistas y la información de la sesión son algunos -ejemplos de lo anterior. El directorio **logs** es utilizado para para escribir +ejemplos de lo anterior. El directorio **logs** es utilizado para escribir ficheros de log por el motor de ``FileLog`` por defecto. Asegúrate de que los directorios **logs**, **tmp** y todos sus subdirectorios tengan permisos de escritura por el usuario del Servidor Web. La instalación de CakePHP a través de Composer se encarga de este proceso haciendo que dichos directorios tengan los permisos abiertos globalmente con el fin de que puedas -tener ajustado todo de manera más rápida. Obviamente es recomendable que revises, y +tener ajustado todo de manera más rápida. Obviamente, es recomendable que revises, y modifiques si es necesario, los permisos tras la instalación vía Composer para mayor seguridad. @@ -129,10 +129,10 @@ colocarla en el directorio raíz de tu Servidor Web, o tan complejo y flexible como lo desees. Esta sección cubrirá los dos tipos principales de instalación de CakePHP: Desarrollo y Producción. -- Desarrollo: fácil de arrancar, las URLs de la aplicación incluyen +- Desarrollo: fácil de arrancar, las URL de la aplicación incluyen el nombre del directorio de la aplicación de CakePHP y es menos segura. - Producción: Requiere tener la habilidad de configurar el directorio raíz - del Servidor Web, cuenta con URLs limpias y es bastante segura. + del Servidor Web, cuenta con URL limpias y es bastante segura. Desarrollo ========== @@ -272,7 +272,7 @@ obtener información. # Deny from all -#. Asegura que tu estás cargando mod\_rewrite correctamente. Debes +#. Asegura que tú estás cargando mod\_rewrite correctamente. Debes ver algo similar a esto: .. code-block:: apacheconf @@ -286,8 +286,8 @@ obtener información. ajustes estén activados. Verifica que tus archivos .htaccess está actualmente en directorio - correcto. Algunos sistemas operativo tratan los archivos que empiezan - con '.' como oculto y por lo tanto no podrás copiarlos. + correcto. Algunos sistemas operativos tratan los archivos que empiezan + con '.' como ocultos y, por lo tanto, no podrás copiarlos. #. Asegúrate que tu copia de CakePHP provenga desde la sección descargas del sitio o de nuestro repositorio de Git, y han sido desempacados correctamente, @@ -340,7 +340,7 @@ obtener información. Para muchos servicios de alojamiento (GoDaddy, 1and1), tu servidor web estará actualmente sirviendo desde un directorio de usuario que - actualmente usa mod\_rewrite. Si tu estás instalando CakePHP en la carpeta + actualmente usa mod\_rewrite. Si tú estás instalando CakePHP en la carpeta de usuario (http://example.com/~username/cakephp/), o alguna otra estructura de URL que ya utilice mod\_rewrite, necesitarás agregar una declaración a los archivos .htaccess que CakePHP usa (.htaccess, @@ -380,20 +380,20 @@ obtener información. Lo anterior simplemente previene que archivos adicionales incorrectos sean enviados a index.php en su lugar muestre la página 404 de tu servidor web. - Adicionalmente puedes crear una página 404 que concuerde, o usar la página 404 + Adicionalmente, puedes crear una página 404 que concuerde, o usar la página 404 incluida en CakePHP agregando una directiva ``ErrorDocument``: .. code-block:: apacheconf ErrorDocument 404 /404-not-found -nginx +Nginx ----- -nginx no hace uso de un archivo .htaccess como Apache, por esto es necesario -crear la reescritura de URL en la configuraciones de *site-available*. Esto +Nginx no hace uso de un archivo .htaccess como Apache, por esto es necesario +crear la reescritura de URL en la configuracion de *sites-available*. Esto usualmente se encuentra en ``/etc/nginx/sites-available/your_virtual_host_conf_file``. -Dependiendo de la configuración, tu necesitarás modificar esto, pero por lo menos, +Dependiendo de la configuración, necesitarás modificar esto, pero por lo menos, necesitas PHP corriendo como una instancia FastCGI: .. code-block:: nginx @@ -466,7 +466,7 @@ a www.example.com consulta el enlace de nginx anterior): IIS7 (Windows) -------------- -IIS7 no soporta de manera nativa los archivos .htaccess. Mientras hayan +IIS7 no soporta de manera nativa los archivos .htaccess. Mientras haya *add-ons* que puedan agregar soporte a estos archivos, puedes también importar las reglas htaccess en IIS para usar las redirecciones nativas de CakePHP. Para hacer esto, sigue los siguientes pasos: @@ -515,7 +515,7 @@ funcionar correctamente. No puedo usar Redireccionamientos de URL ---------------------------------------- -Si no quieres o no puedes obtener mod\_rewirte (o algun otro modulo +Si no quieres o no puedes obtener mod\_rewirte (o algún otro módulo compatible) en el servidor a correr, necesitarás usar el decorador de URL incorporado en CakePHP. En **config/app.php**, descomentar la línea para que se vea así:: diff --git a/es/intro.rst b/es/intro.rst index b35805aae6..76d034d827 100644 --- a/es/intro.rst +++ b/es/intro.rst @@ -9,7 +9,7 @@ manera conjunta o separada. El objetivo de este artículo es introducirte en los conceptos generales de CakePHP y darte un rápido vistazo sobre como esos conceptos están implementados en CakePHP. Si estás deseando comenzar un proyecto puedes :doc:`empezar con el tutorial -`, o :doc:`profundizar en la documentación +`, o :doc:`profundizar en la documentación `. Convenciones sobre configuración @@ -34,15 +34,14 @@ relacionadas con el manejo de datos. En el caso de una red social la capa modelo se encargaría de tareas como guardar los datos del usuario, las asociaciones de amigos, almacenar y obtener fotos, buscar sugerencias de amistad, etc. Los objetos modelo serían "Amigo", -"Usuario", "Comentario" o "Foto". Si quisieramos obtener más datos de nuestra +"Usuario", "Comentario" o "Foto". Si quisiéramos obtener más datos de nuestra tabla ``usuarios`` podríamos hacer lo siguiente:: - use Cake\ORM\TableRegistry; + use Cake\ORM\Locator\LocatorAwareTrait; - // Prior to 3.6 use TableRegistry::get('Usuarios') - $usuarios = TableRegistry::getTableLocator()->get('Usuarios'); - $query = $usuarios->find(); - foreach ($query as $row) { + $usuarios = $this->getTableLocator()->get('Usuarios'); + $resultset = $usuarios->find()->all(); + foreach ($resultset as $row) { echo $row->nombreusuario; } @@ -53,10 +52,9 @@ usará clases estándar para tablas y clases de entidad que no hayan sido defini Si queremos crear un nuevo usuario y guardarlo (con validaciones) podríamos hacer algo como:: - use Cake\ORM\TableRegistry; + use Cake\ORM\Locator\LocatorAwareTrait; - // Prior to 3.6 use TableRegistry::get('Usuarios') - $usuarios = TableRegistry::getTableLocator()->get('Usuarios'); + $usuarios = $this->getTableLocator()->get('Usuarios'); $usuario = $usuarios->newEntity(['email' => 'mark@example.com']); $usuarios->save($usuario); @@ -101,7 +99,7 @@ el registro de un usuario sería:: public function add() { - $usuario = $this->Usuarios->newEntity(); + $usuario = $this->Usuarios->newEmptyEntity(); if ($this->request->is('post')) { $usuario = $this->Usuarios->patchEntity($usuario, $this->request->getData()); if ($this->Usuarios->save($usuario, ['validate' => 'registration'])) { @@ -127,7 +125,7 @@ como funciona el ciclo de una petición: .. figure:: /_static/img/typical-cake-request.png :align: center - :alt: Diagrama de flujo mostrando una petición tipica de CakePHP + :alt: Diagrama de flujo mostrando una petición típica de CakePHP El ciclo de petición típico de CakePHP comienza con un usuario solicitando una página o recurso en tu aplicación. A un alto nivel cada petición sigue los @@ -136,7 +134,7 @@ siguientes pasos: #. Las reglas de rescritura del servidor web envían la petición a **webroot/index.php**. #. Tu aplicación es cargada y ligada a un ``HttpServer``. #. Se inicializa el ``midleware`` de tu aplicación. -#. Una petición y respuesta son precesadas a través del ``Middleware PSR-7`` que tu aplicación utiliza. Normalmente esto incluye la captura de errores y enrutamiento. +#. Una petición y respuesta son precesadas a través del ``Middleware PSR-7`` que tu aplicación utiliza. Normalmente, esto incluye la captura de errores y enrutamiento. #. Si no recibe ninguna respuesta del ``middleware`` y la petición contiene información de enrutamiento, se selecciona un controlador y una acción. #. La acción del controlador es ejecutada y el controlador interactúa con los Modelos y Componentes necesarios. #. El controlador delega la creación de la respuesta a la Vista para generar la salida a partir de los datos del modelo. @@ -157,7 +155,7 @@ geniales de CakePHP son: * :doc:`Framework para la ejecución de pruebas integrado ` para que puedas asegurarte de que tu código funciona perfectamente. Los siguientes pasos obvios son :doc:`descargar CakePHP ` -y leer el :doc:`tutorial y crear algo asombroso `. +y leer el :doc:`tutorial y crear algo asombroso `. Lecturas complementarias ======================== @@ -171,4 +169,4 @@ Lecturas complementarias .. meta:: :title lang=es: Empezando - :keywords lang=es: estructura de carpetas,nombres de tablas,petición inicial,tabla de base de datos,estructura orgaizativa,rst,nombres de archivo,convenciones,mvc,web página,sit + :keywords lang=es: estructura de carpetas,nombres de tablas,petición inicial,tabla de base de datos,estructura organizativa,rst,nombres de archivo,convenciones,mvc,web página,sit diff --git a/es/intro/cakephp-folder-structure.rst b/es/intro/cakephp-folder-structure.rst index f35a1858c5..9d1996d6b5 100644 --- a/es/intro/cakephp-folder-structure.rst +++ b/es/intro/cakephp-folder-structure.rst @@ -1,51 +1,39 @@ -CakePHP Folder Structure -######################## +Estructura de carpetas de CakePHP +################################# -Después de haber descargado y extraido la aplicación CakePHP, estos son los -archivos y directorios que podrás ver: - -- bin -- config -- logs -- plugins -- src -- tests -- tmp -- vendor -- webroot -- .htaccess -- composer.json -- index.php -- README.md - -Notarás unos cuantos directorios de primer nivel: +Después de haber descargado el esqueleto de aplicación de CakePHP, estos son los +directorios de primer nivel que deberías ver: - La carpeta *bin* contiene los ejecutables por consola de Cake. - La carpeta *config* contiene los documentos de :doc:`/development/configuration` que utiliza CakePHP. Detalles de la conexión - a la Base de Datos, bootstrapping, arhivos de configuración del core y otros, + a la Base de Datos, bootstrapping, archivos de configuración del core y otros, serán almacenados aquí. - La carpeta *plugins* es donde se almacenan los :doc:`/plugins` que utiliza tu aplicación. - La carpeta de *logs* contiene normalmente tus archivos de log, dependiendo de tu configuración de log. -- La carpeta *src* será donde tu crearás tu mágia: es donde se almacenarán los +- La carpeta *src* será donde tu crearás tu magia: es donde se almacenarán los archivos de tu aplicación. +- La carpeta *templates* contiene los archivos de presentación: + elementos, páginas de error, plantillas generales y plantillas de vistas. +- La carpeta *resources* contiene sub carpetas para varios tipos de archivos. +- La carpeta *locales* contiene sub carpetas para los archivos de traducción a otros idiomas. - La carpeta *tests* será donde pondrás los test para tu aplicación. - La carpeta *tmp* es donde CakePHP almacenará temporalmente la información. La información actual que almacenará dependerá de cómo se configure CakePHP, pero esta carpeta es normalmente utilizada para almacenar descripciones de modelos y a veces información de sesión. - La carpeta *vendor* es donde CakePHP y otras dependencias de la aplicación - serán instaladas. Comprométete a **no** editar los archivos de esta carpeta. - No podremos ayudarte si modificas el core. + serán instaladas por `Composer `_. Editar estos archivos no es + recomendado, ya que Composer sobreescribirá tus cambios en la próxima actualización. - El directorio *webroot* es la raíz de los documentos públicos de tu aplicación. Contiene todos los archivos que quieres que sean accesibles públicamente. -Asegúrate de que las carpetas *tmp* y *logs* existen y permiten escritura, en -caso contrario el rendimiento de tu aplicación se verá gravemente perjudicado. -En modo debug, CakePHP te avisará si este no es el caso. + Asegúrate de que las carpetas *tmp* y *logs* existen y permiten escritura, en + caso contrario el rendimiento de tu aplicación se verá gravemente perjudicado. + En modo debug, CakePHP te avisará si este no es el caso. La carpeta src ============== @@ -53,21 +41,25 @@ La carpeta src La carpeta *src* de CakePHP es donde tú harás la mayor parte del desarrollo de tu aplicación. Observemos más detenidamente dentro de la carpeta *src*. +Command + Contiene los comandos de consola de tu aplicación. + Para más información mirar :doc:`/console-commands/commands`. Console - Contiene los comandos de consola y las tareas de consola de tu aplicación. - Para más información mirar :doc:`/console-and-shells`. + Contiene los 'scripts' de instalación ejecutados por Composer. Controller - Contiene los controladores de tu aplicación y sus componentes. -Locale - Almacena los ficheros de string para la internacionalización. + Contiene los :doc:`/controllers` de tu aplicación y sus componentes. +Middleware + Contiene cualquier :doc:`/controllers/middleware` para tu aplicación. Model - Contiene las tablas, entidades y funcionamiento de tu aplicación. + Contiene las tablas, entidades y comportamientos de tu aplicación. View - Las clases de presentación se ubican aquí: cells, helpers y templates. -Template - Los archivos de presentación se almacenan aquí: elementos, páginas de error, - layouts, y templates. + Las clases de presentación se ubican aquí: plantillas de vistas, células y ayudantes. + +.. note:: + + La carpeta ``Command`` no está creada por defecto. + Puedes añadirla cuando la necesites. .. meta:: - :title lang=es: CakePHP Structura de Carpetas - :keywords lang=es: librerias internas,configuracion core,descripciones de modelos, vendors externos,detalles de conexión,estructura de carpetas,librerías,compromiso personal,conexión base de datos,internacionalización,archivos de configuración,carpetas,desarrollo de aplicaciones,léeme,lib,configurado,logs,config,third party,cakephp + :title lang=es: CakePHP Estructura de Carpetas + :keywords lang=es: librerías internas,configuración core,descripciones de modelos, vendors externos,detalles de conexión,estructura de carpetas,librerías,compromiso personal,conexión base de datos,internacionalización,archivos de configuración,carpetas,desarrollo de aplicaciones,léeme,lib,configurado,logs,config,third party,cakephp diff --git a/es/intro/conventions.rst b/es/intro/conventions.rst index e54947bda8..a8e01df51f 100644 --- a/es/intro/conventions.rst +++ b/es/intro/conventions.rst @@ -6,7 +6,7 @@ toma algo de tiempo aprender las convenciones de CakePHP, ahorrarás tiempo a la larga. Siguiendo las convenciones obtendrás funcionalidades gratuitas y te liberarás de la pesadilla de mantener archivos de configuración. Las convenciones también hacen que el desarrollo sea uniforme, permitiendo a -otros desarrolladores intervenir y ayudar facilmente. +otros desarrolladores intervenir y ayudar fácilmente. Convenciones de Controlador =========================== @@ -15,9 +15,9 @@ Los nombres de las clases Controlador son en plurar, en formato ``CamelCase``, y finalizan con ``Controller``. Ejemplos de nombres son: ``UsuariosController`` y ``CategoriasArticulosController``. -Los métodos publicos de los Controladores a menudo se exponen como 'acciones' -accesibles a través de un navegador web. Por ejemplo, ``/users/view`` mapea -al método ``view()`` de ``UsersController`` sin tener que hacer nada en el +Los métodos públicos de los Controladores a menudo se exponen como 'acciones' +accesibles a través de un navegador web. Tienen formato ``camelBacked``. Por ejemplo, ``/users/view-me`` mapea +al método ``viewMe()`` de ``UsersController`` sin tener que hacer nada en el enrutamiento de la aplicación. Los métodos protegidos o privados no son accesibles con el enrutamiento. @@ -52,8 +52,8 @@ consultar :ref:`routes-configuration`. Convenciones de nombre de clase y archivo ========================================= -En general los nombres de los archivos coinciden con los nombres de las clases -y siguen los estándares PSR-0 o PSR-4 para cargarse automáticamente. Los +En general, los nombres de los archivos coinciden con los nombres de las clases +y sigue el estándar PSR-4 para cargarse automáticamente. Los siguientes son ejemplos de nombres de clases y de sus archivos: - La clase Controlador ``LatestArticlesController`` debería estar en un archivo llamado **LatestArticlesController.php** @@ -69,25 +69,13 @@ tu carpeta de tu aplicación. .. _model-and-database-conventions: -Convenciones de Modelo y Base de datos -====================================== - -Los nombres de las clases ``table`` son en plural, ``CamelCase`` y terminan en -``Table``. Algunos ejemplos de convención de nombres son: ``UsersTable``, -``ArticleCategoriesTable`` y ``UserFavoritePagesTable``. +Convenciones de base de datos +============================= Los nombres de las tablas correspondientes a los modelos de CakePHP son en plural -y con '_'. Los nombres de las tablas para los modelos arriba mencionados serían -``users``, ``article_categories`` y ``user_favorite_pages`` respectivamente. - -La convención es utilizar palabras en inglés para los nombres de las tablas y de -las columnas. Si utilizas otro idioma CakePHP puede que no sea capaz de procesar -correctamente las conversiones (de singular a plural y viceversa). Si necesitas -añadir reglas para tu idioma para algunas palabras, puedes utilizar la clase -:php:class:`Cake\\Utility\\Inflector`. Además de definir tus reglas de -conversión personalizadas, esta clase te permite comprobar que CakePHP comprenda -tu sintaxis personalizada para palabras en plural y singular. Mira la documentación -sobre :doc:`/core-libraries/inflector` para más información. +y con con '_'. Por ejemplo ``users``, ``menu_links`` y ``user_favorite_pages`` respectivamente. +Los nombres de tablas formadas por múltiples palabras sólo deben usar el plural en la última +palabra, por ejemplo, ``menu_links``. Los nombres de campos con dos o más palabras se escriben con '_', por ejemplo: ``first_name``. @@ -95,19 +83,34 @@ Las claves foráneas en relaciones ``1-n`` (``hasMany``) y ``1-1`` (``belongsTo/ son reconocidas por defecto mediante el nombre (en singular) de la tabla relacionada seguido de ``_id``. De este modo si ``Users`` tiene varios ``Articles`` (relación ``hasMany``), la tabla ``articles`` se relacionará con la tabla ``users`` a través -de la clave foránea ``user_id``. Para una tabla como ``article_categories`` -cuyo nombre está formado por varias palabras, la clave foránea sería ``article_category_id``. +de la clave foránea ``user_id``. Para una tabla como ``menu_links`` +cuyo nombre está formado por varias palabras, la clave foránea sería ``menu_link_id``. Las tablas de unión, usadas en las relaciones ``n-n`` (``BelongsToMany``) entre -modelos, deberían ser nombradas después de las tablas que unirán y en orden -alfabético (``articles_tags`` en lugar de ``tags_articles``). +modelos, deberían ser nombradas después de las tablas que unirán. Los nombres deberán +estar en plural y en orden alfabético: ``articles_tags`` en lugar de ``tags_articles`` +o ``article_tags``. *El comando ``bake`` no funcionará correctamente si ésta convención +no se sigue.* Si la tabla de unión guarda alguna información que no sean las claves +foráneas, debes crear la clase de la entidad y modelo para esa tabla. Además de utilizar claves auto-incrementales como claves primarias, también puedes utilizar columnas UUID. CakePHP creará un único UUID de 36 caracteres (:php:meth:`Cake\\Utility\\Text::uuid()`) cada vez que guardes un nuevo registro usando el método ``Table::save()`` . -Convenciones de Vistas +Convenciones de modelo +====================== + +Los nombres de las clases para las tablas son en plural, formato ``CamelCase`` +y terminan en ``Table``. ``UsersTable``, ``MenuLinksTable`` y ``UserFavoritePagesTable`` +son ejemplos de nombres de clases que corresponden a las tablas ``users``, ``menu_links`` +y ``user_favorite_pages`` respectivamente. + +Los nombres de las clases para las entidades son en singular, formato ``CamelCase`` y no +tienen sufijo. ``User``, ``MenuLink`` y ``UserFavoritePage`` son ejemplos de nombres de clases +que corresponden a las entidades ``users``, ``menu_links`` y ``user_favorite_pages`` respectivamente. + +Convenciones de vistas ====================== Los archivos de las plantillas de vistas son nombrados según las @@ -116,28 +119,130 @@ de la clase ``ArticlesController`` mostrará la vista **templates/Articles/view_ El patrón base es **templates/Controller/nombre_funcion.php**. +.. note:: + + Por defecto CakePHP usa palabras en inglés para las convenciones de nombres. + Si utilizas otro idioma CakePHP puede que no sea capaz de procesar + correctamente las conversiones (de singular a plural y viceversa). Si necesitas + añadir reglas para tu idioma para algunas palabras, puedes utilizar la clase + :php:class:`Cake\\Utility\\Inflector`. Además de definir tus reglas de + conversión personalizadas, esta clase te permite comprobar que CakePHP comprenda + tu sintaxis personalizada para palabras en plural y singular. Mira la documentación + sobre :doc:`/core-libraries/inflector` para más información. + +Convenciones de plugins +======================= + +Es útil añadir el prefijo "cakephp-" en el nombre del paquete para los plugins de CakePHP. +Esto hace que el nombre esté relacionado semánticamente al "framework" del que depende. + +**No** uses el espacio de nombre de CakePHP (cakephp) como nombre de "vendor", ya que es +un espacio reservado para los plugins que son propiedad de CakePHP. La convención es usar +letras en minúscula y guiones como separadores:: + + // Bad + cakephp/foo-bar + + // Good + your-name/cakephp-foo-bar + +Ver `lista asombrosa de recomendaciones +`__ +par mas detalles. + +Resumen +======= + Nombrando los elementos de tu aplicación empleando las convenciones de CakePHP ganarás funcionalidad sin los fastidios y ataduras de mantenimiento de la configuración. Un último ejemplo que enlaza todas las convenciones: -- Tabla de base de datos: "articles" +- Tabla de base de datos: "articles", "menu_links" - Clase Tabla: ``ArticlesTable``, ubicada en **src/Model/Table/ArticlesTable.php** - Clase Entidad: ``Article``, ubicada en **src/Model/Entity/Article.php** - Clase Controlador: ``ArticlesController``, ubicada en **src/Controller/ArticlesController.php** - Plantilla vista, ubicada en **templates/Articles/index.php** -Usando estas convenciones CakePHP redirige una petición a http://example.com/articles/ -a una llamada a la función ``index()`` de la clase ArticlesController, -donde el modelo ``Article`` está disponible automáticamente (y enlazada, automáticamente -también, a la tabla ``articles`` en la base de datos) y renderiza un -archivo. Ninguna de estas relaciones han sido configuradas de ningún modo salvo -creando clases y archivos que has tenido que crear de todas formas. +Usando estas convenciones, CakePHP sabe que una petición a http://example.com/articles/ +hace una llamada a la función ``index()`` de la clase ``ArticlesController``, +donde el modelo ``Articles`` está disponible automáticamente y enlazada automáticamente +a la tabla ``articles`` en la base de datos . Ninguna de estas relaciones han sido +configuradas de ningún modo salvo creando clases y archivos que has tenido que crear de +todas formas. + ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Ejemplo | articles | menu_links | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Tabla base | articles | menu_links | Nombres de tablas que se corresponden a modelos | +| de datos | | | son en plural y con guión bajo '_'. | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Archivo | ArticlesController.php | MenuLinksController.php | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Tabla | ArticlesTable.php | MenuLinksTable.php | Los nombres de clase de las tablas son en plural, | +| | | | formato 'CamelCased' y acaban con el sufijo 'Table' | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Entidad | Article.php | MenuLink.php | Los nombres de clase de las entidades son en | +| | | | singular y 'CamelCased': Article and MenuLink | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Clase | ArticlesController | MenuLinksController | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Controlador | ArticlesController | MenuLinksController | Plural, CamelCased, acaba en 'Controller' | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Plantillas | Articles/index.php | MenuLinks/index.php | Los archivos de plantillas de vistas son nombrados | +| de | Articles/add.php | MenuLinks/add.php | según las funciones que el controlador muestra, | +| vistas | Articles/edit.php | MenuLinks/add.php | en minúscula y guión bajo | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Comportamiento | ArticlesBehavior.php | MenuLinksBehavior.php | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Vista | ArticlesView.php | MenuLinksView.php | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Ayudante | ArticlesHelper.php | MenuLinksHelper.php | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Componente | ArticlesComponent.php | MenuLinksComponent.php | | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Plugin | Mal: cakephp/articles | cakephp/menu-links | Útil añadir el prefijo "cakephp-" a los plugins | +| | Bien: you/cakephp-articles | you/cakephp-menu-links | en el nombre del paquete. No uses el espacio de | +| | | | nombre (cakephp) como nombre de vendor ya que está | +| | | | para los plugins propiedad de CakePHP. La | +| | | | convención es usar letras minúsculas y guiones | +| | | | como separadores. | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ +| Cada fichero estará localizado en la 'carpeta/espacio de nombre' apropiado dentro de la carpeta de tu aplicación. | ++----------------+-----------------------------+-------------------------+------------------------------------------------------+ + +Database Convention Summary +=========================== ++-----------------+--------------------------------------------------------------+ +| Claves foráneas | Las relaciones son reconocidas por defecto como el | +| | nombre (singular) de la tabla relacionada, | +| hasMany | seguida de ``_id``. | +| belongsTo/ | Para Users 'hasMany' Articles, la tabla ``articles`` | +| hasOne | hará referencia a ``users`` a través de la | +| BelongsToMany | clave foránea ``user_id``. | +| | | ++-----------------+--------------------------------------------------------------+ +| Múltiples | ``menu_links`` cuyo nombre contiene múltiples palabras, | +| palabras | su clave foránea será ``menu_link_id``. | ++-----------------+--------------------------------------------------------------+ +| Auto Increment | Además de utilizar claves auto-incrementales como claves | +| | primarias, también puedes utilizar columnas UUID. | +| | CakePHP creará un único UUID de 36 caracteres | +| | usando (:php:meth:`Cake\\Utility\\Text::uuid()`) | +| | cada vez que guardes un nuevo registro | +| | usando el método ``Table::save()`` . | ++-----------------+--------------------------------------------------------------+ +| Join tables | Deberán ser nombradas según las tablas que unirán o el | +| | comando de 'bake' no funcionará y ordenarse alfabéticamente | +| | (``articles_tags`` en vez de ``tags_articles``). | +| | Si tiene campos adicionales que guardan información, debes | +| | crear un archivo de entidad y modelo para esa tabla. | ++-----------------+--------------------------------------------------------------+ Ahora que te has introducido en los fundamentos de CakePHP. puedes tratar de -realizar el tutorial :doc:`/tutorials-and-examples/bookmarks/intro` para ver +realizar el tutorial :doc:`/tutorials-and-examples/cms/installation` para ver como las cosas encajan juntas. .. meta:: diff --git a/es/intro/where-to-get-help.rst b/es/intro/where-to-get-help.rst index 7c633758c1..14f53280ed 100644 --- a/es/intro/where-to-get-help.rst +++ b/es/intro/where-to-get-help.rst @@ -17,8 +17,8 @@ El Cookbook Este manual probablemente debería ser el primer lugar al que debas acudir para obtener respuestas. Como muchos otros proyectos de código libre, nuevos -colaborades se unen regularmente. Intenta encontrar por ti mismo las respuestas -a tus preguntas primero, puede que así tardes más en encontrar las respuestas +colaboradores se unen regularmente. Intenta encontrar por ti mismo las respuestas +a tus preguntas primero, puede que así tardes más en encontrar las respuestas, pero permanecerán durante más tiempo - y además aliviarás nuestra carga de soporte. Tanto el manual como la API tienen una versión online. @@ -62,7 +62,7 @@ El canal IRC - `#cakephp-fr `_ -- Canal francés. Si estás atascado, péganos un grito en el canal IRC de CakePHP. -Alguién del `equipo de desarrollo `_ +Alguien del `equipo de desarrollo `_ está normalmente, especialmente durante las horas de día para usuarios de América del Norte y del Sur. Estaremos encantados de escucharte, tanto si necesitas ayuda como si quieres encontrar usuarios en tu zona o si quieres @@ -141,4 +141,4 @@ Español .. meta:: :title lang=es: Donde obtener ayuda :description lang=es: Donde obtener ayuda con CakePHP: La página oficial de CakePHP, El Cookbook, La Bakery, La API, en los casos de prueba, el canal IRC, El grupo Google de CakePHP o CakePHP Questions. - :keywords lang=es: cakephp,ayuda cakephp ,ayuda con cakephp,donde obtener ayuda,cakephp irc,cakephp preguntas,cakephp api,cakephp casos de prueba,proyectos código abierto,canal irc,código de referencia,canal irc,herramientas de desarrollo,caso de prueba,bakery + :keywords lang=es: cakephp,ayuda cakephp,ayuda con cakephp,donde obtener ayuda,cakephp irc,cakephp preguntas,cakephp api,cakephp casos de prueba,proyectos código abierto,canal irc,código de referencia,canal irc,herramientas de desarrollo,caso de prueba,bakery diff --git a/es/orm.rst b/es/orm.rst index 59c071311b..a24fb3a46d 100644 --- a/es/orm.rst +++ b/es/orm.rst @@ -2,7 +2,7 @@ Acceso a la base de datos & ORM ############################### En CakePHP el acceso a la base de datos se hace por medio de dos objetos primarios. -El primero son **repositories** -repositorios- o **table objects** -objetos de tabla-. +El primer tipo de objeto son **repositories** -repositorios- o **table objects** -objetos de tabla-. Estos objetos proveen acceso a colecciones de datos. Nos permiten guardar nuevos registros, modificar y borrar existentes, definir relaciones y realizar operaciones en masa. El segundo tipo de objeto son **entities** -entidades-. Las Entidades representan registros @@ -38,7 +38,7 @@ comenzar a utilizar el ORM. Por ejemplo si quisieramos leer datos de nuestra tab Como se ve, no es necesario agregar código extra ni ninguna otra configuración, gracias al uso de las convenciones de CakePHP. Si quisieramos modificar nuestra clase ArticlesTable para agregar asociaciones o definir métodos -adicionales deberiamos agregar las siguientes lineas en **src/Model/Table/ArticlesTable.php** :: +adicionales deberiamos agregar las siguientes líneas en **src/Model/Table/ArticlesTable.php** :: namespace App\Model\Table; @@ -50,7 +50,7 @@ adicionales deberiamos agregar las siguientes lineas en **src/Model/Table/Articl } Las clases Table usan una version en CamelCase del nombre de la tabla, con el sufijo ``Table``. -Una vez que tú clase fue creada, puedes obtener una referencia a esta usando :php:class:`~Cake\\ORM\\TableRegistry` como antes:: +Una vez que tu clase fue creada, puedes obtener una referencia a esta usando :php:class:`~Cake\\ORM\\TableRegistry` como antes:: use Cake\ORM\TableRegistry; @@ -59,8 +59,8 @@ Una vez que tú clase fue creada, puedes obtener una referencia a esta usando :p $articles = TableRegistry::getTableLocator()->get('Articles'); Ahora que tenemos una clase Table concreta, probablemente querramos usar una clase Entity concreta. -Las clases Entity permiten definir métodos de acceso y mutación, lógica para registros individuales y mucho mas. -Comenzaremos agregando las siguientes lineas en **src/Model/Entity/Article.php**:: +Las clases Entity permiten definir métodos de acceso y mutación, lógica para registros individuales y mucho más. +Comenzaremos agregando las siguientes líneas en **src/Model/Entity/Article.php**:: namespace App\Model\Entity; @@ -89,7 +89,7 @@ carguemos entidades de nuestra base de datos, vamos a obtener instancias de nues CakePHP usa convenciones de nombres para asociar las clases Table y Entity. Si necesitas modificar qué entidad utilizada una tabla, puedes usar el método ``entityClass()`` para especificar el nombre de una clase. -Vea :doc:`/orm/table-objects` y :doc:`/orm/entities` para mas información sobre como utilizar objetos Table y Entity en su aplicación. +Vea :doc:`/orm/table-objects` y :doc:`/orm/entities` para más información sobre como utilizar objetos Table y Entity en su aplicación. Más información =============== @@ -108,4 +108,4 @@ Más información orm/associations orm/behaviors orm/schema-system - console-and-shells/orm-cache + console-commands/schema-cache diff --git a/es/phinx.rst b/es/phinx.rst new file mode 100644 index 0000000000..53312b3a90 --- /dev/null +++ b/es/phinx.rst @@ -0,0 +1,4 @@ +Migraciones con Phinx +###################### + +Esta página ha sido `movida `__. diff --git a/es/plugins.rst b/es/plugins.rst index 14378d0b78..f6ebd8a323 100644 --- a/es/plugins.rst +++ b/es/plugins.rst @@ -1,15 +1,630 @@ Plugins ####### +CakePHP te permite configurar una combinación de controladores, modelos y vistas y liberarlos como un plugin de aplicación pre-empaquetado que otros pueden usar en sus aplicaciones CakePHP. Si has creado una gran gestión de usuarios, un blog sencillo, o adaptadores de servicios web en una de tus aplicaciones ¿por qué no empaquetarlo como un plugin CakePHP? De esta manera puedes utilizarlo en tus otras aplicaciones y compartirlo con la comunidad. + +Un plugin de CakePHP es independiente de la aplicación principal y generalmente proporciona alguna funcionalidad bien definida que se puede empaquetar de forma ordenada y reutilizar con poco esfuerzo en otras aplicaciones. La aplicación y el plugin operan en sus respectivos espacios, pero comparten los datos de configuración de la aplicación (por ejemplo, conexiones de base de datos, transportes de correo electrónico). + +Los plugins deben definir su propio espacio de nombres (namespace) de nivel superior. Por ejemplo: ``DebugKit``. Por convención, los plugins utilizan el nombre de su paquete como su espacio de nombres. Si deseas utilizar un espacio de nombres diferente, puedes configurarlo cuando se cargan los plugins. + +Instalación de un plugin con Composer +===================================== + +Muchos plugins están disponibles en `Packagist `_ +y se pueden instalar con ``Composer``. Para instalar DebugKit +debes hacer lo siguiente: + +.. code-block:: console + + php composer.phar require cakephp/debug_kit + +Esto instalará la última versión de DebugKit y actualizará tus archivos **composer.json** y **composer.lock**, también actualizará el archivo **vendor/cakephp-plugins.php** y tu cargador automático (autoloader). + + +Instalación manual de un Plugin +=============================== + +Si el complemento que deseas instalar no está disponible en packagist.org, puedes clonar o copiar el código del complemento en tu directorio de **plugins**. Supongamos que deseas instalar un complemento llamado 'ContactManager', debes tener una carpeta en **plugins** llamada 'ContactManager'. En este directorio se encuentran las carpetas src, tests y cualquier otra carpeta del complemento. + +.. _autoloading-plugin-classes: + +Carga automática manual de clases de Plugins +-------------------------------------------- + +Si instalas tus complementos mediante ``composer`` o ``bake``, no deberías necesitar configurar la carga automática de clases para tus plugins. + +Si creas un plugin manualmente en el directorio ``plugins``, entonces necesitarás indicarle a ``composer`` que actualice su caché de carga automática: + +.. code-block:: console + + php composer.phar dumpautoload + +Si estás utilizando espacios de nombres de proveedores para tus plugins, deberás agregar el mapeo de espacio de nombres a la ruta en el archivo ``composer.json`` de la siguiente manera, antes de ejecutar el comando de composer mencionado anteriormente: + +.. code-block:: json + + { + "autoload": { + "psr-4": { + "AcmeCorp\\Users\\": "plugins/AcmeCorp/Users/src/", + } + }, + "autoload-dev": { + "psr-4": { + "AcmeCorp\\Users\\Test\\": "plugins/AcmeCorp/Users/tests/" + } + } + } + +.. _loading-a-plugin: + +Cargar un Plugin +================ + +Si deseas utilizar las rutas, comandos de consola, middlewares, listeners de eventos, plantillas o assets de la carpeta web (webroot) de un plugin, deberás cargar el plugin. + +Si solo deseas utilizar helpers, comportamientos o componentes de un plugin, no es necesario cargar explícitamente el plugin, aunque siempre es recomendable hacerlo. + +También hay un práctico comando de consola para cargar el plugin. Ejecuta la siguiente línea: + +.. code-block:: console + + bin/cake plugin load ContactManager + +Esto actualizaría el array en el archivo ``config/plugins.php`` de tu aplicación con una entrada similar a ``'ContactManager' => []``. + +.. _plugin-configuration: + +Configuración de Hooks del Plugin +================================= + +Los plugins ofrecen varios hooks (ganchos) que permiten que un plugin se inyecte en las partes apropiadas de tu aplicación. Los hooks son: + +* ``bootstrap`` Se utiliza para cargar archivos de configuración predeterminados del plugin, definir constantes y otras funciones globales. +* ``routes`` Se utiliza para cargar las rutas de un plugin. Se activa después de que se cargan las rutas de la aplicación. +* ``middleware`` middleware Se utiliza para agregar el middleware del plugin a la cola de middlewares de una aplicación. +* ``console`` Se utiliza para agregar comandos de consola a la colección de comandos de una aplicación. +* ``services`` Se utiliza para registrar servicios del contenedor de aplicaciones. + +Por defecto, todos los hooks de los plugins están habilitados. Puedes deshabilitar los hooks utilizando las opciones relacionadas del comando plugin load: + +.. code-block:: console + + bin/cake plugin load ContactManager --no-routes + +Esto actualizaría el array en el archivo ``config/plugins.php`` de tu aplicación con una entrada similar a ``'ContactManager' => ['routes' => false]``. + +Opciones de Carga de Plugins +============================ + +Además de las opciones para los hooks de complementos, el comando ``plugin load`` tiene las siguientes opciones para controlar la carga del plugin: + +- ``--only-debug`` Carga el plugin solo cuando el modo de depuración (debug) está habilitado. +- ``--only-cli`` Carga el plugin solo para CLI. +- ``--optional`` No arroja un error si el plugin no está disponible. + +Cargar Plugins a través de ``Application::bootstrap()`` +============================================================== + +Además del array de configuración en ``config/plugins.php``, los plugins también se pueden cargar en el método ``bootstrap()`` de tu aplicación:: + + // In src/Application.php + use Cake\Http\BaseApplication; + use ContactManager\ContactManagerPlugin; + + class Application extends BaseApplication + { + public function bootstrap() + { + parent::bootstrap(); + + // Load the contact manager plugin by class name + $this->addPlugin(ContactManagerPlugin::class); + + // Load a plugin with a vendor namespace by 'short name' with options + $this->addPlugin('AcmeCorp/ContactManager', ['console' => false]); + + // Load a dev dependency that will not exist in production builds. + $this->addOptionalPlugin('AcmeCorp/ContactManager'); + } + } + +Puedes configurar hooks con opciones de array o utilizando los métodos proporcionados por las clases del plugin:: + + // In Application::bootstrap() + use ContactManager\ContactManagerPlugin; + + // Use the disable/enable to configure hooks. + $plugin = new ContactManagerPlugin(); + + $plugin->disable('bootstrap'); + $plugin->enable('routes'); + $this->addPlugin($plugin); + +Los objetos del plugin también conocen sus nombres e información de la ruta:: + + $plugin = new ContactManagerPlugin(); + + // Get the plugin name. + $name = $plugin->getName(); + + // Path to the plugin root, and other paths. + $path = $plugin->getPath(); + $path = $plugin->getConfigPath(); + $path = $plugin->getClassPath(); + +Uso de las Clases del Plugins +----------------------------- + +Puedes hacer referencia a los controladores, modelos, componentes, comportamientos y helpers de un plugin prefijando el nombre del plugin. + +Por ejemplo, supongamos que quieres usar el helper ``ContactInfoHelper`` del plugin ContactManager para mostrar información de contacto formateada en una de tus vistas. En tu controlador, usar ``addHelper()`` podría verse así:: + + $this->viewBuilder()->addHelper('ContactManager.ContactInfo'); + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. + Este nombre de clase separado por puntos se denomina :term:`Sintaxis de plugin`. + +Luego podrías acceder al ``ContactInfoHelper`` de la misma manera que cualquier otro helper en tu vista, como por ejemplo:: + + echo $this->ContactInfo->address($contact); + +Los plugins pueden utilizar los modelos, componentes, comportamientos y helpers proporcionados por la aplicación, o por otros plugins si es necesario:: + + // Use an application component + $this->loadComponent('AppFlash'); + + // Use another plugin's behavior + $this->addBehavior('OtherPlugin.AuditLog'); + +.. _plugin-create-your-own: + +Creación de tus propios Plugins +------------------------------- + +Como ejemplo práctico, comencemos a crear el plugin ContactManager mencionado anteriormente. Para empezar, configuraremos la estructura básica del directorio de nuestro complemento. Debería verse así:: + + /src + /plugins + /ContactManager + /config + /src + /ContactManagerPlugin.php + /Controller + /Component + /Model + /Table + /Entity + /Behavior + /View + /Helper + /templates + /layout + /tests + /TestCase + /Fixture + /webroot + +Ten en cuenta el nombre de la carpeta del plugin, '**ContactManager**'. Es importante que esta carpeta tenga el mismo nombre que el plugin. + +Dentro de la carpeta del plugin, notarás que se parece mucho a una aplicación de CakePHP, y básicamente eso es lo que es. En lugar de un archivo ``Application.php``, tienes un archivo ``ContactManagerPlugin.php``. No es necesario incluir ninguna de las carpetas que no estés usando. Algunos plugins solo pueden definir un Component y un Behavior, y en ese caso pueden omitir completamente el directorio 'templates'. + +Un plugin también puede tener prácticamente cualquiera de los otros directorios que puede tener tu aplicación, como Config, Console, webroot, etc. + +Crear un Plugin Utilizando Bake +------------------------------- + +El proceso de creación de plugins puede simplificarse enormemente utilizando Bake. + +Para hornear (bakear) un plugin, utiliza el siguiente comando: + +.. code-block:: console + + bin/cake bake plugin ContactManager + +Bake puede utilizarse para crear clases en tu plugin. Por ejemplo, para generar +un controlador de plugin podrías ejecutar: + +.. code-block:: console + + bin/cake bake controller --plugin ContactManager Contacts + + +Por favor, consulta el capítulo +:doc:`/bake/usage` si tienes +problemas al utilizar la línea de comandos. Asegúrate de regenerar tu +cargador automático (autoloader) una vez que hayas creado tu plugin: + +.. code-block:: console + + php composer.phar dumpautoload + +.. _plugin-objects: + +Objetos del Plugin +================== + +Los Objetos de Plugin permiten a un autor de plugin definir lógica de configuración, ganchos (hooks) predeterminados, cargar rutas, middleware y comandos de consola. Los objetos de plugin se encuentran en +**src/Plugin.php**. Para nuestro plugin ContactManager, nuestra clase de plugin podría verse así:: + + namespace ContactManager; + + use Cake\Core\BasePlugin; + use Cake\Core\ContainerInterface; + use Cake\Core\PluginApplicationInterface; + use Cake\Console\CommandCollection; + use Cake\Http\MiddlewareQueue; + use Cake\Routing\RouteBuilder; + + class ContactManagerPlugin extends BasePlugin + { + + /** + * @inheritDoc + */ + public function middleware(MiddlewareQueue $middleware): MiddlewareQueue + { + // Add middleware here. + $middleware = parent::middleware($middleware); + + return $middleware; + } + + /** + * @inheritDoc + */ + public function console(CommandCollection $commands): CommandCollection + { + // Add console commands here. + $commands = parent::console($commands); + + return $commands; + } + + /** + * @inheritDoc + */ + public function bootstrap(PluginApplicationInterface $app): void + { + // Add constants, load configuration defaults. + // By default will load `config/bootstrap.php` in the plugin. + parent::bootstrap($app); + } + + /** + * @inheritDoc + */ + public function routes(RouteBuilder $routes): void + { + // Add routes. + // By default will load `config/routes.php` in the plugin. + parent::routes($routes); + } + + /** + * Register application container services. + * + * @param \Cake\Core\ContainerInterface $container The Container to update. + * @return void + * @link https://book.cakephp.org/5/en/development/dependency-injection.html#dependency-injection + */ + public function services(ContainerInterface $container): void + { + // Add your services here + } + } + +.. _plugin-routes: + +Rutas del Plugin +================ + +Los plugins pueden proporcionar archivos de rutas que contienen sus propias rutas. Cada plugin puede contener un archivo **config/routes.php**. Este archivo de rutas se puede cargar cuando se agrega el plugin o en el archivo de rutas de la aplicación. Para crear las rutas del plugin ContactManager, coloca lo siguiente en **plugins/ContactManager/config/routes.php**:: + + plugin( + 'ContactManager', + ['path' => '/contact-manager'], + function ($routes) { + $routes->setRouteClass(DashedRoute::class); + + $routes->get('/contacts', ['controller' => 'Contacts']); + $routes->get('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'view']); + $routes->put('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'update']); + } + ); + +Lo anterior conectará las rutas predeterminadas para tu plugin. Más adelante, puedes personalizar este archivo con rutas más específicas. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +También puedes cargar las rutas del plugin en la lista de rutas de tu aplicación. Hacer esto te proporciona un mayor control sobre cómo se cargan las rutas del plugin y te permite envolver las rutas del plugin en ámbitos o prefijos adicionales:: + + $routes->scope('/', function ($routes) { + // Connect other routes. + $routes->scope('/backend', function ($routes) { + $routes->loadPlugin('ContactManager'); + }); + }); + +Lo anterior resultaría en URLs como ``/backend/contact-manager/contacts``. + +Controladores del Plugin +======================== + +Los controladores para nuestro plugin ContactManager se almacenarán en **plugins/ContactManager/src/Controller/**. Dado que la principal tarea que realizaremos es gestionar contactos, necesitaremos un ContactsController para este plugin. + +Entonces, colocamos nuestro nuevo ContactsController en **plugins/ContactManager/src/Controller** y se verá así:: + + // plugins/ContactManager/src/Controller/ContactsController.php + namespace ContactManager\Controller; + + use ContactManager\Controller\AppController; + + class ContactsController extends AppController + { + public function index() + { + //... + } + } + +También crea el controlador ``AppController`` si aún no lo tienes:: + + // plugins/ContactManager/src/Controller/AppController.php + namespace ContactManager\Controller; + + use App\Controller\AppController as BaseController; + + class AppController extends BaseController + { + } + +El ``AppController`` de un plugin puede contener lógica de controlador común a todos los controladores en un plugin, pero no es obligatorio usarlo. + +Si deseas acceder a lo que hemos hecho hasta ahora, visita ``/contact-manager/contacts``. Deberías obtener un error de "Modelo faltante" porque aún no hemos definido un modelo Contact. + +Si tu aplicación incluye el enrutamiento predeterminado que proporciona CakePHP, podrás acceder a los controladores de tu plugin utilizando URLs como:: + + // Access the index route of a plugin controller. + /contact-manager/contacts + + // Any action on a plugin controller. + /contact-manager/contacts/view/1 + +Si tu aplicación define prefijos de enrutamiento, el enrutamiento predeterminado de CakePHP también conectará rutas que utilizan el siguiente patrón:: + + /{prefix}/{plugin}/{controller} + /{prefix}/{plugin}/{controller}/{action} + +Consulta la sección sobre :ref:`plugin-configuration` para obtener información sobre cómo cargar archivos de rutas específicos del plugin. + +.. _plugin-models: + +Modelos del Plugin +================== + +Los modelos para el plugin se almacenan en **plugins/ContactManager/src/Model**. +Ya hemos definido un ContactsController para este plugin, así que creemos +la tabla y entidad para ese controlador:: + + // plugins/ContactManager/src/Model/Entity/Contact.php: + namespace ContactManager\Model\Entity; + + use Cake\ORM\Entity; + + class Contact extends Entity + { + } + + // plugins/ContactManager/src/Model/Table/ContactsTable.php: + namespace ContactManager\Model\Table; + + use Cake\ORM\Table; + + class ContactsTable extends Table + { + } + +Si necesitas hacer referencia a un modelo dentro de tu plugin al establecer asociaciones o definir clases de entidad, debes incluir el nombre del plugin con el nombre de la clase, separados por un punto. Por ejemplo:: + + // plugins/ContactManager/src/Model/Table/ContactsTable.php: + namespace ContactManager\Model\Table; + + use Cake\ORM\Table; + + class ContactsTable extends Table + { + public function initialize(array $config): void + { + $this->hasMany('ContactManager.AltName'); + } + } + +Si prefieres que las claves del array para la asociación no tengan el prefijo del plugin, puedes utilizar la sintaxis alternativa:: + + // plugins/ContactManager/src/Model/Table/ContactsTable.php: + namespace ContactManager\Model\Table; + + use Cake\ORM\Table; + + class ContactsTable extends Table + { + public function initialize(array $config): void + { + $this->hasMany('AltName', [ + 'className' => 'ContactManager.AltName', + ]); + } + } + +Puedes utilizar ``Cake\ORM\Locator\LocatorAwareTrait`` para cargar las tablas de tu plugin utilizando la familiar :term:`Sintaxis de plugin`:: + + // Controllers already use LocatorAwareTrait, so you don't need this. + use Cake\ORM\Locator\LocatorAwareTrait; + + $contacts = $this->fetchTable('ContactManager.Contacts'); + +Plantillas de Plugin +==================== + +Las vistas se comportan exactamente como lo hacen en las aplicaciones normales. Solo colócalas en la carpeta correcta dentro de la carpeta ``plugins/[NombreDelPlugin]/templates/``. Para nuestro plugin ContactManager, necesitaremos una vista para nuestra acción ``ContactsController::index()``, así que incluyámosla también:: + + // plugins/ContactManager/templates/Contacts/index.php: +

Contacts

+

Following is a sortable list of your contacts

+ + +Los plugins pueden proporcionar sus propios diseños. Para añadir diseños de plugin, coloca tus archivos de plantilla dentro de +``plugins/[NombreDelPlugin]/templates/layout``. Para usar un diseño de plugin en tu controlador, puedes hacer lo siguiente:: + + $this->viewBuilder()->setLayout('ContactManager.admin'); + +Si se omite el prefijo del plugin, el archivo de diseño/vista se ubicará de forma normal. + +.. nota:: + + Para obtener información sobre cómo usar elementos de un plugin, consulta :ref:`view-elements` + +Sobrescribir Plantillas de Plugin desde dentro de tu Aplicación +--------------------------------------------------------------- + +Puedes sobrescribir cualquier vista de un plugin desde dentro de tu aplicación usando rutas especiales. Si tienes un plugin llamado 'ContactManager', puedes sobrescribir los archivos de plantilla del plugin con lógica de vista específica de la aplicación creando archivos utilizando la siguiente plantilla **templates/plugin/[Plugin]/[Controlador]/[vista].php**. Para el controlador Contacts podrías crear el siguiente archivo:: + + templates/plugin/ContactManager/Contacts/index.php + +Crear este archivo te permitiría sobrescribir **plugins/ContactManager/templates/Contacts/index.php**. + +Si tu plugin está en una dependencia de Composer (por ejemplo, 'Company/ContactManager'), la ruta a la vista 'index' del controlador Contacts será:: + + templates/plugin/TheVendor/ThePlugin/Custom/index.php + +Crear este archivo te permitiría sobrescribir **vendor/elproveedor/elplugin/templates/Custom/index.php**. + +Si el plugin implementa un prefijo de enrutamiento, debes incluir el prefijo de enrutamiento en las sobrescrituras de plantillas de tu aplicación. Por ejemplo, si el plugin 'ContactManager' implementara un prefijo 'Admin', la ruta de sobrescritura sería:: + + templates/plugin/ContactManager/Admin/ContactManager/index.php + +.. _plugin-assets: + + +Recursos de Plugin +================== + +Los recursos web de un plugin (pero no los archivos PHP) se pueden servir a través del directorio ``webroot`` del plugin, al igual que los recursos de la aplicación principal:: + + /plugins/ContactManager/webroot/ + css/ + js/ + img/ + flash/ + pdf/ + +Puedes colocar cualquier tipo de archivo en cualquier directorio, al igual que en un webroot regular. + +.. advertencia:: + + Manejar recursos estáticos (como imágenes, archivos JavaScript y CSS) + a través del Dispatcher es muy ineficiente. Consulta :ref:`symlink-assets` + para obtener más información. + +Enlazar a Recursos en Plugins +----------------------------- + +Puedes utilizar la :term:`Sintaxis de plugin` al enlazar a recursos de plugins utilizando los métodos script, image o css del :php:class:`~Cake\\View\\Helper\\HtmlHelper`:: + + // Generates a URL of /contact_manager/css/styles.css + echo $this->Html->css('ContactManager.styles'); + + // Generates a URL of /contact_manager/js/widget.js + echo $this->Html->script('ContactManager.widget'); + + // Generates a URL of /contact_manager/img/logo.jpg + echo $this->Html->image('ContactManager.logo'); + +Los recursos de los plugins se sirven por defecto utilizando el middleware ``AssetMiddleware``. Esto solo se recomienda para desarrollo. En producción, debes :ref:`crear enlaces simbólicos para los recursos del plugin ` para mejorar el rendimiento. + +Si no estás usando los ayudantes (helpers), puedes agregar /nombre-del-plugin/ al principio de la URL para un recurso dentro del plugin y servirlo de esa manera. Enlazar a '/contact_manager/js/some_file.js' serviría el recurso **plugins/ContactManager/webroot/js/some_file.js**. + +Componentes, Helpers y Behaviours +================================= + +Un plugin puede tener Componentes, Helpers y Behaviours, al igual que una aplicación de CakePHP. Incluso puedes crear plugins que consistan solo en Componentes, Helpers y Behaviours, lo que puede ser una excelente manera de construir componentes reutilizables que se pueden integrar en cualquier proyecto. + +La construcción de estos componentes es exactamente igual a construirlos dentro de una aplicación regular, sin ninguna convención de nomenclatura especial. + +Hacer referencia a tu componente desde dentro o fuera de tu plugin solo requiere que agregues el nombre del plugin antes del nombre del componente. Por ejemplo:: + + // Component defined in 'ContactManager' plugin + namespace ContactManager\Controller\Component; + + use Cake\Controller\Component; + + class ExampleComponent extends Component + { + } + + // Within your controllers + public function initialize(): void + { + parent::initialize(); + $this->loadComponent('ContactManager.Example'); + } + +La misma técnica se aplica a Ayudantes y Comportamientos. + +.. _plugin-commands: + +Comandos +======== + +Los plugins pueden registrar sus comandos dentro del gancho ``console()``. Por defecto, todos los comandos de consola en el plugin se descubren automáticamente y se añaden a la lista de comandos de la aplicación. Los comandos de los plugins llevan el prefijo del nombre del plugin. Por ejemplo, el ``UserCommand`` proporcionado por el plugin ``ContactManager`` se registraría tanto como ``contact_manager.user`` como ``user``. El nombre sin prefijo solo será tomado por un plugin si no es utilizado por la aplicación o por otro plugin. + +Puedes personalizar los nombres de los comandos definiendo cada comando en tu plugin:: + + public function console($commands) + { + // Create nested commands + $commands->add('bake model', ModelCommand::class); + $commands->add('bake controller', ControllerCommand::class); + + return $commands; + } + +Probar tu Plugin +================ + +Si estás probando controladores o generando URL, asegúrate de que tu +plugin conecte las rutas en ``tests/bootstrap.php``. + +Para obtener más información, consulta la página de :doc:`pruebas de plugins `. + +Publicar tu Plugin +================== + +Los plugins de CakePHP deben publicarse en `Packagist `__. De esta manera, otras personas pueden usarlo como dependencia de Composer. También puedes proponer tu plugin a la lista de `awesome-cakephp `_. + +Elige un nombre semánticamente significativo para el nombre del paquete. Idealmente, este debería llevar el prefijo del framework, en este caso "cakephp" como el framework. El nombre del proveedor generalmente será tu nombre de usuario de GitHub. **No** uses el espacio de nombres de CakePHP (cakephp), ya que está reservado para los plugins propiedad de CakePHP. La convención es usar letras minúsculas y guiones como separadores. + +Entonces, si creaste un plugin "Logging" con tu cuenta de GitHub "FooBar", un buen nombre sería `foo-bar/cakephp-logging`. +Y el plugin propiedad de CakePHP llamado "Localized" se puede encontrar bajo `cakephp/localized`, respectivamente. + +.. index:: vendor/cakephp-plugins.php + +Archivo de Mapeo del Plugin +=========================== + +Cuando instalas plugins a través de Composer, es posible que notes que se crea **vendor/cakephp-plugins.php**. Este archivo de configuración contiene un mapa de nombres de plugins y sus rutas en el sistema de archivos. Permite que los plugins se instalen en el directorio estándar del proveedor que está fuera de las rutas de búsqueda normales. La clase ``Plugin`` utilizará este archivo para localizar plugins cuando se carguen con ``addPlugin()``. En general, no necesitarás editar este archivo manualmente, ya que Composer y el paquete ``plugin-installer`` se encargarán de gestionarlo por ti. + +Gestiona tus Plugins usando Mixer +================================= + +Otra forma de descubrir y gestionar plugins en tu aplicación de CakePHP es a través de `Mixer `_. Es un plugin de CakePHP que te ayuda a instalar plugins desde Packagist. También te ayuda a gestionar tus plugins existentes. + +.. note:: - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. + IMPORTANTE: No uses esto en un entorno de producción. .. meta:: :title lang=es: Plugins - :keywords lang=es: plugin folder,configuration database,bootstrap,management module,little space,database connection,webroot,user management,contactmanager,array,config,cakephp,models,php,directories,blog,plugins,applications + :keywords lang=es: plugins, controladores, modelos, vistas, paquete, aplicación diff --git a/es/quickstart.rst b/es/quickstart.rst index 75d26113c6..6fe7cb28fe 100644 --- a/es/quickstart.rst +++ b/es/quickstart.rst @@ -5,9 +5,10 @@ La mejor forma de experimentar y aprender CakePHP es sentarse y construir algo. Para empezar crearemos una sencilla aplicación para guardar favoritos. -.. include:: /tutorials-and-examples/bookmarks/intro.rst -.. include:: /tutorials-and-examples/bookmarks/part-two.rst +.. include:: /tutorials-and-examples/cms/installation.rst +.. include:: /tutorials-and-examples/cms/database.rst +.. include:: /tutorials-and-examples/cms/articles-controller.rst .. meta:: :title lang=es: Primeros pasos - :keywords lang=es: estructura de archivos, tabla de nombres, petición inicial, tabla base de datos, estructura organizativa, rst, nomes de archivos, convenciones, mvc, página web, sit + :keywords lang=es: estructura de archivos, tabla de nombres, petición inicial, tabla base de datos, estructura organizativa, rst, nombres de archivos, convenciones, mvc, página web, sit diff --git a/es/release-policy.rst b/es/release-policy.rst new file mode 100644 index 0000000000..5be9e648f4 --- /dev/null +++ b/es/release-policy.rst @@ -0,0 +1,54 @@ +Política de Versiones +##################### + +CakePHP sigue la versión semántica para todas sus versiones. Esto sigue la convención de versionado de **importante.menor.parche**. + +El equipo de desarrollo intenta garantizar que cada versión siga las restricciones y garantías a continuación. + +Versiones Importantes +--------------------- + +Las versiones importantes generalmente no son retrocompatibles. Aunque CakePHP intenta no cambiar muchas características importantes en las versiones importantes, hay cambios en la API. + +Los cambios en las versiones importantes pueden incluir casi cualquier cosa, pero siempre se utilizan para eliminar características obsoletas y actualizar interfaces. + +Cualquier cambio de comportamiento que no sea retrocompatible se realiza en cambios importantes. + +Cada versión importante suele venir con una guía de actualización y muchas actualizaciones automáticas de código utilizando rector. + +Versiones Menores +----------------- + +Las versiones menores son generalmente retrocompatibles con la versión menor y el parche anterior. + +Las características pueden ser obsoletas, pero nunca se eliminan en una versión menor. + +Las interfaces no se cambian, pero se pueden agregar anotaciones para nuevos métodos expuestos en las implementaciones proporcionadas por CakePHP. + +Las nuevas características generalmente solo se agregan en versiones menores para que los usuarios puedan seguir las notas de migración. Las nuevas características también pueden incluir nuevas excepciones lanzadas cuando se corrige un comportamiento o se informan errores. + +Los cambios de comportamiento que requieren documentación se realizan en las versiones menores, pero generalmente son retrocompatibles. Se pueden hacer algunas excepciones si el problema es grave. + +.. nota:: + Las versiones menores también se conocen como versiones de puntos. + +Versiones de Parches +-------------------- + +Las versiones de parches siempre son retrocompatibles. Solo se realizan cambios que solucionan características rotas. + +Por lo general, los usuarios deberían poder confiar en que las versiones de parches no cambiarán el comportamiento, excepto para solucionar un problema. + +Los problemas que cambian un comportamiento de larga data generalmente no se encuentran en versiones de parches. Estos se consideran cambios de comportamiento y se incluirán en versiones menores o importantes para que los usuarios puedan migrar. + +.. nota:: + Las versiones de parches también se conocen como versiones de corrección de errores. + +Funciones Experimentales +------------------------ + +Cuando se agrega una nueva característica donde la API todavía está cambiando, puede marcarse como **experimental**. + +Las características experimentales deben seguir la misma convención de versiones menores y de corrección de errores. Sin embargo, los cambios en la API pueden ir en versiones menores, lo que podría cambiar significativamente el comportamiento. + +Los usuarios siempre deben esperar que la API cambie antes de que las características experimentales se lancen por completo. diff --git a/es/security.rst b/es/security.rst index 6a7ca602a1..e9791bb1b1 100644 --- a/es/security.rst +++ b/es/security.rst @@ -1,22 +1,20 @@ -Security -######## +Seguridad +########## -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +CakePHP provee algunas herramientas para hacer +que tu aplicación sea segura. Las siguientes secciones +cubren estas herramientas: .. toctree:: :maxdepth: 1 core-libraries/security - controllers/components/csrf - controllers/components/security + Form Protection Middleware + CSRF Protection + Content Security Policy + Security Headers + HTTPS Enforcer .. meta:: - :title lang=es: Security + :title lang=es: Seguridad :keywords lang=es: security, csrf, cross site request forgery component diff --git a/es/security/content-security-policy.rst b/es/security/content-security-policy.rst new file mode 100644 index 0000000000..6b797103d2 --- /dev/null +++ b/es/security/content-security-policy.rst @@ -0,0 +1,54 @@ +Content Security Policy Middleware +################################## + +The ``CspMiddleware`` makes it simpler to add Content-Security-Policy headers in +your application. Before using it you should install ``paragonie/csp-builder``: + +.. code-block:: bash + + composer require paragonie/csp-builder + +You can then configure the middleware using an array, or passing in a built +``CSPBuilder`` object:: + + use Cake\Http\Middleware\CspMiddleware; + + $csp = new CspMiddleware([ + 'script-src' => [ + 'allow' => [ + 'https://www.google-analytics.com', + ], + 'self' => true, + 'unsafe-inline' => false, + 'unsafe-eval' => false, + ], + ]); + + $middlewareQueue->add($csp); + +If you want to use a more strict CSP configuration, you can enable nonce based +CSP rules with the ``scriptNonce`` and ``styleNonce`` options. When enabled +these options will modify your CSP policy and set the ``cspScriptNonce`` and +``cspStyleNonce`` attributes in the request. These attributes are applied to +the ``nonce`` attribute of all script and CSS link elements created by +``HtmlHelper``. This simplifies the adoption of policies that use +a `nonce-base64 +`__ +and ``strict-dynamic`` for increased security and easier maintenance:: + + + $policy = [ + // Must exist even if empty to set nonce for for script-src + 'script-src' => [], + 'style-src' => [], + ]; + // Enable automatic nonce addition to script & CSS link tags. + $csp = new CspMiddleware($policy, [ + 'scriptNonce' => true, + 'styleNonce' => true, + ]); + $middlewareQueue->add($csp); + +.. meta:: + :title lang=en: Content Security Policy Middleware + :keywords lang=en: security, content security policy, csp, middleware, cross-site scripting diff --git a/es/security/csrf.rst b/es/security/csrf.rst new file mode 100644 index 0000000000..e83c5559cc --- /dev/null +++ b/es/security/csrf.rst @@ -0,0 +1,174 @@ +Protección CSRF +############### + +Las Solicitudes CSRF son una clase de ataque donde se realizan comandos no autorizados en nombre de un usuario autenticado sin su conocimiento o consentimiento. + +CakePHP ofrece dos formas de protección CSRF: + +* ``SessionCsrfProtectionMiddleware`` almacena tokens CSRF en la sesión. Esto requiere que tu aplicación abra la sesión en cada solicitud. Los beneficios de los tokens CSRF basados en sesiones son que están vinculados a un usuario específico y sólo son válidos durante la duración de una sesión activa. +* ``CsrfProtectionMiddleware`` almacena tokens CSRF en una cookie. Usar una cookie permite realizar verificaciones CSRF sin ningún estado en el servidor. Los valores de las cookies se verifican en términos de autenticidad mediante una comprobación HMAC. Sin embargo, debido a su naturaleza sin estado, los tokens CSRF se pueden reutilizar entre usuarios y sesiones. + +.. nota:: + + No puedes usar ambos enfoques a la vez, debes elegir sólo uno. Si usas ambos, ocurrirá un error de incompatibilidad de tokens CSRF en cada solicitud `PUT` y `POST`. + +.. _csrf-middleware: + +Middleware CSRF +=============== + +La protección CSRF se puede aplicar a toda tu aplicación o a ámbitos de enrutamiento específicos. Al aplicar un middleware CSRF a tu cola de middlewares, proteges todas las acciones en la aplicación:: + + // En src/Application.php + // Para tokens CSRF basados en cookies. + use Cake\Http\Middleware\CsrfProtectionMiddleware; + + // Para tokens CSRF basados en sesiones. + use Cake\Http\Middleware\SessionCsrfProtectionMiddleware; + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $opciones = [ + // ... + ]; + $csrf = new CsrfProtectionMiddleware($opciones); + // o + $csrf = new SessionCsrfProtectionMiddleware($opciones); + + $middlewareQueue->add($csrf); + return $middlewareQueue; + } + +Al aplicar la protección CSRF a los ámbitos de enrutamiento, puedes aplicar condicionalmente CSRF a grupos específicos de rutas:: + + // en src/Application.php + use Cake\Http\Middleware\CsrfProtectionMiddleware; + + public function routes(RouteBuilder $routes) : void + { + $options = [ + // ... + ]; + $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options)); + parent::routes($routes); + } + + // en config/routes.php + $routes->scope('/', function (RouteBuilder $routes) { + $routes->applyMiddleware('csrf'); + }); + +Opciones del middleware CSRF basado en cookies +----------------------------------------------- + +Las opciones de configuración disponibles son: + +- ``cookieName``: El nombre de la cookie que se enviará. Por defecto es ``csrfToken``. +- ``expiry``: Cuánto tiempo debe durar el token CSRF. Por defecto es por la sesión del navegador. +- ``secure``: Si la cookie se establecerá o no con la bandera Secure. Es decir, la cookie sólo se establecerá en una conexión HTTPS y cualquier intento sobre HTTP normal fallará. Por defecto es ``false``. +- ``httponly``: Si la cookie se establecerá o no con la bandera HttpOnly. Por defecto es ``false``. Antes de la versión 4.1.0, usa la opción ``httpOnly``. +- ``samesite``: Te permite declarar si tu cookie debe estar restringida a un contexto de primer partido o mismo sitio. Los valores posibles son ``Lax``, ``Strict`` y ``None``. Por defecto es ``null``. +- ``field``: El campo del formulario a comprobar. Por defecto es ``_csrfToken``. Cambiar esto también requerirá configurar FormHelper. + +Opciones del middleware CSRF basado en sesiones +----------------------------------------------- + +Las opciones de configuración disponibles son: + +- ``key``: La clave de sesión a utilizar. Por defecto es `csrfToken`. +- ``field``: El campo del formulario a comprobar. Cambiar esto también requerirá configurar FormHelper. + +Cuando está habilitado, puedes acceder al token CSRF actual en el objeto de Request:: + + $token = $this->request->getAttribute('csrfToken'); + +Si necesitas rotar o reemplazar el token CSRF de la sesión, puedes hacerlo con:: + + $this->request = SessionCsrfProtectionMiddleware::replaceToken($this->request); + +.. versionadded:: 4.3.0 + Se añadió el método ``replaceToken``. + +Omitir comprobaciones CSRF para acciones específicas +----------------------------------------------------- + +Ambas implementaciones de middleware CSRF te permiten usar la función ``skip check`` para un control más preciso sobre las URL para las cuales se debe hacer la comprobación de tokens CSRF:: + + // en src/Application.php + use Cake\Http\Middleware\CsrfProtectionMiddleware; + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $csrf = new CsrfProtectionMiddleware(); + + // La comprobación del token se omitirá cuando el callback devuelva `true`. + $csrf->skipCheckCallback(function ($request) { + // Omitir la comprobación del token para las URL de la API. + if ($request->getParam('prefix') === 'Api') { + return true; + } + }); + + // Asegúrate de que el middleware de enrutamiento se añada a la cola antes del middleware de protección CSRF. + $middlewareQueue->add($csrf); + + return $middlewareQueue; + } + +.. nota:: + + Debes aplicar el middleware de protección CSRF solo para rutas que manejen + solicitudes con estado que utilicen cookies/sesiones. Por ejemplo, al desarrollar una + API, las solicitudes sin estado que no usan cookies para la autenticación no se ven + afectadas por CSRF, por lo que el middleware no necesita aplicarse para esas rutas. + +Integración con FormHelper +-------------------------- + +El ``CsrfProtectionMiddleware`` se integra perfectamente con ``FormHelper``. Cada vez +que creas un formulario con ``FormHelper``, se insertará un campo oculto que contiene +el token CSRF. + +.. nota:: + + Cuando uses protección CSRF, siempre debes empezar tus formularios con + ``FormHelper``. Si no lo haces, deberás crear manualmente los campos ocultos en + cada uno de tus formularios. + +Protección CSRF y Solicitudes AJAX +----------------------------------- + +Además de los parámetros de datos de la solicitud, los tokens CSRF se pueden enviar a través +de un encabezado especial ``X-CSRF-Token``. Usar un encabezado a menudo facilita la +integración de un token CSRF con aplicaciones JavaScript, o `endpoints` de API +basados en XML/JSON. + +El Token CSRF se puede obtener en JavaScript a través de la Cookie ``csrfToken``, o en PHP +a través del atributo del objeto de Request llamado ``csrfToken``. Usar la cookie puede ser más fácil +cuando tu código JavaScript reside en archivos separados de las plantillas de vista de CakePHP, +y cuando ya tienes funcionalidad para analizar cookies mediante JavaScript. + +Si tienes archivos JavaScript separados, pero no quieres ocuparte de manejar cookies; +podrías, por ejemplo, configurar el token en una variable global de JavaScript en tu diseño, mediante +la definición de un bloque de script como este:: + + echo $this->Html->scriptBlock(sprintf( + 'var csrfToken = %s;', + json_encode($this->request->getAttribute('csrfToken')) + )); + +Luego puedes acceder al token como ``csrfToken`` o ``window.csrfToken`` en cualquier archivo de script +que se cargue después de este bloque de script. + +Otra alternativa sería poner el token en una metaetiqueta personalizada como esta:: + + echo $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); + +Que luego se podría acceder en tus scripts buscando el elemento ``meta`` con +el nombre ``csrfToken``, que podría ser tan simple como esto cuando se usa jQuery:: + + var csrfToken = $('meta[name="csrfToken"]').attr('content'); + +.. meta:: + :title lang=es: Protección CSRF + :keywords lang=es: seguridad, csrf, falsificación de solicitudes entre sitios, middleware, sesión diff --git a/es/security/https-enforcer.rst b/es/security/https-enforcer.rst new file mode 100644 index 0000000000..63e99beeea --- /dev/null +++ b/es/security/https-enforcer.rst @@ -0,0 +1,65 @@ +.. _https-enforcer-middleware: + +HTTPS Enforcer Middleware +######################### + +If you want your application to only be available via HTTPS connections you can +use the ``HttpsEnforcerMiddleware``:: + + use Cake\Http\Middleware\HttpsEnforcerMiddleware; + + // Always raise an exception and never redirect. + $https = new HttpsEnforcerMiddleware([ + 'redirect' => false, + ]); + + // Send a 302 status code when redirecting + $https = new HttpsEnforcerMiddleware([ + 'redirect' => true, + 'statusCode' => 302, + ]); + + // Send additional headers in the redirect response. + $https = new HttpsEnforcerMiddleware([ + 'headers' => ['X-Https-Upgrade' => 1], + ]); + + // Disable HTTPs enforcement when ``debug`` is on. + $https = new HttpsEnforcerMiddleware([ + 'disableOnDebug' => true, + ]); + + // Only trust HTTP_X_ headers from the listed servers. + $https = new HttpsEnforcerMiddleware([ + 'trustProxies' => ['192.168.1.1'], + ]); + +If a non-HTTP request is received that does not use GET a ``BadRequestException`` will be raised. + +NOTE: The Strict-Transport-Security header is ignored by the browser when your site has only been +accessed using HTTP. Once your site is accessed over HTTPS with no certificate errors, the browser +knows your site is HTTPS capable and will honor the Strict-Transport-Security header. + +Adding Strict-Transport-Security +================================ + +When your application requires SSL it is a good idea to set the +``Strict-Transport-Security`` header. This header value is cached in the +browser, and informs browsers that they should always connect with HTTPS connections. +You can configure this header with the ``hsts`` option:: + + $https = new HttpsEnforcerMiddleware([ + 'hsts' => [ + // How long the header value should be cached for. + 'maxAge' => 60 * 60 * 24 * 365, + // should this policy apply to subdomains? + 'includeSubDomains' => true, + // Should the header value be cacheable in google's HSTS preload + // service? While not part of the spec it is widely implemented. + 'preload' => true, + ], + ]); + +.. meta:: + :title lang=en: HTTPS Enforcer Middleware + :keywords lang=en: security, https, require https diff --git a/es/security/security-headers.rst b/es/security/security-headers.rst new file mode 100644 index 0000000000..f6d9901ca0 --- /dev/null +++ b/es/security/security-headers.rst @@ -0,0 +1,34 @@ +.. _security-header-middleware: + +Middleware SecurityHeader +######################### + +El ``SecurityHeaderMiddleware`` te permite agregar headers +relacionados con la seguridad a tu aplicación. Una vez configurado, el middleware +agregará los siguientes headers a las respuestas: + +* ``X-Content-Type-Options`` +* ``X-Download-Options`` +* ``X-Frame-Options`` +* ``Referrer-Policy`` + +Este middleware debe ser configurado antes de agregarlo a la cola de middlewares:: + + use Cake\Http\Middleware\SecurityHeadersMiddleware; + + $securityHeaders = new SecurityHeadersMiddleware(); + $securityHeaders + ->setReferrerPolicy() + ->setXFrameOptions() + ->noOpen() + ->noSniff(); + + $middlewareQueue->add($securityHeaders); + +Aqui está uina lista de `Headers HTTP comunes `__, +y la `configuración recomendada `__ de Mozzilla para +aplicaciones web seguras. + +.. meta:: + :title lang=en: Middleware SecurityHeader + :keywords lang=en: x-frame-options, cross-domain, referrer-policy, download-options, middleware, content-type-options diff --git a/es/standalone-packages.rst b/es/standalone-packages.rst new file mode 100644 index 0000000000..1d78dd3fa1 --- /dev/null +++ b/es/standalone-packages.rst @@ -0,0 +1,76 @@ +Paquetes Independientes +########################glo + +El núcleo de CakePHP está dividido en varios paquetes que pueden +ser usados de manera independiente. + +`ORM `_ +--------------------------------------- + +Un mapeador objeto-relacional flexible, ligero y poderoso para PHP. Ha +sido implementado usando el patrón `DataMapper`. + +`Database `_ +------------------------------------------------- + +Librería flexible de base de datos con un API similar a PDO. + +`Datasource `_ +----------------------------------------------------- + +Provee un manejo de conexiones y `traits` para entidades y `Queries` que +puede ser reusado por diferentes tipos de almacenamiento de datos. + +`HTTP `_ +----------------------------------------- + +Cliente HTTP y librerias compatibles con los estándares PSR-18 y PSR-15. + +`Console `_ +----------------------------------------------- + +Librería para crear aplicaciones de línea de comando. + +`Collection `_ +----------------------------------------------------- + +Librería que provee un conjunto de herramientas para manipular arreglos y objetos de tipo Traversable. + +`I18n `_ +----------------------------------------- + +Provee soporte para traducción y localización para fechas y números. + +`Cache `_ +------------------------------------------- + +Librería de caché que soporta distintos manejadores. Es compatible con PSR-16. + +`Log `_ +--------------------------------------- + +Librería para escribir registros (`logging`) con soporte para múltiples manejadores. Es compatible con PSR-3. + +`Event `_ +------------------------------------------- + +Librería de manejo de eventos. + +`Utility `_ +----------------------------------------------- + +Librería que incluye clases de utilidad como Inflexto, Tet, Hash, Security y Xml. + +`Validation `_ +----------------------------------------------------- + +Librería de validación de CakePHP. + +`Form `_ +----------------------------------------- + +Librería usada para crear formularios no enlazados con el ORM o algún otro almacenamiento de datos. + +.. meta:: + :title lang=en: Packages Independientes + :keywords lang=en: packages, cakephp, orm, database, http client, http server, utility, events, log, cache diff --git a/es/topics.rst b/es/topics.rst index f95387889a..b02564f354 100644 --- a/es/topics.rst +++ b/es/topics.rst @@ -1,14 +1,7 @@ -Using CakePHP -############# +Usando CakePHP +############### -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Introducción a las partes principales de CakePHP: * :doc:`/installation` * :doc:`/development/configuration` @@ -31,15 +24,14 @@ Using CakePHP * :doc:`/core-libraries/form` * :doc:`/development/sessions` * :doc:`/development/rest` -* :doc:`/controllers/components/authentication` -* :doc:`/controllers/components/pagination` -* :doc:`/controllers/components/csrf` +* :doc:`/controllers/pagination` +* :ref:`csrf-middleware` * :doc:`/core-libraries/email` * :doc:`/views/helpers/form` * :doc:`/views/helpers/html` * :doc:`/core-libraries/validation` * :doc:`/development/testing` * :doc:`/deployment` -* :doc:`/console-and-shells` +* :doc:`/console-commands` * :doc:`/contributing` * :doc:`/tutorials-and-examples` diff --git a/es/tutorials-and-examples.rst b/es/tutorials-and-examples.rst index da09e37e13..05673b4b66 100644 --- a/es/tutorials-and-examples.rst +++ b/es/tutorials-and-examples.rst @@ -15,12 +15,14 @@ para usar. tutorials-and-examples/cms/installation tutorials-and-examples/cms/database + tutorials-and-examples/cms/articles-controller + tutorials-and-examples/cms/tags-and-users + tutorials-and-examples/cms/authentication + tutorials-and-examples/cms/authorization .. toctree:: :maxdepth: 1 - tutorials-and-examples/bookmarks/intro - tutorials-and-examples/bookmarks/part-two tutorials-and-examples/blog/blog tutorials-and-examples/blog/part-two tutorials-and-examples/blog/part-three diff --git a/es/tutorials-and-examples/blog-auth-example/auth.rst b/es/tutorials-and-examples/blog-auth-example/auth.rst index cb107e286a..5920a6043e 100644 --- a/es/tutorials-and-examples/blog-auth-example/auth.rst +++ b/es/tutorials-and-examples/blog-auth-example/auth.rst @@ -1,12 +1,15 @@ Tutorial Blog - Autenticación y Autorización ############################################ -Siguiendo con nuestro ejemplo de aplicacion :doc:`/tutorials-and-examples/blog/blog`, imaginá que necesitamos proteger ciertas URLs, dependiendo del usuario logeado. También tenemos otro requisito, permitir que nuestro blog tenga varios autores, cada uno habilitado para crear sus posts, editar y borrarlos a voluntad, evitando que otros autores puedan cambiarlos. +Siguiendo con nuestro ejemplo de aplicación :doc:`/tutorials-and-examples/blog/blog`, imaginá que +necesitamos no permitir que usuarios no autenticados puedan crear artículos. -Creando el codigo para usuarios -=============================== +Creando la tabla users y el Controlador +======================================= + +Primero, vamos a crear una tabla en nuestra base de datos para guardar los datos de usuarios: -Primero, vamos a crear una tabla en nuestra base de datos para guardar los datos de usuarios:: +.. code-block:: mysql CREATE TABLE users ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, @@ -17,9 +20,26 @@ Primero, vamos a crear una tabla en nuestra base de datos para guardar los datos modified DATETIME DEFAULT NULL ); -Siguimos las convenciones de CakePHP para nombrar tablas pero también estamos aprovechando otra convencion: al usar los campos email y password en nuestra tabla CakePHP configurará automáticamente la mayoria de las cosas al momento de implementar el login. +Si estás usando PostgreSQL, conecta a la base de datos cake_blog y ejecuta el siguiente SQL en su lugar: -El siguiente paso es crear Users table, responsable de buscar, guardar y validar los datos de usuario:: +.. code-block:: SQL + + CREATE TABLE users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255), + password VARCHAR(255), + role VARCHAR(20), + created TIMESTAMP DEFAULT NULL, + modified TIMESTAMP DEFAULT NULL + ); + +Seguimos las convenciones de CakePHP para nombrar tablas, pero también +estamos aprovechando otra convención: al usar los campos email y password +en nuestra tabla users, CakePHP configurará automáticamente la mayoría de las cosas +al momento de implementar el login. + +El siguiente paso es crear nuestra clase ``UsersTable``, responsable de buscar, guardar +y validar los datos de usuario:: // src/Model/Table/UsersTable.php namespace App\Model\Table; @@ -29,11 +49,10 @@ El siguiente paso es crear Users table, responsable de buscar, guardar y validar class UsersTable extends Table { - - public function validationDefault(Validator $validator) + public function validationDefault(Validator $validator): Validator { return $validator - ->notEmpty('email', 'A email is required') + ->notEmpty('email', 'An email is required') ->email('email') ->notEmpty('password', 'A password is required') ->notEmpty('role', 'A role is required') @@ -45,44 +64,33 @@ El siguiente paso es crear Users table, responsable de buscar, guardar y validar } -También vamos a crear UsersController; el siguiente contenido fue generado usando baked UsersController con el generador de código incluído con CakePHP:: +También vamos a crear nuestro ``UsersController``. El siguiente contenido se corresponde +con una clase ``UsersController`` básica "baked" usando las utilidades de generación +de código que están incluidas en CakePHP:: // src/Controller/UsersController.php namespace App\Controller; use App\Controller\AppController; - use Cake\Event\Event; - // Prior to 3.6 use Cake\Network\Exception\NotFoundException - use Cake\Http\Exception\NotFoundException; + use Cake\Event\EventInterface; class UsersController extends AppController { - - public function beforeFilter(Event $event) + public function index() { - parent::beforeFilter($event); - $this->Auth->allow('add'); - } - - public function index() - { - $this->set('users', $this->Users->find('all')); + $this->set('users', $this->Users->find()->all()); } public function view($id) { - if (!$id) { - throw new NotFoundException(__('Invalid user')); - } - $user = $this->Users->get($id); $this->set(compact('user')); } public function add() { - $user = $this->Users->newEntity(); + $user = $this->Users->newEmptyEntity(); if ($this->request->is('post')) { $user = $this->Users->patchEntity($user, $this->request->getData()); if ($this->Users->save($user)) { @@ -93,10 +101,11 @@ También vamos a crear UsersController; el siguiente contenido fue generado usan } $this->set('user', $user); } - } -De la misma forma que creamos las vistas para los posts del blog o usando la herramienta de generación de código, creamos las vistas. Para los objetivos de este tutorial, mostraremos solamente add.php: +Creamos las vistas para nuestros artículos de la misma forma que el controlador, usando +las herramientas de generación de código 'bake', lo que nos permite implementar las vistas +de los usuarios. Para el propósito de este tutorial, mostraremos solamente **add.php**: .. code-block:: php @@ -106,9 +115,9 @@ De la misma forma que creamos las vistas para los posts del blog o usando la her Form->create($user) ?>
- Form->input('email') ?> - Form->input('password') ?> - Form->input('role', [ + Form->control('email') ?> + Form->control('password') ?> + Form->control('role', [ 'options' => ['admin' => 'Admin', 'author' => 'Author'] ]) ?>
@@ -116,84 +125,27 @@ De la misma forma que creamos las vistas para los posts del blog o usando la her Form->end() ?> -Autenticación (login y logout) -============================== - -Ya estamos listos para agregar nuestra autenticación. En CakePHP esto es manejado por :php:class:`Cake\\Controller\\Component\\AuthComponent`, responsable de requerir login para ciertas acciones, de manejar el sign-in y el sign-out y también de autorizar usuarios logeados a ciertas acciones que estan autorizados a utilizar. - -Para agregar este componente a tú aplicación abre el archivo **src/Controller/AppController.php** y agrega las siguientes lineas:: +Añadiendo Autenticación +======================= - // src/Controller/AppController.php +Ya estamos listos para agregar nuestra autenticación. En CakePHP esto es manejado +por el plugin ``authentication``. Empezaremos instalándolo. Usa composer para +instalar el plugin: - namespace App\Controller; +.. code-block:: console - use Cake\Controller\Controller; - use Cake\Event\Event; + composer require "cakephp/authentication:^2.0" - class AppController extends Controller - { - //... +Luego añade la siguiente línea en la función ``bootstrap()`` del archivo ``Application.php``:: - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'loginRedirect' => [ - 'controller' => 'Articles', - 'action' => 'index' - ], - 'logoutRedirect' => [ - 'controller' => 'Pages', - 'action' => 'display', - 'home' - ] - ]); - } + // in src/Application.php in the bootstrap() method. + $this->addPlugin('Authentication'); - public function beforeFilter(Event $event) - { - $this->Auth->allow(['index', 'view', 'display']); - } - //... - } - -No hay mucho que configurar, al haber utilizado convenciones para la tabla de usuarios. Simplemente asignamos las URLs que serán cargadas despues del login y del logout, en nuestro caso ``/articles/`` y ``/`` respectivamente. - -Lo que hicimos en ``beforeFilter()`` fue decirle al AuthComponent que no requiera login para las acciones index y view en cada controlador. -Queremos que nuestros visitantes puedan leer y listar las entradas sin registrarse. - -Ahora necesitamos poder registrar nuevos usuarios, guardar el nombre de usuario y contraseña, y hashear su contraseña para que no sea guardada como texto plano. Vamos a decirle al AuthComponent que deje usuarios sin autenticar acceder a la funcion add del controlador users e implementemos las acciones de login y logout:: - - // src/Controller/UsersController.php - - public function beforeFilter(Event $event) - { - parent::beforeFilter($event); - // Allow users to register and logout. - // You should not add the "login" action to allow list. Doing so would - // cause problems with normal functioning of AuthComponent. - $this->Auth->allow(['add', 'logout']); - } - - public function login() - { - if ($this->request->is('post')) { - $user = $this->Auth->identify(); - if ($user) { - $this->Auth->setUser($user); - return $this->redirect($this->Auth->redirectUrl()); - } - $this->Flash->error(__('Invalid email or password, try again')); - } - } - - public function logout() - { - return $this->redirect($this->Auth->logout()); - } +Añadiendo hashing de contraseña +=============================== -El hasheo del password aún no está hecho, necesitamos una clase Entity para nuestra clase User para así manejar esta lógica específica. -Crea el archivo **src/Model/Entity/User.php** y agrega las siguientes lineas:: +Lo siguiente, creamos la entidad ``User`` y añadimos el hashing del password. Crear el +archivo de la entidad **src/Model/Entity/User.php** y añade lo siguiente:: // src/Model/Entity/User.php namespace App\Model\Entity; @@ -203,9 +155,11 @@ Crea el archivo **src/Model/Entity/User.php** y agrega las siguientes lineas:: class User extends Entity { - - // Make all fields mass assignable for now. - protected array $_accessible = ['*' => true]; + // Make all fields mass assignable except for primary key field "id". + protected array $_accessible = [ + '*' => true, + 'id' => false + ]; // ... @@ -219,141 +173,210 @@ Crea el archivo **src/Model/Entity/User.php** y agrega las siguientes lineas:: // ... } -Ahora cada vez que la propiedad password sea asignada a un usuario, será hasheada usando la clase ``DefaultPasswordHasher``. -Solamente nos falta un archivo para la vista de la acción login. Abre tu archivo **templates/Users/login.php** y agrega las siguientes -lineas: +Ahora, siempre que la propiedad ``password`` es asignada a la entidad User, se +le aplicara el Hash usando la clase ``DefaultPasswordHasher``. -.. code-block:: php - - +Configurando Autenticación +========================== -
- Flash->render('auth') ?> - Form->create() ?> -
- - Form->input('email') ?> - Form->input('password') ?> -
- Form->button(__('Login')); ?> - Form->end() ?> -
+Ahora es el momento de configurar el plugin Authentication. +El plugin gestionará el proceso de autenticación usando 3 clases diferentes: -Ya podés registrar un nuevo usuario accediendo a ``/users/add`` e iniciar sesión con las nuevas credenciales ingresando a ``/users/login``. También al intentar acceder a alguna otra URL que no fue explicitamente autorizada, por ejemplo ``/articles/add``, la aplicación te redireccionará automaticamente al la pagina de login. +* ``Application`` usara el Authentication Middleware y proporciona un + AuthenticationService, teniendo todas las configuraciones que queramos para definir + como se van a comprobar las credenciales y donde encontrarlas. +* ``AuthenticationService`` es una clase de utilidad que te permite configurar el + proceso de autenticación. +* ``AuthenticationMiddleware`` será ejecutado como parte de la cola de middleware, + esto será antes de que tu controlador sea procesado por el framework, recogerá + las credenciales y las procesará para comprobar si el usuario está autenticado. -Y eso es todo! Se ve demasiado simple para ser verdad. Volvamos un poco para explicar que pasa. La función ``beforeFilter()`` le dice al AuthComponent que no requiera login para la acción ``add()`` asi como para ``index()`` y ``view()``, autorizadas en el ``beforeFilter()`` del AppController. +La lógica de autenticación es dividida en clases específicas y el proceso +se realiza antes de la capa del controlador. Primero, se comprueba si el +usuario está autenticado (basado en la configuración proporcionada) e inyecta +el usuario y el resultado de la autenticación en la petición para futura referencia. -La función ``login()`` llama a ``$this->Auth->identify()`` del AuthComponent, y funciona sin ninguna otra configuración ya que seguimos la convención. Es decir, tener un modelo llamado User con los campos email y password, y usar un formulario que hace post a un controlador con los datos del usuario. Esta función devuelve si el login fue exitoso o no, y en caso de que tenga exito redirige a la URL puesta en AppController, dentro de la configuracion del AuthComponent. +En **src/Application.php**, añade los siguientes imports:: -El logout funciona simplemente al acceder a ``/users/logout`` y redirecciona al usuario a la URL configurada. + // In src/Application.php add the following imports + use Authentication\AuthenticationService; + use Authentication\AuthenticationServiceInterface; + use Authentication\AuthenticationServiceProviderInterface; + use Authentication\Middleware\AuthenticationMiddleware; + use Psr\Http\Message\ServerRequestInterface; -Autorización (quién está autorizado a acceder qué) -================================================== +Luego implementa el interfaz de autenticación en tu clase Application:: -Como mencionamos antes, estamos convirtiendo este blog en una herramienta de autoría multiusuario, y para hacer esto necesitamos modificar la tabla de posts para agregar referencia al modelo User:: + // in src/Application.php + class Application extends BaseApplication + implements AuthenticationServiceProviderInterface + { - ALTER TABLE articles ADD COLUMN user_id INT(11); +Después añade lo siguiente:: -También, un pequeño cambio en ArticlesController es necesario para guardar el usuario logeado como referencia en los artículos creados:: + // src/Application.php + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue + // ... other middleware added before + ->add(new RoutingMiddleware($this)) + // add Authentication after RoutingMiddleware + ->add(new AuthenticationMiddleware($this)); - // src/Controller/ArticlesController.php + return $middlewareQueue; + } - public function add() + public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - // Added this line - $article->user_id = $this->Auth->user('id'); - // You could also do the following - //$newData = ['user_id' => $this->Auth->user('id')]; - //$article = $this->Articles->patchEntity($article, $newData); - if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been saved.')); - return $this->redirect(['action' => 'index']); - } - $this->Flash->error(__('Unable to add your article.')); - } - $this->set('article', $article); - } + $authenticationService = new AuthenticationService([ + 'unauthenticatedRedirect' => '/users/login', + 'queryParam' => 'redirect', + ]); + + // Carga los identificadores, asegurando que se comprueban los campos email y password + $authenticationService->loadIdentifier('Authentication.Password', [ + 'fields' => [ + 'username' => 'email', + 'password' => 'password', + ], + ]); -La función ``user()`` del AuthComponent devuelve datos del usuario actualmente logeado. Usamos este método para agregar datos a la información que será guardada. + // Carga los authenticators, quieres que la sesión comprueba primero + $authenticationService->loadAuthenticator('Authentication.Session'); + // Configure form data check to pick email and password + $authenticationService->loadAuthenticator('Authentication.Form', [ + 'fields' => [ + 'username' => 'email', + 'password' => 'password', + ], + 'loginUrl' => '/users/login', + ]); + + return $authenticationService; + } -Vamos a prevenir que autores puedan editar o eliminar los artículos de otros autores. La regla básica para nuestra aplicación es que los usuarios admin pueden acceder todas las URL, mientras que los usuarios normales (autores) solamente pueden acceder las acciones permitidas. Abre nuevamente AppController y agregá las siguientes opciones en la configuración del Auth:: +En tu clase ``AppController`` añade el siguiente código:: // src/Controller/AppController.php - - public function initialize() + public function initialize(): void { + parent::initialize(); $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'authorize' => ['Controller'], // Added this line - 'loginRedirect' => [ - 'controller' => 'Articles', - 'action' => 'index' - ], - 'logoutRedirect' => [ - 'controller' => 'Pages', - 'action' => 'display', - 'home' - ] - ]); + + // Añade ésta línea para comprobar la autenticación y asegurar tu aplicación + $this->loadComponent('Authentication.Authentication'); + +Ahora, en cada petición, el ``AuthenticationMiddleware`` inspeccionará la sesión +para comprobar si existe un usuario autenticado. Si estamos cargando la página +``/users/login``, también inspeccionará los datos del formulario enviado en "post" +(si hay alguno) para extraer las credenciales. Por defecto las credenciales se +extraerán de los campos ``email`` y ``password`` de la información del request. +El resultado de la autenticación será inyectado in un atributo de la petición +llamado ``authentication``. Puedes inspeccionar el resultado en cualquier momento +usando ``$this->request->getAttribute('authentication')`` desde la acción de un +controlador. +Todas tus páginas serán restringidas ya que ``AuthenticationComponent`` está +comprobando el resultado en cada petición. Cuando falla al buscar un usuario +autenticado, redirigirá al usuario a la página ``/users/login``. +Te en cuenta que en éste punto del tutorial, la aplicación no funcionará ya que +aún no tenemos la página de login. Si visitas tu aplicación, obtendrás un bucle +infinito de redirección, asi que, vamos a arreglarlo! + +En tu ``UsersController``, añade el siguiente código:: + + public function beforeFilter(\Cake\Event\EventInterface $event) + { + parent::beforeFilter($event); + // Configura la acción de login para no requerir autenticación, para + // prevenir el bucle infinito de redirección + $this->Authentication->addUnauthenticatedActions(['login']); } - public function isAuthorized($user) + public function login() { - // Admin can access every action - if (isset($user['role']) && $user['role'] === 'admin') { - return true; - } + $this->request->allowMethod(['get', 'post']); + $result = $this->Authentication->getResult(); + // sin importar si es POST o GET, redirige si el usuario esta autenticado + if ($result->isValid()) { + // redirige a /articles después de iniciar sesión correctamente + $redirect = $this->request->getQuery('redirect', [ + 'controller' => 'Articles', + 'action' => 'index', + ]); - // Default deny - return false; + return $this->redirect($redirect); + } + // muestra los errors si el usuario envió el formulario y fallo la autenticación + if ($this->request->is('post') && !$result->isValid()) { + $this->Flash->error(__('Invalid email or password')); + } } -Hemos creado un mecanismo de autorización muy simple. En este caso, los usuarios con el rol ``admin`` podrán acceder a cualquier URL del sitio cuando esten logeados, pero el resto de los usuarios no podrán hacer más que los usuarios no logeados. +Añade la lógica de la vista para la acción de login:: -Esto no es exactamente lo que queriamos, por lo que tendremos que agregar mas reglas a nuestro método ``isAuthorized()``. Pero en lugar de hacerlo en AppController, vamos a delegar a cada controlador. Las reglas que vamos a agregar a ArticlesController deberian permitirle a los autores crear artículos, pero prevenir que editen artículos que no le pertenezcan. Abre el archivo ArticlesController.php y agregá las siguientes lineas:: + +
+ Flash->render() ?> +

Login

+ Form->create() ?> +
+ + Form->control('email', ['required' => true]) ?> + Form->control('password', ['required' => true]) ?> +
+ Form->submit(__('Login')); ?> + Form->end() ?> - // src/Controller/ArticlesController.php + Html->link("Add User", ['action' => 'add']) ?> +
- public function isAuthorized($user) - { - // All registered users can add articles - if ($this->request->getParam('action') === 'add') { - return true; - } +Ahora la página de login nos permitirá iniciar sesión en la aplicación. +Compruébalo haciendo una petición a cualquier página de tu aplicación. +Después de haber sido redirigido a la página ``/users/login``, introduce +el email y password que usaste previamente para crear el usuario. +Deberías ser redirigido correctamente después de iniciar sesión. - // The owner of an article can edit and delete it - if (in_array($this->request->getParam('action'), ['edit', 'delete'])) { - $articleId = (int)$this->request->getParam('pass.0'); - if ($this->Articles->isOwnedBy($articleId, $user['id'])) { - return true; - } - } +Necesitamos añadir un par de detalles más para configurar nuestra aplicación. +Queremos que todas las páginas ``view`` e ``index`` sean accesible sin necesitar +iniciar sesión, así que añadiremos ésta configuración específica en ``AppController``:: - return parent::isAuthorized($user); + // en src/Controller/AppController.php + public function beforeFilter(\Cake\Event\EventInterface $event) + { + parent::beforeFilter($event); + // para todos los controladores de nuestra aplicación, hacer el index y view + // acciones públicas, saltándonos la autenticación + $this->Authentication->addUnauthenticatedActions(['index', 'view']); } -Estamos sobreescribiendo el método ``isAuthorized()`` de AppController y comprobando si la clase padre autoriza al usuario. Si no lo hace entonces solamente autorizarlo a acceder a la acción add y condicionalmente acceder a edit y delete. Una última cosa por implementar, decidir si el usuario está autorizador a editar el post o no, estamos llamando la función ``isOwnedBy()`` del modelo Articles. Es en general una buena practica mover la mayor parte de la logica posible hacia los modelos:: +Terminar sesión +=============== - // src/Model/Table/ArticlesTable.php +Añade la acción ``logout`` a la clase ``UsersController``:: - public function isOwnedBy($articleId, $userId) + // en src/Controller/UsersController.php + public function logout() { - return $this->exists(['id' => $articleId, 'user_id' => $userId]); + $result = $this->Authentication->getResult(); + // sin importar si es POST o GET, redirige si el usuario esta autenticado + if ($result->isValid()) { + $this->Authentication->logout(); + return $this->redirect(['controller' => 'Users', 'action' => 'login']); + } } -Esto concluye nuestro simple tutorial de autenticación y autorización. Para proteger el UsersController se puede seguir la misma técnica utilizada para ArticlesController. También es posible implementar una solución mas general en AppController, de acuerdo a tus reglas. +Ahora puedes visitar ``/users/logout`` para terminar la sesión. Luego serás +redirigido a la página de login. Si has llegado tan lejos, felicidades, ahora +tienes un blog simple que: -En caso de necesitar más control, sugerimos leer la guia completa sobre Auth en -:doc:`/controllers/components/authentication`, donde encontrarás mas información para configurar el componente y crear clases de autorizacion a tú medida. +* Permite usuarios autenticados crear y editar artículos. +* Permite usuarios no autenticados ver artículos y etiquetas. Lectura sugerida ---------------- #. :doc:`/bake/usage` Generar código CRUD básico -#. :doc:`/controllers/components/authentication`: Registro y login de usuarios +#. `Authentication Plugin `__ documentación. .. meta:: :title lang=es: Tutorial Blog - Autenticación y Autorización diff --git a/es/tutorials-and-examples/bookmarks/intro.rst b/es/tutorials-and-examples/bookmarks/intro.rst deleted file mode 100644 index 12c308a159..0000000000 --- a/es/tutorials-and-examples/bookmarks/intro.rst +++ /dev/null @@ -1,393 +0,0 @@ -Tutorial Bookmarker (Favoritos) -############################### - -Este tutorial te guiará en la creación de una aplicación sencilla para el guardado de favoritos (Bookmaker). - -Para comenzar instalaremos CakePHP creando nuestra base de datos y utilizaremos las herramientas que CakePHP provee para realizar nuestra aplicación rápidamente. - -Esto es lo que necesitarás: - -#. Un servidor de base de datos. Nosotros utilizaremos MySQL en este tutorial. Necesitarás tener los conocimientos suficientes de SQL para crear una base de datos; CakePHP tomará las riendas desde ahí. Al utilizar MySQL asegúrate de que tienes habilitado ``pdo_mysql`` en PHP. -#. Conocimientos básicos de PHP. - -Antes de empezar deberías de asegurarte de que tienes actualizada la versión de PHP: - -.. code-block:: console - - php -v - -Deberías tener instalado PHP |minphpversion| (CLI) o superior. La versión PHP de tu servidor web deberá ser |minphpversion| o superior y lo ideal es que coincida con la versión de la interfaz de línea de comandos (CLI) de PHP. Si quieres ver la aplicación ya finalizada puedes consultar `cakephp/bookmarker `__. - -Empecemos! - -Instalar CakePHP -================ - -La forma más sencilla de instalar CakePHP es utilizando Composer, una manera sencilla de instalar CakePHP desde tu terminal o prompt de línea de comandos. - -Primero necesitarás descargar e instalar Composer si aún no lo tienes. Si ya tienes instalado cURL es tan sencillo como ejecutar:: - - curl -s https://getcomposer.org/installer | php - -O puedes descargar ``composer.phar`` desde la `Página web de Composer `_. - -Después sencillamente escribe la siguiente línea en tu terminal desde tu directorio de instalación para instalar el esqueleto de la aplicación CakePHP en el directorio **bookmarker**:: - - php composer.phar create-project --prefer-dist cakephp/app:4.* bookmarker - -Si descargaste y ejecutaste el `Instalador Windows de Composer `_, entonces escribe la siguiente línea en tu terminal desde tu directorio de instalación (ie. C:\\wamp\\www\\dev\\cakephp3):: - - composer self-update && composer create-project --prefer-dist cakephp/app:4.* bookmarker - -La ventaja de utilizar Composer es que automáticamente realizará algunas tareas importantes como configurar correctamente el archivo de permisos y crear tu archivo **config/app.php**. - -Hay otras formas de instalar CakePHP. Si no puedes o no quieres utilizar Composer comprueba la sección :doc:`/installation`. - -Sin importar como hayas descargado e instalado CakePHP, una vez hayas finalizado, tu directorio de instalación debería ser algo como:: - - /bookmarker - /bin - /config - /logs - /plugins - /src - /tests - /tmp - /vendor - /webroot - .editorconfig - .gitignore - .htaccess - .travis.yml - composer.json - index.php - phpunit.xml.dist - README.md - -Ahora podría ser un buen momento para que aprendas un poco sobre como funciona la estructura de directorios de CakePHP: :doc:`/intro/cakephp-folder-structure`. - -Comprobar la instalación -======================== - -Podemos comprobar rápidamente que nuestra instalación ha sido correcta accediendo a la página principal que se crea por defecto. - -Pero antes necesitarás inicializar el servidor de desarrollo:: - - bin/cake server - -.. note:: - - Para Windows introduce el comando ``bin\cake server`` (fíjate en la \\ ). - -Esto arrancará el servidor integrado en el puerto 8765. Accede a **http://localhost:8765** a través de tu navegador para ver la página de bienvenida. Todos los items deberán estar marcados como correctos para que CakePHP pueda conectarse a tu base de datos. Si no, puede que necesites instalar extensiones adicionales de PHP, o dar permisos de directorio. - -Crear la base de datos -====================== - -Continuamos, creemos ahora la base de datos para nuestra aplicación de favoritos. - -Si aún no lo has hecho, crea una base de datos vacía para usar en este tutorial con el nombre que tu quieras, e.g. ``cake_bookmarks``. - -Puedes ejecutar la siguiente sentencia SQL para crear las tablas necesarias: - -.. code-block:: mysql - - CREATE TABLE users ( - id INT AUTO_INCREMENT PRIMARY KEY, - email VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - created DATETIME, - modified DATETIME - ); - - CREATE TABLE bookmarks ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL, - title VARCHAR(50), - description TEXT, - url TEXT, - created DATETIME, - modified DATETIME, - FOREIGN KEY user_key (user_id) REFERENCES users(id) - ); - - CREATE TABLE tags ( - id INT AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(255), - created DATETIME, - modified DATETIME, - UNIQUE KEY (title) - ); - - CREATE TABLE bookmarks_tags ( - bookmark_id INT NOT NULL, - tag_id INT NOT NULL, - PRIMARY KEY (bookmark_id, tag_id), - FOREIGN KEY tag_key(tag_id) REFERENCES tags(id), - FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id) - ); - -Puedes ver que la tabla ``bookmarks_tags`` utiliza una clave primaria compuesta. CakePHP soporta claves primarias compuestas en casi cualquier lado, haciendo más fácil construir aplicaciones multi-anidadas. - -Los nombres de las tablas y columnas que hemos utilizado no son aleatorios. Utilizando las :doc:`convenciones de nombres ` podemos hacer mejor uso de CakePHP y evitar tener que configurar el framework. - -CakePHP es lo suficientemente flexible para acomodarse incluso a esquemas inconsistentes de bases de datos heredados, pero siguiendo las convenciones ahorrarás tiempo. - -Configuración de la base de datos -================================= - -Siguiente, indiquémosle a CakePHP donde está nuestra base de datos y como conectarse a ella. Para la mayoría de las veces esta será la primera y última vez que necesitarás configurar algo. - -La configuración debería ser bastante sencilla: sólo cambia los valores del array ``Datasources.default`` en el archivo **config/app.php** por aquellos que apliquen a tu instalación. Un ejemplo de array de configuración completado puede lucir así:: - - return [ - // More configuration above. - 'Datasources' => [ - 'default' => [ - 'className' => 'Cake\Database\Connection', - 'driver' => 'Cake\Database\Driver\Mysql', - 'persistent' => false, - 'host' => 'localhost', - 'username' => 'cakephp', - 'password' => 'AngelF00dC4k3~', - 'database' => 'cake_bookmarks', - 'encoding' => 'utf8', - 'timezone' => 'UTC', - 'cacheMetadata' => true, - ], - ], - // More configuration below. - ]; - -Una vez hayas guardado tu archivo **config/app.php** deberías ver que la sección 'CakePHP is -able to connect to the database' tiene un chechmark de correcto. - -.. note:: - - Puedes encontrar una copia de la configuración por defecto de CakePHP en **config/app.default.php**. - -Crear el esqueleto del código -============================== - -Gracias a que nuestra base de datos sigue las convenciones de CakePHP podemos utilizar la :doc:`consola de bake ` de la aplicación para crear rápidamente una aplicación básica. - -En tu línea de comandos ejecuta las siguientes instrucciones:: - - // En Windows necesitarás utilizar bin\cake. - bin/cake bake all users - bin/cake bake all bookmarks - bin/cake bake all tags - -Esto creará los controladores, modelos, vistas, sus correspondientes casos de prueba y accesorios para nuestros recursos de users, bookmarks y tags. - -Si detuviste tu servidor reinícialo. - -Vete a **http://localhost:8765/bookmarks**, deberías poder ver una básica pero funcional aplicación provista de acceso a las tablas de tu base de datos. - -Una vez estés en la lista de bookmarks añade unos cuantos usuarios (users), favoritos (bookmarks) y etiquetas (tags) - -.. note:: - - Si ves una página de error Not Found (404) comprueba que el módulo de Apache mod_rewrite está cargado. - -Añadir encriptación (hashing) a la contraseña -============================================== - -Cuando creaste tus usuarios (visitando **http://localhost:8765/users**) probablemente te darías cuenta de que las contraseñas (password) se almacenaron en texto plano. Algo muy malo desde un punto de vista de seguridad, así que arreglémoslo. - -Éste es también un buen momento para hablar de la capa de modelo en CakePHP. - -En CakePHP separamos los métodos que operan con una colección de objetos y los que lo hacen con un único objeto en diferentes clases. - -Los métodos que operan con una coleccion de entidades van en la clase ``Table``, mientras que los que lo hacen con una sola van en la clase ``Entity``. - -Por ejemplo: el encriptado de una contraseña se hace en un registro individual, por lo que implementaremos este comportamiento en el objeto Entity. - -Ya que lo que queremos es encriptar la contraseña cada vez que la introduzcamos en la base de datos utilizaremos un método mutador/setter. - -CakePHP utilizará la convención para métodos setter cada vez que una propiedad se introducida en una de tus entidades. - -Añadamos un setter para la contraseña añadiendo el siguiente código en **src/Model/Entity/User.php**:: - - namespace App\Model\Entity; - - use Cake\Auth\DefaultPasswordHasher; //include this line - use Cake\ORM\Entity; - - class User extends Entity - { - - // Code from bake. - - protected function _setPassword($value) - { - $hasher = new DefaultPasswordHasher(); - return $hasher->hash($value); - } - } - -Ahora actualiza uno de los usuarios que creaste antes, si cambias su contraseña deberías ver una contraseña encriptada en vez del valor original en la lista de usuarios o en su página de View. - -CakePHP encripta contraseñas con `bcrypt -`_ por defecto. Puedes usar también sha1 o md5 si estás trabajando con bases de datos ya existentes. - -.. note:: - - Si la contraseña no se ha encriptado asegúrate de que has usado el mismo estilo de escritura que el del atributo password de la clase cuando nombraste la función setter. - -Obtener bookmarks con un tag específico -======================================== - -Ahora que estamos almacenando contraseñas con seguridad podemos añadir alguna funcionalidad interesante a nuestra aplicación. - -Cuando acumulas una colección de favoritos es útil poder buscarlos a través de etiquetas. - -Implementemos una ruta, una acción de controlador y un método finder para buscar bookmarks mediante etiquetas. - -Idealmente tendríamos una URL como **http://localhost:8765/bookmarks/tagged/funny/cat/gifs** que nos permitiría encontrar todos los bookmarks que tienen las etiquetas 'funny', 'cat' o 'gifs'. - -Antes de que podamos implementarlo añadiremos una nueva ruta. - -Modifica tu **config/routes.php** para que se vea como ésto:: - - 'Bookmarks'], - function ($routes) { - $routes->connect('/tagged/*', ['action' => 'tags']); - } - ); - - Router::scope('/', function ($routes) { - // Connect the default home and /pages/* routes. - $routes->connect('/', [ - 'controller' => 'Pages', - 'action' => 'display', 'home' - ]); - $routes->connect('/pages/*', [ - 'controller' => 'Pages', - 'action' => 'display' - ]); - - // Connect the conventions based default routes. - $routes->fallbacks(); - }); - -Lo cual define una nueva 'ruta' que conecta el path **/bookmarks/tagged/** a ``BookmarksController::tags()``. - -Con la definición de rutas puedes separar como se ven tus URLs de como se implementan. Si visitamos **http://localhost:8765/bookmarks/tagged**, podremos ver una página de error bastante útil de CakePHP informando que no existe la acción del controlador. - -Implementemos ahora ese método. - -En **src/Controller/BookmarksController.php** añade:: - - public function tags() - { - // The 'pass' key is provided by CakePHP and contains all - // the passed URL path segments in the request. - $tags = $this->request->getParam('pass'); - - // Use the BookmarksTable to find tagged bookmarks. - $bookmarks = $this->Bookmarks->find('tagged', [ - 'tags' => $tags - ]); - - // Pass variables into the view template context. - $this->set([ - 'bookmarks' => $bookmarks, - 'tags' => $tags - ]); - } - -Para acceder a otras partes del request consulta :ref:`cake-request`. - -Crear el método finder ----------------------- - -En CakePHP nos gusta mantener las acciones de los controladores sencillas y poner la mayoría de la lógica de la aplicación en los modelos. Si visitas ahora la URL **/bookmarks/tagged** verás un error de que el método ``findTagged()`` no ha sido implementado todavía, asi que hagámoslo. - -En **src/Model/Table/BookmarksTable.php** añade lo siguiente:: - - // El argumento $query es una instancia de query. - // El array $options contendrá las opciones de 'tags' que pasemos - // para encontrar'tagged') en nuestra acción del controlador. - public function findTagged(Query $query, array $options) - { - $bookmarks = $this->find() - ->select(['id', 'url', 'title', 'description']); - - if (empty($options['tags'])) { - $bookmarks - ->leftJoinWith('Tags') - ->where(['Tags.title IS' => null]); - } else { - $bookmarks - ->innerJoinWith('Tags') - ->where(['Tags.title IN ' => $options['tags']]); - } - - return $bookmarks->group(['Bookmarks.id']); - } - -Acabamos de implementar un :ref:`método finder personalizado `. - -Esto es un concepto muy poderoso en CakePHP que te permite empaquetar queries re-utilizables. - -Los métodos finder siempre reciben un objeto :doc:`/orm/query-builder` y un array de opciones como parámetros. Estos métodos pueden manipular la query y añadir cualquier condición o criterio requerido; cuando se completan devuelven un objeto query modificado. - -En nuestro método finder sacamos provecho de los métodos ``distinct()`` y ``matching()`` que nos permiten encontrar distintos ('distincts') bookmarks que tienen un tag coincidente (matching). El método ``matching()`` acepta una `función anónima `_ que recibe un generador de consultas. Dentro del callback usaremos este generador para definir las condiciones que filtrarán bookmarks que tienen las etiquetas (tags) especificadas. - -Crear la vista --------------- - -Ahora si visitas la URL **/bookmarks/tagged**, CakePHP mostrará un error advirtiéndote de que no has creado un archivo de vista. - -Siguiente paso, creemos un archivo de vista para nuestro método ``tags()``. - -En **templates/Bookmarks/tags.php** añade el siguiente código:: - -

- Bookmarks tagged with - Text->toList(h($tags)) ?> -

- -
- -
- -

Html->link($bookmark->title, $bookmark->url) ?>

- url) ?> - - - Text->autoParagraph(h($bookmark->description)) ?> -
- -
- -En el código de arriba utilizamos los helpers :doc:`/views/helpers/html` y :doc:`/views/helpers/text` para que asistan en la generación de nuestra salida de la vista. - -También utilizamos la función de atajo ``h()`` para salidas de código HTML. Deberías acordarte siempre de utilizar ``h()`` cuando muestres datos del usuario para evitar problemas de inyección HTML. - -El archivo **tags.php** que acabamos de crear sigue las convenciones de CakePHP para archivos de vistas. La convención es que el nombre del archivo sea una versión en minúsculas y subrayados del nombre de la acción del controlador. - -Puedes observar que hemos podido usar las variables ``$tags`` y ``$bookmarks`` en nuestra vista. - -Cuando utilizamos el método ``set()`` en nuestro controlador especificamos variables para enviarlas a la vista. Ésta hará disponibles todas las variables que se le pasen como variables locales. - -Ahora deberías poder visitar la URL **/bookmarks/tagged/funny** y ver todos los favoritos etiquetados con 'funny'. - -Hasta aquí hemos creado una aplicación básica para manejar favoritos (bookmarks), etiquetas (tags) y usuarios (users). Sin embargo todo el mundo puede ver las etiquetas de los demás. En el siguiente capítulo implementaremos autenticación y restringiremos el uso de etiquetas únicamente a aquellas que pertenezcan al usuario actual. - -Ahora ve a :doc:`/tutorials-and-examples/bookmarks/part-two` para continuar construyendo tu apliación o :doc:`sumérgete en la documentación ` para aprender más sobre que puede hacer CakePHP por ti. - -.. meta:: - :title lang=es: Tutorial Bookmarker (Favoritos) diff --git a/es/tutorials-and-examples/bookmarks/part-two.rst b/es/tutorials-and-examples/bookmarks/part-two.rst deleted file mode 100644 index 900acd1c6f..0000000000 --- a/es/tutorials-and-examples/bookmarks/part-two.rst +++ /dev/null @@ -1,455 +0,0 @@ -Tutorial Bookmarker (Favoritos) - Parte 2 -######################################### - -Tras realizar :doc:`la primera parte de este tutorial ` -deberías tener una aplicación muy básica para guardar favoritos. - -En este capítulo añadiremos la autenticación y restringiremos los favoritos -(bookmarks) para que cada usuario pueda consultar o modificar solamente los suyos. - -Añadir login -============ - -En CakePHP, la autenticación se maneja mediante :doc:`/controllers/components`. - -Los componentes pueden verse como una forma de crear trozos reutilizables de -código de controlador para una finalidad o idea. Además pueden engancharse al -evento de ciclo de vida de los controladores e interactuar con tu aplicación -de ese modo. - -Para empezar añadiremos el componente :doc:`AuthComponent
` -a nuestra aplicación. - -Como queremos que todos nuestros métodos requieran de autenticación añadimos -*AuthComponent* en *AppController* del siguiente modo:: - - // En src/Controller/AppController.php - namespace App\Controller; - - use Cake\Controller\Controller; - - class AppController extends Controller - { - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Form' => [ - 'fields' => [ - 'username' => 'email', - 'password' => 'password' - ] - ] - ], - 'loginAction' => [ - 'controller' => 'Users', - 'action' => 'login' - ], - 'unauthorizedRedirect' => $this->referer() // Si no está autorizado, - //el usuario regresa a la página que estaba - ]); - - // Permite ejecutar la acción display para que nuestros controladores de páginas - // sigan funcionando. - $this->Auth->allow(['display']); - } - } - -Acabamos de decirle a CakePHP que queremos cargar los compomentes ``Flash`` y -``Auth``. Además hemos personalizado la configuración de *AuthComponent* indicando -que utilice como *username* el campo *email* de la tabla *Users* de la base de datos. - -Ahora si vas a cualquier *URL* serás enviado a **/users/login**, que mostrará una -página de error ya que no hemos escrito el código de la función *login* todavía, -así que hagámoslo ahora:: - - // En src/Controller/UsersController.php - public function login() - { - if ($this->request->is('post')) { - $user = $this->Auth->identify(); - if ($user) { - $this->Auth->setUser($user); - return $this->redirect($this->Auth->redirectUrl()); - } - $this->Flash->error('Tu usuario o contraseña es incorrecta.'); - } - } - -Y en **templates/Users/login.php** añade lo siguiente:: - -

Login

- Form->create() ?> - Form->input('email') ?> - Form->input('password') ?> - Form->button('Login') ?> - Form->end() ?> - -Ahora que tenemos un formulario de *login* sencillo deberíamos poder loguearnos -con algún usuario que tenga contraseña encriptada. - -.. note:: - - Si ninguno de tus usuarios tiene contraseña encriptada comenta la línea - ``loadComponent('Auth')``, a continuación edita un usuario y modifica - la contraseña. - -Ahora deberías poder loguearte, si no es así asegúrate de que estás utilizando -un usuario con contraseña encriptada. - -Añadir *logout* -=============== - -Ahora que la gente puede loguearse probablemente quieras añadir una forma de -desloguearse también. - -Otra vez en ``UsersController``, añade el siguiente código:: - - public function initialize() - { - parent::initialize(); - $this->Auth->allow(['logout']); - } - - public function logout() - { - $this->Flash->success('Ahora estás deslogueado.'); - return $this->redirect($this->Auth->logout()); - } - -Este código añade la acción ``logout`` como una acción pública e implementa -la función. - -Ahora puedes visitar ``/users/logout`` para desloguearte, deberías ser enviado -a la página de inicio. - -Habilitar registros -=================== - -Si no estás logueado e intentas acceder a **/users/add** eres reenviado a la -página de login. Deberíamos arreglar esto si queremos permitir que la gente se -pueda registrar en nuestra aplicación. - -En el controlador ``UsersController`` añade lo siguiente:: - - public function initialize() - { - parent::initialize(); - // Añade logout a la lista de actiones permitidas. - $this->Auth->allow(['logout', 'add']); - } - -El código anterior le dice a ``AuthComponent`` que la acción ``add()`` no -necesita autenticación ni autorización. - -Tal vez quieras tomarte un tiempo para limpiar **Users/add.php** y eliminar los -enlaces erróneos o continuar con el siguiente apartado. No vamos a crear la -edición de usuarios, consulta o listado en este tutorial así que no funcionará -el control de ``AuthComponent`` para el acceso a esas acciones del controlador. - -Restringiendo el acceso a favoritos -=================================== - -Ahora que los usuarios pueden loguearse queremos restringir los favoritos que -uno puede ver a los que creó. Esto lo haremos usando un adaptador de -'authorization'. - -Ya que nuestro requisito es muy sencillo podremos escribir un código también muy -sencillo en nuestro ``BookmarksController``. - -Pero antes necesitamos decirle al componente *AuthComponent* cómo va a autorizar -acciones nuestra aplicación. Para ello añade en ``AppController``:: - - public function isAuthorized($user) - { - return false; - } - -Además añade la siguiente línea a la configuración de ``Auth`` en tu ``AppController``:: - - 'authorize' => 'Controller', - -Tú método ``initialize()`` debería verse así:: - - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'authorize'=> 'Controller',// línea añadida - 'authenticate' => [ - 'Form' => [ - 'fields' => [ - 'username' => 'email', - 'password' => 'password' - ] - ] - ], - 'loginAction' => [ - 'controller' => 'Users', - 'action' => 'login' - ], - 'unauthorizedRedirect' => $this->referer() - ]); - - // Permite ejecutar la acción display para que nuestros controladores - // de páginas sigan funcionando. - $this->Auth->allow(['display']); - } - -Por defecto denegaremos el acceso siempre y concederemos los accesos donde tenga -sentido. - -Primero añadiremos la lógica de autorización para favoritos. - -En tu ``BookmarksController`` añade lo siguiente:: - - public function isAuthorized($user) - { - $action = $this->request->getParam('action'); - - // Las acciones add e index están siempre permitidas. - if (in_array($action, ['index', 'add', 'tags'])) { - return true; - } - // El resto de acciones requieren un id. - if (!$this->request->getParam('pass.0')) { - return false; - } - - // Comprueba que el favorito pertenezca al usuario actual. - $id = $this->request->getParam('pass.0'); - $bookmark = $this->Bookmarks->get($id); - if ($bookmark->user_id == $user['id']) { - return true; - } - return parent::isAuthorized($user); - } - -Ahora si intentas consultar, editar o borrar un favorito que no te pertenece -deberías ser redirigido a la página desde la que accediste. - -Si no se muestra ningún mensaje de error añade lo siguiente a tu layout:: - - // En templates/layout/default.php - Flash->render() ?> - -Deberías poder ver ahora los mensajes de error de autorización. - -Arreglar lista de consulta y formularios -======================================== - -Mientras que *view* y *delete* están funcionando, *edit*, *add* e *index* presentan un -par de problemas: - -#. Cuando añades un favorito puedes elegir el usuario. -#. Cuando editas un favorito puedes elegir un usuario. -#. La página con el listado muestra favoritos de otros usuarios. - -Abordemos el formulario de añadir favorito primero. - -Para empezar elimina ``input('user_id')`` de **templates/Bookmarks/add.php**. - -Con esa parte eliminada actualizaremos la acción ``add()`` de -**src/Controller/BookmarksController.php** para que luzca así:: - - public function add() - { - $bookmark = $this->Bookmarks->newEntity(); - if ($this->request->is('post')) { - $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData()); - $bookmark->user_id = $this->Auth->user('id'); - if ($this->Bookmarks->save($bookmark)) { - $this->Flash->success('El favorito se ha guardado.'); - return $this->redirect(['action' => 'index']); - } - $this->Flash->error('El favorito podría no haberse guardado. Por favor, inténtalo de nuevo.'); - } - $tags = $this->Bookmarks->Tags->find('list'); - $this->set(compact('bookmark', 'tags')); - $this->set('_serialize', ['bookmark']); - } - -Completando la propiedad de la entidad con datos de la sesión eliminaremos -cualquier posibilidad de que el usuario modifique el usuario al que pertenece -el favorito. Haremos lo mismo para el formulario de edición. - -Tu acción ``edit()`` de **src/Controller/BookmarksController.php** debería ser -así:: - - public function edit($id = null) - { - $bookmark = $this->Bookmarks->get($id, [ - 'contain' => ['Tags'] - ]); - if ($this->request->is(['patch', 'post', 'put'])) { - $bookmark = $this->Bookmarks->patchEntity($bookmark, $this->request->getData()); - $bookmark->user_id = $this->Auth->user('id'); - if ($this->Bookmarks->save($bookmark)) { - $this->Flash->success('El favorito se ha guardado.'); - return $this->redirect(['action' => 'index']); - } - $this->Flash->error('El favorito podría no haberse guardado. Por favor, inténtalo de nuevo.'); - } - $tags = $this->Bookmarks->Tags->find('list'); - $this->set(compact('bookmark', 'tags')); - $this->set('_serialize', ['bookmark']); - } - -Listado consulta ----------------- - -Ahora solo necesitamos mostrar los favoritos del usuario actualmente logueado. - -Podemos hacer eso actualizando la llamada a ``paginate()``. Haz que tu método -``index()`` de **src/Controller/BookmarksController.php** se vea así:: - - public function index() - { - $this->paginate = [ - 'conditions' => [ - 'Bookmarks.user_id' => $this->Auth->user('id'), - ] - ]; - $this->set('bookmarks', $this->paginate($this->Bookmarks)); - $this->set('_serialize', ['bookmarks']); - } - -Deberíamos actualizar también el método ``tags()`` y el método finder relacionado, -pero lo dejaremos como un ejercicio para que lo hagas por tu cuenta. - -Mejorar la experiencia de etiquetado -==================================== - -Ahora mismo añadir nuevos tags es un proceso complicado desde que -``TagsController`` desautorizó todos los accesos. - -En vez de permitirlos podemos mejorar la *UI* para la selección de tags -utilizando un campo de texto separado por comas. Esto proporcionará una mejor -experiencia para nuestros usuarios y usa algunas de las mejores características de *ORM*. - -Añadir un campo calculado -------------------------- - -Para acceder de forma sencilla a las etiquetas formateadas podemos añadir un -campo virtual/calculado a la entidad. - -En **src/Model/Entity/Bookmark.php** añade lo siguiente:: - - use Cake\Collection\Collection; - - protected function _getTagString() - { - if (isset($this->_fields['tag_string'])) { - return $this->_fields['tag_string']; - } - if (empty($this->tags)) { - return ''; - } - $tags = new Collection($this->tags); - $str = $tags->reduce(function ($string, $tag) { - return $string . $tag->title . ', '; - }, ''); - return trim($str, ', '); - } - -Esto nos dará acceso a la propiedad calculada ``$bookmark->tag_string`` que -utilizaremos más adelante. - -Recuerda añadir la propiedad ``tag_string`` a la lista ``_accessible`` en tu -entidad para poder 'guardarla' más adelante. - -En **src/Model/Entity/Bookmark.php** añade ``tag_string`` a ``$_accessible`` de -este modo:: - - protected array $_accessible = [ - 'user_id' => true, - 'title' => true, - 'description' => true, - 'url' => true, - 'user' => true, - 'tags' => true, - 'tag_string' => true, - ]; - -Actualizar las vistas ---------------------- - -Con la entidad actualizada podemos añadir un nuevo campo de entrada para nuestros -tags. En **templates/Bookmarks/add.php** y **templates/Bookmarks/edit.php**, -cambia el campo ``tags._ids`` por el siguiente:: - - echo $this->Form->input('tag_string', ['type' => 'text']); - -Guardar el string de tags -------------------------- - -Ahora que podemos ver los tags existentes como un string querremos guardar -también esa información. - -Al haber marcado ``tag_string`` como accesible el ORM copiará esa información -del request a nuestra entidad. Podemos usar un método de gancho ``beforeSave()`` -para parsear el *string* de etiquetas y encontrar/crear las entidades relacionadas. - -Añade el siguiente código a **src/Model/Table/BookmarksTable.php**:: - - public function beforeSave($event, $entity, $options) - { - if ($entity->tag_string) { - $entity->tags = $this->_buildTags($entity->tag_string); - } - } - - protected function _buildTags($tagString) - { - // Hace trim a las etiquetas - $newTags = array_map('trim', explode(',', $tagString)); - // Elimina las etiquetas vacías - $newTags = array_filter($newTags); - // Elimina las etiquetas duplicadas - $newTags = array_unique($newTags); - - $out = []; - $query = $this->Tags->find() - ->where(['Tags.title IN' => $newTags]); - - // Elimina las etiquetas existentes de la lista de nuevas etiquetas. - foreach ($query->extract('title') as $existing) { - $index = array_search($existing, $newTags); - if ($index !== false) { - unset($newTags[$index]); - } - } - // Añade las etiquetas existentes. - foreach ($query as $tag) { - $out[] = $tag; - } - // Añade las etiquetas nuevas. - foreach ($newTags as $tag) { - $out[] = $this->Tags->newEntity(['title' => $tag]); - } - return $out; - } - -Aunque este código sea algo más complicado de lo que hemos hecho hasta ahora, nos -ayudará a ver lo potente que es el *ORM* en CakePHP. - -Puedes manipular los resultados de la consulta usando los métodos -:doc:`/core-libraries/collections` y manejar escenearios en los que estás -creando entidades *on the fly* con facilidad. - -Para finalizar -============== - -Hemos mejorado nuestra aplicación de favoritos para manejar escenarios de -autenticación y de autorización/control de acceso básicos. - -Además hemos añadido algunas mejoras interesantes de experiencia de usuario -sacándole provecho a *FormHelper* y al potencial de *ORM*. - -Gracias por tomarte tu tiempo para explorar CakePHP. Ahora puedes realizar -el tutorial :doc:`/tutorials-and-examples/blog/blog`, aprender más sobre :doc:`/orm`, -o puedes leer detenidamente los :doc:`/topics`. - -.. meta:: - :title lang=es: Tutorial Bookmarker (Favoritos) - Parte 2 diff --git a/es/console-and-shells/completion-shell.rst b/es/tutorials-and-examples/cms/articles-controller.rst similarity index 82% rename from es/console-and-shells/completion-shell.rst rename to es/tutorials-and-examples/cms/articles-controller.rst index c6dace293e..15e9db6600 100644 --- a/es/console-and-shells/completion-shell.rst +++ b/es/tutorials-and-examples/cms/articles-controller.rst @@ -1,5 +1,5 @@ -Completion Shell -################ +CMS Tutorial - Creating the Articles Controller +=============================================== .. note:: La documentación no es compatible actualmente con el idioma español en esta página. diff --git a/es/console-and-shells/orm-cache.rst b/es/tutorials-and-examples/cms/authentication.rst similarity index 88% rename from es/console-and-shells/orm-cache.rst rename to es/tutorials-and-examples/cms/authentication.rst index a9417ea1bc..4fc26cdd4f 100644 --- a/es/console-and-shells/orm-cache.rst +++ b/es/tutorials-and-examples/cms/authentication.rst @@ -1,5 +1,5 @@ -ORM Cache Shell -############### +CMS Tutorial - Authentication +============================= .. note:: La documentación no es compatible actualmente con el idioma español en esta página. diff --git a/es/console-and-shells/helpers.rst b/es/tutorials-and-examples/cms/authorization.rst similarity index 88% rename from es/console-and-shells/helpers.rst rename to es/tutorials-and-examples/cms/authorization.rst index 32c2f07175..d2e7c224ae 100644 --- a/es/console-and-shells/helpers.rst +++ b/es/tutorials-and-examples/cms/authorization.rst @@ -1,5 +1,5 @@ -Shell Helpers -############# +CMS Tutorial - Authorization +============================ .. note:: La documentación no es compatible actualmente con el idioma español en esta página. diff --git a/es/console-and-shells/cache.rst b/es/tutorials-and-examples/cms/tags-and-users.rst similarity index 88% rename from es/console-and-shells/cache.rst rename to es/tutorials-and-examples/cms/tags-and-users.rst index 9fd9f36233..e6424af39c 100644 --- a/es/console-and-shells/cache.rst +++ b/es/tutorials-and-examples/cms/tags-and-users.rst @@ -1,5 +1,5 @@ -Cache Shell -=========== +CMS Tutorial - Tags and Users +============================= .. note:: La documentación no es compatible actualmente con el idioma español en esta página. diff --git a/es/views.rst b/es/views.rst index 4c155ebaaa..2e0a099e31 100644 --- a/es/views.rst +++ b/es/views.rst @@ -5,30 +5,684 @@ Vistas .. php:class:: View -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +Las vistas son la **V** en MVC. Son responsables de generar la salida específica requerida para la solicitud. A menudo, esto se hace en forma de HTML, XML o JSON, pero también es responsabilidad de la Capa de Vistas transmitir archivos y crear PDFs que los usuarios puedan descargar. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +CakePHP viene con algunas clases de Vista incorporadas para manejar los escenarios de renderizado más comunes: - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +- Para crear servicios web XML o JSON, puedes usar los :doc:`views/json-and-xml-views`. +- Para servir archivos protegidos o archivos generados dinámicamente, puedes usar :ref:`cake-response-file`. +- Para crear vistas con varios temas, puedes usar :doc:`views/themes`. + +.. _app-view: + +La Vista de la Aplicación +========================= + +``AppView`` es la clase de Vista predeterminada de tu aplicación. ``AppView`` en sí misma extiende la clase ``Cake\View\View`` incluida en CakePHP y está definida en **src/View/AppView.php** de la siguiente manera: + +.. code-block:: php + + addHelper('MyUtils'); + } + } .. _view-templates: -Plantillas de vistas -==================== +Plantillas de Vista +=================== + +La capa de vista de CakePHP es la forma en que te comunicas con tus usuarios. La mayor parte del tiempo, tus vistas estarán renderizando documentos HTML/XHTML para los navegadores, pero también podrías necesitar responder a una aplicación remota a través de JSON o generar un archivo CSV para un usuario. + +Los archivos de plantilla de CakePHP son archivos PHP regulares y utilizan la `sintaxis PHP alternativa `_ para las estructuras de control y la salida. Estos archivos contienen la lógica necesaria para preparar los datos recibidos del controlador en un formato de presentación que está listo para tu audiencia. + +Alternativas de impresion +------------------------- + +Puedes imprimir o mostrar una variable en tu plantilla de la siguiente manera:: + + + +Utilizando soporte para etiquetas cortas:: + + + +Estructuras de Control Alternativas +----------------------------------- + +Las estructuras de control, como ``if``, ``for``, ``foreach``, ``switch`` y ``while``, pueden escribirse en un formato simplificado. Observa que no hay llaves. En su lugar, la llave de cierre para el ``foreach`` se reemplaza con ``endforeach``. Cada una de las estructuras de control mencionadas anteriormente tiene una sintaxis de cierre similar: ``endif``, ``endfor``, ``endforeach`` y ``endwhile``. También observa que en lugar de usar un ``punto y coma`` después de cada estructura (excepto la última), hay dos puntos ``:``. + +El siguiente es un ejemplo utilizando ``foreach``: + + +.. code-block:: php + +
    + +
  • + +
+ +Otro ejemplo, usando if/elseif/else. Observa los dos puntos: + +.. code-block:: php + + +

Hi Sally

+ +

Hi Joe

+ +

Hi unknown user

+ + +Si prefieres utilizar un lenguaje de plantillas como +`Twig `_, una subclase de View facilitará la conexión +entre tu lenguaje de plantillas y CakePHP. + +Los archivos de plantilla se almacenan en **templates/**, en una carpeta nombrada según el +controlador que utiliza los archivos y el nombre de la acción a la que corresponde. +Por ejemplo, el archivo de vista para la acción ``view()`` del controlador ``Products``, normalmente se encontraría en **templates/Products/view.php**. + +La capa de vista en CakePHP puede estar compuesta por varias partes diferentes. Cada +parte tiene usos distintos y se cubrirán en este capítulo: + +- **templates**: Las plantillas son la parte de la página que es única para la acción + que se está ejecutando. Constituyen el contenido principal de la respuesta de tu aplicación. +- **elements**: pequeños fragmentos de código de vista reutilizables. Por lo general, los elementos se renderizan + dentro de las vistas. +- **layouts**: archivos de plantilla que contienen código de presentación que envuelve muchas + interfaces en tu aplicación. La mayoría de las vistas se renderizan dentro de un diseño. +- **helpers**: estas clases encapsulan la lógica de la vista que se necesita en muchos + lugares en la capa de vista. Entre otras cosas, los helpers en CakePHP pueden ayudarte a + construir formularios, funcionalidades AJAX, paginar datos de modelos o servir feeds RSS. +- **cells**: estas clases proporcionan características similares a un controlador para + crear componentes de interfaz de usuario autosuficientes. Consulta la :doc:`/views/cells` + documentación para obtener más información. + +Variables de Vista +------------------ + +Cualquier variable que establezcas en tu controlador con ``set()`` estará disponible tanto en la vista como en el diseño que tu acción renderiza. Además, cualquier variable establecida también estará disponible en cualquier elemento. Si necesitas pasar variables adicionales desde la vista al diseño, puedes llamar a ``set()`` en la plantilla de vista o utilizar :ref:`Bloques de Vista `. + +Debes recordar **siempre** escapar cualquier dato del usuario antes de mostrarlo, ya que CakePHP no escapa automáticamente la salida. Puedes escapar el contenido del usuario con la función ``h()``::" + + bio); ?> + +Estableciendo Variables de Vista +-------------------------------- + +.. php:method:: set(string $var, mixed $value) + +Las vistas tienen un método ``set()`` que es análogo al ``set()`` que se encuentra en +los objetos del Controlador. Utilizar ``set()`` desde tu archivo de vista agregará las variables al +diseño y a los elementos que se renderizarán más adelante. Consulta +:ref:`setting-view_variables` para obtener más información sobre el uso de ``set()``. + +En tu archivo de vista puedes hacer:: + + $this->set('activeMenuButton', 'posts'); + +Entonces, en tu diseño, la variable ``$activeMenuButton`` estará disponible y +contendrá el valor 'posts'." + +.. _extendiendo-vistas: + +Extendiendo Vistas +------------------ + +La extensión de vistas te permite envolver una vista dentro de otra. Combinar esto con los +:ref:`bloques de vista ` te brinda una forma poderosa de mantener tus vistas +:term:`DRY` (Don't Repeat Yourself o No te repitas). Por ejemplo, tu aplicación tiene una barra lateral que necesita cambiar +según la vista específica que se está renderizando. Al extender un archivo de vista común, +puedes evitar repetir el marcado común para tu barra lateral y solo definir las partes que cambian: + +.. code-block:: php + + +

fetch('title')) ?>

+ fetch('content') ?> + +
+

Acciones relacionadas

+
    + fetch('sidebar') ?> +
+
+ +El archivo de vista anterior podría ser utilizado como una vista principal. Espera que la vista +que lo extiende definirá los bloques ``sidebar`` y ``title``. El bloque ``content`` +es un bloque especial que CakePHP crea. Contendrá todo el contenido no capturado de la vista que lo extiende. Suponiendo que nuestro archivo de vista tiene una variable ``$post`` con los datos de nuestra publicación, la vista podría verse así: + +.. code-block:: php + + + extend('/Common/view'); + + $this->assign('title', $post->title); + + $this->start('sidebar'); + ?> +
  • + Html->link('edit', [ + 'action' => 'edit', + $post->id, + ]); + ?> +
  • + end(); ?> + + // The remaining content will be available as the 'content' block + // In the parent view. + body) ?> + +La vista de la publicación anterior muestra cómo puedes extender una vista y llenar un conjunto de bloques. Cualquier contenido que no esté ya definido en un bloque será capturado y colocado en un bloque especial llamado ``content``. Cuando una vista contiene una llamada a ``extend()``, la ejecución continúa hasta el final del archivo de vista actual. Una vez que se completa, se renderizará la vista extendida. Llamar a ``extend()`` más de una vez en un archivo de vista anulará la vista principal que se procesará a continuación:: + + $this->extend('/Common/view'); + $this->extend('/Common/index'); + +Lo anterior hará que **/Common/index.php** se renderice como la vista principal para la vista actual. + +Puedes anidar vistas extendidas tantas veces como sea necesario. Cada vista puede extender otra vista si así lo deseas. Cada vista principal obtendrá el contenido de la vista anterior como el bloque ``content``. + +.. nota:: + + Debes evitar usar ``content`` como nombre de bloque en tu aplicación. + CakePHP lo utiliza para el contenido no capturado en vistas extendidas. + +Extendiendo Layouts +=================== + +Al igual que las vistas, los layouts también pueden ser extendidos. Al igual que con las vistas, se utiliza ``extend()`` +para extender los layouts. Las extensiones de layouts pueden actualizar o reemplazar bloques y actualizar o +reemplazar el contenido renderizado por el layout secundario. Por ejemplo, si quisiéramos +envolver un bloque con un marcado adicional, podríamos hacer lo siguiente:: + + // Nuestro layout extiende el layout de la aplicación. + $this->extend('application'); + $this->prepend('content', '
    '); + $this->append('content', '
    '); + + // Generar más marcado. + + // Recuerda imprimir el contenido del layout anterior. + echo $this->fetch('content'); + +.. _view-blocks: + +Uso de Bloques de Vista +======================= + +Los bloques de vista proporcionan una API flexible que te permite definir ranuras o bloques en +tus vistas/diseños que se definirán en otro lugar. Por ejemplo, los bloques son ideales +para implementar cosas como barras laterales o regiones para cargar activos en la parte inferior o superior del diseño. Los bloques se pueden definir de dos maneras: como un +bloque capturador o mediante asignación directa. Los métodos ``start()``, ``append()``, +``prepend()``, ``assign()``, ``fetch()``, y ``end()`` te permiten +trabajar con bloques capturadores:: + + // Crear el bloque de la barra lateral. + $this->start('sidebar'); + echo $this->element('sidebar/recent_topics'); + echo $this->element('sidebar/recent_comments'); + $this->end(); + + // Anexar contenido al bloque de la barra lateral más adelante. + $this->start('sidebar'); + echo $this->fetch('sidebar'); + echo $this->element('sidebar/popular_topics'); + $this->end(); + +También puedes añadir contenido a un bloque usando ``append()``:: + + $this->append('sidebar'); + echo $this->element('sidebar/popular_topics'); + $this->end(); + + // Lo mismo que el ejemplo anterior. + $this->append('sidebar', $this->element('sidebar/popular_topics')); + +Si necesitas borrar u sobrescribir un bloque, hay un par de alternativas. +El método ``reset()`` eliminará o sobrescribirá un bloque en cualquier momento. El +método ``assign()`` con una cadena de contenido vacía también se puede utilizar para borrar el +bloque especificado.:: + + // Limpiar el contenido anterior del bloque de la barra lateral. + $this->reset('sidebar'); + + // Asignar una cadena vacía también borrará el bloque de la barra lateral. + $this->assign('sidebar', ''); + +Asignar el contenido de un bloque a menudo es útil cuando deseas convertir una variable de vista +en un bloque. Por ejemplo, es posible que desees usar un bloque para el título de la página y, a veces, asignar el título como una variable de vista en el controlador:: + + // En el archivo de vista o diseño, arriba de $this->fetch('title') + $this->assign('title', $title); + +El método ``prepend()`` te permite agregar contenido al principio de un bloque existente:: + + // Agregar al principio de la barra lateral + $this->prepend('sidebar', 'este contenido va en la parte superior de la barra lateral'); + +Mostrar Bloques +--------------- + +Puedes mostrar bloques usando el método ``fetch()``. ``fetch()`` mostrará un +bloque, devolviendo '' si un bloque no existe:: + + fetch('sidebar') ?> + +También puedes usar ``fetch()`` para mostrar condicionalmente contenido que debería rodear a un +bloque si existe. Esto es útil en diseños o vistas extendidas donde +quieres mostrar condicionalmente encabezados u otro marcado: + +.. code-block:: php + + // En templates/layout/default.php + fetch('menu')): ?> + + + +También puedes proporcionar un valor predeterminado para un bloque si no existe. +Esto te permite agregar contenido de marcador de posición cuando un bloque no existe. +Puedes proporcionar un valor predeterminado usando el segundo argumento: + +.. code-block:: php + +
    +

    Tu Carrito

    + fetch('carrito', 'Tu carrito está vacío') ?> +
    + +Usando Bloques para Archivos de Scripts y CSS +--------------------------------------------- + +El `HtmlHelper` se integra con bloques de vista, y sus métodos `script()`, `css()`, y `meta()` actualizan un bloque con el mismo nombre cuando se usan con la opción `block = true`: + +.. code-block:: php + + Html->script('carousel', ['block' => true]); + $this->Html->css('carousel', ['block' => true]); + ?> + + // En tu archivo de diseño. + + + + <?= h($this->fetch('title')) ?> + fetch('script') ?> + fetch('css') ?> + + // El resto del diseño sigue + +El :php:meth:`Cake\\View\\Helper\\HtmlHelper` también te permite controlar a qué bloque van los scripts y el CSS:: + + // En tu vista + $this->Html->script('carousel', ['block' => 'scriptBottom']); + + // En tu diseño + fetch('scriptBottom') ?> .. _view-layouts: Layouts ======= +Un layout contiene código de presentación que envuelve una vista. Todo lo que desees +ver en todas tus vistas debe colocarse en un layout. + +El layout predeterminado de CakePHP se encuentra en **templates/layout/default.php**. +Si deseas cambiar el aspecto general de tu aplicación, este es el +lugar correcto para comenzar, porque el código de vista renderizado por el controlador se coloca dentro del layout predeterminado cuando se renderiza la página. + +Otros archivos de layout deben colocarse en **templates/layout**. Cuando creas +un layout, necesitas decirle a CakePHP dónde colocar la salida de tus vistas. Para +hacerlo, asegúrate de que tu layout incluye un lugar para ``$this->fetch('content')``. +Aquí tienes un ejemplo de cómo podría verse un layout predeterminado: + +.. code-block:: php + + + + + <?= h($this->fetch('title')) ?> + + + fetch('meta'); + echo $this->fetch('css'); + echo $this->fetch('script'); + ?> + + + + + + + + fetch('content') ?> + + + + + + + +Los bloques ``script``, ``css`` y ``meta`` contienen cualquier contenido definido en las +vistas usando el ayudante HTML incorporado. Útil para incluir archivos JavaScript y CSS +desde vistas. + +.. nota:: + + Al usar ``HtmlHelper::css()`` o ``HtmlHelper::script()`` en archivos de plantilla, + especifica ``'block' => true`` para colocar la fuente HTML en un bloque con + el mismo nombre. (Consulta la API para obtener más detalles sobre el uso). + +El bloque ``content`` contiene el contenido de la vista renderizada. + +Puedes establecer el contenido del bloque ``title`` desde dentro de tu archivo de vista:: + + $this->assign('title', 'Ver Usuarios Activos'); + +Los valores vacíos para el bloque ``title`` se reemplazarán automáticamente con +una representación de la ruta de la plantilla actual, como ``'Admin/Artículos'``. + +Puedes crear tantos layouts como desees: solo colócalos en el directorio +**templates/layout**, y alterna entre ellos dentro de tus +acciones del controlador usando la propiedad ``$layout`` del controlador o la vista:: + + // Desde un controlador + public function vista() + { + // Establecer el diseño. + $this->viewBuilder()->setLayout('admin'); + } + + // Desde un archivo de vista + $this->layout = 'registrado'; + +Por ejemplo, si una sección de mi sitio incluyera un espacio para un banner publicitario más pequeño, podría +crear un nuevo layout con el espacio publicitario más pequeño y especificarlo como el +layout para todas las acciones de los controladores usando algo como:: + + namespace App\Controller; + + class UsuariosController extends AppController + { + public function verActivos() + { + $this->set('title', 'Ver Usuarios Activos'); + $this->viewBuilder()->setLayout('default_small_ad'); + } + + public function verImagen() + { + $this->viewBuilder()->setLayout('imagen'); + + // Mostrar imagen del usuario + } + } + +Además de un layout predeterminado, la aplicación de esqueleto oficial de CakePHP también tiene un layout 'ajax'. +El layout Ajax es útil para crear respuestas AJAX; es un diseño vacío. +(La mayoría de las llamadas AJAX solo requieren un poco de marcado como respuesta, en lugar de una +interfaz completamente renderizada). + +La aplicación de esqueleto también tiene un diseño predeterminado para ayudar a generar RSS. + +Usando Layouts desde Plugins +---------------------------- + +Si deseas usar un layout que existe en un plugin, puedes utilizar la :term:`sintaxis de plugin`. Por ejemplo, para usar el diseño de contacto del plugin Contacts:: + + namespace App\Controller; + + class UsersController extends AppController + { + public function verActivos() + { + $this->viewBuilder()->setLayout('Contacts.contact'); + } + } + .. _view-elements: Elementos ========= +.. php:method:: element(string $elementPath, array $data, array $options = []) + +Muchas aplicaciones tienen pequeños bloques de código de presentación que deben +repetirse de una página a otra, a veces en diferentes lugares en el diseño. CakePHP +puede ayudarte a repetir partes de tu sitio web que necesitan ser reutilizadas. Estas partes reutilizables +se llaman Elementos. Los anuncios, los cuadros de ayuda, los controles de navegación, los menús adicionales, +los formularios de inicio de sesión y las llamadas a la acción a menudo se implementan en CakePHP como elementos. Un +elemento es básicamente una mini-vista que se puede incluir en otras vistas, en +diseños e incluso dentro de otros elementos. Los elementos se pueden usar para hacer que una vista +sea más legible, colocando la representación de elementos repetidos en su propio archivo. También +te pueden ayudar a reutilizar fragmentos de contenido en tu aplicación. + +Los elementos se encuentran en la carpeta **templates/element/** y tienen la extensión de archivo .php. +Se generan utilizando el método element de la vista:: + + echo $this->element('helpbox'); + +Pasar Variables a un Elemento +----------------------------- + +Puedes pasar datos a un elemento a través del segundo argumento del elemento:: + + echo $this->element('helpbox', [ + 'helptext' => 'Oh, este texto es muy útil.', + ]); + +Dentro del archivo del elemento, todas las variables pasadas están disponibles como miembros del +array de parámetros (de la misma manera que ``Controller::set()`` en el +controlador funciona con los archivos de plantilla). En el ejemplo anterior, el +archivo **templates/element/helpbox.php** puede usar la variable ``$helptext``:: + + // Dentro de templates/element/helpbox.php + echo $helptext; // Muestra `Oh, este texto es muy útil.` + +Ten en cuenta que en esas variables de vista se fusionan con las variables de vista desde la vista +en sí misma. Entonces, todas las variables de vista establecidas usando ``Controller::set()`` en el controlador y +``View::set()`` en la vista en sí también están disponibles dentro del elemento. + +El método ``View::element()`` también admite opciones para el elemento. +Las opciones admitidas son 'cache' y 'callbacks'. Un ejemplo:: + + echo $this->element('helpbox', [ + 'helptext' => "Esto se pasa al elemento como $helptext", + 'foobar' => "Esto se pasa al elemento como $foobar", + ], + [ + // utiliza la configuración de caché `long_view` + 'cache' => 'long_view', + // establece en true para que se llame a before/afterRender para el elemento + 'callbacks' => true, + ] + ); + +El almacenamiento en caché del elemento se facilita a través de la clase ``Cache``. Puedes configurar +elementos para que se almacenen en cualquier configuración de caché que hayas establecido. Esto te brinda +una gran cantidad de flexibilidad para decidir dónde y por cuánto tiempo se almacenan los elementos. +Para almacenar en caché diferentes versiones del mismo elemento en una aplicación, +proporciona un valor de clave de caché único usando el siguiente formato:: + + $this->element('helpbox', [], [ + 'cache' => ['config' => 'short', 'key' => 'valor único'], + ] + ); + +Si necesitas más lógica en tu elemento, como datos dinámicos de una fuente de datos, +considera usar un View Cell en lugar de un elemento. Descubre más :doc:`sobre View +Cells `. + +Almacenamiento en Caché de Elementos +------------------------------------ + +Puedes aprovechar el almacenamiento en caché de CakePHP si proporcionas un parámetro de caché. +Si se establece en ``true``, almacenará en caché el elemento en la configuración de Caché 'default'. +De lo contrario, puedes establecer qué configuración de caché se debe utilizar. +Consulta :doc:`/core-libraries/caching` para obtener más información sobre cómo configurar +``Cache``. Un ejemplo simple de cómo almacenar en caché un elemento sería:: + + echo $this->element('helpbox', [], ['cache' => true]); + +Si renderizas el mismo elemento más de una vez en una vista y tienes el almacenamiento en caché +habilitado, asegúrate de establecer el parámetro 'key' con un nombre diferente cada vez. Esto +evitará que cada llamada sucesiva sobrescriba el resultado en caché de la llamada anterior a ``element()``. +Por ejemplo:: + + echo $this->element( + 'helpbox', + ['var' => $var], + ['cache' => ['key' => 'primer_uso', 'config' => 'view_long']] + ); + + echo $this->element( + 'helpbox', + ['var' => $otraVar], + ['cache' => ['key' => 'segundo_uso', 'config' => 'view_long']] + ); + +Lo anterior asegurará que ambos resultados del elemento se almacenen en caché por separado. Si deseas que todos los elementos en caché utilicen la misma configuración de caché, puedes evitar algo de repetición configurando ``View::$elementCache`` con la configuración de caché que deseas utilizar. CakePHP utilizará esta configuración cuando no se proporcione ninguna. + +Solicitando Elementos desde un Plugin +------------------------------------- + +Si estás usando un plugin y deseas utilizar elementos desde dentro del plugin, simplemente +usa la conocida :term:`sintaxis de plugin`. Si la vista se está renderizando para un +controlador/acción del plugin, el nombre del plugin se agregará automáticamente +a todos los elementos utilizados, a menos que haya otro nombre de plugin presente. +Si el elemento no existe en el plugin, buscará en la carpeta principal de la APLICACIÓN +(APP):: + + echo $this->element('Contacts.helpbox'); + +Si tu vista es parte de un plugin, puedes omitir el nombre del plugin. Por ejemplo, +si estás en el ``ContactsController`` del plugin Contacts, lo siguiente:: + + echo $this->element('helpbox'); + // y + echo $this->element('Contacts.helpbox'); + +son equivalentes y darán como resultado que se renderice el mismo elemento. + +Para elementos dentro de una subcarpeta de un plugin +(por ejemplo, **plugins/Contacts/Template/element/sidebar/helpbox.php**), usa el +siguiente formato:: + + echo $this->element('Contacts.sidebar/helpbox'); + +Prefijo de Enrutamiento y Elementos +----------------------------------- + +Si tienes un prefijo de enrutamiento configurado, la resolución de la ruta del Elemento puede cambiar +a una ubicación con prefijo, como sucede con los Diseños (Layouts) y la Vista de acción. +Supongamos que tienes configurado un prefijo "Admin" y llamas a:: + + echo $this->element('mi_elemento'); + +El elemento se buscará primero en **templates/Admin/element/**. Si dicho archivo no existe, +se buscará en la ubicación predeterminada. + +Almacenamiento en Caché de Secciones de tu Vista +------------------------------------------------ + +.. php:method:: cache(callable $block, array $options = []) + +A veces, generar una sección de la salida de tu vista puede ser costoso debido a +:doc:`/views/cells` renderizados u operaciones de ayuda costosas. Para ayudar a que tu +aplicación se ejecute más rápido, CakePHP proporciona una forma de almacenar en caché secciones de vista:: + + // Suponiendo algunas variables locales + echo $this->cache(function () use ($usuario, $articulo) { + echo $this->cell('PerfilUsuario', [$usuario]); + echo $this->cell('ArticuloCompleto', [$articulo]); + }, ['key' => 'mi_clave_de_vista']); + +Por defecto, el contenido de la vista almacenado en caché se guardará en la configuración de caché ``View::$elementCache``, +pero puedes usar la opción ``config`` para cambiar esto. + +Eventos de Vista +================ + +Al igual que los Controladores, la vista activa varios eventos o llamadas de retorno (callbacks) que puedes utilizar para +insertar lógica alrededor del ciclo de vida de renderización: + +Lista de Eventos +---------------- + +* ``View.beforeRender`` +* ``View.beforeRenderFile`` +* ``View.afterRenderFile`` +* ``View.afterRender`` +* ``View.beforeLayout`` +* ``View.afterLayout`` + +Puedes adjuntar :doc:`escuchadores de eventos de la aplicación ` a +estos eventos o utilizar :ref:`Llamadas de Retorno de Ayudantes (Helper Callbacks) `. + +Creando tus Propias Clases de Vista +=================================== + +Puede que necesites crear clases de vista personalizadas para habilitar nuevos tipos de vistas de datos o +agregar lógica de renderización de vista personalizada adicional a tu aplicación. Como la mayoría de +componentes de CakePHP, las clases de vista tienen algunas convenciones: + +* Los archivos de clases de vista deben colocarse en **src/View**. Por ejemplo: + **src/View/PdfView.php** +* Las clases de vista deben tener el sufijo ``View``. Por ejemplo: ``PdfView``. +* Al referenciar nombres de clases de vista, deberías omitir el sufijo ``View``. Por ejemplo: ``$this->viewBuilder()->setClassName('Pdf');``. + +También querrás extender ``View`` para asegurar que las cosas funcionen correctamente:: + + // En src/View/PdfView.php + namespace App\View; + + use Cake\View\View; + + class PdfView extends View + { + public function render($view = null, $layout = null) + { + // Lógica personalizada aquí. + } + } + +Reemplazar el método render te permite tener control total sobre cómo se renderiza tu contenido. + Más acerca de Vistas ==================== diff --git a/es/views/helpers.rst b/es/views/helpers.rst index 500f5e5813..e33bada0c5 100644 --- a/es/views/helpers.rst +++ b/es/views/helpers.rst @@ -1,14 +1,15 @@ Helpers ####### -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +Los Helpers son clases similares a componentes para la capa de presentación de tu aplicación. +Contienen lógica de presentación que se comparte entre muchas vistas, elementos o diseños. +Este capítulo te mostrará cómo configurar los helpers, cómo cargarlos y usarlos, y te guiará +en los simples pasos para crear tus propios helpers personalizados. - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +CakePHP incluye varios helpers que ayudan en la creación de vistas. Ayudan en la creación de +estructuras bien formadas (incluyendo formularios), ayudan en el formato de texto, horas y números, +e incluso pueden acelerar la funcionalidad AJAX. Para obtener más información sobre los helpers +incluidos en CakePHP, consulta el capítulo correspondiente para cada helper: .. toctree:: :maxdepth: 1 @@ -19,12 +20,332 @@ Helpers /views/helpers/html /views/helpers/number /views/helpers/paginator - /views/helpers/rss - /views/helpers/session /views/helpers/text /views/helpers/time /views/helpers/url +.. _configuring-helpers: + +Configurando Helpers +==================== + +Configuras los helpers en CakePHP declarándolos en una clase de vista. Cada aplicación +CakePHP incluye una clase ``AppView`` que es el lugar ideal para añadir helpers para su uso global:: + + class AppView extends View + { + public function initialize(): void + { + parent::initialize(); + $this->addHelper('Html'); + $this->addHelper('Form'); + $this->addHelper('Flash'); + } + } + +Para añadir helpers desde plugins, utiliza la :term:`Sintaxis de plugin` utilizada en otros lugares en CakePHP:: + + $this->addHelper('Blog.Comment'); + +No es necesario añadir explícitamente los Helpers que provienen de CakePHP o de tu aplicación. Estos helpers se pueden cargar de forma tardía (lazy loaded) cuando se utilizan por primera vez. Por ejemplo:: + + // Carga el FormHelper si aún no se ha añadido/cargado explícitamente. + $this->Form->create($article); + +Desde las vistas de un plugin, los helpers del plugin también se pueden cargar de forma tardía. Por ejemplo, las plantillas de vista en el plugin 'Blog' pueden cargar los helpers del mismo plugin. + +Carga Condicional de Helpers +---------------------------- + +Puedes utilizar el nombre de la acción actual para añadir helpers de forma condicional:: + + class AppView extends View + { + public function initialize(): void + { + parent::initialize(); + if ($this->request->getParam('action') === 'index') { + $this->addHelper('ListPage'); + } + } + } + +También puedes utilizar el método ``beforeRender`` de tu controlador para añadir helpers:: + + class ArticlesController extends AppController + { + public function beforeRender(EventInterface $event) + { + parent::beforeRender($event); + $this->viewBuilder()->addHelper('MyHelper'); + } + } + +Opciones de Configuración +------------------------- + +Puedes pasar opciones de configuración a los helpers. Estas opciones se pueden utilizar para establecer valores de atributos o modificar el comportamiento de un helper:: + + namespace App\View\Helper; + + use Cake\View\Helper; + use Cake\View\View; + + class AwesomeHelper extends Helper + { + public function initialize(array $config): void + { + debug($config); + } + } + +Por defecto, todas las opciones de configuración se fusionarán con la propiedad ``$_defaultConfig``. Esta propiedad debe definir los valores por defecto de cualquier configuración que tu helper requiera. Por ejemplo:: + + namespace App\View\Helper; + + use Cake\View\Helper; + use Cake\View\StringTemplateTrait; + + class AwesomeHelper extends Helper + { + use StringTemplateTrait; + + /** + * @var array + */ + protected $_defaultConfig = [ + 'errorClass' => 'error', + 'templates' => [ + 'label' => '', + ], + ]; + } + +Cualquier configuración proporcionada al constructor de tu helper se fusionará con los valores por defecto durante la construcción y los datos fusionados se establecerán en ``_config``. Puedes utilizar el método ``getConfig()`` para leer la configuración en tiempo de ejecución:: + + // Lee la opción de configuración errorClass. + $class = $this->Awesome->getConfig('errorClass'); + +Usar la configuración del helper te permite configurar declarativamente tus helpers y mantener la lógica de configuración fuera de las acciones de tu controlador. Si tienes opciones de configuración que no se pueden incluir como parte de una declaración de clase, puedes configurarlas en el callback ``beforeRender`` de tu controlador:: + + class PostsController extends AppController + { + public function beforeRender(EventInterface $event) + { + parent::beforeRender($event); + $builder = $this->viewBuilder(); + $builder->helpers([ + 'CustomStuff' => $this->_getCustomStuffConfig(), + ]); + } + } + +.. _aliasing-helpers: + +Alias de Helpers +---------------- + +Un ajuste común para usar es la opción ``className``, que te permite crear alias para los helpers en tus vistas. Esta característica es útil cuando quieres reemplazar ``$this->Html`` u otra referencia común de Helper con una implementación personalizada:: + + // src/View/AppView.php + class AppView extends View + { + public function initialize(): void + { + $this->addHelper('Html', [ + 'className' => 'MyHtml', + ]); + } + } + + // src/View/Helper/MyHtmlHelper.php + namespace App\View\Helper; + + use Cake\View\Helper\HtmlHelper; + + class MyHtmlHelper extends HtmlHelper + { + // Agrega tu código para sobrescribir el HtmlHelper principal + } + +Lo anterior haría que ``MyHtmlHelper`` se pudiera utilizar usando el *alias* ``$this->Html`` en tus vistas. + +.. note:: + + Hacer un alias de un helper reemplaza esa instancia en cualquier lugar donde se utilice ese helper, incluso dentro de otros Helpers. + +Usar Helpers +============= + +Una vez que hayas configurado qué helpers quieres usar en tu controlador, cada helper se expone como una propiedad pública en la vista. Por ejemplo, si estás utilizando el :php:class:`HtmlHelper`, podrías acceder a él haciendo lo siguiente:: + + echo $this->Html->css('styles'); + +Lo anterior llamaría al método ``css()`` en el HtmlHelper. Puedes acceder a cualquier helper cargado usando ``$this->{$nombreDelHelper}``. + +Cargar Helpers Dinámicamente +---------------------------- + +Puede haber situaciones en las que necesites cargar dinámicamente un helper desde dentro de una vista. Puedes usar el :php:class:`Cake\\View\\HelperRegistry` de la vista para hacer esto:: + + // Cualquiera de los dos funciona. + $mediaHelper = $this->loadHelper('Media', $mediaConfig); + $mediaHelper = $this->helpers()->load('Media', $mediaConfig); + +El HelperRegistry es un :doc:`registro ` y es compatible con la API de registro utilizada en otros lugares en CakePHP. + +Métodos de Eventos +================== + +Los helpers tienen varios métodos que se ejecutan con determinados eventos yque te permiten cambiar el proceso de renderización de la vista. Consulta el :ref:`helper-api` y la documentación de :doc:`/core-libraries/events` para obtener más información. + +Creación de Helpers +=================== + +Puedes crear clases de helper personalizadas para su uso en tu aplicación o plugins. Al igual que la mayoría de los componentes de CakePHP, las clases de helper tienen algunas convenciones: + +* Los archivos de clase del helper deben colocarse en **src/View/Helper**. Por ejemplo: + **src/View/Helper/LinkHelper.php** +* Los nombres de las clases de helper deben llevar el sufijo ``Helper``. Por ejemplo: ``LinkHelper``. +* Al hacer referencia a los nombres de los helper, debes omitir el sufijo ``Helper``. Por ejemplo: ``$this->addHelper('Link');`` o ``$this->loadHelper('Link');``. + +También querrás extender ``Helper`` para asegurarte de que todo funcione correctamente:: + + /* src/View/Helper/LinkHelper.php */ + namespace App\View\Helper; + + use Cake\View\Helper; + + class LinkHelper extends Helper + { + public function makeEdit($title, $url) + { + // Lógica para crear un enlace con formato especial va aquí... + } + } + +Inclusión de otros Helpers +-------------------------- + +Es posible que desees utilizar alguna funcionalidad ya existente en otro helper. Para hacerlo, puedes especificar los helpers que deseas utilizar con un array ``$helpers``, formateado de la misma manera que en un controlador:: + + /* src/View/Helper/LinkHelper.php (usando otros helpers) */ + + namespace App\View\Helper; + + use Cake\View\Helper; + + class LinkHelper extends Helper + { + protected $helpers = ['Html']; + + public function makeEdit($title, $url) + { + // Usa el helper HTML para generar + // Datos formateados: + + $link = $this->Html->link($title, $url, ['class' => 'edit']); + + return '
    ' . $link . '
    '; + } + } + +.. _using-helpers: + +Usando tu nuevo Helper +---------------------- + +Una vez que hayas creado tu helper y lo hayas colocado en **src/View/Helper/**, puedes cargarlo en tus vistas:: + + class AppView extends View + { + public function initialize(): void + { + parent::initialize(); + $this->addHelper('Link'); + } + } + +Una vez que se ha cargado tu helper, puedes usarlo en tus vistas accediendo a la propiedad de vista correspondiente:: + + + Link->makeEdit('Cambiar esta Receta', '/recipes/edit/5') ?> + +.. note:: + + El ``HelperRegistry`` intentará cargar de forma tardía cualquier helper que no esté + específicamente identificado en tu ``Controller``. + +Acceso a Variables de Vista dentro de tu Helper +----------------------------------------------- + +Si deseas acceder a una variable de vista dentro de un helper, puedes utilizar ``$this->getView()->get()`` como sigue:: + + class AwesomeHelper extends Helper + { + public $helpers = ['Html']; + + public function someMethod() + { + // establece la meta descripción + return $this->Html->meta( + 'description', $this->getView()->get('metaDescription'), ['block' => 'meta'] + ); + } + } + +Renderizar un Elemento de Vista dentro de tu Helper +---------------------------------------------------- + +Si deseas renderizar un elemento dentro de tu Helper, puedes usar ``$this->getView()->element()`` como sigue:: + + class AwesomeHelper extends Helper + { + public function someFunction() + { + return $this->getView()->element( + '/ruta/a/elemento', + ['foo'=>'bar','bar'=>'foo'] + ); + } + } + +.. _helper-api: + +Clase Helper +============ + +.. php:class:: Helper + +Métodos de Eventos +------------------- + +Implementando un método de evento en un helper, CakePHP suscribirá automáticamente tu helper al evento relevante. A diferencia de las versiones anteriores de CakePHP, *no* debes llamar a ``parent`` en tus métodos, ya que la clase base Helper no implementa ninguno de los métodos de evento. + +.. php:method:: beforeRenderFile(EventInterface $event, $viewFile) + + Se llama antes de renderizar cada archivo de vista. Esto incluye elementos, vistas, vistas principales y diseños (layouts). + +.. php:method:: afterRenderFile(EventInterface $event, $viewFile, $content) + + Se llama después de renderizar cada archivo de vista. Esto incluye elementos, vistas, vistas principales y diseños (layouts). Este evento puede modificar y devolver ``$content`` para cambiar cómo se mostrará el contenido renderizado en el navegador. + +.. php:method:: beforeRender(EventInterface $event, $viewFile) + + El método beforeRender se llama después del método beforeRender del controlador pero antes de que el controlador renderice la vista y el diseño (layout). Recibe el archivo que se está renderizando como argumento. + +.. php:method:: afterRender(EventInterface $event, $viewFile) + + Se llama después de que la vista ha sido renderizada pero antes de que comience el renderizado del diseño (layout). + +.. php:method:: beforeLayout(EventInterface $event, $layoutFile) + + Se llama antes de que comience el renderizado del diseño (layout). Recibe el nombre del archivo del diseño como argumento. + +.. php:method:: afterLayout(EventInterface $event, $layoutFile) + + Se llama después de que se haya completado el renderizado del diseño (layout). Recibe el nombre del archivo del diseño (layout) como argumento. + .. meta:: :title lang=es: Helpers - :keywords lang=es: php class,time function,presentation layer,processing power,ajax,markup,array,functionality,logic,syntax,elements,cakephp,plugins + :keywords lang=en: php class,time function,presentation layer,processing power,ajax,markup,array,functionality,logic,syntax,elements,cakephp,plugins diff --git a/es/views/helpers/breadcrumbs.rst b/es/views/helpers/breadcrumbs.rst index 7795586ae5..e35e3eff81 100644 --- a/es/views/helpers/breadcrumbs.rst +++ b/es/views/helpers/breadcrumbs.rst @@ -5,16 +5,189 @@ Breadcrumbs .. php:class:: BreadcrumbsHelper(View $view, array $config = []) -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +El Helper de Navegación por rastro proporciona una manera sencilla de gestionar la creación y representación de un rastro +de migas de pan para tu aplicación. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +Creando un Rastro de Migas de Pan +==================================== - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +Puedes agregar una miga a la lista utilizando el método "add()". Este método toma tres argumentos: + +- **title** Un texto que se mostrará como el título de la miga. +- **url** Un textto o un arreglo de parámetros que se proporcionarán a la + :doc:`/views/helpers/url` +- **options** Un arreglo de atributos para el ``item`` y ``itemWithoutLink`` + template. Ver la section acerca :ref:`definir atributos para el registro + ` para más información. + +Además de agregar al final del rastro, puedes realizar una variedad de operaciones:: + + // Añade una miga al final. + $this->Breadcrumbs->add( + 'Products', + ['controller' => 'products', 'action' => 'index'] + ); + + // Añade varias migas al final del rastro. + $this->Breadcrumbs->add([ + ['title' => 'Products', 'url' => ['controller' => 'products', 'action' => 'index']], + ['title' => 'Product name', 'url' => ['controller' => 'products', 'action' => 'view', 1234]], + ]); + + // Añade una miga al principio. + $this->Breadcrumbs->prepend( + 'Products', + ['controller' => 'products', 'action' => 'index'] + ); + + // Añade múltiple migas al final, en el orden dado + $this->Breadcrumbs->prepend([ + ['title' => 'Products', 'url' => ['controller' => 'products', 'action' => 'index']], + ['title' => 'Product name', 'url' => ['controller' => 'products', 'action' => 'view', 1234]], + ]); + + // Inserta la miga en una posición específica. Si la posición está fuera de los + // límites, se generará una excepción. + $this->Breadcrumbs->insertAt( + 2, + 'Products', + ['controller' => 'products', 'action' => 'index'] + ); + + // Inserta la miga antes de una miga específica, basado en el título. + // Si no se puede encontrar el título de la miga nombrada, + // se generará una excepción. + $this->Breadcrumbs->insertBefore( + 'A product name', // el título de la miga para insertar antes + 'Products', + ['controller' => 'products', 'action' => 'index'] + ); + + // Inserta la miga despues de una miga específica, basado en el título. + // Si no se puede encontrar el título de la miga nombrada, + // se generará una excepción. + $this->Breadcrumbs->insertAfter( + 'A product name', // el título de la miga para insertar despues + 'Products', + ['controller' => 'products', 'action' => 'index'] + ); + + + +El uso de estos métodos te proporciona la capacidad de trabajar con el proceso de representación de dos pasos de CakePHP. +Dado que los ``templates`` y ``layouts`` se representan de adentro hacia afuera (es decir, los elementos incluidos se representan primero), +esto te permite definir con precisión dónde deseas agregar una miga de pan. + + +Renderización del Rastro de Migas de Pan +=========================================== + +Después de agregar migas al rastro, puedes representarlo fácilmente utilizando el método +``render()`` . Este método acepta dos argumentos en forma de arreglos: + +- ``$attributes`` : Un arreglo de atributos que se aplicarán a la plantilla "wrapper". + Esto te permite agregar atributos a la etiqueta HTML. Acepta la clave especial "templateVars" + para permitir la inserción de variables de plantilla personalizadas en el ``template``. +- ``$separator`` : Un arreglo de atributos para el ``separator`` template. + Las propiedades posibles son: + + - ``separator`` El texto que se mostrará como separador. + - ``innerAttrs`` Para proporcionar atributos en caso de que tu separador esté dividido en dos elementos. + - ``templateVars`` Permite la inserción de una variable de plantilla personalizada en el ``template``. + + Todas las demás propiedades se convertirán en atributos HTML y reemplazarán la clave "attrs" en la + plantilla. Si utilizas el valor predeterminado para esta opción ``(empty)``, + no se representará un separador. + +Aquí tienes un ejemplo de cómo representar un rastro:: + + echo $this->Breadcrumbs->render( + ['class' => 'breadcrumbs-trail'], + ['separator' => ''] + ); + +Personalizando el resultado +----------------------------- + +El ``BreadcrumbsHelper`` internamente usa el ``StringTemplateTrait``, lo que proporciona +la capacidad de personalizar fácilmente la salida de varias cadenas HTML. +Incluye cuatro plantillas, con la siguiente declaración predeterminada:: + + [ + 'wrapper' => '{{content}}', + 'item' => '{{title}}{{separator}}', + 'itemWithoutLink' => '{{title}}{{separator}}', + 'separator' => '{{separator}}' + ] + +Puedes personalizarlos fácilmente utilizando el método ``setTemplates()`` de +``StringTemplateTrait``:: + + $this->Breadcrumbs->setTemplates([ + 'wrapper' => '', + ]); + +Dado que tus ``templates`` serán renderizados, la opción ``templateVars`` +te permite agregar tu propio template de variables a los diferentes templates :: + + $this->Breadcrumbs->setTemplates([ + 'item' => '{{icon}}{{title}}{{separator}}' + ]); + +Para definir el parámetro ``{{icon}}``, simplemente especifícala al agregar la miga:: + + $this->Breadcrumbs->add( + 'Products', + ['controller' => 'products', 'action' => 'index'], + [ + 'templateVars' => [ + 'icon' => '', + ], + ] + ); + +.. _defining_attributes_item: + +Definiendo Atributos +-------------------------------- +Si deseas aplicar atributos HTML específicos tanto al elemento como a su subelemento, +puedes aprovechar la clave ``innerAttrs``, que proporciona el argumento ``$options``. +Todo excepto ``innerAttrs`` y ``templateVars`` se representará como atributos HTML.:: + + $this->Breadcrumbs->add( + 'Products', + ['controller' => 'products', 'action' => 'index'], + [ + 'class' => 'products-crumb', + 'data-foo' => 'bar', + 'innerAttrs' => [ + 'class' => 'inner-products-crumb', + 'id' => 'the-products-crumb', + ], + ] + ); + + // Según la plantilla predeterminada, esto representará el siguiente HTML: +
  • + Products +
  • + +Borrando las Migas de Pan +============================ + +Puedes borrar las migas de pan utilizando el método ``reset()``. +Esto puede ser útil cuando deseas transformar las migas y sobrescribir la lista:: + + $crumbs = $this->Breadcrumbs->getCrumbs(); + $crumbs = collection($crumbs)->map(function ($crumb) { + $crumb['options']['class'] = 'breadcrumb-item'; + return $crumb; + })->toArray(); + + $this->Breadcrumbs->reset()->add($crumbs); .. meta:: :title lang=es: BreadcrumbsHelper - :description lang=es: The role of the BreadcrumbsHelper in CakePHP is provide a way to easily manage breadcrumbs. - :keywords lang=es: breadcrumbs helper,cakephp crumbs + :description lang=es: El papel del BreadcrumbsHelper en CakePHP es proporcionar una forma sencilla de gestionar las migas de pan. + :keywords lang=en: breadcrumbs helper,cakephp migas de pan, migas + diff --git a/es/views/helpers/flash.rst b/es/views/helpers/flash.rst index 38b3de5e02..277364483b 100644 --- a/es/views/helpers/flash.rst +++ b/es/views/helpers/flash.rst @@ -5,11 +5,67 @@ FlashHelper .. php:class:: FlashHelper(View $view, array $config = []) +FlashHelper proporciona una forma de representar mensajes flash que se establecieron en +``$_SESSION`` a través de :doc:`FlashComponent `. Tanto +:doc:`FlashComponent ` como FlashHelper +utilizan principalmente ``elements`` para renderizar mensajes flash. Flash elements se pueden encontrar en +el directorio **templates/element/flash**. Puedes notar CakePHP's App +template viene con tres elementos flash: **success.php**, **default.php**, and +**error.php**. + +Renderizando Mensajes Flash +============================= + +Para renderizar un mensaje flash, puedes simplemente utilizar el método ``render()`` +del FlashHelper en el template default ``templates/layout/default.php``:: + + Flash->render() ?> + +Por defecto, CakePHP utiliza un "flash" key para los mensajes flash messages en la sesión. Sin embargo, +si has especificado un "key" al establecer el mensaje flash en +:doc:`FlashComponent `, puedes especificar cuál "key" de flash renderizar:: + + Flash->render('other') ?> + +También puedes sobreescribir cualquiera de las opciones establecidas en FlashComponent:: + + // En el Controller + $this->Flash->set('The user has been saved.', [ + 'element' => 'success' + ]); + + // En el template: Utilizará great_success.php en vez de success.php + Flash->render('flash', [ + 'element' => 'great_success' + ]); + + // En el template: el elemento flashy del plugin "Company" + Flash->render('flash', [ + 'element' => 'Company.flashy' + ]); + .. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. + Cuando construyas algun template personalizado para mensajes flash, asegúrate de + codificar (encode) correctamente en HTML cualquier dato del usuario. + CakePHP no escapará (escape) los parámetros de los mensajes flash por ti. + + +Para obtener más información sobre las opciones disponibles en el arreglo, consulta la sección +:doc:`FlashComponent ` + +Routing Prefix y Mensajes Flash +================================= + +If you have a Routing prefix configured, ahora puedes tener tus elementos Flash almacenados en +**templates/{Prefix}/element/flash**. De esta manera, puedes tener +mensajes específicos para cada parte de tu aplicación. Por ejemplo, puedes utilizar +diferentes "layoouts" para la sección de frontend y la sección de administración. + + +Mensajes Flash y Tema +========================= - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +El The FlashHelper utiliza ``elements`` normales para renderizar los mensajes y, por lo tanto, +respetará cualquier Tema que hayas especificado. Entonces, cuando tu Tema tiene un archivo +templates/element/flash/error.php, se utilizará, al igual que con cualquier otro ``Elements`` y ``Views``. diff --git a/es/views/helpers/number.rst b/es/views/helpers/number.rst index b35eb70ae7..1071e9dd3e 100644 --- a/es/views/helpers/number.rst +++ b/es/views/helpers/number.rst @@ -1,20 +1,24 @@ -NumberHelper -############ +Helper Number +############# .. php:namespace:: Cake\View\Helper .. php:class:: NumberHelper(View $view, array $config = []) -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. +El NumberHelper contiene métodos convenientes que permiten mostrar números +en formatos comunes en tus vistas. Estos métodos incluyen formas de formatear moneda, +porcentajes, tamaños de datos, precisiones específicas y también +ofrecen mayor flexibilidad en el formato de números. - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. +.. include:: /core-libraries/number.rst + :start-after: start-cakenumber + :end-before: end-cakenumber - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. +.. warning:: + + Todos los símbolos son UTF-8. .. meta:: :title lang=es: NumberHelper - :description lang=es: The Number Helper contains convenience methods that enable display numbers in common formats in your views. - :keywords lang=es: number helper,currency,number format,number precision,format file size,format numbers + :description lang=es: El NumberHelper contiene métodos convenientes que permiten mostrar números en formatos comunes en tus vistas. + :keywords lang=es: number helper, moneda, currency, number format,number precision, formato de número, precisión de número, format file size,format numbers, formatear el tamaño del archivo, formatear números diff --git a/es/views/helpers/rss.rst b/es/views/helpers/rss.rst deleted file mode 100644 index 4ed27df6a9..0000000000 --- a/es/views/helpers/rss.rst +++ /dev/null @@ -1,20 +0,0 @@ -RSS -### - -.. php:namespace:: Cake\View\Helper - -.. php:class:: RssHelper(View $view, array $config = []) - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: RssHelper - :description lang=es: The RSS helper makes generating XML for RSS feeds easy. - :keywords lang=es: rss helper,rss feed,isrss,rss item,channel data,document data,parse extensions,request handler diff --git a/es/views/helpers/session.rst b/es/views/helpers/session.rst deleted file mode 100644 index 189a8333e8..0000000000 --- a/es/views/helpers/session.rst +++ /dev/null @@ -1,20 +0,0 @@ -SessionHelper -############# - -.. php:namespace:: Cake\View\Helper - -.. php:class:: SessionHelper(View $view, array $config = []) - -.. note:: - La documentación no es compatible actualmente con el idioma español en esta página. - - Por favor, siéntase libre de enviarnos un pull request en - `Github `_ o utilizar el botón **Improve this Doc** para proponer directamente los cambios. - - Usted puede hacer referencia a la versión en Inglés en el menú de selección superior - para obtener información sobre el tema de esta página. - -.. meta:: - :title lang=es: SessionHelper - :description lang=es: As a natural counterpart to the Session Component, the Session Helper replicates most of the component's functionality and makes it available in your view. - :keywords lang=es: session helper,flash messages,session flash,session read,session check diff --git a/es/views/json-and-xml-views.rst b/es/views/json-and-xml-views.rst index 3e7e3e86f8..eef756f76d 100644 --- a/es/views/json-and-xml-views.rst +++ b/es/views/json-and-xml-views.rst @@ -1,35 +1,30 @@ Vistas JSON y XML ################# -JsonView y XmlView le permiten crear respuestas JSON y XML, e integrarse con el -:php:class:`Cake\\Controller\\Component\\RequestHandlerComponent`. +La integración de ``JsonView`` y ``XmlView`` con las funcionalidades de :ref:`controller-viewclasses` +de CakePHP y te permite crear respuestas JSON y XML. -Al habilitar ``RequestHandlerComponent`` en su aplicación y habilitar la -compatibilidad con las extensiones ``json`` y/o ``xml``, puede aprovechar -automáticamente las nuevas clases de vista. ``JsonView`` y ``XmlView`` se -denominarán vistas de datos para el resto de esta página. +Éstas clases View son usadas de forma mas común junto con :php:meth:`\Cake\Controller\Controller::viewClasses()`. Hay dos formas de generar vistas de datos. La primera es mediante el uso de la opción ``serialize`` y la segunda es mediante la creación de archivos de plantilla normales. -Habilitación de vistas de datos en su aplicación -================================================ +Definiendo clases View pata Negociar +==================================== -Antes de poder usar las clases de vista de datos, primero deberá cargar el -:php:class:`Cake\\Controller\\Component\\RequestHandlerComponent` en su -controlador:: +En tu ``AppController`` o en un controlador individual puedes implementar la +función ``viewClasses()`` y proporcionarle todas las clases View que +quieras soportar:: - public function initialize(): void + use Cake\View\JsonView; + use Cake\View\XmlView; + + public function viewClasses(): array { - ... - $this->loadComponent('RequestHandler'); + return [JsonView::class, XmlView::class]; } -Esto se puede hacer en su ``AppController`` y habilitará el cambio automático de -clase de vista en los tipos de contenido. También puede configurar el componente -con la configuración ``viewClassMap``, para asignar tipos a sus clases -personalizadas y/o asignar otros tipos de datos. Opcionalmente, puede habilitar las extensiones json y/o xml con `file-extensions`. Esto le permitirá acceder a ``JSON``, ``XML`` o cualquier @@ -57,19 +52,20 @@ serializar:: namespace App\Controller; + use Cake\View\JsonView; + class ArticlesController extends AppController { - public function initialize(): void + public function viewClasses(): array { - parent::initialize(); - $this->loadComponent('RequestHandler'); + return [JsonView::class]; } public function index() { - // Set the view vars that have to be serialized. + // Asigna las variables a la vista $this->set('articles', $this->paginate()); - // Specify which view vars JsonView should serialize. + // Especifica las variables de vista que JsonView deberá serializar $this->viewBuilder()->setOption('serialize', 'articles'); } } @@ -79,19 +75,20 @@ combinar:: namespace App\Controller; + use Cake\View\JsonView; + class ArticlesController extends AppController { - public function initialize(): void + public function viewClasses(): array { - parent::initialize(); - $this->loadComponent('RequestHandler'); + return [JsonView::class]; } public function index() { - // Some code that created $articles and $comments + // Código que crear las variables $articles y $comments - // Set the view vars that have to be serialized. + // Asigna las variables a la vista $this->set(compact('articles', 'comments')); // Specify which view vars JsonView should serialize. @@ -108,9 +105,9 @@ Sin un solo elemento de nivel superior, el Xml no podrá generarse. Uso de una vista de datos con archivos de plantilla =================================================== -Debe usar archivos de plantilla si necesita realizar alguna manipulación del +Debe usar archivos de plantilla si necesita manipular el contenido de la vista antes de crear el resultado final. Por ejemplo, si tuviéramos -artículos que tuvieran un campo que contuviera HTML generado, probablemente +artículos con un campo que contuviera HTML generado, probablemente querríamos omitirlo de una respuesta JSON. Esta es una situación en la que un archivo de vista sería útil:: @@ -125,7 +122,7 @@ de vista sería útil:: } // View code - templates/Articles/json/index.php - foreach ($articles as &$article) { + foreach ($articles as $article) { unset($article->generated_html); } echo json_encode(compact('articles')); @@ -162,7 +159,7 @@ mediante el prefijo ``@``:: 'loc' => Router::url(['controller' => 'Pages', 'action' => 'view', $page->slug, '_full' => true]), 'lastmod' => $page->modified->format('Y-m-d'), 'changefreq' => 'daily', - 'priority' => '0.5' + 'priority' => '0.5', ]; } @@ -173,7 +170,7 @@ mediante el prefijo ``@``:: $this->set([ // Define an attribute on the root node. '@xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9', - 'url' => $urls + 'url' => $urls, ]); } @@ -199,21 +196,20 @@ CakePHP en una forma coherente de JSON:: Respuestas JSONP ---------------- -Al utilizar ``JsonView``, puede utilizar la variable de vista especial ``_jsonp`` +Al utilizar ``JsonView``, puede utilizar la variable de vista especial ``jsonp`` para habilitar la devolución de una respuesta JSONP. Si se establece en ``true`` la clase de vista comprueba si se establece el parámetro de string de consulta denominado "callback" y, de ser así, envuelve la respuesta json en el nombre de función proporcionado. Si desea utilizar un nombre de parámetro de string de consulta -personalizado en lugar de "callback", establezca ``_jsonp`` al nombre requerido en +personalizado en lugar de "callback", establezca ``jsonp`` al nombre requerido en lugar de ``true.``. -Ejemplo de uso -============== +Eligiendo una clase View +======================== -Si bien el :doc:`RequestHandlerComponent -` puede establecer automáticamente la -vista en función del tipo de contenido o la extensión de la solicitud, también puede -controlar las asignaciones de vistas en el controlador:: +Aunque puedes usar la función ``viewClasses`` la mayoría de las veces, si quieres +un control total sobre la selección de la clase de vista, puedes elegir directamente +la clase:: // src/Controller/VideosController.php namespace App\Controller; @@ -252,3 +248,7 @@ controlar las asignaciones de vistas en el controlador:: return $this->response->withDownload('report-' . date('YmdHis') . '.' . $format); } } + +.. meta:: + :title lang=es: Vistas JSON y XML + :keywords lang=es: json,xml,presentation layer,view,ajax,logic,syntax,templates,cakephp