From 105f3cbb5b9b6b5e11d98d990027d980b756901c Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 00:56:03 -0300 Subject: [PATCH 01/46] [refact]: multiple files + deletions --- pt/appendices.rst | 27 +- pt/appendices/4-0-migration-guide.rst | 12 - pt/console-and-shells/cache.rst | 11 - pt/console-and-shells/completion-shell.rst | 12 - pt/console-and-shells/cron-jobs.rst | 27 - pt/console-and-shells/helpers.rst | 12 - pt/console-and-shells/i18n-shell.rst | 12 - pt/console-and-shells/orm-cache.rst | 22 - pt/console-and-shells/plugin-shell.rst | 18 - pt/console-and-shells/repl.rst | 12 - pt/console-and-shells/routes-shell.rst | 12 - pt/console-and-shells/server-shell.rst | 12 - pt/console-commands/cache.rst | 14 + pt/console-commands/commands.rst | 574 +++++++++++++++ pt/console-commands/completion.rst | 187 +++++ pt/contents.rst | 37 +- pt/controllers.rst | 2 +- pt/controllers/components.rst | 2 +- pt/controllers/components/pagination.rst | 332 --------- pt/intro/cakephp-folder-structure.rst | 26 +- pt/intro/conventions.rst | 2 + pt/orm.rst | 154 ++-- pt/orm/database-basics.rst | 2 +- pt/quickstart.rst | 10 +- pt/topics.rst | 8 +- pt/tutorials-and-examples.rst | 13 +- .../blog-auth-example/auth.rst | 435 ------------ pt/tutorials-and-examples/blog/blog.rst | 217 ------ pt/tutorials-and-examples/blog/part-three.rst | 395 ----------- pt/tutorials-and-examples/blog/part-two.rst | 671 ------------------ pt/tutorials-and-examples/bookmarks/intro.rst | 367 ---------- .../bookmarks/part-two.rst | 411 ----------- .../cms/articles-controller.rst | 563 ++++++++++++++- pt/tutorials-and-examples/cms/database.rst | 132 ++-- .../cms/installation.rst | 17 +- 35 files changed, 1552 insertions(+), 3208 deletions(-) delete mode 100644 pt/appendices/4-0-migration-guide.rst delete mode 100644 pt/console-and-shells/cache.rst delete mode 100644 pt/console-and-shells/completion-shell.rst delete mode 100644 pt/console-and-shells/cron-jobs.rst delete mode 100644 pt/console-and-shells/helpers.rst delete mode 100644 pt/console-and-shells/i18n-shell.rst delete mode 100644 pt/console-and-shells/orm-cache.rst delete mode 100644 pt/console-and-shells/plugin-shell.rst delete mode 100644 pt/console-and-shells/repl.rst delete mode 100644 pt/console-and-shells/routes-shell.rst delete mode 100644 pt/console-and-shells/server-shell.rst create mode 100644 pt/console-commands/cache.rst create mode 100644 pt/console-commands/commands.rst create mode 100644 pt/console-commands/completion.rst delete mode 100644 pt/controllers/components/pagination.rst delete mode 100644 pt/tutorials-and-examples/blog-auth-example/auth.rst delete mode 100755 pt/tutorials-and-examples/blog/blog.rst delete mode 100644 pt/tutorials-and-examples/blog/part-three.rst delete mode 100644 pt/tutorials-and-examples/blog/part-two.rst delete mode 100644 pt/tutorials-and-examples/bookmarks/intro.rst delete mode 100644 pt/tutorials-and-examples/bookmarks/part-two.rst diff --git a/pt/appendices.rst b/pt/appendices.rst index 4e51a999f9..804abaa20c 100644 --- a/pt/appendices.rst +++ b/pt/appendices.rst @@ -4,16 +4,31 @@ Apêndices Os apêndices contêm informações sobre os novos recursos introduzidos em cada versão e a forma de executar a migração entre versões. -Guia de Migração para a versão 4.x +Guia de Migração +================ + +:doc:`appendices/migration-guides` + +Retrocompatibilidade por Adaptação ================================== -.. toctree:: - :maxdepth: 1 +Se você precisar/quiser corrigir o comportamento do 4.x ou migrar parcialmente em etapas, confira +o plugin `Shim `__ que pode ajudar a mitigar algumas alterações que quebram o BC. + +Compatibilidade Futura +====================== + +A correção de compatibilidade com versões anteriores pode preparar seu aplicativo 4.x para o próximo grande +lançamento (5.x). - appendices/4-0-migration-guide +Se você já deseja aplicar o comportamento do 5.x no 4.x, confira o `plugin Shim +`__. Este plugin visa mitigar +algumas falhas de compatibilidade com versões anteriores e ajudar a retroportar recursos do 5.x para +o 4.x. Quanto mais próximo o seu aplicativo 3.x estiver do 4.x, menor será a diferença +das mudanças e mais suave será a atualização final. Informações Gerais -================== +=================== .. toctree:: :maxdepth: 1 @@ -23,4 +38,4 @@ Informações Gerais .. meta:: :title lang=pt: Apêndices - :keywords lang=pt: guia de migração,como migrar,migração,nova versão,ajuda,glossário,rota de migração,novas funcionalidades + :keywords lang=pt: guia de migração,rota de migração,novas funcionalidades,glossário, diff --git a/pt/appendices/4-0-migration-guide.rst b/pt/appendices/4-0-migration-guide.rst deleted file mode 100644 index 1ac9c3ea33..0000000000 --- a/pt/appendices/4-0-migration-guide.rst +++ /dev/null @@ -1,12 +0,0 @@ -4.0 Migration Guide -################### - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. diff --git a/pt/console-and-shells/cache.rst b/pt/console-and-shells/cache.rst deleted file mode 100644 index fa0dbbfe56..0000000000 --- a/pt/console-and-shells/cache.rst +++ /dev/null @@ -1,11 +0,0 @@ -Cache Shell -=========== - -Para ajudá-lo a gerenciar melhor os dados armazenados em cache a partir de um ambiente CLI, um comando shell está disponível -para limpar os dados em cache que seu aplicativo possui:: - - // Limpar uma configuração de cache - bin/cake cache clear - - // Limpar todas as configurações de cache - bin/cake cache clear_all diff --git a/pt/console-and-shells/completion-shell.rst b/pt/console-and-shells/completion-shell.rst deleted file mode 100644 index dd2df27659..0000000000 --- a/pt/console-and-shells/completion-shell.rst +++ /dev/null @@ -1,12 +0,0 @@ -Completion Shell -################ - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file diff --git a/pt/console-and-shells/cron-jobs.rst b/pt/console-and-shells/cron-jobs.rst deleted file mode 100644 index 6bfddc7f86..0000000000 --- a/pt/console-and-shells/cron-jobs.rst +++ /dev/null @@ -1,27 +0,0 @@ -Executando Shells como Cron Jobs -################################ - -Uma coisa comum a fazer com um shell é torná-lo executado como um cronjob para limpar o banco de dados de vez em quando ou -enviar newsletters. Isso é trivial para configurar, por exemplo:: - - */5 * * * * cd /full/path/to/root && bin/cake myshell myparam - # * * * * * comando para executar - # │ │ │ │ │ - # │ │ │ │ │ - # │ │ │ │ \───── day of week (0 - 6) (0 a 6 são de domingo a sábado, ou use os nomes) -   # │   │   │   \────────── mês (1 - 12) -   # │   │   \─────────────── dia do mês (1 - 31) - # │ \──────────────────── hora (0 - 23) - # \───────────────────────── minuto (0 - 59) - -Você pode ver mais informações aqui: https://pt.wikipedia.org/wiki/Crontab - -.. tip:: - - Use ``-q`` (ou `--quiet`) para silenciar qualquer saída para cronjobs. - -.. meta:: - - :Title lang=pt: Executando Shells como cronjobs - :keywords lang=pt: crontab, script bash, crontab - diff --git a/pt/console-and-shells/helpers.rst b/pt/console-and-shells/helpers.rst deleted file mode 100644 index 57a41379ea..0000000000 --- a/pt/console-and-shells/helpers.rst +++ /dev/null @@ -1,12 +0,0 @@ -Shell Helpers -############# - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. diff --git a/pt/console-and-shells/i18n-shell.rst b/pt/console-and-shells/i18n-shell.rst deleted file mode 100644 index 4b1b80b8fa..0000000000 --- a/pt/console-and-shells/i18n-shell.rst +++ /dev/null @@ -1,12 +0,0 @@ -I18N Shell -########## - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file diff --git a/pt/console-and-shells/orm-cache.rst b/pt/console-and-shells/orm-cache.rst deleted file mode 100644 index 04fb6b15e7..0000000000 --- a/pt/console-and-shells/orm-cache.rst +++ /dev/null @@ -1,22 +0,0 @@ -ORM Cache Shell -############### - -O OrmCacheShell fornece uma ferramenta CLI simples para gerenciar caches de metadados da sua aplicação. Em situações de -implantação, é útil reconstruir o cache de metadados no local sem limpar os dados de cache existentes. Você pode fazer isso -executando:: - - bin/cake orm_cache build --connection default - -Isso irá reconstruir o cache de metadados para todas as tabelas na conexão ``default``. Se você só precisa reconstruir uma -única tabela, você pode fazer isso fornecendo seu nome:: - - bin/cake orm_cache build --connection default <> - -Além de criar dados em cache, você pode usar o OrmCacheShell para remover metadados em cache também:: - - # Limpar todos os metadados - bin/cake orm_cache clear - - # Limpar uma única tabela de metadados - bin/cake orm_cache clear <> - diff --git a/pt/console-and-shells/plugin-shell.rst b/pt/console-and-shells/plugin-shell.rst deleted file mode 100644 index c180b8307b..0000000000 --- a/pt/console-and-shells/plugin-shell.rst +++ /dev/null @@ -1,18 +0,0 @@ -.. _plugin-shell: - -Plugin Shell -############ - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. - -.. meta:: - :title lang=pt: Plugin Shell - :keywords lang=pt: api docs,shell,plugin,load,unload diff --git a/pt/console-and-shells/repl.rst b/pt/console-and-shells/repl.rst deleted file mode 100644 index 402bba12b0..0000000000 --- a/pt/console-and-shells/repl.rst +++ /dev/null @@ -1,12 +0,0 @@ -Console Interativo (REPL) -######################### - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file diff --git a/pt/console-and-shells/routes-shell.rst b/pt/console-and-shells/routes-shell.rst deleted file mode 100644 index 9ee380bc0e..0000000000 --- a/pt/console-and-shells/routes-shell.rst +++ /dev/null @@ -1,12 +0,0 @@ -Routes Shell -############ - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. diff --git a/pt/console-and-shells/server-shell.rst b/pt/console-and-shells/server-shell.rst deleted file mode 100644 index 7d772f9318..0000000000 --- a/pt/console-and-shells/server-shell.rst +++ /dev/null @@ -1,12 +0,0 @@ -Server Shell -############ - -.. note:: - Atualmente, a documentação desta página não é suportada em português. - - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. - - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. diff --git a/pt/console-commands/cache.rst b/pt/console-commands/cache.rst new file mode 100644 index 0000000000..4bb1ea3761 --- /dev/null +++ b/pt/console-commands/cache.rst @@ -0,0 +1,14 @@ +Cache Tool +########## + +To help you better manage cached data from a CLI environment, a console command +is available for clearing cached data your application has:: + + // Clear one cache config + bin/cake cache clear + + // Clear all cache configs + bin/cake cache clear_all + + // Clear one cache group + bin/cake cache clear_group diff --git a/pt/console-commands/commands.rst b/pt/console-commands/commands.rst new file mode 100644 index 0000000000..9493a24b5c --- /dev/null +++ b/pt/console-commands/commands.rst @@ -0,0 +1,574 @@ +Command Objects +############### + +.. php:namespace:: Cake\Console +.. php:class:: Command + +CakePHP comes with a number of built-in commands for speeding up your +development, and automating routine tasks. You can use these same libraries to +create commands for your application and plugins. + +Creating a Command +================== + +Let's create our first Command. For this example, we'll create a +simple Hello world command. In your application's **src/Command** directory create +**HelloCommand.php**. Put the following code inside it:: + + out('Hello world.'); + + return static::CODE_SUCCESS; + } + } + +Command classes must implement an ``execute()`` method that does the bulk of +their work. This method is called when a command is invoked. Lets call our first +command application directory, run: + +.. code-block:: console + + bin/cake hello + +You should see the following output:: + + Hello world. + +Our ``execute()`` method isn't very interesting let's read some input from the +command line:: + + 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; + } + } + + +After saving this file, you should be able to run the following command: + +.. code-block:: console + + bin/cake hello jillian + + # Outputs + Hello jillian + +Changing the Default Command Name +================================= + +CakePHP will use conventions to generate the name your commands use on the +command line. If you want to overwrite the generated name implement the +``defaultName()`` method in your command:: + + public static function defaultName(): string + { + return 'oh_hi'; + } + +The above would make our ``HelloCommand`` accessible by ``cake oh_hi`` instead +of ``cake hello``. + +Defining Arguments and Options +============================== + +As we saw in the last example, we can use the ``buildOptionParser()`` hook +method to define arguments. We can also define options. For example, we could +add a ``yell`` option to our ``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; + } + +See the :doc:`/console-commands/option-parsers` section for more information. + +Creating Output +=============== + +Commands are provided a ``ConsoleIo`` instance when executed. This object allows +you to interact with ``stdout``, ``stderr`` and create files. See the +:doc:`/console-commands/input-output` section for more information. + +Using Models in Commands +======================== + +You'll often need access to your application's business logic in console +commands. You can load models in commands, just as you would in a controller +using ``$this->fetchTable()`` since command use the ``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; + } + } + +The above command, will fetch a user by username and display the information +stored in the database. + +Exit Codes and Stopping Execution +================================= + +When your commands hit an unrecoverable error you can use the ``abort()`` method +to terminate execution:: + + // ... + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + if (strlen($name) < 5) { + // Halt execution, output to stderr, and set exit code to 1 + $io->error('Name must be at least 4 characters long.'); + $this->abort(); + } + + return static::CODE_SUCCESS; + } + +You can also use ``abort()`` on the ``$io`` object to emit a message and code:: + + public function execute(Arguments $args, ConsoleIo $io): int + { + $name = $args->getArgument('name'); + if (strlen($name) < 5) { + // Halt execution, output to stderr, and set exit code to 99 + $io->abort('Name must be at least 4 characters long.', 99); + } + + return static::CODE_SUCCESS; + } + +You can pass any desired exit code into ``abort()``. + +.. tip:: + + Avoid exit codes 64 - 78, as they have specific meanings described by + ``sysexits.h``. Avoid exit codes above 127, as these are used to indicate + process exit by signal, such as SIGKILL or SIGSEGV. + + You can read more about conventional exit codes in the sysexit manual page + on most Unix systems (``man sysexits``), or the ``System Error Codes`` help + page in Windows. + +Calling other Commands +====================== + +You may need to call other commands from your command. You can use +``executeCommand`` to do that:: + + // You can pass an array of CLI options and arguments. + $this->executeCommand(OtherCommand::class, ['--verbose', 'deploy']); + + // Can pass an instance of the command if it has constructor args + $command = new OtherCommand($otherArgs); + $this->executeCommand($command, ['--verbose', 'deploy']); + +.. note:: + + When calling ``executeCommand()`` in a loop, it is recommended to pass in the + parent command's ``ConsoleIo`` instance as the optional 3rd argument to + avoid a potential "open files" limit that could occur in some environments. + +Setting Command Description +=========================== + +You may want to set a command description via:: + + class UserCommand extends Command + { + public static function getDescription(): string + { + return 'My custom description'; + } + } + +This will show your description in the Cake CLI: + +.. code-block:: console + + bin/cake + + App: + - user + └─── My custom description + +As well as in the help section of your command: + +.. code-block:: console + + cake user --help + My custom description + + Usage: + cake user [-h] [-q] [-v] + +.. _console-integration-testing: + +Testing Commands +================ + +To make testing console applications easier, CakePHP comes with a +``ConsoleIntegrationTestTrait`` trait that can be used to test console applications +and assert against their results. + +To get started testing your console application, create a test case that uses the +``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait. This trait contains a method +``exec()`` that is used to execute your command. You can pass the same string +you would use in the CLI to this method. + +Let's start with a very simple command, located in +**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; + } + } + +To write an integration test for this command, we would create a test case in +**tests/TestCase/Command/UpdateTableTest.php** that uses the +``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait. This command doesn't do much at the +moment, but let's just test that our command's description is displayed in ``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'); + } + } + +Our test passes! While this is very trivial example, it shows that creating an +integration test case for console applications can follow command line +conventions. Let's continue by adding more logic to our command:: + + 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; + } + } + +This is a more complete command that has required options and relevant logic. +Modify your test case to the following snippet of code:: + + 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 = [ + // assumes you have a 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); + } + } + +As you can see from the ``testUpdateModified`` method, we are testing that our +command updates the table that we are passing as the first argument. First, we +assert that the command exited with the proper status code, ``0``. Then we check +that our command did its work, that is, updated the table we provided and set +the ``modified`` column to the current time. + +Remember, ``exec()`` will take the same string you type into your CLI, so you +can include options and arguments in your command string. + +Testing Interactive Commands +---------------------------- + +Consoles are often interactive. Testing interactive commands with the +``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait only requires passing the +inputs you expect as the second parameter of ``exec()``. They should be +included as an array in the order that you expect them. + +Continuing with our example command, let's add an interactive confirmation. +Update the command class to the following:: + + 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; + } + } + +Now that we have an interactive command, we can add a test case that tests +that we receive the proper response, and one that tests that we receive an +incorrect response. Remove the ``testUpdateModified`` method and, add the following methods to +**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); + } + +In the first test case, we confirm the question, and records are updated. In the +second test we don't confirm and records are not updated, and we can check that +our error message was written to ``stderr``. + +Assertion methods +----------------- + +The ``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait provides a number of +assertion methods that make help assert against console output:: + + // assert that the command exited as success + $this->assertExitSuccess(); + + // assert that the command exited as an error + $this->assertExitError(); + + // assert that the command exited with the expected code + $this->assertExitCode($expected); + + // assert that stdout contains a string + $this->assertOutputContains($expected); + + // assert that stderr contains a string + $this->assertErrorContains($expected); + + // assert that stdout matches a regular expression + $this->assertOutputRegExp($expected); + + // assert that stderr matches a regular expression + $this->assertErrorRegExp($expected); + +Debug Helpers +------------- + +You can use ``debugOutput()`` to output the exit code, stdout and stderr of the +last run command:: + + $this->exec('update_table Users'); + $this->assertExitCode(Command::CODE_SUCCESS); + $this->debugOutput(); + +.. versionadded:: 4.2.0 + The ``debugOutput()`` method was added. + + +Lifecycle Callbacks +=================== + +Like Controllers, Commands offer lifecycle events that allow you to observe +the framework calling your application code. Commands have: + +- ``Command.beforeExecute`` Is called before a command's ``execute()`` method + is. The event is passed the ``ConsoleArguments`` parameter as ``args``. This + event cannot be stopped or have its result replaced. +- ``Command.afterExecute`` Is called after a command's ``execute()`` method is + complete. The event contains ``ConsoleArguments`` as ``args`` and the command + result as ``result``. This event cannot be stopped or have its result + replaced. diff --git a/pt/console-commands/completion.rst b/pt/console-commands/completion.rst new file mode 100644 index 0000000000..c197d5e4a2 --- /dev/null +++ b/pt/console-commands/completion.rst @@ -0,0 +1,187 @@ +Completion Tool +################ + +Working with the console gives the developer a lot of possibilities but having +to completely know and write those commands can be tedious. Especially when +developing new shells where the commands differ per minute iteration. The +Completion Shells aids in this matter by providing an API to write completion +scripts for shells like bash, zsh, fish etc. + +Sub Commands +============ + +The Completion Shell consists of a number of sub commands to assist the +developer creating its completion script. Each for a different step in the +autocompletion process. + +Commands +-------- + +For the first step commands outputs the available Shell Commands, including +plugin name when applicable. (All returned possibilities, for this and the other +sub commands, are separated by a space.) For example:: + + bin/cake Completion commands + +Returns:: + + acl api bake command_list completion console i18n schema server test testsuite upgrade + +Your completion script can select the relevant commands from that list to +continue with. (For this and the following sub commands.) + +subCommands +----------- + +Once the preferred command has been chosen subCommands comes in as the second +step and outputs the possible sub command for the given shell command. For +example:: + + bin/cake Completion subcommands bake + +Returns:: + + controller db_config fixture model plugin project test view + +options +------- + +As the third and final options outputs options for the given (sub) command as +set in getOptionParser. (Including the default options inherited from Shell.) +For example:: + + bin/cake Completion options bake + +Returns:: + + --help -h --verbose -v --quiet -q --everything --connection -c --force -f --plugin -p --prefix --theme -t + +You can also pass an additional argument being the shell sub-command : it will +output the specific options of this sub-command. + +How to enable Bash autocompletion for the CakePHP Console +========================================================= + +First, make sure the **bash-completion** library is installed. If not, you do it +with the following command:: + + apt-get install bash-completion + +Create a file named **cake** in **/etc/bash_completion.d/** and put the +:ref:`bash-completion-file-content` inside it. + +Save the file, then restart your console. + +.. note:: + + If you are using MacOS X, you can install the **bash-completion** library + using **homebrew** with the command ``brew install bash-completion``. + The target directory for the **cake** file will be + **/usr/local/etc/bash_completion.d/**. + +.. _bash-completion-file-content: + +Bash Completion file content +---------------------------- + +This is the code you need to put inside the **cake** file in the correct location +in order to get autocompletion when using the CakePHP console: + +.. 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 + +Using autocompletion +==================== + +Once enabled, the autocompletion can be used the same way than for other +built-in commands, using the **TAB** key. +Three type of autocompletion are provided. The following output are from a fresh CakePHP install. + +Commands +-------- + +Sample output for commands autocompletion: + +.. code-block:: console + + $ bin/cake + bake i18n schema_cache routes + console migrations plugin server + +Subcommands +----------- + +Sample output for subcommands autocompletion: + +.. code-block:: console + + $ bin/cake bake + behavior helper command + cell mailer command_helper + component migration template + controller migration_snapshot test + fixture model + form plugin + +Options +------- + +Sample output for subcommands options autocompletion: + +.. 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/pt/contents.rst b/pt/contents.rst index c2cbcf258f..c87f94badd 100644 --- a/pt/contents.rst +++ b/pt/contents.rst @@ -11,9 +11,10 @@ Conteúdo intro quickstart - appendices/4-0-migration-guide + appendices/migration-guides tutorials-and-examples contributing + release-policy .. toctree:: :caption: Começando @@ -21,6 +22,7 @@ Conteúdo installation development/configuration development/application + development/dependency-injection development/routing controllers/request-response controllers/middleware @@ -31,9 +33,8 @@ Conteúdo .. toctree:: :caption: Using CakePHP - controllers/components/authentication core-libraries/caching - console-and-shells + console-commands development/debugging deployment core-libraries/email @@ -42,7 +43,7 @@ Conteúdo core-libraries/internationalization-and-localization core-libraries/logging core-libraries/form - controllers/components/pagination + controllers/pagination plugins development/rest security @@ -55,24 +56,29 @@ Conteúdo 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 e Pacotes + + standalone-packages + Authentication + Authorization + Bake + Debug Kit + Migrations + Elasticsearch + Phinx + Chronos + Queue .. toctree:: :caption: Diversos @@ -85,15 +91,16 @@ Conteúdo topics chronos + debug-kit + elasticsearch bake bake/development bake/usage - debug-kit - elasticsearch migrations + phinx .. todolist:: .. meta:: :title lang=pt: Conteúdo - :keywords lang=pt: bibliotecas do core,busca,filtro,índice,shells,deployment,apêndices,glossário,models,lib + :keywords lang=pt: bibliotecas do core,busca,comandos,deployment,apêndices,glossário,models diff --git a/pt/controllers.rst b/pt/controllers.rst index d44cd86f29..7f3d361694 100644 --- a/pt/controllers.rst +++ b/pt/controllers.rst @@ -400,7 +400,7 @@ Paginando um model Este método é usado para fazer a paginação dos resultados retornados por seus models. Você pode especificar o tamanho da página (quantos resultados serão retornados), as condições de busca e outros parâmetros. Veja a seção -:doc:`pagination ` para mais detalhes +:doc:`pagination ` para mais detalhes sobre como usar o método ``paginate()`` O atributo paginate lhe oferece uma forma fácil de customizar como diff --git a/pt/controllers/components.rst b/pt/controllers/components.rst index 43609cb2ed..411fd7910f 100644 --- a/pt/controllers/components.rst +++ b/pt/controllers/components.rst @@ -17,7 +17,7 @@ capítulo para cada componente: /controllers/components/authentication /controllers/components/flash /controllers/components/security - /controllers/components/pagination + /controllers/pagination /controllers/components/request-handling .. _configuring-components: diff --git a/pt/controllers/components/pagination.rst b/pt/controllers/components/pagination.rst deleted file mode 100644 index 82e735c141..0000000000 --- a/pt/controllers/components/pagination.rst +++ /dev/null @@ -1,332 +0,0 @@ -Pagination -########## - -.. php:namespace:: Cake\Controller\Component - -.. php:class:: PaginatorComponent - -Um dos principais obstáculos da criação de aplicativos Web flexíveis e fáceis de usar -é o design de uma interface de usuário intuitiva. Muitos aplicativos tendem a crescer -em tamanho e complexidade rapidamente, e designers e programadores acham que não conseguem -lidar com a exibição de centenas ou milhares de registros. A refatoração leva tempo, e o -desempenho e a satisfação do usuário podem sofrer. - -A exibição de um número razoável de registros por página sempre foi uma parte crítica -de todos os aplicativos e usada para causar muitas dores de cabeça aos desenvolvedores. -O CakePHP facilita a carga para o desenvolvedor, fornecendo uma maneira rápida e fácil -de paginar os dados. - -A paginação no CakePHP é oferecida por um componente no controlador, para facilitar a -criação de consultas paginadas. A View :php:class:`~Cake\\View\\Helper\\PaginatorHelper` -é usada para simplificar a geração de links e botões de paginação. - -Usando Controller::paginate() -============================= - -No controlador, começamos definindo as condições de consulta padrão que a paginação usará -na variável do controlador ``$paginate``. Essas condições servem como base para suas -consultas de paginação. Eles são aumentados pelos parâmetros ``sort``, ``direction``, -``limit`` e ``page`` transmitidos a partir da URL. É importante notar que a chave ``order`` -deve ser definida em uma estrutura de matriz como abaixo:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'limit' => 25, - 'order' => [ - 'Articles.title' => 'asc' - ] - ]; - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Paginator'); - } - } - -Você também pode incluir qualquer uma das opções suportadas -por :php:meth:`~Cake\\ORM\\Table::find()`, como ``fields``:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'fields' => ['Articles.id', 'Articles.created'], - 'limit' => 25, - 'order' => [ - 'Articles.title' => 'asc' - ] - ]; - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Paginator'); - } - } - -Enquanto você pode passar a maioria das opções de consulta da propriedade -paginate, geralmente é mais fácil e simples agrupar suas opções de paginação -em :ref:`custom-find-methods`. Você pode definir o uso da paginação do -localizador, definindo a opção ``finder``:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'finder' => 'published', - ]; - } - -Como os métodos localizadores personalizados também podem receber opções, é assim -que você passa as opções para um método find personalizado dentro da propriedade -paginate:: - - class ArticlesController extends AppController - { - // encontrar artigos por tag - public function tags() - { - $tags = $this->request->getParam('pass'); - - $customFinderOptions = [ - 'tags' => $tags - ]; - // o método find personalizado é chamado findTagged dentro de ArticlesTable.php, - // e deve ter se parecer com: public function findTagged(Query $query, array $options) { - // portanto, você usa tags como a chave - $this->paginate = [ - 'finder' => [ - 'tagged' => $customFinderOptions - ] - ]; - $articles = $this->paginate($this->Articles); - $this->set(compact('articles', 'tags')); - } - } - -Além de definir valores gerais de paginação, você pode definir mais de um -conjunto de padrões de paginação no controlador, basta nomear as chaves da -matriz após o modelo que deseja configurar:: - - class ArticlesController extends AppController - { - public $paginate = [ - 'Articles' => [], - 'Authors' => [], - ]; - } - -Os valores das chaves ``Articles`` e ``Authors`` podem conter todas as propriedades -que um modelo/chave menos a matriz ``$paginate``. - -Depois de definida a propriedade ``$paginate``, podemos usar o método :php:meth:`~Cake\\Controller\\Controller::paginate()` -para criar os dados de paginação e adicionar o ``PaginatorHelper `` se ainda não foi adicionado. -O método paginado do controlador retornará o conjunto de resultados da consulta paginada e -definirá os metadados de paginação para a solicitação. Você pode acessar os metadados da -paginação em ``$this->request->getParam('paging')``. Um exemplo mais completo do uso de -``paginate()`` seria:: - - class ArticlesController extends AppController - { - public function index() - { - $this->set('articles', $this->paginate()); - } - } - -Por padrão, o método ``paginate()`` usará o modelo padrão para -um controlador. Você também pode passar a consulta resultante de um método find:: - - public function index() - { - $query = $this->Articles->find('popular')->where(['author_id' => 1]); - $this->set('articles', $this->paginate($query)); - } - -Se você quiser paginar um modelo diferente, poderá fornecer uma consulta para ele, -sendo o próprio objeto de tabela ou seu nome:: - - // Usando a query. - $comments = $this->paginate($commentsTable->find()); - - // Usando o nome do modelo. - $comments = $this->paginate('Comments'); - - // Usando um objeto de tabela. - $comments = $this->paginate($commentTable); - -Usando o Paginator Diretamente -============================== - -Se você precisar paginar os dados de outro componente, poderá usar o PaginatorComponent -diretamente. O PaginatorComponent possui uma API semelhante ao método do controlador:: - - $articles = $this->Paginator->paginate($articleTable->find(), $config); - - // Ou - $articles = $this->Paginator->paginate($articleTable, $config); - -O primeiro parâmetro deve ser o objeto de consulta de um objeto de localização na tabela -do qual você deseja paginar os resultados. Opcionalmente, você pode passar o objeto de -tabela e permitir que a consulta seja construída para você. O segundo parâmetro deve ser -a matriz de configurações a serem usadas para paginação. Essa matriz deve ter a mesma estrutura -que a propriedade ``$paginate`` em um controlador. Ao paginar um objeto ``Query``, a opção -``finder`` será ignorada. Supõe-se que você esteja passando a consulta que deseja paginar. - -.. _paginating-multiple-queries: - -Paginando Múltiplas Queries -=========================== - -Você pode paginar vários modelos em uma única ação do controlador, usando a opção -``scope`` na propriedade ``$paginate`` do controlador e na chamada para o -método ``paginate()``:: - - // Propriedade Paginar - public $paginate = [ - 'Articles' => ['scope' => 'article'], - 'Tags' => ['scope' => 'tag'] - ]; - - // Em um método do controlador - $articles = $this->paginate($this->Articles, ['scope' => 'article']); - $tags = $this->paginate($this->Tags, ['scope' => 'tag']); - $this->set(compact('articles', 'tags')); - -A opção ``scope`` resultará na ``PaginatorComponent`` procurando nos parâmetros da -string de consulta com escopo definido. Por exemplo, o URL a seguir pode ser usado -para paginar tags e artigos ao mesmo tempo:: - - /dashboard?article[page]=1&tag[page]=3 - -Veja a seção `paginator-helper-multiple` para saber como gerar elementos HTML -com escopo e URLs para paginação. - -Paginando o Mesmo Modelo Várias Vezes -------------------------------------- - -Para paginar o mesmo modelo várias vezes em uma única ação do controlador, é -necessário definir um alias para o modelo. Consulte `table-registry-usage` -para obter detalhes adicionais sobre como usar o registro da tabela:: - - // Em um método do controlador - $this->paginate = [ - 'ArticlesTable' => [ - 'scope' => 'published_articles', - 'limit' => 10, - 'order' => [ - 'id' => 'desc', - ], - ], - 'UnpublishedArticlesTable' => [ - 'scope' => 'unpublished_articles', - 'limit' => 10, - 'order' => [ - 'id' => 'desc', - ], - ], - ]; - - // Registre um objeto de tabela adicional para permitir a diferenciação no componente de paginação - 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 Quais Campos Usados para Ordenamento -============================================== - -Por padrão, a classificação pode ser feita em qualquer coluna não virtual que uma -tabela tenha. Às vezes, isso é indesejável, pois permite que os usuários classifiquem -em colunas não indexadas que podem ser caras de solicitar. Você pode definir a lista de -permissões dos campos que podem ser classificados usando a opção ``sortableFields``. Essa -opção é necessária quando você deseja classificar os dados associados ou os campos computados -que podem fazer parte da sua consulta de paginação:: - - public $paginate = [ - 'sortableFields' => [ - 'id', 'title', 'Users.username', 'created' - ] - ]; - -Quaisquer solicitações que tentem classificar campos que não estão na lista de permissões serão ignoradas. - -Limitar o Número Máximo de Linhas por Página -============================================ - -O número de resultados que são buscados por página é exposto ao usuário como o -parâmetro ``limit``. Geralmente, é indesejável permitir que os usuários busquem -todas as linhas em um conjunto paginado. A opção ``maxLimit`` afirma que ninguém -pode definir esse limite muito alto do lado de fora. Por padrão, o CakePHP limita -o número máximo de linhas que podem ser buscadas para 100. Se esse padrão não for -apropriado para a sua aplicação, você poderá ajustá-lo como parte das opções de paginação, -por exemplo, reduzindo-o para ``10``:: - - public $paginate = [ - // Outras chaves aqui. - 'maxLimit' => 10 - ]; - -Se o parâmetro de limite da solicitação for maior que esse valor, -ele será reduzido ao valor ``maxLimit``. - -Juntando Associações Adicionais -=============================== - -Associações adicionais podem ser carregadas na tabela paginada usando o -parâmetro ``contains``:: - - public function index() - { - $this->paginate = [ - 'contain' => ['Authors', 'Comments'] - ]; - - $this->set('articles', $this->paginate($this->Articles)); - } - -Solicitações de Página Fora do Intervalo -======================================== - -O PaginatorComponent lançará uma ``NotFoundException`` ao tentar acessar uma página -inexistente, ou seja, o número da página solicitada é maior que a contagem total de páginas. - -Portanto, você pode permitir que a página de erro normal seja renderizada ou usar um -bloco try catch e executar a ação apropriada quando um ``NotFoundException`` for capturado:: - - use Cake\Http\Exception\NotFoundException; - - public function index() - { - try { - $this->paginate(); - } catch (NotFoundException $e) { - // Faça algo aqui como redirecionar para a primeira ou a última página. - // $this->request->getParam('paging') fornecerá as informações necessárias. - } - } - -Paginação na View -================= - -Verifique a documentação :php:class:`~Cake\\View\\Helper\\PaginatorHelper` -para saber como criar links para navegação de paginação. - -.. meta:: - :title lang=pt: Paginação - :keywords lang=pt: matriz de pedidos, condições de consulta, classe php, aplicativos web, dores de cabeça, obstáculos, complexidade, programadores, parâmetros, paginar, designers, cakephp, satisfação, desenvolvedores diff --git a/pt/intro/cakephp-folder-structure.rst b/pt/intro/cakephp-folder-structure.rst index bee60c6a7a..3c4734f7d7 100644 --- a/pt/intro/cakephp-folder-structure.rst +++ b/pt/intro/cakephp-folder-structure.rst @@ -37,9 +37,9 @@ Você notará alguns diretórios principais: como os dados serão armazenados depende da configuração do CakePHP, mas esse diretório é comumente usado para armazenar descrições de modelos e algumas vezes informação de sessão. -- O diretório *vendor* será onde o CakePHP e outras dependências da aplicação - serão instalados. Faça uma nota pessoal para **não** editar arquivos deste - diretório. Nós não podemos ajudar se você tivé-lo feito. +- O diretório *vendor* é onde o CakePHP e outras dependências do aplicativo serão + instaladas pelo `Composer `_. Editar esses arquivos não é + aconselhável, pois o Composer substituirá suas alterações na próxima atualização. - O diretório *webroot* será a raíz pública de documentos da sua aplicação. Ele contem todos os arquivos que você gostaria que fossem públicos. @@ -53,20 +53,24 @@ O diretório src O diretório *src* do CakePHP é onde você fará a maior parte do desenvolvimento de sua aplicação. Vamos ver mais de perto a estrutura de pastas dentro de *src*. +Command + Contém os comandos de console para sua aplicação. Consulte + :doc:`/console-commands/commands` para saber mais. Console - Contém os comandos e tarefas de console para sua aplicação. - Para mais informações veja :doc:`/console-and-shells`. + Contém o script de instalação executado pelo Composer. Controller - Contém os controllers de sua aplicação e seus componentes. -Locale - Armazena arquivos textuais para internacionalização. + Contém os :doc:`/controllers` de sua aplicação e seus componentes. +Middleware + Armazena qualquer :doc:`/controllers/middleware` para seu aplicativo. Model Contém as tables, entities e behaviors de sua aplicação. View Classes de apresentação são alocadas aqui: cells, helpers, e arquivos view. -Template - Arquivos de apresentação são alocados aqui: elements, páginas de erro, - layouts, e templates view. + +.. note:: + + A pasta ``Command`` não está presente por padrão. + Você pode adicioná-la quando precisar. .. meta:: :title lang=pt: Estrutura de pastas do CakePHP diff --git a/pt/intro/conventions.rst b/pt/intro/conventions.rst index 1aed04f31c..4300dc66e6 100644 --- a/pt/intro/conventions.rst +++ b/pt/intro/conventions.rst @@ -76,6 +76,8 @@ nomes de classes e de seus arquivos: Cada arquivo deveria estar localizado no diretório/namespace apropriado de sua aplicação. +.. _model-and-database-conventions: + Convenções para Banco de Dados ============================== diff --git a/pt/orm.rst b/pt/orm.rst index 1a1008bc36..1f50eb16d8 100644 --- a/pt/orm.rst +++ b/pt/orm.rst @@ -1,81 +1,78 @@ -Models (Modelos) -################ - -Models (Modelos) são as classes que servem como camada de negócio na sua -aplicação. Isso significa que eles devem ser responsáveis pela gestão de quase -tudo o que acontece em relação a seus dados, sua validade, interações e evolução -do fluxo de trabalho de informação no domínio do trabalho. - -No CakePHP seu modelo de domínio da aplicação é dividido em 2 tipos de objetos -principais. Os primeiros são **repositories (repositórios)** ou **table objects -(objetos de tabela)**. Estes objetos fornecem acesso a coleções de dados. Eles -permitem a você salvar novos registros, modificar/deletar os que já existem, -definir relacionamentos, e executar operações em massa. O segundo tipo de -objetos são as **entities (entidades)**. Entities representam registros -individuais e permitem a você definir comportamento em nível de linha/registro -e funcionalidades. - -O ORM (MOR - Mapeamento Objeto-Relacional) nativo do CakePHP especializa-se em -banco de dados relacionais, mas pode ser extendido para suportar fontes de dados -alternativas. - -O ORM do Cakephp toma emprestadas ideias e conceitos dos padrões ActiveRecord e -Datamapper. Isso permite criar uma implementação híbrida que combina aspectos de -ambos padrões para criar uma ORM rápida e simples de utilizar. - -Antes de nós começarmos explorando o ORM, tenha certeza que você -:ref:`configure your database connections `. +Acesso a Banco de Dados e ORM +############################# -Exemplo rápido -============== +No CakePHP, o trabalho com dados por meio do banco de dados é feito com dois tipos principais de objetos: -Para começar você não precisa escrever código. Se você seguiu as convenções do -CakePHP para suas tabelas de banco de dados, você pode simplesmente começar a -usar o ORM. Por exemplo, se quiséssemos carregar alguns dados da nossa tabela -``articles`` poderíamos fazer:: +- **Repositórios** ou **objetos de tabela** fornecem acesso a coleções de dados. + Eles permitem salvar novos registros, modificar/excluir os existentes, definir + relações e executar operações em massa. +- **Entidades** representam registros individuais e permitem que você defina o comportamento e a funcionalidade + em nível de linha/registro. - use Cake\ORM\TableRegistry; +Essas duas classes geralmente são responsáveis ​​por gerenciar quase tudo +que acontece em relação aos seus dados, sua validade, interações e evolução +do fluxo de trabalho de informações em seu domínio de trabalho. - // Prior to 3.6 use TableRegistry::get('Articles') - $articles = TableRegistry::getTableLocator()->get('Articles'); - $query = $articles->find(); - foreach ($query as $row) { - echo $row->title; - } +O ORM integrado do CakePHP é especializado em bancos de dados relacionais, mas pode ser estendido +para oferecer suporte a fontes de dados alternativas. + +O ORM CakePHP utiliza ideias e conceitos dos padrões ActiveRecord e Datamapper. +O objetivo é criar uma implementação híbrida que combine aspectos de +ambos os padrões para criar um ORM rápido e fácil de usar. -Nota-se que nós não temos que criar qualquer código ou definir qualquer -configuração. As convenções do CakePHP nos permitem pular alguns códigos clichê, -e permitir que o framework insera classes básicas enquanto sua aplicação não -criou uma classe concreta. Se quiséssemos customizar nossa classe ArticlesTable -adicionando algumas associações ou definir alguns métodos adicionais, deveriamos -acrescentar o seguinte a **src/Model/Table/ArticlesTable.php** após a tag de -abertura ```. +Exemplo rápido +============== + +Para começar, você não precisa escrever nenhum código. Se você seguiu as +:ref:`Convenções para Banco de Dados +`, pode simplesmente começar a usar o ORM. Por exemplo, +se quiséssemos carregar alguns dados da nossa tabela ``articles``, começaríamos +criando nossa classe de tabela ``Articles``. Crie +**src/Model/Table/ArticlesTable.php** com o seguinte código:: + + fetchTable('Articles')->find()->all(); + foreach ($resultset as $row) { + echo $row->title; + } } -Classes de tabela usam a versão CamelCased do nome da tabela com o sufixo -``Table`` como o nome da classe. Uma vez que sua classe fora criada, você recebe -uma referência para esta utilizando o :php:class:`~Cake\\ORM\\TableRegistry` -como antes:: +Em outros contextos, você pode usar o ``LocatorAwareTrait`` que adiciona métodos de acesso para tabelas ORM:: - use Cake\ORM\TableRegistry; + use Cake\ORM\Locator\LocatorAwareTrait; + + public function someMethod() + { + $articles = $this->fetchTable('Articles'); + // mais código. + } + +Dentro de um método estático, você pode usar :php:class:`~Cake\\Datasource\\FactoryLocator` +para obter o localizador da tabela:: - // Agora $articles é uma instância de nossa classe ArticlesTable. - // Prior to 3.6 use TableRegistry::get('Articles') $articles = TableRegistry::getTableLocator()->get('Articles'); -Agora que temos uma classe de tabela concreta, nós provavelmente vamos querer -usar uma classe de entidade concreta. As classes de entidade permitem definir -métodos de acesso, métodos mutantes, definir lógica personalizada para os -registros individuais e muito mais. Vamos começar adicionando o seguinte para -**src/Model/Entity/Article.php** após a tag de abertura ``get('Articles'); - $query = $articles->find(); + $articles = $this->fetchTable('Articles'); + $resultset = $articles->find()->all(); - foreach ($query as $row) { - // Cada linha é agora uma instância de nossa classe Article. + foreach ($resultset as $row) { + // Each row is now an instance of our Article class. echo $row->title; } -CakePHP utiliza convenções de nomenclatura para ligar as classes de tabela e -entidade juntas. Se você precisar customizar qual entidade uma tabela utiliza, -você pode usar o método ``entityClass()`` para definir nomes de classe -específicos. +O CakePHP usa convenções de nomenclatura para vincular as classes Table e Entity. Se +você precisar personalizar qual entidade uma tabela usa, pode usar o método +``entityClass()`` para definir um nome de classe específico. -Veja os capítulos em :doc:`/orm/table-objects` e :doc:`/orm/entities` para mais -informações sobre como usar objetos de tabela e entidades em sua aplicação. +Consulte os capítulos em :doc:`/orm/table-objects` e :doc:`/orm/entities` para obter mais +informações sobre como usar objetos de tabela e entidades em seu aplicativo. -Mais informação -=============== +Mais informações +================ .. toctree:: :maxdepth: 2 @@ -121,11 +113,11 @@ Mais informação orm/query-builder orm/table-objects orm/entities + orm/associations orm/retrieving-data-and-resultsets orm/validation orm/saving-data orm/deleting-data - orm/associations orm/behaviors orm/schema-system - console-and-shells/orm-cache + console-commands/schema-cache diff --git a/pt/orm/database-basics.rst b/pt/orm/database-basics.rst index fc338e054b..946f1bda09 100644 --- a/pt/orm/database-basics.rst +++ b/pt/orm/database-basics.rst @@ -967,7 +967,7 @@ com o método ``cacheMetadata()``:: $connection->cacheMetadata('orm_metadata'); O CakePHP também inclui uma ferramenta CLI para gerenciar caches de metadados. -Confira o capítulo :doc:`/console-and-shells/orm-cache` para obter mais informações. +Confira o capítulo :doc:`/console-commands/schema-cache` para obter mais informações. Criando Banco de Dados ====================== diff --git a/pt/quickstart.rst b/pt/quickstart.rst index 4f38d0bf2d..d593ca20d6 100644 --- a/pt/quickstart.rst +++ b/pt/quickstart.rst @@ -1,11 +1,13 @@ Guia de Início Rápido ********************* -A melhor forma de viver experiências e aprender sobre CakePHP é sentar e -construir algo. Para começar nós iremos construir uma aplicação simples de blog. +A melhor maneira de experimentar e aprender CakePHP é sentar e construir algo. +Para começar, construiremos um aplicativo simples de gerenciamento de conteúdo. -.. 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-model.rst +.. include:: /tutorials-and-examples/cms/articles-controller.rst .. meta:: :title lang=pt: Guia de Início Rápido diff --git a/pt/topics.rst b/pt/topics.rst index 2db490545d..35216f055b 100644 --- a/pt/topics.rst +++ b/pt/topics.rst @@ -24,14 +24,14 @@ Introdução aos principais tópicos do CakePHP: * :doc:`/core-libraries/form` * :doc:`/development/sessions` * :doc:`/development/rest` -* :doc:`/controllers/components/authentication` -* :doc:`/controllers/components/pagination` +* :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` +* :doc:`/tutorials-and-examples` \ No newline at end of file diff --git a/pt/tutorials-and-examples.rst b/pt/tutorials-and-examples.rst index 678097e49d..b61e652e17 100644 --- a/pt/tutorials-and-examples.rst +++ b/pt/tutorials-and-examples.rst @@ -14,18 +14,11 @@ componentes existentes. tutorials-and-examples/cms/installation tutorials-and-examples/cms/database + tutorials-and-examples/cms/articles-model tutorials-and-examples/cms/articles-controller + tutorials-and-examples/cms/tags-and-users tutorials-and-examples/cms/authentication - -.. 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 - tutorials-and-examples/blog-auth-example/auth + tutorials-and-examples/cms/authorization .. meta:: :title lang=pt: Tutoriais & Examplos diff --git a/pt/tutorials-and-examples/blog-auth-example/auth.rst b/pt/tutorials-and-examples/blog-auth-example/auth.rst deleted file mode 100644 index 9f3cb94f4a..0000000000 --- a/pt/tutorials-and-examples/blog-auth-example/auth.rst +++ /dev/null @@ -1,435 +0,0 @@ -Tutorial - Criando um Blog - Autenticação e Autorização -####################################################### - -Continuando com o exemplo de :doc:`/tutorials-and-examples/blog/blog`, imagine -que queríamos garantir o acesso a certas URLs, com base no usuário logado. Temos -também uma outra exigência: permitir que o nosso blog para tenha vários autores -que podem criar, editar e excluir seus próprios artigos, e bloquear para que -outros autores não façam alterações nos artigos que não lhes pertencem. - -Criando todo o código relacionado ao Usuário -============================================ - -Primeiro, vamos criar uma nova tabela no banco de dados do blog para armazenar -dados de nossos usuários:: - - CREATE TABLE users ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - email VARCHAR(255), - password VARCHAR(255), - role VARCHAR(20), - created DATETIME DEFAULT NULL, - modified DATETIME DEFAULT NULL - ); - -Respeitado as convenções do CakePHP para nomear tabelas, mas também aproveitando -de outras convenção: Usando as colunas ``email`` e ``password`` da tabela de -usuários, CakePHP será capaz de configurar automaticamente a maioria das coisas -para nós, na implementação do login do usuário. - -O próximo passo é criar a nossa classe UsersTable, responsável por encontrar, -salvar e validar os dados do usuário:: - - // src/Model/Table/UsersTable.php - namespace App\Model\Table; - - use Cake\ORM\Table; - use Cake\Validation\Validator; - - class UsersTable extends Table - { - - public function validationDefault(Validator $validator) - { - return $validator - ->notEmpty('email', 'Email é necessário') - ->email('email') - ->notEmpty('password', 'Senha é necessária') - ->notEmpty('role', 'Função é necessária') - ->add('role', 'inList', [ - 'rule' => ['inList', ['admin', 'author']], - 'message' => 'Por favor informe uma função válida' - ]); - } - - } - -Vamos também criar o nosso UsersController. O conteúdo a seguir corresponde a -partes de uma classe UsersController básica gerado atráves do utilitário de -geração de código ``bake`` fornecido com CakePHP:: - - // src/Controller/UsersController.php - - namespace App\Controller; - - use App\Controller\AppController; - use Cake\Event\Event; - - class UsersController extends AppController - { - - public function beforeFilter(Event $event) - { - parent::beforeFilter($event); - $this->Auth->allow('add'); - } - - public function index() - { - $this->set('users', $this->Users->find('all')); - } - - public function view($id) - { - $user = $this->Users->get($id); - $this->set(compact('user')); - } - - public function add() - { - $user = $this->Users->newEntity(); - if ($this->request->is('post')) { - $user = $this->Users->patchEntity($user, $this->request->getData()); - if ($this->Users->save($user)) { - $this->Flash->success(__('O usuário foi salvo.')); - - return $this->redirect(['action' => 'add']); - } - $this->Flash->error(__('Não é possível adicionar o usuário.')); - } - $this->set('user', $user); - } - - } - -Da mesma maneira que criamos as ``views`` para os nossos artigos usando -a ferramenta de geração de código, podemos implementar as ``views`` do -usuário. Para o propósito deste tutorial, vamos mostrar apenas o add.php: - -.. code-block:: php - - - -
- Form->create($user) ?> -
- - Form->input('email') ?> - Form->input('password') ?> - Form->input('role', [ - 'options' => ['admin' => 'Admin', 'author' => 'Author'] - ]) ?> -
- Form->button(__('Submit')); ?> - Form->end() ?> -
- -Autenticação (Login e Logout) -============================= - -Agora estamos prontos para adicionar a nossa camada de autenticação. Em CakePHP -isso é tratado pelo :php:class:`Cake\\Controller\\Component\\AuthComponent`, uma -classe responsável por exigir o ``login`` para determinadas ações, a manipulação -de ``login`` e ``logout`` de usuário, e também permite as ações para que estão -autorizados. - -Para adicionar este componente em sua aplicação abra o arquivos -**src/Controller/AppController.php** e adicione as seguintes linha:: - - // src/Controller/AppController.php - - namespace App\Controller; - - use Cake\Controller\Controller; - use Cake\Event\Event; - - class AppController extends Controller - { - //... - - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'loginRedirect' => [ - 'controller' => 'Articles', - 'action' => 'index' - ], - 'logoutRedirect' => [ - 'controller' => 'Pages', - 'action' => 'display', - 'home' - ] - ]); - } - - public function beforeFilter(Event $event) - { - $this->Auth->allow(['index', 'view', 'display']); - } - //... - } - -Não há muito para ser configurado, como usamos as convenções para a tabela -de usuários. Nós apenas configuramos as URLs que serão carregados após o -``login`` e ``logout``, estás ações são realizadas no nosso caso para os -``/articles/`` e ``/`` respectivamente. - -O que fizemos na função ``beforeFilter()`` foi dizer ao ``AuthComponent`` para -não exigir ``login`` em todos ``index()`` e ``view()``, em cada controlador. -Queremos que os nossos visitantes sejam capaz de ler e listar as entradas sem -registrar-se no site. - -Agora, precisamos ser capaz de registrar novos usuários, salvar seu ``email`` -e ``password``, e mais importante, o hash da senha para que ele não seja -armazenado como texto simples no nosso banco de dados. Vamos dizer ao -``AuthComponet`` para permitir que usuários deslogados acessem a função add e -execute as ações de ``login`` e ``logout``:: - - // src/Controller/UsersController.php - - public function beforeFilter(Event $event) - { - parent::beforeFilter($event); - // Permitir aos usuários se registrarem e efetuar logout. - // Você não deve adicionar a ação de "login" a lista de permissões. - // Isto pode causar problemas com o funcionamento normal do 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(__('Usuário ou senha ínvalido, tente novamente')); - } - } - - public function logout() - { - return $this->redirect($this->Auth->logout()); - } - -O hashing da senha ainda não está feito, precisamos de uma classe a fim de -manipular sua geração. Crie o arquivo **src/Model/Entity/User.php** -e adicione a seguinte trecho:: - - // src/Model/Entity/User.php - namespace App\Model\Entity; - - use Cake\Auth\DefaultPasswordHasher; - use Cake\ORM\Entity; - - class User extends Entity - { - - // Gera conjunto de todos os campos exceto o com a chave primária. - protected array $_accessible = [ - '*' => true, - 'id' => false - ]; - - // ... - - protected function _setPassword($password) - { - if (strlen($password) > 0) { - return (new DefaultPasswordHasher)->hash($password); - } - } - - // ... - } - -Agora, a senha criptografada usando a classe ``DefaultPasswordHasher``. -Está faltando apenas o arquivo para exibição da tela de login. -Abra o arquivo **templates/Users/login.php** e adicione as seguintes linhas: - -.. code-block:: php - - - -
- Flash->render('auth') ?> - Form->create() ?> -
- - Form->input('email') ?> - Form->input('password') ?> -
- Form->button(__('Login')); ?> - Form->end() ?> -
- -Agora você pode registrar um novo usuário, acessando a URL ``/users/add`` -e faça login com o usuário recém-criado, indo para a URL ``/users/login``. -Além disso, tente acessar qualquer outro URL que não tenha sido explicitamente -permitido, como ``/articles/add``, você vai ver que o aplicativo redireciona -automaticamente para a página de login. - -E é isso! Parece simples demais para ser verdade. Vamos voltar um pouco para -explicar o que aconteceu. A função ``beforeFilter()`` está falando para o -AuthComponent não solicitar um login para a ação ``add()`` em adição as ações -``index()`` e ``view()`` que foram prontamente autorizadas na função -``beforeFilter()`` do AppController. - -A ação ``login()`` chama a função ``$this->Auth->identify()`` da AuthComponent, -que funciona sem qualquer outra configuração porque estamos seguindo convenções, -como mencionado anteriormente. Ou seja, ter uma tabela de usuários com um -``email`` e uma coluna de ``password``, e usamos um form para postar os dados -do usuário para o controller. Esta função retorna se o login foi bem sucedido ou -não, e caso ela retorne sucesso, então nós redirecionamos o usuário para a URL -que configuramos quando adicionamos o AuthComponent em nossa aplicação. - -O logout funciona quando acessamos a URL ``/users/logout`` que irá redirecionar -o usuário para a url configurada em logoutUrl. Essa url é acionada quando a -função ``AuthComponent::logout()``. - -Autorização (quem tem permissão para acessar o que) -=================================================== - -Como afirmado anteriormente, nós estamos convertendo esse blog em uma ferramenta -multi usuário de autoria, e para fazer isso, precisamos modificar a tabela de -artigos um pouco para adicionar a referência à tabela de Usuários:: - - ALTER TABLE articles ADD COLUMN user_id INT(11); - -Além disso, uma pequena mudança no ArticlesController é necessário para -armazenar o usuário conectado no momento como uma referência para o artigo -criado:: - - // src/Controller/ArticlesController.php - - public function add() - { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - // Adicione esta linha - $article->user_id = $this->Auth->user('id'); - // Você também pode fazer o seguinte - //$newData = ['user_id' => $this->Auth->user('id')]; - //$article = $this->Articles->patchEntity($article, $newData); - if ($this->Articles->save($article)) { - $this->Flash->success(__('Seu artigo foi salvo.')); - - return $this->redirect(['action' => 'index']); - } - $this->Flash->error(__('Não foi possível adicionar seu artigo.')); - } - $this->set('article', $article); - } - -A função ``user()`` fornecida pelo componente retorna qualquer coluna do usuário -logado no momento. Nós usamos esse metódo para adicionar a informação dentro de -request data para que ela seja salva. - -Vamos garantir que nossa app evite que alguns autores editem ou apaguem posts de -outros. Uma regra básica para nossa aplicação é que usuários admin possam -acessar qualquer url, enquanto usuários normais (o papel author) podem somente -acessar as actions permitidas. Abra novamente a classe AppController e adicione -um pouco mais de opções para as configurações do Auth:: - - // src/Controller/AppController.php - - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'authorize' => ['Controller'], // Adicione está linha - 'loginRedirect' => [ - 'controller' => 'Articles', - 'action' => 'index' - ], - 'logoutRedirect' => [ - 'controller' => 'Pages', - 'action' => 'display', - 'home' - ] - ]); - } - - public function isAuthorized($user) - { - // Admin pode acessar todas as actions - if (isset($user['role']) && $user['role'] === 'admin') { - return true; - } - - // Bloqueia acesso por padrão - return false; - } - -Acabamos de criar um mecanismo de autorização muito simples. Nesse caso os -usuários com papel ``admin`` poderão acessar qualquer url no site quando -estiverem logados, mas o restante dos usuários (author) não podem acessar -qualquer coisa diferente dos usuários não logados. - -Isso não é exatamente o que nós queremos, por isso precisamos corrigir nosso -metódo ``isAuthorized()`` para fornecer mais regras. Mas ao invés de fazer -isso no AppController, vamos delegar a cada controller para suprir essas -regras extras. As regras que adicionaremos para o ``add`` de ArticlesController -deve permitir ao autores criarem os posts mas evitar a edição de posts que não -sejam deles. Abra o arquivo **src/Controller/ArticlesController.php** e adicione -o seguinte conteúdo:: - - // src/Controller/ArticlesController.php - - public function isAuthorized($user) - { - // Todos os usuários registrados podem adicionar artigos - if ($this->request->getParam('action') === 'add') { - return true; - } - - // Apenas o proprietário do artigo pode editar e excluí - 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; - } - } - - return parent::isAuthorized($user); - } - -Estamos sobrescrevendo a chamada ``isAuthorized()``do AppController e -internamente verificando na classe pai se o usuário está autorizado. Caso não -esteja, então apenas permitem acessar a action ``add``, e condicionalmente -action ``edit`` e ``delete``. Uma última coisa não foi implementada. Para dizer -ou não se o usuário está autorizado a editar o artigo, nós estamos chamando uma -função ``isOwnedBy()`` na tabela artigos. Vamos, então, implementar essa -função:: - - // src/Model/Table/ArticlesTable.php - - public function isOwnedBy($articleId, $userId) - { - return $this->exists(['id' => $articleId, 'user_id' => $userId]); - } - -Isso conclui então nossa autorização simples e nosso tutorial de autorização. -Para garantir o UsersController você pode seguir as mesmas técnicas que usamos -para ArticlesController, você também pode ser mais criativo e codificar algumas -coisas mais gerais no AppController para suas próprias regras baseadas em -papéis. - -Se precisar de mais controle, nós sugerimos que leia o guia completo do Auth -:doc:`/controllers/components/authentication` seção onde você encontrará mais -sobre a configuração do componente, criação de classes de Autorização -customizadas, e muito mais. - -Sugerimos as seguintes leituras -------------------------------- - -1. :doc:`/bake/usage` Generating basic CRUD code -2. :doc:`/controllers/components/authentication`: User registration and login - -.. meta:: - :title lang=pt: Simple Authentication and Authorization Application - :keywords lang=pt: auto increment,authorization application,model user,array,conventions,authentication,urls,cakephp,delete,doc,columns diff --git a/pt/tutorials-and-examples/blog/blog.rst b/pt/tutorials-and-examples/blog/blog.rst deleted file mode 100755 index 5b3837f376..0000000000 --- a/pt/tutorials-and-examples/blog/blog.rst +++ /dev/null @@ -1,217 +0,0 @@ -Tutorial - Criando um Blog - Parte 1 -#################################### - -Este tutorial irá orientá-lo através da criação de um simples blog. -Faremos a instalação do CakePHP, criaremos um banco de dados e implementaremos a -lógica capaz de listar, adicionar, editar e apagar postagens do blog. - -Aqui está o que você vai precisar: - -#. Um servidor web em funcionamento. Nós iremos assumir que você esteja usando - o Apache, embora as instruções para outros servidores sejam bem similares. - Talvez seja preciso alterar um pouco a configuração do servidor, mas a - maioria das pessoas pode ter o CakePHP instalado e funcionando sem qualquer - trabalho extra. Certifique-se de que você tem o PHP |minphpversion| ou superior, - e que as extensões *mbstring* e *intl* estejam habilitadas no PHP. - Caso não saiba a versão do PHP que está instalada, utilize a função - ``phpinfo()`` ou digite ``php -v`` no seu terminal de comando. - -#. Um servidor de banco de dados. Nós vamos usar o servidor *MySQL* neste - tutorial. Você precisa saber o mínimo sobre SQL para então criar um banco de - dados, depois disso o CakePHP vai assumir as rédeas. Já que usaremos - o *MySQL*, também certifique-se que a extensão ``pdo_mysql`` está - habilitada no PHP. - -#. Conhecimento básico sobre PHP. - -Vamos começar! - -Instalação do CakePHP -===================== - -A maneira mais fácil de instalar o CakePHP é usando Composer, um gerenciador -de dependências para o PHP. Se trata de uma forma simples de instalar o -CakePHP a partir de seu terminal ou prompt de comando. Primeiro, você -precisa baixar e instalar o Composer, caso você já não o tenha. Se possuir -instalado o programa *cURL*, basta executar o seguinte comando:: - -.. code-block:: console - - curl -s https://getcomposer.org/installer | php - -Você também pode baixar o arquivo ``composer.phar`` do -`site `_ oficial do Composer. - -Em seguida, basta digitar a seguinte linha de comando no seu terminal a partir -do diretório onde se localiza o arquivo ``composer.phar`` para instalar o -esqueleto da aplicação do CakePHP no diretório [nome_do_app]. :: - - php composer.phar create-project --prefer-dist cakephp/app:4.* [nome_do_app] - -A vantagem de usar o Composer é que ele irá completar automaticamente um conjunto -importante de tarefas, como configurar corretamente as permissões de pastas -e criar o **config/app.php** para você. - -Há outras maneiras de instalar o CakePHP. Se você não puder ou não quiser usar -o Composer, confira a seção :doc:`/installation`. - -Independentemente de como você baixou o CakePHP, uma vez que sua instalação -for concluída, a estrutura dos diretórios deve ficar parecida com o seguinte:: - - /nome_do_app - /bin - /config - /logs - /plugins - /src - /tests - /tmp - /vendor - /webroot - .editorconfig - .gitignore - .htaccess - .travis.yml - composer.json - index.php - phpunit.xml.dist - README.md - -Agora pode ser um bom momento para aprender sobre como a estrutura de diretórios -do CakePHP funciona: Confira a seção :doc:`/intro/cakephp-folder-structure`. - -Permissões dos diretórios tmp e logs -==================================== - -Os diretórios **tmp** e **logs** precisam ter permissões adequadas para que -possam ser alterados pelo seu servidor web. Se você usou o Composer na -instalação, ele deve ter feito isso por você e confirmado com uma mensagem -"Permissions set on ". Se você ao invés disso, recebeu uma mensagem de -erro ou se quiser fazê-lo manualmente, a melhor forma seria descobrir por qual -usuário o seu servidor web é executado (````) e alterar o -proprietário desses dois diretórios para este usuário. -Os comandos finais a serem executados (em \*nix) podem ser algo como:: - - chown -R www-data tmp - chown -R www-data logs - -Se por alguma razão o CakePHP não puder escrever nesses diretórios, você será -informado por uma advertência enquanto não estiver em modo de produção. - -Embora não seja recomendado, se você é incapaz de redefinir as permissões -do seu servidor web, você pode simplesmente alterar as permissões de gravação -diretamente nos diretórios, executando os seguintes comandos:: - - chmod -R 777 tmp - chmod -R 777 logs - -Criando o banco de dados do Blog -================================ - -Em seguida, vamos configurar o banco de dados para o nosso blog. Se você -ainda não tiver feito isto, crie um banco de dados vazio para usar -neste tutorial, com um nome de sua escolha, por exemplo, ``cake_blog``. -Agora, vamos criar uma tabela para armazenar nossos artigos: - -.. code-block:: mysql - - -- Primeiro, criamos a tabela articles - CREATE TABLE articles ( - id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, - title VARCHAR(50), - body TEXT, - created DATETIME DEFAULT NULL, - modified DATETIME DEFAULT NULL - ); - -Nós vamos também inserir alguns artigos para usarmos em nossos testes. -Execute os seguintes comandos SQL em seu banco de dados: - -.. code-block:: mysql - - -- Então inserimos articles para testes - INSERT INTO articles (title,body,created) - VALUES ('The title', 'This is the article body.', NOW()); - INSERT INTO articles (title,body,created) - VALUES ('A title once again', 'And the article body follows.', NOW()); - INSERT INTO articles (title,body,created) - VALUES ('Title strikes back', 'This is really exciting! Not.', NOW()); - -Os nomes de tabelas e colunas que usamos não foram arbitrárias. Usando -:doc:`convenções de nomenclatura ` do CakePHP, podemos -alavancar o desenvolvimento e acelerar a configuração do framework. O CakePHP -é flexível o suficiente para acomodar até mesmo esquemas de banco de dados -legados inconsistentes, mas aderir às convenções vai lhe poupar tempo. - -Configurando o banco de dados do Blog -===================================== - -Em seguida, vamos dizer ao CakePHP onde nosso banco de dados está e como se -conectar a ele. Para muitos, esta será a primeira e última vez que será -necessário configurar algo. - -A configuração é bem simples e objetiva: basta alterar os valores no array -``Datasources.default`` localizado no arquivo **config/app.php**, pelos valores -que se aplicam à sua configuração. Um exemplo completo de configurações deve -se parecer como o seguinte:: - - return [ - // Mais configurações acima. - 'Datasources' => [ - 'default' => [ - 'className' => 'Cake\Database\Connection', - 'driver' => 'Cake\Database\Driver\Mysql', - 'persistent' => false, - 'host' => 'localhost', - 'username' => 'cakephp', - 'password' => 'AngelF00dC4k3~', - 'database' => 'cake_blog', - 'encoding' => 'utf8', - 'timezone' => 'UTC', - 'cacheMetadata' => true, - ], - ], - // Mais configurações abaixo. - ]; - -Depois de salvar o arquivo **config/app.php**, você deve notar a -mensagem *CakePHP is able to connect to the database* ao acessar o Blog pelo -seu navegador. - -.. note:: - Uma cópia do arquivo de configuração padrão do CakePHP pode ser encontrada - em **config/app.default.php**. - -Configurações opcionais -======================= - -Há alguns outros itens que podem ser configurados. Muitos desenvolvedores -completam esta lista de itens, mas os mesmos não são obrigatórios para este -tutorial. Um deles é definir uma sequência personalizada (ou "salt") para uso em -hashes de segurança. - -A sequência personalizada (ou salt) é utilizada para gerar hashes de segurança. -Se você utilizou o Composer, ele cuidou disso para você durante a instalação. -Apesar disso, você precisa alterar a sequência personalizada padrão editando -o arquivo **config/app.php**. Não importa qual será o novo valor, somente deverá ser -algo difícil de descobrir:: - - 'Security' => [ - 'salt' => 'algum valor longo contendo uma mistura aleatória de valores.', - ], - -Observação sobre o mod_rewrite -============================== - -Ocasionalmente, novos usuários irão se atrapalhar com problemas de mod_rewrite. -Por exemplo, se a página de boas vindas do CakePHP parecer estranha (sem -imagens ou estilos CSS). Isto provavelmente significa que o mod_rewrite não está -funcionando em seu servidor. Por favor, verifique a seção -:ref:`url-rewriting` para obter ajuda e resolver qualquer problema relacionado. - -Agora continue o tutorial em :doc:`/tutorials-and-examples/blog/part-two` e -inicie a construção do seu Blog com o CakePHP. - -.. meta:: - :title lang=pt: Tutorial - Criando um Blog - :keywords lang=pt: tutorial, guide, blog diff --git a/pt/tutorials-and-examples/blog/part-three.rst b/pt/tutorials-and-examples/blog/part-three.rst deleted file mode 100644 index f7276e1a5b..0000000000 --- a/pt/tutorials-and-examples/blog/part-three.rst +++ /dev/null @@ -1,395 +0,0 @@ -Tutorial - Criando um Blog - Parte 3 -#################################### - -Criar uma arvore de Categoria -============================= - -Vamos continuar o nosso aplicativo de blog e imaginar que queremos categorizar -os nossos artigos. Queremos que as categorias sejam ordenadas, e para isso, -vamos usar o comportamento de árvore para nos ajudar a organizar as categorias. - -Mas primeiro, precisamos modificar nossas tabelas. - -Migração de Plugin -================== - -Nós vamos usar o plugin de migrações para criar uma tabela em nosso banco de -dados. Se você tem a tabela articles no seu banco de dados, apague. Agora abra -o arquivo composer.json do seu aplicativo. Normalmente, você veria que o plugin -de migração já está requisitando. Se não, addicione atráves da execução:: - - composer require cakephp/migrations:~1.0 - -O plugin de migração agora está na pasta de sua aplicação. Também, adicionar -``Plugin::load('Migrations');`` para o arquivo bootstrap.php do seu aplicativo. - -Uma vez que o plugin está carregado, execute o seguinte comando para criar um -arquivo de migração:: - - bin/cake bake migration CreateArticles title:string body:text category_id:integer created modified - -Um arquivo de migração será gerado na pasta /config/Migrations com o seguinte: - -.. code-block:: php - - table('articles'); - $table->addColumn('title', 'string', [ - 'default' => null, - 'limit' => 255, - 'null' => false, - ]); - $table->addColumn('body', 'text', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('category_id', 'integer', [ - 'default' => null, - 'limit' => 11, - 'null' => false, - ]); - $table->addColumn('created', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('modified', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->create(); - } - } - -Executar outro comando para criar uma tabela de categorias. Se você precisar -especificar um comprimento de campo, você pode fazê-lo dentro de colchetes no -tipo de campo, ou seja:: - - bin/cake bake migration CreateCategories parent_id:integer lft:integer[10] rght:integer[10] name:string[100] description:string created modified - -Isso irá gerar o seguinte arquivo no config/Migrations: - -.. code-block:: php - - table('categories'); - $table->addColumn('parent_id', 'integer', [ - 'default' => null, - 'limit' => 11, - 'null' => false, - ]); - $table->addColumn('lft', 'integer', [ - 'default' => null, - 'limit' => 10, - 'null' => false, - ]); - $table->addColumn('rght', 'integer', [ - 'default' => null, - 'limit' => 10, - 'null' => false, - ]); - $table->addColumn('name', 'string', [ - 'default' => null, - 'limit' => 100, - 'null' => false, - ]); - $table->addColumn('description', 'string', [ - 'default' => null, - 'limit' => 255, - 'null' => false, - ]); - $table->addColumn('created', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->addColumn('modified', 'datetime', [ - 'default' => null, - 'null' => false, - ]); - $table->create(); - } - } - -Agora que os arquivos de migração estão criadas, você pode editá-los antes de -criar suas tabelas. Precisamos mudar o 'null' => false para o campo parent_id -com ``'null' => true`` porque uma categoria de nível superior tem null no parent_id - -Execute o seguinte comando para criar suas tabelas:: - - bin/cake migrations migrate - -Modificando as Tabelas -====================== - -Com nossas tabelas configuradas, agora podemos nos concentrar em categorizar os -nossos artigos. - -Supomos que você já tem os arquivos (Tabelas, controladores e modelos dos -artigos) da parte 2. Então vamos adicionar as referências a categorias. - -Precisamos associar os artigos e categorias juntos nas tabelas. Abra o arquivo -src/Model/Table/ArticlesTable.php e adicione o seguinte: - -.. code-block:: php - - // src/Model/Table/ArticlesTable.php - namespace App\Model\Table; - - use Cake\ORM\Table; - - class ArticlesTable extends Table - { - public function initialize(array $config) - { - $this->addBehavior('Timestamp'); - // Just add the belongsTo relation with CategoriesTable - $this->belongsTo('Categories', [ - 'foreignKey' => 'category_id', - ]); - } - } - -Gerar código esqueleto por categorias -===================================== - -Crie todos os arquivos pelo comando bake:: - - bin/cake bake model Categories - bin/cake bake controller Categories - bin/cake bake template Categories - -A ferramenta bake criou todos os seus arquivos em um piscar de olhos. Você pode -fazer uma leitura rápida se quiser familiarizar como o CakePHP funciona. - -.. note:: - Se você estiver no Windows lembre-se de usar \\ em vez de /. - -Você vai precisar editar o seguinte em **templates/Categories/add.php** -e **templates/Categories/edit.php**:: - - echo $this->Form->input('parent_id', [ - 'options' => $parentCategories, - 'empty' => 'No parent category' - ]); - -Anexar árvore de compartamento para CategoriesTable -=================================================== - -O :doc:`TreeBehavior ` ajuda você a gerenciar as estruturas -de árvore hierárquica na tabela do banco de dados. Usa a lógica MPTT para -gerenciar os dados. Estruturas de árvore MPTT são otimizados para lê, o que -muitas vezes torna uma boa opção para aplicações pesadas, como ler blogs. - -Se você abrir o arquivo src/Model/Table/CategoriesTable.php, você verá que -o TreeBehavior foi anexado a sua CategoriesTable no método initialize(). Bake -acrescenta esse comportamento para todas as tabelas que contêm lft e colunas -rght:: - - $this->addBehavior('Tree'); - -Com o TreeBehavior anexado você vai ser capaz de acessar alguns recursos como -a reordenação das categorias. Vamos ver isso em um momento. - -Mas, por agora, você tem que remover as seguintes entradas em seus Categorias de -adicionar e editar arquivos de modelo:: - - echo $this->Form->input('lft'); - echo $this->Form->input('rght'); - -Além disso, você deve desabilitar ou remover o requirePresence do validador, -tanto para a ``lft`` e ``rght`` nas colunas em seu modelo CategoriesTable: - -.. code-block:: php - - public function validationDefault(Validator $validator) - { - $validator - ->add('id', 'valid', ['rule' => 'numeric']) - ->allowEmpty('id', 'create'); - - $validator - ->add('lft', 'valid', ['rule' => 'numeric']) - // ->requirePresence('lft', 'create') - ->notEmpty('lft'); - - $validator - ->add('rght', 'valid', ['rule' => 'numeric']) - // ->requirePresence('rght', 'create') - ->notEmpty('rght'); - } - -Esses campos são automaticamente gerenciados pelo TreeBehavior quando uma -categoria é salvo. - -Usando seu navegador, adicione algumas novas categorias usando os -``/yoursite/categories/add`` ação do controlador. - -Reordenar categorias com TreeBahavior -===================================== - -Em seu arquivo de modelo de índices de categorias, você pode listar as -categorias e reordená-los. - -Vamos modificar o método de índice em sua CategoriesController.php e adicionar -moveUp() e moveDown() para ser capaz de reordenar as categorias na árvore: - -.. code-block:: php - - class CategoriesController extends AppController - { - public function index() - { - $categories = $this->Categories->find() - ->order(['lft' => 'ASC']); - $this->set(compact('categories')); - $this->set('_serialize', ['categories']); - } - - public function moveUp($id = null) - { - $this->request->allowMethod(['post', 'put']); - $category = $this->Categories->get($id); - if ($this->Categories->moveUp($category)) { - $this->Flash->success('The category has been moved Up.'); - } else { - $this->Flash->error('The category could not be moved up. Please, try again.'); - } - - return $this->redirect($this->referer(['action' => 'index'])); - } - - public function moveDown($id = null) - { - $this->request->allowMethod(['post', 'put']); - $category = $this->Categories->get($id); - if ($this->Categories->moveDown($category)) { - $this->Flash->success('The category has been moved down.'); - } else { - $this->Flash->error('The category could not be moved down. Please, try again.'); - } - - return $this->redirect($this->referer(['action' => 'index'])); - } - } - -Em templates/Categories/index.php substituir o conteúdo existente com: - -.. code-block:: php - -
-

-
    -
  • Html->link(__('New Category'), ['action' => 'add']) ?>
  • -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
IdParent IdLftRghtNameDescriptionCreated
id ?>parent_id ?>lft ?>rght ?>name) ?>description) ?>created) ?> - Html->link(__('View'), ['action' => 'view', $category->id]) ?> - Html->link(__('Edit'), ['action' => 'edit', $category->id]) ?> - Form->postLink(__('Delete'), ['action' => 'delete', $category->id], ['confirm' => __('Are you sure you want to delete # {0}?', $category->id)]) ?> - Form->postLink(__('Move down'), ['action' => 'moveDown', $category->id], ['confirm' => __('Are you sure you want to move down # {0}?', $category->id)]) ?> - Form->postLink(__('Move up'), ['action' => 'moveUp', $category->id], ['confirm' => __('Are you sure you want to move up # {0}?', $category->id)]) ?> -
-
- -Modificando o ArticlesController -================================ - -Em nossa ArticlesController, vamos obter a lista de todas as categorias. Isto -irá permitir-nos para escolher uma categoria para um artigo ao criar ou editar -ele: - -.. code-block:: php - - // src/Controller/ArticlesController.php - namespace App\Controller; - - // Prior to 3.6 use Cake\Network\Exception\NotFoundException - use Cake\Http\Exception\NotFoundException; - - class ArticlesController extends AppController - { - // ... - - public function add() - { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - 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); - - // Just added the categories list to be able to choose - // one category for an article - $categories = $this->Articles->Categories->find('treeList'); - $this->set(compact('categories')); - } - } - -Modificando os artigos Templates -================================ - -O artigo adicionado deveria se parecer como isto: - -.. code-block:: php - - - -

Add Article

- Form->create($article); - // just added the categories input - echo $this->Form->input('category_id'); - echo $this->Form->input('title'); - echo $this->Form->input('body', ['rows' => '3']); - echo $this->Form->button(__('Save Article')); - echo $this->Form->end(); - -Quando você vai para o endereço ``/yoursite/categories/add`` você deve ver uma lista -de categorias para escolher. diff --git a/pt/tutorials-and-examples/blog/part-two.rst b/pt/tutorials-and-examples/blog/part-two.rst deleted file mode 100644 index 5f30bc5d4a..0000000000 --- a/pt/tutorials-and-examples/blog/part-two.rst +++ /dev/null @@ -1,671 +0,0 @@ -Tutorial - Criando um Blog - Parte 2 -#################################### - -Criando o model -=============== - -Após criar um model (modelo) no CakePHP, nós teremos a base necessária para -interagirmos com o banco de dados e executar operações. - -Os arquivos de classes, correspondentes aos models, no CakePHP estão divididos -entre os objetos ``Table`` e ``Entity``. Objetos ``Table`` provêm acesso à -coleção de entidades armazenada em uma tabela e são alocados em -**src/Model/Table**. - -O arquivo que criaremos deverá ficar salvo em -**src/Model/Table/ArticlesTable.php**:: - - // src/Model/Table/ArticlesTable.php - - namespace App\Model\Table; - - use Cake\ORM\Table; - - class ArticlesTable extends Table - { - public function initialize(array $config) - { - $this->addBehavior('Timestamp'); - } - } - -Convenções de nomenclatura são muito importantes no CakePHP. Ao nomear nosso -objeto como ``ArticlesTable``, o CakePHP automaticamente deduz que o mesmo -utilize o ``ArticlesController`` e seja relacionado à tabela ``articles``. - -.. note:: - - O CakePHP criará automaticamente um objeto model se não puder encontrar um - arquivo correspondente em **src/Model/Table**. Se você nomear incorretamente - seu arquivo (isto é, artciclestable.php ou ArticleTable.php), o CakePHP não - reconhecerá suas definições e usará o model gerado como alternativa. - -Para mais informações sobre models, como callbacks e validação, visite o -capítulo :doc:`/orm` do manual. - -.. note:: - - Se você completou a - :doc:`primeira parte ` do tutorial e - criou a tabela ``articles``, você pode tomar proveito da capacidade de - geração de código do bake através do console do CakePHP para criar o model - ``ArticlesTable``:: - - bin/cake bake model Articles - -Para mais informações sobre o bake e suas características relacionadas a -geração de código, visite o capítulo :doc:`/bake/usage` do manual. - -Criando o controller -==================== - -A seguir, criaremos um controller (controlador) para nossos artigos. O -controller é responsável pela lógica de interação da aplicação. É o lugar onde -você utilizará as regras contidas nos models e executará tarefas relacionadas -aos artigos. Criaremos um arquivo chamado **ArticlesController.php** no -diretório **src/Controller**:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - } - -Agora, vamos adicionar uma action (ação) ao nosso controller. Actions -frequentemente, representam uma função ou interface em uma aplicação. -Por exemplo, quando os usuários requisitarem www.example.com/articles/index -(sendo o mesmo que www.example.com/articles/), eles esperam ver uma lista de -artigos:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - - public function index() - { - $articles = $this->Articles->find('all'); - $this->set(compact('articles')); - } - } - -Ao definir a função ``index()`` em nosso ``ArticlesController``, os usuários -podem acessá-la requisitando www.example.com/articles/index. Similarmente, se -definíssemos uma função chamada ``foobar()``, os usuários poderiam acessá-la em -www.example.com/articles/foobar. - -.. warning:: - - Vocês podem ser tentados a nomear seus controllers e actions para obter uma - certa URL. Resista a essa tentação. Siga as :doc:`/intro/conventions` e crie - nomes de action legíveis e compreensíveis. Você pode mapear URLs para o seu - código utilizando :doc:`/development/routing`. - -A instrução na action usa ``set()`` para passar dados do controller para a view. -A variável é definida como 'articles', sendo igual ao valor retornado do método -``find('all')`` do objeto ``ArticlesTable``. - -.. note:: - - Se você completou a - :doc:`primeira parte ` do tutorial e - criou a tabela ``articles``, você pode tomar proveito da capacidade de - geração de código do bake através do console do CakePHP para criar o - controller ``ArticlesController``:: - - bin/cake bake controller Articles - -Para mais informações sobre o bake e suas características sobre geração de -código, visite o capítulo :doc:`/bake/usage` do manual. - -Criando as views -================ - -Agora que nós temos os dados fluindo pelo nosso model, e nossa lógica da -aplicação definida em nosso controller, vamos criar uma view -(visualização) para a action ``index()``. - -As views do CakePHP são camadas de apresentação que se encaixam nos layouts -da aplicação. Para a maioria das aplicações, elas são uma mescla entre HTML e -PHP, mas também podem ser distribuídas como XML, CSV, ou ainda dados binários. - -Um layout é um conjunto de códigos encontrado ao redor das views. Múltiplos -layouts podem ser definidos, e você pode alterar entre eles, mas agora, vamos -usar o default, localizado em **templates/layout/default.php**. - -Lembra que na última sessão atribuímos a variável 'articles' à view usando o -método ``set()``? Isso levará a coleção de objetos gerada pela query a ser -invocada numa iteração ``foreach``. - -Arquivos de template do CakePHP são armazenados em **src/Template** dentro de -uma pasta com o nome do controller correspondente (nós teremos que criar a -pasta 'Articles' nesse caso). Para distribuir os dados de artigos em uma tabela, -precisamos criar uma view assim: - -.. code-block:: php - - - -

Blog articles

- - - - - - - - - - - - - - - - -
IdTitleCreated
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> -
- -Você deve ter notado o uso de um objeto chamado ``$this->Html``, uma -instância da classe :php:class:`Cake\\View\\Helper\\HtmlHelper` do CakePHP. -O CakePHP vem com um conjunto de view helpers que simplificam tarefas como gerar -links e formulários. Você pode aprender como usá-los em :doc:`/views/helpers`, -mas aqui é importante notar que o método ``link()`` irá gerar um link HTML -com o referido título (primeiro parâmetro) e URL (segundo parâmetro). - -Quando se especifíca URLs no CakePHP, é recomendado o uso do formato de array. -Isto será melhor explicado posteriormente na seção Rotas. Usando o formato de -array para URLs, você toma vantagem das capacidades de roteamento -reverso do CakePHP. Você também pode especificar URLs relativas a base da -aplicação com o formato ``/controller/action/param1/param2`` ou usar -:ref:`named routes `. - -Neste ponto, você pode visitar http://www.example.com/articles/index no seu -navegador. Você deve ver sua view corretamente formatada listando os artigos. - -Se você clicar no link do título de um artigo listado, provavelmente será -informado pelo CakePHP que a action ainda não foi definida, então vamos criá-la -no ``ArticlesController`` agora:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - class ArticlesController extends AppController - { - - public function index() - { - $this->set('articles', $this->Articles->find('all')); - } - - public function view($id = null) - { - $article = $this->Articles->get($id); - $this->set(compact('article')); - } - } - -O uso do ``set()`` deve parecer familiar. Repare que você está usando -``get()`` ao invés de ``find('all')`` porquê nós queremos a informação de apenas -um artigo. - -Repare que nossa action recebe um parâmetro: o ID do artigo que gostariamos de -visualizar. Esse parâmetro é entregue para a action através da URL solicitada. -Se o usuário requisitar ``/articles/view/3``, então o valor '3' é passado como -``$id`` para a action. - -Ao usar a função ``get()``, fazemos também algumas verificações para garantir -que o usuário realmente está acessando um registro existente , se não -ou se o ``$id`` for indefinido, a função irá lançar uma ``NotFoundException``. - -Agora vamos criar a view para nossa action em -**templates/Articles/view.php** - -.. code-block:: php - - - -

title) ?>

-

body) ?>

-

Criado: created->format(DATE_RFC850) ?>

- -Verifique se está tudo funcionando acessando os links em ``/articles/index`` ou -manualmente solicite a visualização de um artigo acessando ``articles/view/{id}``. -Lembre-se de substituir ``{id}`` por um 'id' de um artigo. - -Adicionando artigos -=================== - -Primeiro, comece criando a action ``add()`` no ``ArticlesController``:: - - // src/Controller/ArticlesController.php - - namespace App\Controller; - - use App\Controller\AppController; - - class ArticlesController extends AppController - { - - public function initialize() - { - parent::initialize(); - - $this->loadComponent('Flash'); // Inclui o FlashComponent - } - - public function index() - { - $this->set('articles', $this->Articles->find('all')); - } - - public function view($id) - { - $article = $this->Articles->get($id); - $this->set(compact('article')); - } - - public function add() - { - $article = $this->Articles->newEmptyEntity(); - if ($this->request->is('post')) { - $article = $this->Articles->patchEntity($article, $this->request->getData()); - if ($this->Articles->save($article)) { - $this->Flash->success(__('Seu artigo foi salvo.')); - - return $this->redirect(['action' => 'index']); - } - $this->Flash->error(__('Não é possível adicionar o seu artigo.')); - } - $this->set('article', $article); - } - } - -.. note:: - - Você precisa incluir o :doc:`/controllers/components/flash` component em qualquer - controller que vá usá-lo. Se necessário, inclua no ``AppController`` e - assim o ``FlashComponent`` estará disponível para todos os controllers da - aplicação. - -A action ``add()`` checa se o método HTTP da solicitação foi POST, e então tenta -salvar os dados utilizando o model Articles. Se por alguma razão ele não salvar, -apenas renderiza a view. Isto nos dá a chance de exibir erros de validação ou -outros alertas. - -Cada requisição do CakePHP instancia um objeto ``ServerRequest`` que é acessível -usando ``$this->request``. O objeto contém informações úteis sobre a requisição -que foi recebida e pode ser usado para controlar o fluxo de sua aplicação. Nesse -caso, nós usamos o método :php:meth:`Cake\\Network\\ServerRequest::is()` para checar -se a requisição é do tipo HTTP POST. - -Quando se usa um formulário para postar dados, essa informação fica disponível -em ``$this->request->getData()``. Você pode usar as funções :php:func:`pr()` ou -:php:func:`debug()` caso queira verificar esses dados. - -Usamos os métodos ``success()`` e ``error()`` do ``FlashComponent`` para definir -uma mensagem que será armazenada numa variável de sessão. Esses métodos são -gerados usando os `recursos de métodos mágicos -`_ do PHP. -Mensagens flash serão exibidas na página após um redirecionamento. No layout nós -temos ``Flash->render() ?>`` que exibe a mensagem e limpa a variável -de sessão. A função do controller -:php:meth:`Cake\\Controller\\Controller::redirect` redireciona para qualquer -outra URL. O parâmetro ``['action' => 'index']`` corresponde a URL /articles, -isto é, a action ``index()`` do ``ArticlesController``. Você pode consultar a -função -:php:func:`Cake\\Routing\\Router::url()` na `API `_ e -checar os formatos a partir dos quais você pode montar uma URL. - -Chamar o método ``save()`` vai checar erros de validação e abortar o processo -caso os encontre. Nós vamos abordar como esses erros são tratados nas sessões -a seguir. - -Validando artigos -================= - -O CakePHP torna mais prática e menos monótona a validação de dados de -formulário. - -Para tirar proveito dos recursos de validação, você vai precisar usar o -:doc:`/views/helpers/form` helper em suas views. O -:php:class:`Cake\\View\\Helper\\FormHelper` está disponível por padrão em todas -as views pelo uso do ``$this->Form``. - -Segue a view correspondente a action add: - -.. code-block:: php - - - -

Add Article

- Form->create($article); - echo $this->Form->input('title'); - echo $this->Form->input('body', ['rows' => '3']); - echo $this->Form->button(__('Salvar artigo')); - echo $this->Form->end(); - ?> - -Nós usamos o ``FormHelper`` para gerar a tag de abertura HTML de um formulário. -Segue o HTML gerado por ``$this->Form->create()``: - -.. code-block:: html - -
- -Se ``create()`` é chamado sem parâmetros fornecidos, assume-se a construção de -um formulário que submete dados via POST para a action ``add()`` (ou ``edit()`` -no caso de um ``id`` estar incluído nos dados do formulário). - -O método ``$this->Form->input()`` é usado para criar elementos do formulário do -mesmo nome. O primeiro parâmetro diz ao CakePHP qual é o campo correspondente, e -o segundo parâmetro permite que você especifique um vasto array de opções, -nesse, o número de linhas para o textarea. ``input()`` vai gerar diferentes -elementos de formulários baseados no tipo de campo especificado no model. - -O ``$this->Form->end()`` fecha o formulário, entregando também elementos ocultos -caso a prevenção contra CSRF/Form Tampering esteja habilitada. - -Agora vamos voltar e atualizar nossa view **templates/Articles/index.php** -para incluir um novo link. Antes do ````, adicione a seguinte linha: - -.. code-block:: php - - Html->link('Adicionar artigo', ['action' => 'add']) ?> - -Você deve estar se perguntando: como eu digo ao CakePHP meus critérios de -validação? Regras de validação são definidas no model. Vamos fazer alguns -ajustes no nosso model:: - - // src/Model/Table/ArticlesTable.php - - namespace App\Model\Table; - - use Cake\ORM\Table; - use Cake\Validation\Validator; - - class ArticlesTable extends Table - { - public function initialize(array $config) - { - $this->addBehavior('Timestamp'); - } - - public function validationDefault(Validator $validator) - { - $validator - ->notEmpty('title') - ->notEmpty('body'); - - return $validator; - } - } - -O método ``validationDefault()`` diz ao CakePHP como validar seus dados quando -o método ``save()`` for solicitado. Aqui, estamos especificando que tanto o -campo body quanto title não devem estar vazios. O CakePHP possui muitos recursos -de validação e um bom número de regras pré-determinadas (número de cartões, -endereços de email, etc), além de flexibilidade para adicionar regras de -validação customizadas. Para mais informações sobre configuração de validações, -visite a documentação em :doc:`/core-libraries/validation`. - -Agora que suas regras de validação estão definidas, tente adicionar um artigo -sem definir o campo title e body para ver como a validação funciona. Desde que -tenhamos usado o método :php:meth:`Cake\\View\\Helper\\FormHelper::input()` do -``FormHelper`` para criar nossos elementos, nossas mensagens de alerta da -validação serão exibidas automaticamente. - -Editando artigos -================ - -Edição, aí vamos nós! Você já é um profissional do CakePHP agora, então -possivelmente detectou um padrão... Cria-se a action e então a view. Aqui segue -a action ``edit()`` que deverá ser inserida no ``ArticlesController``:: - - // src/Controller/ArticlesController.php - - public function edit($id = null) - { - $article = $this->Articles->get($id); - if ($this->request->is(['post', 'put'])) { - $this->Articles->patchEntity($article, $this->request->getData()); - if ($this->Articles->save($article)) { - $this->Flash->success(__('Seu artigo foi atualizado.')); - - return $this->redirect(['action' => 'index']); - } - $this->Flash->error(__('Seu artigo não pôde ser atualizado.')); - } - - $this->set('article', $article); - } - -Essa action primeiramente certifica-se que o registro apontado existe. Se o -parâmetro ``$id`` não foi passado ou se o registro é inexistente, uma -``NotFoundException`` é lançada pelo ``ErrorHandler`` do CakePHP. - -Em seguida, a action verifica se a requisição é POST ou PUT e caso seja, os -dados são usados para atualizar a entidade de artigo em questão ao usar -o método ``patchEntity()``. Então finalmente usamos o ``ArticlesTable`` para -salvar a entidade. - -Segue a view correspondente a action edit: - -.. code-block:: php - - - -

Edit Article

- Form->create($article); - echo $this->Form->input('title'); - echo $this->Form->input('body', ['rows' => '3']); - echo $this->Form->button(__('Salvar artigo')); - echo $this->Form->end(); - ?> - -Essa view retorna o formulário de edição com os dados populados, juntamente -com qualquer mensagem de erro proveniente de validações. - -O CakePHP irá determinar se o ``save()`` vai inserir ou atualizar um registro -baseado nos dados da entidade. - -Você pode atualizar sua view index com os links para editar artigos: - -.. code-block:: php - - - -

Blog articles

-

Html->link("Adicionar artigo", ['action' => 'add']) ?>

-
- - - - - - - - - - - - - - - - - - -
IdTítuloCriadoAções
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> - - Html->link('Editar', ['action' => 'edit', $article->id]) ?> -
- -Deletando artigos -================= - -A seguir, vamos criar uma forma de deletar artigos. Comece com uma action -``delete()`` no ``ArticlesController``:: - - // src/Controller/ArticlesController.php - - public function delete($id) - { - $this->request->allowMethod(['post', 'delete']); - - $article = $this->Articles->get($id); - if ($this->Articles->delete($article)) { - $this->Flash->success(__('O artigo com id: {0} foi deletado.', h($id))); - - return $this->redirect(['action' => 'index']); - } - } - -Essa lógica deleta o artigo especificado pelo ``$id`` e usa -``$this->Flash->success()`` para exibir uma mensagem de confirmação após -o redirecionamento para ``/articles``. Tentar excluir um registro usando uma -requisição GET, fará com que o ``allowMethod()`` lance uma exceção. Exceções -são capturadas pelo gerenciador de exceções do CakePHP e uma página de erro é -exibida. Existem muitos :doc:`Exceptions ` embutidos que -podem indicar variados erros HTTP que sua aplicação possa precisar. - -Por estarmos executando apenas lógica e redirecionando, essa action não -tem uma view. Vamos atualizar nossa view index com links para excluir artigos: - -.. code-block:: php - - - -

Blog articles

-

Html->link('Adicionar artigo', ['action' => 'add']) ?>

- - - - - - - - - - - - - - - - - - - -
IdTítuloCriadoAções
id ?> - Html->link($article->title, ['action' => 'view', $article->id]) ?> - - created->format(DATE_RFC850) ?> - - Form->postLink( - 'Deletar', - ['action' => 'delete', $article->id], - ['confirm' => 'Tem certeza?']) - ?> - Html->link('Edit', ['action' => 'edit', $article->id]) ?> -
- -Usar :php:meth:`~Cake\\View\\Helper\\FormHelper::postLink()` vai criar um link -que usa JavaScript para criar uma requisição POST afim de deletar um artigo. - -.. warning:: - - Permitir que registros sejam deletados usando requisições GET é perigoso, - pois rastreadores na web podem acidentalmente deletar todo o seu conteúdo. - -.. note:: - - Esse código da view também usa o ``FormHelper`` para confirmar a action - através de JavaScript. - -Rotas -===== - -Para muitos o roteamento padrão do CakePHP funciona bem o suficiente. -Desenvolvedores que consideram facilidade de uso e SEO irão apreciar a forma -como o CakePHP mapeia determinadas URLs para actions específicas. Vamos realizar -uma pequena mudança nas rotas neste tutorial. - -Para mais informações sobre técnicas avançadas de roteamento, visite -:ref:`routes-configuration`. - -Por padrão, o CakePHP responde a uma requisição pela raíz do seu site usando o -``PagesController``, ao renderizar uma view chamada **home.php**. -Alternativamente, nós vamos substituir esse comportamento pelo -``ArticlesController`` ao criar uma regra de roteamento. - -A configuração de rotas do CakePHP pode ser encontrada em **config/routes.php**. -Você deve comentar ou remover a linha que define o roteamento padrão: - -.. code-block:: php - - $routes->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); - -Essa linha conecta a URL '/' com a página padrão do CakePHP. Nós queremos que -ela conecte-se ao nosso próprio controller, então a substitua por esta: - -.. code-block:: php - - $routes->connect('/', ['controller' => 'Articles', 'action' => 'index']); - -Isso irá conectar requisições por '/' a action ``index()`` do nosso -``ArticlesController`` - -.. note:: - - O CakePHP aproveita-se do uso de roteamento reverso. Se com a rota anterior - definida você gerar um link com a seguinte estrutura de array: - ``['controller' => 'Articles', 'action' => 'index']``, a URL resultante - será '/'. Portanto, é uma boa ideia sempre usar arrays para URLs, pois assim - suas rotas definem o endereço gerado e certificam-se que os links apontem - sempre para o mesmo lugar. - -Conclusão -========= - -Simples, não é? Tenha em mente que esse foi um tutorial básico. O CakePHP tem -*muito* mais recursos a oferecer. Não abordamos outros tópicos aqui para manter -a simplicidade. Use o restante do manual como um guia para criar aplicações -mais ricas. - -Agora que você criou uma aplicação básica no CakePHP, você pode continuar no -:doc:`/tutorials-and-examples/blog/part-three`, ou começar seu próprio projeto. -Você também pode folhear os :doc:`/topics` ou a -`API ` para aprender mais sobre o CakePHP. - -Se você precisar de ajuda, há muitas formas de conseguir, por favor, visite a -página :doc:`/intro/where-to-get-help` e bem-vindo(a) ao CakePHP! - -Leitura complementar --------------------- - -Existem tópicos comuns que as pessoas que estão estudando o CakePHP normalmente -visitam a seguir: - -1. :ref:`view-layouts`: Customizando o layout da aplicação -2. :ref:`view-elements`: Inclusão e reutilização de elementos na view -3. :doc:`/bake/usage`: Gerando código CRUD -4. :doc:`/tutorials-and-examples/blog-auth-example/auth`: Tutorial de - autorização e autenticação - -.. meta:: - :title lang=pt: Tutorial - Criando um Blog - Parte 2 - :keywords lang=pt: actions,view,add,edit,delete,validation,model,post,request,validate,error diff --git a/pt/tutorials-and-examples/bookmarks/intro.rst b/pt/tutorials-and-examples/bookmarks/intro.rst deleted file mode 100644 index 86fa41d627..0000000000 --- a/pt/tutorials-and-examples/bookmarks/intro.rst +++ /dev/null @@ -1,367 +0,0 @@ -Tutorial - Criando um Bookmarker - Parte 1 -########################################## - -Esse tutorial vai guiar você através da criação de uma simples aplicação de -marcação (bookmarker). Para começar, nós vamos instalar o CakePHP, criar -nosso banco de dados, e usar as ferramentas que o CakePHP fornece para subir -nossa aplicação de forma rápida. - -Aqui está o que você vai precisar: - -#. Um servidor de banco de dados. Nós vamos usar o servidor MySQL neste - tutorial. Você precisa saber o suficiente sobre SQL para criar um banco de - dados: O CakePHP vai tomar as rédeas a partir daí. Por nós estarmos - usando o MySQL, também certifique-se que você tem a extensão ``pdo_mysql`` - habilitada no PHP. - -#. Conhecimento básico sobre PHP. - -Vamos começar! - -Instalação do CakePHP -===================== - -A maneira mais fácil de instalar o CakePHP é usando Composer, um gerenciador -de dependências para o PHP. É uma forma simples de instalar o CakePHP a -partir de seu terminal ou prompt de comando. Primeiro, você precisa baixar e -instalar o Composer. Se você tiver instalada a extensão cURL do PHP, execute -o seguinte comando:: - - curl -s https://getcomposer.org/installer | php - -Ao invés disso, você também pode baixar o arquivo ``composer.phar`` do -`site `_ oficial. - -Em seguida, basta digitar a seguinte linha no seu terminal a partir do diretório -onde se localiza o arquivo ``composer.phar`` para instalar o esqueleto de -aplicações do CakePHP no diretório ``bookmarker``. :: - - php composer.phar create-project --prefer-dist cakephp/app:4.* bookmarker - -A vantagem de usar Composer é que ele irá completar automaticamente um conjunto -importante de tarefas, como configurar as permissões de arquivo e criar a sua -**config/app.php**. - -Há outras maneiras de instalar o CakePHP. Se você não puder ou não quiser usar -Composer, veja a seção :doc:`/installation`. - -Independentemente de como você baixou o CakePHP, uma vez que sua instalação -for concluída, a estrutura dos diretórios deve ficar parecida com o seguinte:: - - /bookmarker - /bin - /config - /logs - /plugins - /src - /tests - /tmp - /vendor - /webroot - .editorconfig - .gitignore - .htaccess - .travis.yml - composer.json - index.php - phpunit.xml.dist - README.md - -Agora pode ser um bom momento para aprender sobre como a estrutura de diretórios -do CakePHP funciona: Confira a seção :doc:`/intro/cakephp-folder-structure`. - -Verificando nossa instalação -============================ - -Podemos checar rapidamente que a nossa instalação está correta, verificando a -página inicial padrão. Antes que você possa fazer isso, você vai precisar -iniciar o servidor de desenvolvimento:: - - bin/cake server - -Isto irá iniciar o servidor embutido do PHP na porta 8765. Abra -``http://localhost:8765`` em seu navegador para ver a página de boas-vindas. -Todas as verificações devem estar checadas corretamente, a não ser a conexão com -banco de dados do CakePHP. Se não, você pode precisar instalar extensões do PHP -adicionais, ou definir permissões de diretório. - -Criando o banco de dados -======================== - -Em seguida, vamos criar o banco de dados para a nossa aplicação. Se você -ainda não tiver feito isso, crie um banco de dados vazio para uso -nesse tutorial, com um nome de sua escolha, por exemplo, ``cake_bookmarks``. -Você pode executar o seguinte SQL para criar as tabelas necessárias: - -.. 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), - INDEX tag_idx (tag_id, bookmark_id), - FOREIGN KEY tag_key(tag_id) REFERENCES tags(id), - FOREIGN KEY bookmark_key(bookmark_id) REFERENCES bookmarks(id) - ); - -Você deve ter notado que a tabela ``bookmarks_tags`` utilizada uma chave -primária composta. O CakePHP suporta chaves primárias compostas em quase todos os -lugares, tornando mais fácil construir aplicações multi-arrendados. - -Os nomes de tabelas e colunas que usamos não foram arbitrárias. Usando -:doc:`convenções de nomenclatura ` do CakePHP, podemos -alavancar o desenvolvimento e evitar ter de configurar o framework. O CakePHP -é flexível o suficiente para acomodar até mesmo esquemas de banco de dados -legados inconsistentes, mas aderir às convenções vai lhe poupar tempo. - -Configurando o banco de dados -============================= - -Em seguida, vamos dizer ao CakePHP onde o nosso banco de dados está e como se -conectar a ele. Para muitos, esta será a primeira e última vez que você vai -precisar configurar qualquer coisa. - -A configuração é bem simples: basta alterar os valores do array -``Datasources.default`` no arquivo **config/app.php** pelos que se -aplicam à sua configuração. A amostra completa da gama de configurações pode -ser algo como o seguinte:: - - return [ - // Mais configuração acima. - '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, - ], - ], - // Mais configuração abaixo. - ]; - -Depois de salvar o seu arquivo **config/app.php**, você deve notar que a -mensagem 'CakePHP is able to connect to the database' tem uma marca de -verificação. - -.. note:: - - Uma cópia do arquivo de configuração padrão do CakePHP é encontrado em - **config/app.default.php**. - -Gerando o código base -===================== - -Devido a nosso banco de dados seguir as convenções do CakePHP, podemos usar o -:doc:`bake console ` para gerar rapidamente uma aplicação básica -. Em sua linha de comando execute:: - - bin/cake bake all users - bin/cake bake all bookmarks - bin/cake bake all tags - -Isso irá gerar os controllers, models, views, seus casos de teste -correspondentes, e fixtures para os nossos users, bookmarks e tags. Se você -parou seu servidor, reinicie-o e vá para ``http://localhost:8765/bookmarks``. - -Você deverá ver uma aplicação que dá acesso básico, mas funcional a tabelas -de banco de dados. Adicione alguns users, bookmarks e tags. - -Adicionando criptografia de senha -================================= - -Quando você criou seus users, você deve ter notado que as senhas foram -armazenadas como texto simples. Isso é muito ruim do ponto de vista da -segurança, por isso vamos consertar isso. - -Este também é um bom momento para falar sobre a camada de modelo. No CakePHP, -separamos os métodos que operam em uma coleção de objetos, e um único objeto -em diferentes classes. Métodos que operam na recolha de entidades são -colocadas na classe *Table*, enquanto as características pertencentes a um -único registro são colocados na classe *Entity*. - -Por exemplo, a criptografia de senha é feita no registro individual, por -isso vamos implementar esse comportamento no objeto entidade. Dada a -circunstância de nós querermos criptografar a senha cada vez que é -definida, vamos usar um método modificador/definidor. O CakePHP vai chamar -métodos de definição baseados em convenções a qualquer momento que uma -propriedade é definida em uma de suas entidades. Vamos adicionar um definidor -para a senha. Em **src/Model/Entity/User.php** adicione o seguinte:: - - namespace App\Model\Entity; - - use Cake\ORM\Entity; - use Cake\Auth\DefaultPasswordHasher; - - class User extends Entity - { - - // Code from bake. - - protected function _setPassword($value) - { - $hasher = new DefaultPasswordHasher(); - - return $hasher->hash($value); - } - } - -Agora atualize um dos usuários que você criou anteriormente, se você alterar -sua senha, você deve ver um senha criptografada ao invés do valor original nas -páginas de lista ou visualização. O CakePHP criptografa senhas com -`bcrypt `_ por padrão. -Você também pode usar sha1 ou md5 caso venha a trabalhar com um -banco de dados existente. - -Recuperando bookmarks com uma tag específica -============================================ - -Agora que estamos armazenando senhas com segurança, podemos construir algumas -características mais interessantes em nossa aplicação. Uma vez que você -acumulou uma coleção de bookmarks, é útil ser capaz de pesquisar através -deles por tag. Em seguida, vamos implementar uma rota, a ação do controller, e -um método localizador para pesquisar através de bookmarks por tag. - -Idealmente, nós teríamos uma URL que se parece com -``http://localhost:8765/bookmarks/tagged/funny/cat/gifs``. Isso deveria nos -permitir a encontrar todos os bookmarks que têm as tags 'funny', 'cat' e -'gifs'. Antes de podermos implementar isso, vamos adicionar uma nova rota. Em -**config/routes.php**, adicione o seguinte na parte superior do arquivo:: - - Router::scope( - '/bookmarks', - ['controller' => 'Bookmarks'], - function ($routes) { - $routes->connect('/tagged/*', ['action' => 'tags']); - } - ); - -O trecho acima define uma nova "rota" que liga o caminho ``/bookmarks/tagged/*``, a -``BookmarksController::tags()``. Ao definir rotas, você pode isolar como -suas URLs parecerão, de como eles são implementadas. Se fôssemos visitar -``http://localhost:8765/bookmarks/tagged``, deveriamos ver uma página de erro -informativa do CakePHP. Vamos implementar esse método ausente agora. Em -**src/Controller/BookmarksController.php** adicione o seguinte trecho:: - - public function tags() - { - $tags = $this->request->getParam('pass'); - $bookmarks = $this->Bookmarks->find('tagged', [ - 'tags' => $tags - ]); - $this->set(compact('bookmarks', 'tags')); - } - -Criando o método localizador -============================ - -No CakePHP nós gostamos de manter as nossas ações do controller enxutas, e -colocar a maior parte da lógica de nossa aplicação nos modelos. Se você fosse -visitar a URL ``/bookmarks/tagged`` agora, você veria um erro sobre o -método ``findTagged`` não estar implementado ainda, então vamos fazer isso. Em -**src/Model/Table/BookmarksTable.php** adicione o seguinte:: - - 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']); - } - -Nós implementamos um método -:ref:`localizador customizado `. Este é um conceito -muito poderoso no CakePHP que lhe permite construir consultas reutilizáveis. -Em nossa pesquisa, nós alavancamos o método ``matching()`` que nos habilita -encontrar bookmarks que têm uma tag 'correspondente'. - -Criando a view -============== - -Agora, se você visitar a URL ``/bookmarks/tagged``, o CakePHP irá mostrar um -erro e deixá-lo saber que você ainda não fez um arquivo view. Em seguida, -vamos construir o arquivo view para a nossa ação ``tags``. Em -**templates/Bookmarks/tags.php** coloque o seguinte conteúdo:: - -

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

- -
- -
-

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

- url) ?> - Text->autoParagraph(h($bookmark->description)) ?> -
- -
- -O CakePHP espera que os nossos templates sigam a convenção de nomenclatura onde -o nome do template é a versão minúscula e grifada do nome da ação do -controller. - -Você pode perceber que fomos capazes de utilizar as variáveis ``$tags`` e -``bookmarks`` em nossa view. Quando usamos o método ``set()`` em nosso -controller, automaticamente definimos variáveis específicas que devem ser -enviadas para a view. A view vai tornar todas as variáveis passadas -disponíveis nos templates como variáveis locais. - -Em nossa view, usamos alguns dos :doc:`helpers ` nativos do -CakePHP. Helpers são usados para criar lógica re-utilizável para a -formatação de dados, a criação de HTML ou outra saída da view. - -Agora você deve ser capaz de visitar a URL ``/bookmarks/tagged/funny`` e ver -todas os bookmarks com a tag 'funny'. - -Até agora, nós criamos uma aplicação básica para gerenciar bookmarks, tags e -users. No entanto, todos podem ver as tags de todos os usuários. No próximo -capítulo, vamos implementar a autenticação e restringir os bookmarks visíveis -para somente aqueles que pertencem ao usuário atual. - -Agora vá a :doc:`/tutorials-and-examples/bookmarks/part-two` para continuar a -construir sua aplicação ou :doc:`mergulhe na documentação ` para -saber mais sobre o que CakePHP pode fazer por você. diff --git a/pt/tutorials-and-examples/bookmarks/part-two.rst b/pt/tutorials-and-examples/bookmarks/part-two.rst deleted file mode 100644 index 1db885160b..0000000000 --- a/pt/tutorials-and-examples/bookmarks/part-two.rst +++ /dev/null @@ -1,411 +0,0 @@ -Tutorial - Criando um Bookmarker - Parte 2 -########################################## - -Depois de terminar a :doc:`primeira parte deste tutorial -`, você deve ter uma -aplicação muito básica. Neste capítulo iremos adicionar autenticação e -restringir as bookmarks para que cada usuário possa ver/modificar somente -aquelas tags que possuam. - -Adicionando login -================= - -No CakePHP, a autenticação é feita por :doc:`/controllers/components`. Os -Components podem ser considerados como formas de criar pedaços reutilizáveis -de código relacionado a controllers com uma característica específica ou -conceito. Os components também podem se ligar ao evento do ciclo de vida do -controller e interagir com a sua aplicação. Para começar, vamos -adicionar o AuthComponent a nossa aplicação. É essencial que -cada método exija autenticação, por isso vamos acrescentar o -:doc:`AuthComponent` em nosso -AppController:: - - // Em 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' - ] - ]); - - // Permite a ação display, assim nosso pages controller - // continua a funcionar. - $this->Auth->allow(['display']); - } - } - -Acabamos de dizer ao CakePHP que queremos carregar os components ``Flash`` e -``Auth``. Além disso, temos a configuração personalizada do AuthComponent, -assim a nossa tabela users pode usar ``email`` como username. Agora, se você for -a qualquer URL, você vai ser chutado para ``/users/login``, que irá -mostrar uma página de erro já que não escrevemos o código ainda. -Então, vamos criar a ação de login:: - - // Em 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('Your username or password is incorrect.'); - } - } - -E em **templates/Users/login.php** adicione o seguinte trecho:: - -

Login

- Form->create() ?> - Form->input('email') ?> - Form->input('password') ?> - Form->button('Login') ?> - Form->end() ?> - -Agora que temos um formulário de login simples, devemos ser capazes de efetuar -login com um dos users que tenham senha criptografada. - -.. note:: - - Se nenhum de seus users tem senha criptografada, comente a linha - ``loadComponent('Auth')``. Então vá e edite o user, salvando uma nova - senha para ele. - -Agora você deve ser capaz de entrar. Se não, certifique-se que você está -usando um user que tenha senha criptografada. - -Adicionando logout -================== - -Agora que as pessoas podem efetuar o login, você provavelmente vai querer -fornecer uma maneira de encerrar a sessão também. Mais uma vez, no -``UsersController``, adicione o seguinte código:: - - public function logout() - { - $this->Flash->success('You are now logged out.'); - - return $this->redirect($this->Auth->logout()); - } - -Agora você pode visitar ``/users/logout`` para sair e ser enviado à -página de login. - -Ativando inscrições -=================== - -Se você não estiver logado e tentar visitar / usuários / adicionar você vai -ser expulso para a página de login. Devemos corrigir isso se -quisermos que as pessoas se inscrevam em nossa aplicação. No -UsersController adicione o seguinte trecho:: - - public function beforeFilter(\Cake\Event\Event $event) - { - $this->Auth->allow(['add']); - } - -O texto acima diz ao ``AuthComponent`` que a ação ``add`` não requer -autenticação ou autorização. Você pode querer dedicar algum tempo para limpar a -``/users/add`` e remover os links enganosos, ou continuar para a próxima -seção. Nós não estaremos construindo a edição do usuário, visualização ou -listagem neste tutorial, então eles não vão funcionar, já que o -``AuthComponent`` vai negar-lhe acesso a essas ações do controller. - -Restringindo acesso -=================== - -Agora que os usuários podem se conectar, nós vamos querer limitar os -bookmarks que podem ver para aqueles que fizeram. Nós vamos fazer isso -usando um adaptador de 'autorização'. Sendo os nossos requisitos -bastante simples, podemos escrever um código em nossa -``BookmarksController``. Mas antes de fazer isso, vamos querer dizer ao -AuthComponent como nossa aplicação vai autorizar ações. Em seu ``AppController`` -adicione o seguinte:: - - public function isAuthorized($user) - { - return false; - } - -Além disso, adicione o seguinte à configuração para ``Auth`` em seu -``AppController``:: - - 'authorize' => 'Controller', - -Seu método ``initialize`` agora deve parecer com:: - - public function initialize() - { - $this->loadComponent('Flash'); - $this->loadComponent('Auth', [ - 'authorize'=> 'Controller',//adicionado essa linha - 'authenticate' => [ - 'Form' => [ - 'fields' => [ - 'username' => 'email', - 'password' => 'password' - ] - ] - ], - 'loginAction' => [ - 'controller' => 'Users', - 'action' => 'login' - ], - 'unauthorizedRedirect' => $this->referer() - ]); - - // Permite a ação display, assim nosso pages controller - // continua a funcionar. - $this->Auth->allow(['display']); - } - -Vamos usar como padrão, negação do acesso, e de forma incremental conceder -acesso onde faça sentido. Primeiro, vamos adicionar a lógica de autorização -para os bookmarks. Em seu ``BookmarksController`` adicione o seguinte:: - - public function isAuthorized($user) - { - $action = $this->request->params['action']; - - // As ações add e index são permitidas sempre. - if (in_array($action, ['index', 'add', 'tags'])) { - return true; - } - // Todas as outras ações requerem um id. - if (!$this->request->getParam('pass.0')) { - return false; - } - - // Checa se o bookmark pertence ao user atual. - $id = $this->request->getParam('pass.0'); - $bookmark = $this->Bookmarks->get($id); - if ($bookmark->user_id == $user['id']) { - return true; - } - - return parent::isAuthorized($user); - } - -Agora, se você tentar visualizar, editar ou excluir um bookmark que não -pertença a você, você deve ser redirecionado para a página de onde veio. No -entanto, não há nenhuma mensagem de erro sendo exibida, então vamos -corrigir isso a seguir:: - - // In templates/layout/default.php - // Under the existing flash message. - Flash->render('auth') ?> - -Agora você deve ver as mensagens de erro de autorização. - -Corrigindo a view de listagem e formulários -=========================================== - -Enquanto view e delete estão trabalhando, edit, add e index tem -alguns problemas: - -#. Ao adicionar um bookmark, você pode escolher o user. -#. Ao editar um bookmark, você pode escolher o user. -#. A página de listagem mostra os bookmarks de outros users. - -Primeiramente, vamos refatorar o formulário de adição. Para começar -remova o ``input('user_id')`` a partir de **templates/Bookmarks/add.php**. -Com isso removido, nós também vamos atualizar o método add:: - - 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('The bookmark has been saved.'); - - return $this->redirect(['action' => 'index']); - } - $this->Flash->error('The bookmark could not be saved. Please, try again.'); - } - $tags = $this->Bookmarks->Tags->find('list'); - $this->set(compact('bookmark', 'tags')); - } - -Ao definir a propriedade da entidade com os dados da sessão, nós removemos -qualquer possibilidade do user modificar algo que não pertenca a ele. -Nós vamos fazer o mesmo para o formulário edit e action edit. Sua -ação edit deve ficar assim:: - - 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('The bookmark has been saved.'); - - return $this->redirect(['action' => 'index']); - } - $this->Flash->error('The bookmark could not be saved. Please, try again.'); - } - $tags = $this->Bookmarks->Tags->find('list'); - $this->set(compact('bookmark', 'tags')); - } - -View de listagem ----------------- - -Agora, nós precisamos apenas exibir bookmarks para o user logado. -Nós podemos fazer isso ao atualizar a chamada para ``paginate()``. Altere sua -ação index:: - - public function index() - { - $this->paginate = [ - 'conditions' => [ - 'Bookmarks.user_id' => $this->Auth->user('id'), - ] - ]; - $this->set('bookmarks', $this->paginate($this->Bookmarks)); - } - -Nós também devemos atualizar a action ``tags()`` e o método localizador relacionado, -mas vamos deixar isso como um exercício para que você conclua por sí. - -Melhorando a experiência com as tags -==================================== - -Agora, adicionar novas tags é um processo difícil, pois o ``TagsController`` -proíbe todos os acessos. Em vez de permitir o acesso, podemos melhorar a -interface do usuário para selecionar tags usando um campo de texto separado por -vírgulas. Isso permitirá dar uma melhor experiência para os nossos -usuários, e usar mais alguns grandes recursos no ORM. - -Adicionando um campo computado ------------------------------- - -Porque nós queremos uma maneira simples de acessar as tags formatados -para uma entidade, podemos adicionar um campo virtual/computado para a -entidade. Em **src/Model/Entity/Bookmark.php** adicione o seguinte:: - - 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, ', '); - } - -Isso vai nos deixar acessar a propriedade computada ``$bookmark->tag_string``. -Vamos usar essa propriedade em inputs mais tarde. Lembre-se de adicionar a -propriedade ``tag_string`` a lista ``_accessible`` em sua entidade. - -Em **src/Model/Entity/Bookmark.php** adicione o ``tag_string`` ao -``_accessible`` desta forma:: - - protected array $_accessible = [ - 'user_id' => true, - 'title' => true, - 'description' => true, - 'url' => true, - 'user' => true, - 'tags' => true, - 'tag_string' => true, - ]; - -Atualizando as views --------------------- - -Com a entidade atualizado, podemos adicionar uma nova entrada para as nossas -tags. Nas views add e edit, substitua ``tags._ids`` pelo seguinte:: - - Form->input('tag_string', ['type' => 'text']) ?> - -Persistindo a string tag ------------------------- - -Agora que podemos ver as tags como uma string existente, vamos querer salvar -os dados também. Por marcar o ``tag_string`` como acessível, o ORM irá -copiar os dados do pedido em nossa entidade. Podemos usar um método -``beforeSave`` para analisar a cadeia tag e encontrar/construir as -entidades relacionadas. Adicione o seguinte em -**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) - { - $new = array_unique(array_map('trim', explode(',', $tagString))); - $out = []; - $query = $this->Tags->find() - ->where(['Tags.title IN' => $new]); - - // Remove tags existentes da lista de novas tags. - foreach ($query->extract('title') as $existing) { - $index = array_search($existing, $new); - if ($index !== false) { - unset($new[$index]); - } - } - // Adiciona tags existentes. - foreach ($query as $tag) { - $out[] = $tag; - } - // Adiciona novas tags. - foreach ($new as $tag) { - $out[] = $this->Tags->newEntity(['title' => $tag]); - } - - return $out; - } - -Embora esse código seja um pouco mais complicado do que o que temos feito até -agora, ele ajuda a mostrar o quão poderosa a ORM do CakePHP é. Você pode -facilmente manipular resultados da consulta usando os métodos de -:doc:`/core-libraries/collections`, e lidar com situações em que você está -criando entidades sob demanda com facilidade. - -Terminando -========== - -Nós expandimos nossa aplicação bookmarker para lidar com situações de -autenticação e controle de autorização/acesso básico. Nós também adicionamos -algumas melhorias agradáveis à UX, aproveitando os recursos FormHelper e ORM. - -Obrigado por dispor do seu tempo para explorar o CakePHP. Em seguida, você pode -saber mais sobre o :doc:`/orm`, ou você pode ler os :doc:`/topics`. diff --git a/pt/tutorials-and-examples/cms/articles-controller.rst b/pt/tutorials-and-examples/cms/articles-controller.rst index 0a13500782..872668024a 100644 --- a/pt/tutorials-and-examples/cms/articles-controller.rst +++ b/pt/tutorials-and-examples/cms/articles-controller.rst @@ -1,4 +1,565 @@ CMS Tutorial - Creating the Articles Controller ############################################### -TODO +With our model created, we need a controller for our articles. Controllers in +CakePHP handle HTTP requests and execute business logic contained in model +methods, to prepare the response. We'll place this new controller in a file +called **ArticlesController.php** inside the **src/Controller** directory. +Here's what the basic controller should look like:: + + paginate($this->Articles); + $this->set(compact('articles')); + } + } + +By defining function ``index()`` in our ``ArticlesController``, users can now +access the logic there by requesting **www.example.com/articles/index**. +Similarly, if we were to define a function called ``foobar()``, users would be +able to access that at **www.example.com/articles/foobar**. You may be tempted +to name your controllers and actions in a way that allows you to obtain specific +URLs. Resist that temptation. Instead, follow the :doc:`/intro/conventions` +creating readable, meaningful action names. You can then use +:doc:`/development/routing` to connect the URLs you want to the actions you've +created. + +Our controller action is very simple. It fetches a paginated set of articles +from the database, using the Articles Model that is automatically loaded via naming +conventions. It then uses ``set()`` to pass the articles into the Template (which +we'll create soon). CakePHP will automatically render the template after our +controller action completes. + +Create the Article List Template +================================ + +Now that we have our controller pulling data from the model, and preparing our +view context, let's create a view template for our index action. + +CakePHP view templates are presentation-flavored PHP code that is inserted inside +the application's layout. While we'll be creating HTML here, Views can also +generate JSON, CSV or even binary files like PDFs. + +A layout is presentation code that is wrapped around a view. Layout files +contain common site elements like headers, footers and navigation elements. Your +application can have multiple layouts, and you can switch between them, but for +now, let's just use the default layout. + +CakePHP's template files are stored in **templates** inside a folder +named after the controller they correspond to. So we'll have to create +a folder named 'Articles' in this case. Add the following code to your +application: + +.. code-block:: php + + + +

Articles

+ + + + + + + + + + + + + + +
TitleCreated
+ Html->link($article->title, ['action' => 'view', $article->slug]) ?> + + created->format(DATE_RFC850) ?> +
+ +In the last section we assigned the 'articles' variable to the view using +``set()``. Variables passed into the view are available in the view templates as +local variables which we used in the above code. + +You might have noticed the use of an object called ``$this->Html``. This is an +instance of the CakePHP :doc:`HtmlHelper `. CakePHP comes +with a set of view helpers that make tasks like creating links, forms, and +pagination buttons. You can learn more about :doc:`/views/helpers` in their +chapter, but what's important to note here is that the ``link()`` method will +generate an HTML link with the given link text (the first parameter) and URL +(the second parameter). + +When specifying URLs in CakePHP, it is recommended that you use arrays or +:ref:`named routes `. These syntaxes allow you to +leverage the reverse routing features CakePHP offers. + +At this point, you should be able to point your browser to +**http://localhost:8765/articles/index**. You should see your list view, +correctly formatted with the title and table listing of the articles. + +Create the View Action +====================== + +If you were to click one of the 'view' links in our Articles list page, you'd +see an error page saying that action hasn't been implemented. Lets fix that now:: + + // Add to existing src/Controller/ArticlesController.php file + + public function view($slug = null) + { + $article = $this->Articles->findBySlug($slug)->firstOrFail(); + $this->set(compact('article')); + } + +While this is a simple action, we've used some powerful CakePHP features. We +start our action off by using ``findBySlug()`` which is +a :ref:`Dynamic Finder `. This method allows us to create a basic query that +finds articles by a given slug. We then use ``firstOrFail()`` to either fetch +the first record, or throw a ``\Cake\Datasource\Exception\RecordNotFoundException``. + +Our action takes a ``$slug`` parameter, but where does that parameter come from? +If a user requests ``/articles/view/first-post``, then the value 'first-post' is +passed as ``$slug`` by CakePHP's routing and dispatching layers. If we +reload our browser with our new action saved, we'd see another CakePHP error +page telling us we're missing a view template; let's fix that. + +Create the View Template +======================== + +Let's create the view for our new 'view' action and place it in +**templates/Articles/view.php** + +.. code-block:: php + + + +

title) ?>

+

body) ?>

+

Created: created->format(DATE_RFC850) ?>

+

Html->link('Edit', ['action' => 'edit', $article->slug]) ?>

+ +You can verify that this is working by trying the links at ``/articles/index`` or +manually requesting an article by accessing URLs like +``/articles/view/first-post``. + +Adding Articles +=============== + +With the basic read views created, we need to make it possible for new articles +to be created. Start by creating an ``add()`` action in the +``ArticlesController``. Our controller should now look like:: + + paginate($this->Articles); + $this->set(compact('articles')); + } + + public function view($slug) + { + $article = $this->Articles->findBySlug($slug)->firstOrFail(); + $this->set(compact('article')); + } + + public function add() + { + $article = $this->Articles->newEmptyEntity(); + if ($this->request->is('post')) { + $article = $this->Articles->patchEntity($article, $this->request->getData()); + + // Hardcoding the user_id is temporary, and will be removed later + // when we build authentication out. + $article->user_id = 1; + + 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); + } + } + +.. note:: + + You need to include the :doc:`/controllers/components/flash` component in + any controller where you will use it. Often it makes sense to include it in + your ``AppController``, which is there already for this tutorial. + +Here's what the ``add()`` action does: + +* If the HTTP method of the request was POST, try to save the data using the Articles model. +* If for some reason it doesn't save, just render the view. This gives us a + chance to show the user validation errors or other warnings. + +Every CakePHP request includes a request object which is accessible using +``$this->request``. The request object contains information regarding the +request that was just received. We use the +:php:meth:`Cake\\Http\\ServerRequest::is()` method to check that the request +is a HTTP POST request. + +Our POST data is available in ``$this->request->getData()``. You can use the +:php:func:`pr()` or :php:func:`debug()` functions to print it out if you want to +see what it looks like. To save our data, we first 'marshal' the POST data into +an Article Entity. The Entity is then persisted using the ArticlesTable we +created earlier. + +After saving our new article we use FlashComponent's ``success()`` method to set +a message into the session. The ``success`` method is provided using PHP's +`magic method features +`_. Flash +messages will be displayed on the next page after redirecting. In our layout we have +``Flash->render() ?>`` which displays flash messages and clears the +corresponding session variable. Finally, after saving is complete, we use +:php:meth:`Cake\\Controller\\Controller::redirect` to send the user back to the +articles list. The param ``['action' => 'index']`` translates to URL +``/articles`` i.e the index action of the ``ArticlesController``. You can refer +to :php:func:`Cake\\Routing\\Router::url()` function on the `API +`_ to see the formats in which you can specify a URL +for various CakePHP functions. + +Create Add Template +=================== + +Here's our add view template: + +.. code-block:: php + + + +

Add Article

+ Form->create($article); + // Hard code the user for now. + echo $this->Form->control('user_id', ['type' => 'hidden', 'value' => 1]); + echo $this->Form->control('title'); + echo $this->Form->control('body', ['rows' => '3']); + echo $this->Form->button(__('Save Article')); + echo $this->Form->end(); + ?> + +We use the FormHelper to generate the opening tag for an HTML +form. Here's the HTML that ``$this->Form->create()`` generates: + +.. code-block:: html + + + +Because we called ``create()`` without a URL option, ``FormHelper`` assumes we +want the form to submit back to the current action. + +The ``$this->Form->control()`` method is used to create form elements +of the same name. The first parameter tells CakePHP which field +they correspond to, and the second parameter allows you to specify +a wide array of options - in this case, the number of rows for the +textarea. There's a bit of introspection and conventions used here. The +``control()`` will output different form elements based on the model +field specified, and use inflection to generate the label text. You can +customize the label, the input or any other aspect of the form controls using +options. The ``$this->Form->end()`` call closes the form. + +Now let's go back and update our **templates/Articles/index.php** +view to include a new "Add Article" link. Before the ````, add +the following line:: + + Html->link('Add Article', ['action' => 'add']) ?> + +Adding Simple Slug Generation +============================= + +If we were to save an Article right now, saving would fail as we are not +creating a slug attribute, and the column is ``NOT NULL``. Slug values are +typically a URL-safe version of an article's title. We can use the +:ref:`beforeSave() callback ` of the ORM to populate our slug:: + + isNew() && !$entity->slug) { + $sluggedTitle = Text::slug($entity->title); + // trim slug to maximum length defined in schema + $entity->slug = substr($sluggedTitle, 0, 191); + } + } + +This code is simple, and doesn't take into account duplicate slugs. But we'll +fix that later on. + +Add Edit Action +=============== + +Our application can now save articles, but we can't edit them. Lets rectify that +now. Add the following action to your ``ArticlesController``:: + + // in src/Controller/ArticlesController.php + + // Add the following method. + + public function edit($slug) + { + $article = $this->Articles + ->findBySlug($slug) + ->firstOrFail(); + + if ($this->request->is(['post', 'put'])) { + $this->Articles->patchEntity($article, $this->request->getData()); + if ($this->Articles->save($article)) { + $this->Flash->success(__('Your article has been updated.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('Unable to update your article.')); + } + + $this->set('article', $article); + } + +This action first ensures that the user has tried to access an existing record. +If they haven't passed in an ``$slug`` parameter, or the article does not exist, +a ``RecordNotFoundException`` will be thrown, and the CakePHP ErrorHandler will render +the appropriate error page. + +Next the action checks whether the request is either a POST or a PUT request. If +it is, then we use the POST/PUT data to update our article entity by using the +``patchEntity()`` method. Finally, we call ``save()``, set the appropriate flash +message, and either redirect or display validation errors. + +Create Edit Template +==================== + +The edit template should look like this: + +.. code-block:: php + + + +

Edit Article

+ Form->create($article); + echo $this->Form->control('user_id', ['type' => 'hidden']); + echo $this->Form->control('title'); + echo $this->Form->control('body', ['rows' => '3']); + echo $this->Form->button(__('Save Article')); + echo $this->Form->end(); + ?> + +This template outputs the edit form (with the values populated), along +with any necessary validation error messages. + +You can now update your index view with links to edit specific +articles: + +.. code-block:: php + + + +

Articles

+

Html->link("Add Article", ['action' => 'add']) ?>

+
+ + + + + + + + + + + + + + + + +
TitleCreatedAction
+ Html->link($article->title, ['action' => 'view', $article->slug]) ?> + + created->format(DATE_RFC850) ?> + + Html->link('Edit', ['action' => 'edit', $article->slug]) ?> +
+ +Update Validation Rules for Articles +==================================== + +Up until this point our Articles had no input validation done. Lets fix that by +using :ref:`a validator `:: + + // src/Model/Table/ArticlesTable.php + + // add this use statement right below the namespace declaration to import + // the Validator class + use Cake\Validation\Validator; + + // Add the following method. + public function validationDefault(Validator $validator): Validator + { + $validator + ->notEmptyString('title') + ->minLength('title', 10) + ->maxLength('title', 255) + + ->notEmptyString('body') + ->minLength('body', 10); + + return $validator; + } + +The ``validationDefault()`` method tells CakePHP how to validate your data when +the ``save()`` method is called. Here, we've specified that both the title, and +body fields must not be empty, and have certain length constraints. + +CakePHP's validation engine is powerful and flexible. It provides a suite of +frequently used rules for tasks like email addresses, IP addresses etc. and the +flexibility for adding your own validation rules. For more information on that +setup, check the :doc:`/core-libraries/validation` documentation. + +Now that your validation rules are in place, use the app to try to add +an article with an empty title or body to see how it works. Since we've used the +:php:meth:`Cake\\View\\Helper\\FormHelper::control()` method of the FormHelper to +create our form elements, our validation error messages will be shown +automatically. + +Add Delete Action +================= + +Next, let's make a way for users to delete articles. Start with a +``delete()`` action in the ``ArticlesController``:: + + // src/Controller/ArticlesController.php + + // Add the following method. + + public function delete($slug) + { + $this->request->allowMethod(['post', 'delete']); + + $article = $this->Articles->findBySlug($slug)->firstOrFail(); + if ($this->Articles->delete($article)) { + $this->Flash->success(__('The {0} article has been deleted.', $article->title)); + + return $this->redirect(['action' => 'index']); + } + } + +This logic deletes the article specified by ``$slug``, and uses +``$this->Flash->success()`` to show the user a confirmation +message after redirecting them to ``/articles``. If the user attempts to +delete an article using a GET request, ``allowMethod()`` will throw an exception. +Uncaught exceptions are captured by CakePHP's exception handler, and a nice +error page is displayed. There are many built-in +:doc:`Exceptions ` that can be used to indicate the various +HTTP errors your application might need to generate. + +.. warning:: + + Allowing content to be deleted using GET requests is *very* dangerous, as web + crawlers could accidentally delete all your content. That is why we used + ``allowMethod()`` in our controller. + +Because we're only executing logic and redirecting to another action, this +action has no template. You might want to update your index template with links +that allow users to delete articles: + +.. code-block:: php + + + +

Articles

+

Html->link("Add Article", ['action' => 'add']) ?>

+ + + + + + + + + + + + + + + + + +
TitleCreatedAction
+ Html->link($article->title, ['action' => 'view', $article->slug]) ?> + + created->format(DATE_RFC850) ?> + + Html->link('Edit', ['action' => 'edit', $article->slug]) ?> + Form->deleteLink( + 'Delete', + ['action' => 'delete', $article->slug], + ['confirm' => 'Are you sure?']) + ?> +
+ +Using :php:meth:`~Cake\\View\\Helper\\FormHelper::deleteLink()` will create a link +that uses JavaScript to do a DELETE request deleting our article. +Prior to CakePHP 5.2 you need to use ``postLink()`` instead. + +.. note:: + + This view code also uses the ``FormHelper`` to prompt the user with a + JavaScript confirmation dialog before they attempt to delete an + article. + +.. tip:: + + The ``ArticlesController`` can also be built with ``bake``: + + .. code-block:: console + + /bin/cake bake controller articles + + However, this does not build the **templates/Articles/*.php** files. + +With a basic articles management setup, we'll create the :doc:`basic actions +for our Tags and Users tables `. diff --git a/pt/tutorials-and-examples/cms/database.rst b/pt/tutorials-and-examples/cms/database.rst index 925cb5b54b..5ba82fe955 100644 --- a/pt/tutorials-and-examples/cms/database.rst +++ b/pt/tutorials-and-examples/cms/database.rst @@ -10,6 +10,8 @@ as tabelas necessárias: .. code-block:: SQL + CREATE DATABASE cake_cms; + USE cake_cms; CREATE TABLE users ( @@ -134,22 +136,16 @@ pelos que se aplicam a sua instalação. Um exemplo completo de como deve ficar configuração segue abaixo:: [ 'default' => [ - 'className' => 'Cake\Database\Connection', - // Substitua Mysql por Postgres se você estiver usando PostgreSQL - 'driver' => 'Cake\Database\Driver\Mysql', - 'persistent' => false, 'host' => 'localhost', 'username' => 'cakephp', - 'password' => 'sua_senha', + 'password' => 'AngelF00dC4k3~', 'database' => 'cake_cms', - // Comente a linha abaixo se estiver usando PostgreSQL - 'encoding' => 'utf8mb4', - 'timezone' => 'UTC', - 'cacheMetadata' => true, + 'url' => env('DATABASE_URL', null), ], ], // Mais configurações abaixo. @@ -162,81 +158,61 @@ com o chapéu de chefe na cor verde. .. note:: - Se você não tiver o arquivo **config/app_local.php** na sua aplicação, - você deve configurar sua conexão no arquivo **config/app.php**. - + O arquivo **config/app_local.php** é uma substituição local do arquivo **config/app.php** + usado para configurar seu ambiente de desenvolvimento rapidamente. -Criando nosso Primeiro Modelo -============================= +Migrations +========== -Modelos são o coração de uma aplicação CakePHP. Ele permite a nós ler e -escrever nossos dados. Eles possibilitam a criação de relacionamentos -entre nossos dados, validar dados, e aplicar regras da aplicação. Modelos -formam a fundação necessária para construir nossas ações de controles e -templates. +As instruções SQL para criar as tabelas deste tutorial também podem ser geradas +usando o plugin Migrations. As migrações oferecem uma maneira independente de plataforma de +executar consultas, de modo que as diferenças sutis entre MySQL, PostgreSQL, SQLite, etc. +não se tornem obstáculos. -Modelos no CakePHP são compostos dos objetos ``Table`` (Tabela) e ``Entity`` -(Entidade). Objetos ``Table`` fornecem acesso a coleção de entidades armazenadas -em uma tabela específica. Elas ficam salvas em **src/Model/Table**. O arquivo -que iremos criar ficará salvo em **src/Model/Table/ArticlesTable.php**. O arquivo -completo deve se parecer com isso:: +.. code-block:: console - addBehavior('Timestamp'); - } - } - -Nós vinculamos o behavior :doc:`/orm/behaviors/timestamp` que irá preencher -automaticamente as colunas ``created`` (criado) e ``modified`` (modificado) -de nossa tabela. -Ao nomear nosso objeto Table ``ArticlesTable``, o CakePHP se baseia nas -convenções de nomes para saber que nosso modelo utiliza a tabela ``articles``. -O CakePHP também usa as convenções para saber que a coluna ``id`` é a chave -primária da tabela. + bin/cake bake migration CreateUsers email:string password:string created modified + bin/cake bake migration CreateArticles user_id:integer title:string slug:string[191]:unique body:text published:boolean created modified + bin/cake bake migration CreateTags title:string[191]:unique created modified + bin/cake bake migration CreateArticlesTags article_id:integer:primary tag_id:integer:primary created modified .. note:: + Alguns ajustes no código gerado podem ser necessários. Por exemplo, a + chave primária composta em ``articles_tags`` será definida para incrementar automaticamente + ambas as colunas:: - O CakePHP criará dinamicamente um objeto de modelo para você se ele - não conseguir encontrar o arquivo correspondente em **src/Model/Table**. - Isso significa que se você acidentalmente nomear errado o arquivo (ex. - articlestable.php ou ArticleTable.php), o CakePHP não reconhecerá nenhuma - de suas configurações e utilizará o modelo dinâmicamente gerado no lugar. + $table->addColumn('article_id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); + $table->addColumn('tag_id', 'integer', [ + 'autoIncrement' => true, + 'default' => null, + 'limit' => 11, + 'null' => false, + ]); -Nós também vamos criar uma classe Entity para nossa Articles. Entidades -representam um único registro do nosso banco de dados, e implementam -comportamento a nível de linha para nossos dados. Nossa entidade será -salva em **src/Model/Entity/Article.php**. O arquivo complete deve parecer -com este:: + Remova essas linhas para evitar problemas com chaves estrangeiras. Assim que os ajustes estiverem + concluídos:: - true, - 'id' => false, - 'slug' => false, - ]; - } - -Nossa entidade está bem curta agora, e nós iremos configurar apenas a -propriedade ``_accessible`` que controla quais propriedades podem ser -modificadas com :ref:`entities-mass-assignment`. - -Nós não podemos fazer muito com nossos modelos agora, então a seguir -iremos criar nossos :doc:`Controller e Template -` que nos permitirá -interagir com nosso modelo. + bin/cake migrations migrate + +Da mesma forma, os registros de dados iniciais podem ser feitos com seeds. + +.. code-block:: console + + bin/cake bake seed Users + bin/cake bake seed Articles + +Preencha os dados iniciais acima nas novas classes ``UsersSeed`` e ``ArticlesSeed`` +e então:: + + bin/cake migrations seed + +Saiba mais sobre migrações de construção e semeadura de dados: `Migrations +`__ + +Com o banco de dados construído, agora podemos construir :doc:`Models +`. diff --git a/pt/tutorials-and-examples/cms/installation.rst b/pt/tutorials-and-examples/cms/installation.rst index a9e63cf316..35de1b3b23 100644 --- a/pt/tutorials-and-examples/cms/installation.rst +++ b/pt/tutorials-and-examples/cms/installation.rst @@ -33,7 +33,7 @@ A maneira mais fácil de instalar o CakePHP é usando Composer, um gerenciador de dependências para o PHP. Se trata de uma forma simples de instalar o CakePHP a partir de seu terminal ou prompt de comando. Primeiro, você precisa baixar e instalar o Composer, caso você já não o tenha. Se possuir -instalado o programa *cURL*, basta executar o seguinte comando:: +instalado o programa *cURL*, basta executar o seguinte comando: .. code-block:: console @@ -44,11 +44,11 @@ Você também pode baixar o arquivo ``composer.phar`` do Em seguida, basta digitar a seguinte linha de comando no seu terminal a partir do diretório onde se localiza o arquivo ``composer.phar`` para instalar o -esqueleto da aplicação do CakePHP no diretório **cms**. :: +esqueleto da aplicação do CakePHP no diretório **cms**. : .. code-block:: console - php composer.phar create-project --prefer-dist cakephp/app:5.* cms + php composer.phar create-project --prefer-dist cakephp/app:5 cms Caso você tenha feito o download e executado o `Instalador para Windows do Composer `_, então digite a linha @@ -57,7 +57,7 @@ C:\\wamp\\www\\dev): .. code-block:: console - composer self-update && composer create-project --prefer-dist cakephp/app:4.* cms + composer self-update && composer create-project --prefer-dist cakephp/app:5.* cms A vantagem de usar o Composer é que ele irá completar automaticamente um conjunto importante de tarefas, como configurar corretamente as permissões de pastas @@ -96,6 +96,13 @@ do CakePHP funciona: Confira a seção :doc:`/intro/cakephp-folder-structure`. Caso tenha dificuldades durante este tutorial, você pode ver o resultado final no `GitHub `_. +.. tip:: + + O utilitário de console ``bin/cake`` pode construir a maioria das classes e tabelas de dados + deste tutorial automaticamente. No entanto, recomendamos acompanhar + os exemplos de código manual para entender como as peças se encaixam e + como adicionar a lógica da sua aplicação. + Verificando sua Instalação ========================== @@ -119,4 +126,4 @@ tópicos devem ter chapéus de chef verdes, exceto diz sobre o CakePHP estar apt seu banco de dados. Caso contrário, voc%e pode precisar instalar alguma extensão PHP ou definir permissão de diretórios. -A seguir, nós iremos construir o :doc:`Banco de Dados e criar nosso primeiro modelo `. +A seguir, nós iremos construir o :doc:`Banco de Dados `. From 26405c0481a99ec41db06d8dac8f8a28e755048c Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 00:57:07 -0300 Subject: [PATCH 02/46] [refact]: removed osbolete file --- pt/console-and-shells.rst | 953 -------------------------------------- 1 file changed, 953 deletions(-) delete mode 100644 pt/console-and-shells.rst diff --git a/pt/console-and-shells.rst b/pt/console-and-shells.rst deleted file mode 100644 index f25b6e8281..0000000000 --- a/pt/console-and-shells.rst +++ /dev/null @@ -1,953 +0,0 @@ -Console e Shells -################ - -.. php:namespace:: Cake\Console - -O CakePHP não oferece um framework apenas para desenvolvimento web, -mas também um framework para criação de aplicações de console. Estas -aplicações são ideais para manipular variadas tarefas em segundo plano como -manutenção e complementação de trabalho fora do ciclo requisição-resposta. -As aplicações de console do CakePHP permitem a vocë reutilizar suas classes -de aplicação a partir da linha de comando. - -O CakePHP traz consigo algumas aplicações de console nativas. Algumas dessas -aplicações são utilizadas em conjunto com outros recursos do CakePHP -(como i18n), e outros de uso geral para aceleração de trabalho. - -O Console do CakePHP -==================== - -Esta seção provê uma introdução à linha de comando do CakePHP. -Ferramentas de console são ideais para uso em cron jobs, ou utilitários -baseados em linha de comando que não precisam ser acessíveis por um navegador -web. - -O PHP provê um cliente CLI que faz interface com o seu -sistema de arquivos e aplicações de forma muito mais suave. O console do CakePHP -provê um framework para criar scripts shell. O console utiliza uma configuração -tipo dispatcher para carregar uma shell ou tarefa, e prover seus parâmetros. - -.. note:: - - Uma linha de comando (CLI) constutuída a partir do PHP deve estar - disponível no sistema se você planeja utilizr o Console. - -Antes de entrar em detalhes, vamos ter certeza de que você pode executar o -console do CakePHP. Primeiro, você vai precisar executar um sistema shell. Os -exemplos apresentados nesta seção serão em bash, mas o Console do CakePHP é -compatível com o Windows também. Este exemplo assume que o usuário está -conectado em um prompt do bash e está atualmente na raiz de uma aplicação -CakePHP. - -Aplicações CakePHP possuem um diretório `Console``` que contém todas as -shells e tarefas para uma aplicação. Ele também vem com um executável:: - - $ cd /path/to/app - $ bin/cake - -Executar o Console sem argumentos produz esta mensagem de ajuda:: - - Welcome to CakePHP v3.0.0 Console - --------------------------------------------------------------- - App : App - Path: /Users/markstory/Sites/cakephp-app/src/ - --------------------------------------------------------------- - Current Paths: - - -app: src - -root: /Users/markstory/Sites/cakephp-app - -core: /Users/markstory/Sites/cakephp-app/vendor/cakephp/cakephp - - Changing Paths: - - Your working path should be the same as your application path. To change your path use the '-app' param. - Example: -app relative/path/to/myapp or -app /absolute/path/to/myapp - - Available Shells: - - [Bake] bake - - [Migrations] migrations - - [CORE] i18n, orm_cache, plugin, server - - [app] behavior_time, console, orm - - To run an app or core command, type cake shell_name [args] - To run a plugin command, type cake Plugin.shell_name [args] - To get help on a specific command, type cake shell_name --help - -A primeira informação impressa refere-se a caminhos. Isso é útil se você estiver -executando o console a partir de diferentes partes do sistema de arquivos. - -Criando uma Shell -================= - -Vamos criar uma shell para utilizar no Console. Para este exemplo, -criaremos uma simples Hello World (Olá Mundo) shell. No diretório -**src/Shell** de sua aplicação crie **HelloShell.php**. Coloque o seguinte -código dentro do arquivo recem criado:: - - namespace App\Shell; - - use Cake\Console\Shell; - - class HelloShell extends Shell - { - public function main() - { - $this->out('Hello world.'); - } - } - -As convenções para as classes de shell são de que o nome da classe deve -corresponder ao nome do arquivo, com o sufixo de Shell. No nosso shell criamos -um método ``main()``. Este método é chamado quando um shell é chamado sem -comandos adicionais. Vamos adicionar alguns comandos daqui a pouco, mas por -agora vamos executar a nossa shell. A partir do diretório da aplicação, -execute:: - - bin/cake hello - -Você deve ver a seguinte saída:: - - Welcome to CakePHP Console - --------------------------------------------------------------- - App : app - Path: /Users/markstory/Sites/cake_dev/src/ - --------------------------------------------------------------- - Hello world. - -Como mencionado antes, o método ``main()`` em shells é um método especial -chamado sempre que não há outros comandos ou argumentos dados para uma shell. -Por nosso método principal não ser muito interessante, vamos adicionar outro -comando que faz algo:: - - namespace App\Shell; - - use Cake\Console\Shell; - - class HelloShell extends Shell - { - public function main() - { - $this->out('Hello world.'); - } - - public function heyThere($name = 'Anonymous') - { - $this->out('Hey there ' . $name); - } - } - -Depois de salvar o arquivo, você deve ser capaz de executar o seguinte comando e -ver o seu nome impresso:: - - bin/cake hello hey_there your-name - -Qualquer método público não prefixado por um ``_`` é permitido para ser chamado -a partir da linha de comando. Como você pode ver, os métodos invocados a partir -da linha de comando são transformados do argumento prefixado para a forma -correta do nome camel-cased (camelizada) -na classe. - -No nosso método ``heyThere()`` podemos ver que os argumentos posicionais são -providos para nossa função ``heyThere()``. Argumentos posicionais também estão -disponívels na propriedade ``args``. Você pode acessar switches ou opções em -aplicações shell, estando disponíveis em ``$this->params``, mas nós iremos -cobrir isso daqui a pouco. - -Quando utilizando um método ``main()`` você não estará liberado para utilizar -argumentos posicionais. Isso se deve ao primeiro argumento posicional ou opção -ser interpretado(a) como o nome do comando. Se você quer utilizar argumentos, -você deve usar métodos diferentes de ``main()``. - -Usando Models em suas shells ----------------------------- - -Você frequentemente precisará acessar a camada lógica de negócios em seus -utilitários shell; O CakePHP faz essa tarefa super fácil. Você pode carregar models em -shells assim como faz em um controller utilizando ``loadModel()``. Os models carregados -são definidos como propriedades anexas à sua shell:: - - namespace App\Shell; - - use Cake\Console\Shell; - - class UserShell extends Shell - { - - public function initialize() - { - parent::initialize(); - $this->loadModel('Users'); - } - - public function show() - { - if (empty($this->args[0])) { - return $this->error('Por favor, indique um nome de usuário.'); - } - $user = $this->Users->findByUsername($this->args[0])->first(); - $this->out(print_r($user, true)); - } - } - -A shell acima, irá preencher um user pelo seu username e exibir a informação -armazenada no banco de dados. - -Tasks de Shell -============== - -Haverão momentos construindo aplicações mais avançadas de console que você vai -querer compor funcionalidades em classes reutilizáveis que podem ser -compartilhadas através de muitas shells. Tasks permitem que você extraia -comandos em classes. Por exemplo, o ``bake`` é feito quase que completamente de -tasks. Você define tasks para uma shell usando a propriedade ``$tasks``:: - - class UserShell extends Shell - { - public $tasks = ['Template']; - } - -Você pode utilizar tasks de plugins utilizando o padrão :term:`sintaxe plugin`. -Tasks são armazenadas sob ``Shell/Task/`` em arquivos nomeados depois de suas -classes. Então se nós estivéssemos criando uma nova task 'FileGenerator', você -deveria criar **src/Shell/Task/FileGeneratorTask.php**. - -Cada task deve ao menos implementar um método ``main()``. O ShellDispatcher, -vai chamar esse método quando a task é invocada. Uma classe task se parece com:: - - namespace App\Shell\Task; - - use Cake\Console\Shell; - - class FileGeneratorTask extends Shell - { - public function main() - { - } - } - -Uma shell também pode prover acesso a suas tasks como propriedades, que fazem -tasks serem ótimas para criar punhados de funcionalidade reutilizáveis similares -a :doc:`/controllers/components`:: - - // Localizado em src/Shell/SeaShell.php - class SeaShell extends Shell - { - // Localizado em src/Shell/Task/SoundTask.php - public $tasks = ['Sound']; - - public function main() - { - $this->Sound->main(); - } - } - -Você também pode acessar tasks diretamente da linha de comando:: - - $ cake sea sound - -.. note:: - - Para acessar tasks diretamente através da linha de comando, a task - **deve** ser incluída na propriedade da classe shell ``$tasks``. - Portanto, esteja ciente que um método chamado "sound" na classe SeaShell - deve sobrescrever a habilidade de acessar a funcionalidade na task - Sound, especificada no array $tasks. - -Carregando Tasks em tempo-real com TaskRegistry ------------------------------------------------ - -Você pode carregar arquivos em tempo-real utilizando o Task registry object. -Você pode carregar tasks que não foram declaradas no $tasks dessa forma:: - - $project = $this->Tasks->load('Project'); - -Carregará e retornará uma instância ProjectTask. Você pode carregar tasks de -plugins usando:: - - $progressBar = $this->Tasks->load('ProgressBar.ProgressBar'); - -.. _invoking-other-shells-from-your-shell: - -Invocando outras Shells a partir da sua Shell -============================================= - -.. php:method:: dispatchShell($args) - -Existem ainda muitos casos onde você vai querer invocar uma shell a partir de -outra. ``Shell::dispatchShell()`` lhe dá a habilidade de chamar outras shells -ao providenciar o ``argv`` para a sub shell. Você pode providenciar argumentos -e opções tanto como variáveis ou como strings:: - - // Como uma string - $this->dispatchShell('schema create Blog --plugin Blog'); - - // Como um array - $this->dispatchShell('schema', 'create', 'Blog', '--plugin', 'Blog'); - -O conteúdo acima mostra como você pode chamar a shell schema para criar o schema -de um plugin de dentro da shell do próprio. - -Recenendo Input de usuários -=========================== - -.. php:method:: in($question, $choices = null, $defaut = null) - -Quando construir aplicações interativas pelo console você irá precisar receber -inputs dos usuários. CakePHP oferece uma forma fácil de fazer isso:: - - // Receber qualquer texto dos usuários. - $color = $this->in('What color do you like?'); - - // Receber uma escolha dos usuários. - $selection = $this->in('Red or Green?', ['R', 'G'], 'R'); - -A validação de seleção é insensitiva a maiúsculas / minúsculas. - -Criando Arquivos -================ - -.. php:method:: createFile($path, $contents) - -Muitas aplicações Shell auxiliam tarefas de desenvolvimento e implementação. -Criar arquivos é frequentemente importante nestes casos de uso. O CakePHP -oferece uma forma fácil de criar um arquivo em um determinado diretório:: - - $this->createFile('bower.json', $stuff); - -Se a Shell for interativa, um alerta vai ser gerado, e o usuário questionado se -ele quer sobreescrever o arquivo caso já exista. Se a propriedade de interação -da shell for ``false``, nenhuma questão será disparada e o arquivo será -simplesmente sobreescrito. - -Saída de dados do Console -========================= - -.. php:method:out($message, $newlines, $level) -.. php:method:err($message, $newlines) - -A classe ``Shell`` oferece alguns métodos para direcionar conteúdo:: - - // Escreve para stdout - $this->out('Normal message'); - - // Escreve para stderr - $this->err('Error message'); - - // Escreve para stderr e para o processo - $this->error('Fatal error'); - -A Shell também inclui métodos para limpar a saída de dados, criando linhas -em branco, ou desenhando uma linha de traços:: - - // Exibe 2 linhas novas - $this->out($this->nl(2)); - - // Limpa a tela do usuário - $this->clear(); - - // Desenha uma linha horizontal - $this->hr(); - -Por último, você pode atualizar a linha atual de texto na tela usando -``_io->overwrite()``:: - - $this->out('Counting down'); - $this->out('10', 0); - for ($i = 9; $i > 0; $i--) { - sleep(1); - $this->_io->overwrite($i, 0, 2); - } - -É importante lembrar, que você não pode sobreescrever texto -uma vez que uma nova linha tenha sido exibida. - -.. _shell-output-level: - -Console Output Levels ---------------------- - -Shells frequentemente precisam de diferentes níveis de verbosidade. Quando -executadas como cron jobs, muitas saídas são desnecessárias. E há ocasiões que -você não estará interessado em tudo que uma shell tenha a dizer. Você pode usar -os níveis de saída para sinalizar saídas apropriadamente. O usuário da shell, -pode então decidir qual nível de detalhe ele está interessado ao sinalizar o -chamado da shell. :php:meth:`Cake\\Console\\Shell::out()` suporta 3 tipos de -saída por padrão. - -* QUIET - Apenas informação absolutamente importante deve ser sinalizada. -* NORMAL - O nível padrão, e uso normal. -* VERBOSE - Sinalize mensagens que podem ser irritantes em demasia para uso - diário, mas informativas para depuração como VERBOSE. - -Você pode sinalizar a saíde da seguinte forma:: - - // Deve aparecer em todos os níveis. - $this->out('Quiet message', 1, Shell::QUIET); - $this->quiet('Quiet message'); - - // Não deve aparecer quando a saída quiet estiver alternado. - $this->out('normal message', 1, Shell::NORMAL); - $this->out('loud message', 1, Shell::VERBOSE); - $this->verbose('Verbose output'); - - // Deve aparecer somente quando a saíde verbose estiver habilitada. - $this->out('extra message', 1, Shell::VERBOSE); - $this->verbose('Verbose output'); - -Você pode controlar o nível de saída das shells, ao usar as opções ``--quiet`` -e ``--verbose``. Estas opções são adicionadas por padrão, e permitem a você -controlar consistentemente níveis de saída dentro das suas shells do CakePHP. - -Estilizando a saída de dados ----------------------------- - -Estilizar a saída de dados é feito ao incluir tags - como no HTML - em sua -saída. O ConsoleOutput irá substituir estas tags com a seqüência correta de -código ansi. Hão diversos estilos nativos, e você pode criar mais. Os nativos -são: - -* ``error`` Mensagens de erro. Texto sublinhado vermelho. -* ``warning`` Mensagens de alerta. Texto amarelo. -* ``info`` Mensagens informativas. Texto ciano. -* ``comment`` Texto adicional. Texto azul. -* ``question`` Texto que é uma questão, adicionado automaticamente pela shell. - -Você pode criar estilos adicionais usando ``$this->stdout->styles()``. Para -declarar um novo estilo de saíde você pode fazer:: - - $this->_io->styles('flashy', ['text' => 'magenta', 'blink' => true]); - -Isso deve então permití-lo usar uma ```` tag na saída de sua shell, e se -as cores ansi estiverem habilitadas, o seguinte pode ser renderizado como texto -magenta piscante ``$this->out('Whoooa Something went wrong');``. -Quando definir estilos você pode usar as seguintes cores para os atributos -``text`` e ``background``: - -* black -* red -* green -* yellow -* blue -* magenta -* cyan -* white - -Você também pode usar as seguintes opções através de valores boleanos, -defini-los com valor positivo os habilita. - -* bold -* underline -* blink -* reverse - -Adicionar um estilo o torna disponível para todas as instâncias do -ConsoleOutput, então você não tem que redeclarar estilos para os objetos stdout -e stderr respectivamente. - -Desabilitando a colorização ---------------------------- - -Mesmo que a colorização seja incrível, haverão ocasiões que você quererá -desabilitá-la, ou forçá-la:: - - $this->_io->outputAs(ConsoleOutput::RAW); - -O citado irá colocar o objeto de saída em modo raw. Em modo raw, -nenhum estilo é aplicado. Existem três modos que você pode usar. - -* ``ConsoleOutput::RAW`` - Saída raw, nenhum estilo ou formatação serão - aplicados. Este é um modo indicado se você estiver exibindo XML ou, quiser - depurar porquê seu estilo não está funcionando. -* ``ConsoleOutput::PLAIN`` - Saída de texto simples, tags conhecidas de estilo - serão removidas da saída. -* ``ConsoleOutput::COLOR`` - Saída onde a cor é removida. - -Por padrão em sistemas \*nix objetos ConsoleOutput padronizam-se a a saída de -cores. Em sistemas Windows, a saída simples é padrão a não ser que a variável de -ambiente ``ANSICON`` esteja presente. - -Opções de configuração e Geração de ajuda -========================================= - -.. php:class:: ConsoleOptionParser - -``ConsoleOptionParser`` oferece uma opção de CLI e analisador de argumentos. - -OptionParsers permitem a você completar dois objetivos ao mesmo tempo. Primeiro, -eles permitem definir opções e argumentos para os seus comandos. Isso permite -separar validação básica de dados e seus comandos do console. Segundo, -permite prover documentação, que é usada para gerar arquivos de ajuda bem -formatados. - -O console framework no CakePHP recebe as opções do seu interpetador shell ao -chamar ``$this->getOptionParser()``. Sobreescrever esse método permite -configurar o OptionParser para definir as entradas aguardadas da sua shell. -Você também pode configurar interpetadores de subcomandos, que permitem ter -diferentes interpretadores para subcomandos e tarefas. -O ConsoleOptionParser implementa uma interface fluida e inclui métodos para -facilmente definir múltiplas opções/argumentos de uma vez:: - - public function getOptionParser() - { - $parser = parent::getOptionParser(); - // Configure parser - return $parser; - } - -Configurando um interpretador de opção com a interface fluida -------------------------------------------------------------- - -Todos os métodos que configuram um interpretador de opções podem ser -encadeados, permitindo definir um interpretador de opções completo em uma -série de chamadas de métodos:: - - public function getOptionParser() - { - $parser = parent::getOptionParser(); - $parser->addArgument('type', [ - 'help' => 'Either a full path or type of class.' - ])->addArgument('className', [ - 'help' => 'A CakePHP core class name (e.g: Component, HtmlHelper).' - ])->addOption('method', [ - 'short' => 'm', - 'help' => __('The specific method you want help on.') - ])->description(__('Lookup doc block comments for classes in CakePHP.')); - - return $parser; - } - -Os métodos que permitem encadeamento são: - -- description() -- epilog() -- command() -- addArgument() -- addArguments() -- addOption() -- addOptions() -- addSubcommand() -- addSubcommands() - -.. php:method:: description($text = null) - -Recebe ou define a descrição para o interpretador de opções. A -descrição é exibida acima da informação do argumento e da opção. Ao -instanciar tanto em array como em string, você pode definir o valor -da descrição. Instanciar sem argumentos vai retornar o valor atual:: - - // Define múltiplas linhas de uma vez - $parser->description(['line one', 'line two']); - - // Lê o valor atual - $parser->description(); - -.. php:method:: epilog($text = null) - -Recebe ou define o epílogo para o interpretador de opções. O epílogo -é exibido depois da informação do argumento e da opção. Ao instanciar -tanto em array como em string, você pode definir o valor do epílogo. -Instanciar sem argumentos vai retornar o valor atual:: - - // Define múltiplas linhas de uma vez - $parser->epilog(['line one', 'line two']); - - // Lê o valor atual - $parser->epilog(); - -Adicionando argumentos ----------------------- - -.. php:method:: addArgument($name, $params = []) - -Argumentos posicionais são frequentemente usados em ferramentas -de linha de comando, e ``ConsoleOptionParser`` permite definir -argumentos bem como torná-los requiríveis. Você pode adicionar -argumentos um por vez com ``$parser->addArgument();`` ou múltiplos -de uma vez com ``$parser->addArguments();``:: - - $parser->addArgument('model', ['help' => 'The model to bake']); - -Você pode usar as seguintes opções ao criar um argumento: - -* ``help`` O texto de ajuda a ser exibido para este argumento. -* ``required`` Se esse parâmetro é requisito. -* ``index`` O índice do argumento, se deixado indefinido, o argumento será - colocado no final dos argumentos. Se você definir o mesmo índice duas vezes, - a primeira opção será sobreescrita. -* ``choices`` Um array de opções válidas para esse argumento. Se deixado vazio, - todos os valores são válidos. Uma exceção será lançada quando parse() - encontrar um valor inválido. - -Argumentos que forem definidos como requisito lançarão uma exceção quando -interpretarem o comando se eles forem omitidos. Então você não tem que lidar -com isso em sua shell. - -.. php:method:: addArguments(array $args) - -Se você tem um array com múltiplos argumentos você pode usar -``$parser->addArguments()`` para adicioná-los de uma vez.:: - - $parser->addArguments([ - 'node' => ['help' => 'The node to create', 'required' => true], - 'parent' => ['help' => 'The parent node', 'required' => true] - ]); - -Assim como todos os métodos de construção no ConsoleOptionParser, addArguments -pode ser usado como parte de um fluido método encadeado. - -Validando argumentos --------------------- - -Ao criar argumentos posicionais, você pode usar a marcação ``required`` para -indicar que um argumento deve estar presente quando uma shell é chamada. -Adicionalmente você pode usar o ``choices`` para forçar um argumento a -ser de uma lista de escolhas válidas:: - - $parser->addArgument('type', [ - 'help' => 'The type of node to interact with.', - 'required' => true, - 'choices' => ['aro', 'aco'] - ]); - -O código acima irá criar um argumento que é requisitado e tem validação -no input. Se o argumento está tanto indefinodo, ou possui um valor -incorreto, uma exceção será lançada e a shell parará. - -Adicionando opções ------------------- - -.. php:method:: addOption($name, $options = []) - -Opções são frequentemente usadas em ferramentas CLI. -``ConsoleOptionParser`` suporta a criação de opções com -verbose e aliases curtas, suprindo padrões e criando ativadores -boleanos. Opções são criadas tanto com -``$parser->addOption()`` ou ``$parser->addOptions()``.:: - - $parser->addOption('connection', [ - 'short' => 'c', - 'help' => 'connection', - 'default' => 'default', - ]); - -O código citado permite a você usar tanto ``cake myshell --connection=other``, -``cake myshell --connection other``, ou ``cake myshell -c other`` -quando invocando a shell. Você também criar ativadores boleanos. Estes -ativadores não consumem valores, e suas presenças apenas os habilitam nos -parâmetros interpretados.:: - - $parser->addOption('no-commit', ['boolean' => true]); - -Com essa opção, ao chamar uma shell como ``cake myshell --no-commit something`` -o parâmetro no-commit deve ter um valor de ``true``, e `something` -deve ser tratado como um argumento posicional. -As opções nativas ``--help``, ``--verbose``, e ``--quiet`` -usam essa funcionalidade. - -Ao criar opções você pode usar os seguintes argumentos para definir -o seu comportamento: - -* ``short`` - A variação de letra única para essa opção, deixe indefinido para - none. -* ``help`` - Texto de ajuda para essa opção. Usado ao gerar ajuda para a opção. -* ``default`` - O valor padrão para essa opção. Se não estiver definido o valor - padrão será ``true``. -* ``boolean`` - A opção não usa valor, é apenas um ativador boleano. Por padrão - ``false``. -* ``choices`` - Um array de escolhas válidas para essa opção. Se deixado vazio, - todos os valores são considerados válidos. Uma exceção será lançada quando - parse() encontrar um valor inválido. - -.. php:method:: addOptions(array $options) - -Se você tem um array com múltiplas opções, você pode usar -``$parser->addOptions()`` para adicioná-las de uma vez.:: - - $parser->addOptions([ - 'node' => ['short' => 'n', 'help' => 'The node to create'], - 'parent' => ['short' => 'p', 'help' => 'The parent node'] - ]); - -Assim como com todos os métodos construtores, no ConsoleOptionParser, addOptions -pode ser usado como parte de um método fluente encadeado. - -Validando opções ----------------- - -Opções podem ser fornecidas com um conjunto de escolhas bem como argumentos -posicionais podem ser. Quando uma opção define escolhas, essas são as únicas -opções válidas para uma opção. Todos os outros valores irão gerar um -``InvalidArgumentException``:: - - $parser->addOption('accept', [ - 'help' => 'What version to accept.', - 'choices' => ['working', 'theirs', 'mine'] - ]); - -Usando opções boleanas ----------------------- - -As opções podem ser definidas como opções boleanas, que são úteis quando você -precisa criar algumas opções de marcação. Como opções com padrões, opções -boleanas sempre irão incluir -se nos parâmetros analisados. Quando as marcações -estão presentes elas são definidas para ``true``, quando elas estão ausentes, -são definidas como ``false``:: - - $parser->addOption('verbose', [ - 'help' => 'Enable verbose output.', - 'boolean' => true - ]); - -A opção seguinte resultaria em ``$this->params['verbose']`` sempre -estando disponível. Isso permite a você omitir verificações ``empty()`` ou -``isset()`` em marcações boleanas:: - - if ($this->params['verbose']) { - // Do something. - } - -Desde que as opções boleanas estejam sempre definidas como ``true`` ou -``false``, você pode omitir métodos de verificação adicionais. - -Adicionando subcomandos ------------------------ - -.. php:method:: addSubcommand($name, $options = []) - -Aplicativos de console são muitas vezes feitas de subcomandos, e esses -subcomandos podem exigir a análise de opções especiais e terem a sua própria -ajuda. Um perfeito exemplo disso é ``bake``. Bake é feita de muitas tarefas -separadas e todas têm a sua própria ajuda e opções. ``ConsoleOptionParser`` -permite definir subcomandos e fornecer comandos analisadores de opção -específica, de modo que a shell sabe como analisar os comandos para as suas -funções:: - - $parser->addSubcommand('model', [ - 'help' => 'Bake a model', - 'parser' => $this->Model->getOptionParser() - ]); - -A descrição acima é um exemplo de como você poderia fornecer ajuda e um -especializado interpretador de opção para a tarefa de uma shell. Ao chamar a -tarefa de ``getOptionParser()`` não temos de duplicar a geração do interpretador -de opção, ou misturar preocupações no nosso shell. Adicionar subcomandos desta -forma tem duas vantagens. Primeiro, ele permite que o seu shell documente -facilmente seus subcomandos na ajuda gerada. Ele também dá fácil acesso ao -subcomando help. Com o subcomando acima criado você poderia chamar -``cake myshell --help`` e ver a lista de subcomandos, e -também executar o ``cake myshell model --help`` para exibir a ajuda -apenas o modelo de tarefa. - -.. note:: - - Uma vez que seu Shell define subcomandos, todos os subcomandos deve ser - explicitamente definidos. - -Ao definir um subcomando, você pode usar as seguintes opções: - -* ``help`` - Texto de ajuda para o subcomando. -* ``parser`` - Um ConsoleOptionParser para o subcomando. Isso permite que você - crie métodos analisadores de opção específios. Quando a ajuda é gerada por um - subcomando, se um analisador está presente ele vai ser usado. Você também - pode fornecer o analisador como uma matriz que seja compatível com - :php:meth:`Cake\\Console\\ConsoleOptionParser::buildFromArray()` - -Adicionar subcomandos pode ser feito como parte de uma cadeia de métodos -fluente. - -Construir uma ConsoleOptionParser de uma matriz ------------------------------------------------ - -.. php:method:: buildFromArray($spec) - -Como mencionado anteriormente, ao criar interpretadores de opção de subcomando, -você pode definir a especificação interpretadora como uma matriz para esse -método. Isso pode ajudar fazer analisadores mais facilmente, já que tudo é um -array:: - - $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 da especificação do interpretador, você pode definir as chaves para -``arguments``, ``options``, ``description`` e ``epilog``. Você não pode definir -``subcommands`` dentro de um construtor estilo array. Os valores para os -argumentos e opções, devem seguir o formato que -:php:func:`Cake\\Console\\ConsoleOptionParser::addArguments()` e -:php:func:`Cake\\Console\\ConsoleOptionParser::addOptions()` usam. Você também -pode usar buildFromArray por conta própria, para construir um interpretador de -opção:: - - 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')] - ] - ]); - } - -Recebendo ajuda das Shells --------------------------- - -Com a adição de ConsoleOptionParser receber ajuda de shells é feito -de uma forma consistente e uniforme. Ao usar a opção ``--help`` ou ``-h`` você -pode visualizar a ajuda para qualquer núcleo shell, e qualquer shell que -implementa um ConsoleOptionParser:: - - cake bake --help - cake bake -h - -Ambos devem gerar a ajuda para o bake. Se o shell suporta subcomandos -você pode obter ajuda para estes de uma forma semelhante:: - - cake bake model --help - cake bake model -h - -Isso deve fornecer a você a ajuda específica para a tarefa bake dos models. - -Recebendo ajuda como XML ------------------------- - -Quando a construção de ferramentas automatizadas ou ferramentas de -desenvolvimento que necessitam interagir com shells do CakePHP, é bom ter ajuda -disponível em uma máquina capaz interpretar formatos. O ConsoleOptionParser pode -fornecer ajuda em xml, definindo um argumento adicional:: - - cake bake --help xml - cake bake -h xml - -O trecho acima deve retornar um documento XML com a ajuda gerada, opções, -argumentos e subcomando para o shell selecionado. Um documento XML de amostra -seria algo como: - -.. 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. - - - - - - - - - - - - - - - - - - -Roteamento em Shells / CLI -========================== - -Na interface de linha de comando (CLI), especificamente suas shells e tarefas, -``env('HTTP_HOST')`` e outras variáveis de ambiente webbrowser específica, não -estão definidas. - -Se você gerar relatórios ou enviar e-mails que fazem uso de ``Router::url()``, -estes conterão a máquina padrão ``http://localhost/`` e resultando assim em -URLs inválidas. Neste caso, você precisa especificar o domínio manualmente. -Você pode fazer isso usando o valor de configuração ``App.fullBaseUrl`` no seu -bootstrap ou na sua configuração, por exemplo. - -Para enviar e-mails, você deve fornecer a classe CakeEmail com o host que você -deseja enviar o e-mail:: - - $Email = new CakeEmail(); - $Email->domain('www.example.org'); - -Iste afirma que os IDs de mensagens geradas são válidos e adequados para o -domínio a partir do qual os e-mails são enviados. - -Métodos enganchados -=================== - -.. php:method:: initialize() - - Inicializa a Shell para atua como construtor de subclasses e permite - configuração de tarefas antes de desenvolver a execução. - -.. php:method:: startup() - - Inicia-se a Shell e exibe a mensagem de boas-vindas. Permite a verificação - e configuração antes de comandar ou da execução principal. - - Substitua este método se você quiser remover as informações de boas-vindas, - ou outra forma modificar o fluxo de pré-comando. - -Mais tópicos -============ - -.. 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/server-shell - console-and-shells/cache - -.. meta:: - :title lang=pt: Console e Shells - :keywords lang=pt: shell scripts,system shell,classes de aplicação,tarefas background,line script,cron job,request response,system path,acl,novos projetos,shells,parametros,i18n,cakephp,directory,manutenção,ideal,aplicações,mvc From 65880e93cf8e62ffdec34b47e22818355a104eda Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 01:03:35 -0300 Subject: [PATCH 03/46] [feat]: missing files --- pt/appendices/5-0-migration-guide.rst | 428 +++++++++++++++ pt/appendices/5-0-upgrade-guide.rst | 74 +++ pt/appendices/5-1-migration-guide.rst | 198 +++++++ pt/appendices/5-2-migration-guide.rst | 137 +++++ pt/appendices/migration-guides.rst | 14 + pt/appendices/phpunit10.rst | 65 +++ pt/console-commands.rst | 183 +++++++ pt/console-commands/counter-cache.rst | 24 + pt/console-commands/cron-jobs.rst | 43 ++ pt/console-commands/i18n.rst | 92 ++++ pt/console-commands/input-output.rst | 375 +++++++++++++ pt/console-commands/option-parsers.rst | 367 +++++++++++++ pt/console-commands/plugin.rst | 65 +++ pt/console-commands/repl.rst | 50 ++ pt/console-commands/routes.rst | 40 ++ pt/console-commands/schema-cache.rst | 30 ++ pt/console-commands/server.rst | 30 ++ pt/controllers/pagination.rst | 296 ++++++++++ pt/core-libraries/plugin.rst | 53 ++ pt/development/dependency-injection.rst | 352 ++++++++++++ pt/phinx.rst | 4 + pt/release-policy.rst | 69 +++ pt/standalone-packages.rst | 77 +++ .../cms/articles-model.rst | 88 +++ .../cms/authorization.rst | 255 +++++++++ .../cms/tags-and-users.rst | 505 ++++++++++++++++++ 26 files changed, 3914 insertions(+) create mode 100644 pt/appendices/5-0-migration-guide.rst create mode 100644 pt/appendices/5-0-upgrade-guide.rst create mode 100644 pt/appendices/5-1-migration-guide.rst create mode 100644 pt/appendices/5-2-migration-guide.rst create mode 100644 pt/appendices/migration-guides.rst create mode 100644 pt/appendices/phpunit10.rst create mode 100644 pt/console-commands.rst create mode 100644 pt/console-commands/counter-cache.rst create mode 100644 pt/console-commands/cron-jobs.rst create mode 100644 pt/console-commands/i18n.rst create mode 100644 pt/console-commands/input-output.rst create mode 100644 pt/console-commands/option-parsers.rst create mode 100644 pt/console-commands/plugin.rst create mode 100644 pt/console-commands/repl.rst create mode 100644 pt/console-commands/routes.rst create mode 100644 pt/console-commands/schema-cache.rst create mode 100644 pt/console-commands/server.rst create mode 100644 pt/controllers/pagination.rst create mode 100644 pt/core-libraries/plugin.rst create mode 100644 pt/development/dependency-injection.rst create mode 100644 pt/phinx.rst create mode 100644 pt/release-policy.rst create mode 100644 pt/standalone-packages.rst create mode 100644 pt/tutorials-and-examples/cms/articles-model.rst create mode 100644 pt/tutorials-and-examples/cms/authorization.rst create mode 100644 pt/tutorials-and-examples/cms/tags-and-users.rst diff --git a/pt/appendices/5-0-migration-guide.rst b/pt/appendices/5-0-migration-guide.rst new file mode 100644 index 0000000000..d81e7561de --- /dev/null +++ b/pt/appendices/5-0-migration-guide.rst @@ -0,0 +1,428 @@ +5.0 Migration Guide +################### + +CakePHP 5.0 contains breaking changes, and is not backwards compatible with 4.x +releases. Before attempting to upgrade to 5.0, first upgrade to 4.5 and resolve +all deprecation warnings. + +Refer to the :doc:`/appendices/5-0-upgrade-guide` for step by step instructions +on how to upgrade to 5.0. + +Deprecated Features Removed +=========================== + +All methods, properties and functionality that were emitting deprecation warnings +as of 4.5 have been removed. + +Breaking Changes +================ + +In addition to the removal of deprecated features there have been breaking +changes made: + +Global +------ + +- Type declarations were added to all function parameter and returns where possible. These are intended + to match the docblock annotations, but include fixes for incorrect annotations. +- Type declarations were added to all class properties where possible. These also include some fixes for + incorrect annotations. +- The ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``, ``WEEK``, ``MONTH``, ``YEAR`` constants were removed. +- Use of ``#[\AllowDynamicProperties]`` removed everywhere. It was used for the following classes: + - ``Command/Command`` + - ``Console/Shell`` + - ``Controller/Component`` + - ``Controller/Controller`` + - ``Mailer/Mailer`` + - ``View/Cell`` + - ``View/Helper`` + - ``View/View`` +- The supported database engine versions were updated: + - MySQL (5.7 or higher) + - MariaDB (10.1 or higher) + - PostgreSQL (9.6 or higher) + - Microsoft SQL Server (2012 or higher) + - SQLite 3 (3.16 or higher) + +Auth +---- + +- `Auth` has been removed. Use the `cakephp/authentication `__ and + `cakephp/authorization `__ plugins instead. + +Cache +----- + +- The ``Wincache`` engine was removed. The wincache extension is not supported + on PHP 8. + +Collection +---------- + +- ``combine()`` now throws an exception if the key path or group path doesn't exist or contains a null value. + This matches the behavior of ``indexBy()`` and ``groupBy()``. + +Console +------- + +- ``BaseCommand::__construct()`` was removed. +- ``ConsoleIntegrationTestTrait::useCommandRunner()`` was removed since it's no longer needed. +- ``Shell`` has been removed and should be replaced with `Command `__ +- ``ConsoleOptionParser::addSubcommand()`` was removed alongside the removal of + ``Shell``. Subcommands should be replaced with ``Command`` classes that + implement ``Command::defaultName()`` to define the necessary command name. +- ``BaseCommand`` now emits ``Command.beforeExecute`` and + ``Command.afterExecute`` events around the command's ``execute()`` method + being invoked by the framework. + +Connection +---------- + +- ``Connection::prepare()`` has been removed. You can use ``Connection::execute()`` + instead to execute a SQL query by specifing the SQL string, params and types in a single call. +- ``Connection::enableQueryLogging()`` has been removed. If you haven't enabled logging + through the connection config then you can later set the logger instance for the + driver to enable query logging ``$connection->getDriver()->setLogger()``. + +Controller +---------- + +- The method signature for ``Controller::__construct()`` has changed. + So you need to adjust your code accordingly if you are overriding the constructor. +- After loading components are no longer set as dynamic properties. Instead + ``Controller`` uses ``__get()`` to provide property access to components. This + change can impact applications that use ``property_exists()`` on components. +- The components' ``Controller.shutdown`` event callback has been renamed from + ``shutdown`` to ``afterFilter`` to match the controller one. This makes the callbacks more consistent. +- ``PaginatorComponent`` has been removed and should be replaced by calling ``$this->paginate()`` in your controller or + using ``Cake\Datasource\Paging\NumericPaginator`` directly +- ``RequestHandlerComponent`` has been removed. See the `4.4 migration `__ guide for how to upgrade +- ``SecurityComponent`` has been removed. Use ``FormProtectionComponent`` for form tampering protection + or ``HttpsEnforcerMiddleware`` to enforce use of HTTPS for requests instead. +- ``Controller::paginate()`` no longer accepts query options like ``contain`` for + its ``$settings`` argument. You should instead use the ``finder`` option + ``$this->paginate($this->Articles, ['finder' => 'published'])``. Or you can + create required select query before hand and then pass it to ``paginate()`` + ``$query = $this->Articles->find()->where(['is_published' => true]); $this->paginate($query);``. + +Core +---- + +- The function ``getTypeName()`` has been dropped. Use PHP's ``get_debug_type()`` instead. +- The dependency on ``league/container`` was updated to ``4.x``. This will + require the addition of typehints to your ``ServiceProvider`` implementations. +- ``deprecationWarning()`` now has a ``$version`` parameter. +- The ``App.uploadedFilesAsObjects`` configuration option has been removed + alongside of support for PHP file upload shaped arrays throughout the + framework. +- ``ClassLoader`` has been removed. Use composer to generate autoload files instead. + +Database +-------- + +- The ``DateTimeType`` and ``DateType`` now always return immutable objects. + Additionally the interface for ``Date`` objects reflects the ``ChronosDate`` + interface which lacks all of the time related methods that were present in + CakePHP 4.x. +- ``DateType::setLocaleFormat()`` no longer accepts an array. +- ``Query`` now accepts only ``\Closure`` parameters instead of ``callable``. Callables can be converted + to closures using the new first-class array syntax in PHP 8.1. +- ``Query::execute()`` no longer runs results decorator callbacks. You must use ``Query::all()`` instead. +- ``TableSchemaAwareInterface`` was removed. +- ``Driver::quote()`` was removed. Use prepared statements instead. +- ``Query::orderBy()`` was added to replace ``Query::order()``. +- ``Query::groupBy()`` was added to replace ``Query::group()``. +- ``SqlDialectTrait`` has been removed and all its functionality has been moved + into the ``Driver`` class itself. +- ``CaseExpression`` has been removed and should be replaced with + ``QueryExpression::case()`` or ``CaseStatementExpression`` +- ``Connection::connect()`` has been removed. Use + ``$connection->getDriver()->connect()`` instead. +- ``Connection::disconnect()`` has been removed. Use + ``$connection->getDriver()->disconnect()`` instead. +- ``cake.database.queries`` has been added as an alternative to the ``queriesLog`` scope +- The ability to enable/disable ResultSet buffering has been removed. Results are always buffered. + +Datasource +---------- + +- The ``getAccessible()`` method was added to ``EntityInterface``. Non-ORM + implementations need to implement this method now. +- The ``aliasField()`` method was added to ``RepositoryInterface``. Non-ORM + implementations need to implement this method now. + +Event +----- + +- Event payloads must be an array. Other object such as ``ArrayAccess`` are no longer cast to array and will raise a ``TypeError`` now. +- It is recommended to adjust event handlers to be void methods and use ``$event->setResult()`` instead of returning the result + +Error +----- + +- ``ErrorHandler`` and ``ConsoleErrorHandler`` have been removed. See the `4.4 migration `__ guide for how to upgrade +- ``ExceptionRenderer`` has been removed and should be replaced with ``WebExceptionRenderer`` +- ``ErrorLoggerInterface::log()`` has been removed and should be replaced with ``ErrorLoggerInterface::logException()`` +- ``ErrorLoggerInterface::logMessage()`` has been removed and should be replaced with ``ErrorLoggerInterface::logError()`` + +Filesystem +---------- + +- The Filesystem package was removed, and ``Filesystem`` class was moved to the Utility package. + +Http +---- + +- ``ServerRequest`` is no longer compatible with ``files`` as arrays. This + behavior has been disabled by default since 4.1.0. The ``files`` data will now + always contain ``UploadedFileInterfaces`` objects. + +I18n +---- + +- ``FrozenDate`` was renamed to `Date` and ``FrozenTime`` was renamed to `DateTime`. +- ``Time`` now extends ``Cake\Chronos\ChronosTime`` and is therefore immutable. +- ``Date`` objects do not extend ``DateTimeInterface`` anymore - therefore you can't compare them with ``DateTime`` objects. + See the `cakephp/chronos release documentation `__ for more information. +- ``Date::parseDateTime()`` was removed. +- ``Date::parseTime()`` was removed. +- ``Date::setToStringFormat()`` and ``Date::setJsonEncodeFormat()`` no longer accept an array. +- ``Date::i18nFormat()`` and ``Date::nice()`` no longer accept a timezone parameter. +- Translation files for plugins with vendor prefixed names (``FooBar/Awesome``) will now have that + prefix in the file name, e.g. ``foo_bar_awesome.po`` to avoid collision with a ``awesome.po`` file + from a corresponding plugin (``Awesome``). + +Log +--- + +- Log engine config now uses ``null`` instead of ``false`` to disable scopes. + So instead of ``'scopes' => false`` you need to use ``'scopes' => null`` in your log config. + +Mailer +------ + +- ``Email`` has been removed. Use `Mailer `__ instead. +- ``cake.mailer`` has been added as an alternative to the ``email`` scope + +ORM +--- + +- ``EntityTrait::has()`` now returns ``true`` when an attribute exists and is + set to ``null``. In previous versions of CakePHP this would return ``false``. + See the release notes for 4.5.0 for how to adopt this behavior in 4.x. +- ``EntityTrait::extractOriginal()`` now returns only existing fields, similar to ``extractOriginalChanged()``. +- Finder arguments are now required to be associative arrays as they were always expected to be. +- ``TranslateBehavior`` now defaults to the ``ShadowTable`` strategy. If you are + using the ``Eav`` strategy you will need to update your behavior configuration + to retain the previous behavior. +- ``allowMultipleNulls`` option for ``isUnique`` rule now default to true matching + the original 3.x behavior. +- ``Table::query()`` has been removed in favor of query-type specific functions. +- ``Table::updateQuery()``, ``Table::selectQuery()``, ``Table::insertQuery()``, and + ``Table::deleteQuery()``) were added and return the new type-specific query objects below. +- ``SelectQuery``, ``InsertQuery``, ``UpdateQuery`` and ``DeleteQuery`` were added + which represent only a single type of query and do not allow switching between query types nor + calling functions unrelated to the specific query type. +- ``Table::_initializeSchema()`` has been removed and should be replaced by calling + ``$this->getSchema()`` inside the ``initialize()`` method. +- ``SaveOptionsBuilder`` has been removed. Use a normal array for options instead. + +Routing +------- + +- Static methods ``connect()``, ``prefix()``, ``scope()`` and ``plugin()`` of the ``Router`` have been removed and + should be replaced by calling their non-static method variants via the ``RouteBuilder`` instance. +- ``RedirectException`` has been removed. Use ``\Cake\Http\Exception\RedirectException`` instead. + +TestSuite +--------- + +- ``TestSuite`` was removed. Users should use environment variables to customize + unit test settings instead. +- ``TestListenerTrait`` was removed. PHPUnit dropped support for these listeners. + See :doc:`/appendices/phpunit10` +- ``IntegrationTestTrait::configRequest()`` now merges config when called multiple times + instead of replacing the currently present config. + +Validation +---------- + +- ``Validation::isEmpty()`` is no longer compatible with file upload shaped + arrays. Support for PHP file upload arrays has been removed from + ``ServerRequest`` as well so you should not see this as a problem outside of + tests. +- Previously, most data validation error messages were simply ``The provided value is invalid``. + Now, the data validation error messages are worded more precisely. + For example, ``The provided value must be greater than or equal to \`5\```. + +View +---- + +- ``ViewBuilder`` options are now truly associative (string keys). +- ``NumberHelper`` and ``TextHelper`` no longer accept an ``engine`` config. +- ``ViewBuilder::setHelpers()`` parameter ``$merge`` was removed. Use ``ViewBuilder::addHelpers()`` instead. +- Inside ``View::initialize()``, prefer using ``addHelper()`` instead of ``loadHelper()``. + All configured helpers will be loaded afterwards, anyway. +- ``View\Widget\FileWidget`` is no longer compatible with PHP file upload shaped + arrays. This is aligned with ``ServerRequest`` and ``Validation`` changes. +- ``FormHelper`` no longer sets ``autocomplete=off`` on CSRF token fields. This + was a workaround for a Safari bug that is no longer relevant. + +Deprecations +============ + +The following is a list of deprecated methods, properties and behaviors. These +features will continue to function in 5.x and will be removed in 6.0. + +Database +-------- + +- ``Query::order()`` was deprecated. Use ``Query::orderBy()`` instead now that + ``Connection`` methods are no longer proxied. This aligns the function name + with the SQL statement. +- ``Query::group()`` was deprecated. Use ``Query::groupBy()`` instead now that + ``Connection`` methods are no longer proxied. This aligns the function name + with the SQL statement. + +ORM +--- + +- Calling ``Table::find()`` with options array is deprecated. Use `named arguments `__ + instead. For e.g. instead of ``find('all', ['conditions' => $array])`` use + ``find('all', conditions: $array)``. Similarly for custom finder options, instead + of ``find('list', ['valueField' => 'name'])`` use ``find('list', valueField: 'name')`` + or multiple named arguments like ``find(type: 'list', valueField: 'name', conditions: $array)``. + +New Features +============ + +Improved type checking +----------------------- + +CakePHP 5 leverages the expanded type system feature available in PHP 8.1+. +CakePHP also uses ``assert()`` to provide improved error messages and additional +type soundness. In production mode, you can configure PHP to not generate +code for ``assert()`` yielding improved application performance. See the +:ref:`symlink-assets` for how to do this. + +Collection +---------- + +- Added ``unique()`` which filters out duplicate value specified by provided callback. +- ``reject()`` now supports a default callback which filters out truthy values which is + the inverse of the default behavior of ``filter()`` + +Core +---- + +- The ``services()`` method was added to ``PluginInterface``. +- ``PluginCollection::addFromConfig()`` has been added to :ref:`simplify plugin loading `. + +Database +-------- + +- ``ConnectionManager`` now supports read and write connection roles. Roles can be configured + with ``read`` and ``write`` keys in the connection config that override the shared config. +- ``Query::all()`` was added which runs result decorator callbacks and returns a result set for select queries. +- ``Query::comment()`` was added to add a SQL comment to the executed query. This makes it easier to debug queries. +- ``EnumType`` was added to allow mapping between PHP backed enums and a string or integer column. +- ``getMaxAliasLength()`` and ``getConnectionRetries()`` were added + to ``DriverInterface``. +- Supported drivers now automatically add auto-increment only to integer primary keys named "id" instead + of all integer primary keys. Setting 'autoIncrement' to false always disables on all supported drivers. + +Http +---- + +- Added support for `PSR-17 `__ factories + interface. This allows ``cakephp/http`` to provide a client implementation to + libraries that allow automatic interface resolution like php-http. +- Added ``CookieCollection::__get()`` and ``CookieCollection::__isset()`` to add + ergonomic ways to access cookies without exceptions. + +ORM +--- + +Required Entity Fields +---------------------- + +Entities have a new opt-in functionality that allows making entities handle +properties more strictly. The new behavior is called 'required fields'. When +enabled, accessing properties that are not defined in the entity will raise +exceptions. This impacts the following usage:: + + $entity->get(); + $entity->has(); + $entity->getOriginal(); + isset($entity->attribute); + $entity->attribute; + +Fields are considered defined if they pass ``array_key_exists``. This includes +null values. Because this can be a tedious to enable feature, it was deferred to +5.0. We'd like any feedback you have on this feature as we're considering making +this the default behavior in the future. + + +Typed Finder Parameters +----------------------- + +Table finders can now have typed arguments as required instead of an options array. +For e.g. a finder for fetching posts by category or user:: + + 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; + } + +can now be written as:: + + 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; + } + +The finder can then be called as ``find('byCategoryOrUser', userId: $somevar)``. +You can even include the special named arguments for setting query clauses. +``find('byCategoryOrUser', userId: $somevar, conditions: ['enabled' => true])``. + +A similar change has been applied to the ``RepositoryInterface::get()`` method:: + + public function view(int $id) + { + $author = $this->Authors->get($id, [ + 'contain' => ['Books'], + 'finder' => 'latest', + ]); + } + +can now be written as:: + + public function view(int $id) + { + $author = $this->Authors->get($id, contain: ['Books'], finder: 'latest'); + } + +TestSuite +--------- + +- ``IntegrationTestTrait::requestAsJson()`` has been added to set JSON headers for the next request. + +Plugin Installer +---------------- +- The plugin installer has been updated to automatically handle class autoloading + for your app plugins. So you can remove the namespace to path mappings for your + plugins from your ``composer.json`` and just run ``composer dumpautoload``. diff --git a/pt/appendices/5-0-upgrade-guide.rst b/pt/appendices/5-0-upgrade-guide.rst new file mode 100644 index 0000000000..5f34ec911c --- /dev/null +++ b/pt/appendices/5-0-upgrade-guide.rst @@ -0,0 +1,74 @@ +5.0 Upgrade Guide +################# + +First, check that your application is running on latest CakePHP 4.x version. + +Fix Deprecation Warnings +======================== + +Once your application is running on latest CakePHP 4.x, enable deprecation warnings in **config/app.php**:: + + 'Error' => [ + 'errorLevel' => E_ALL, + ] + +Now that you can see all the warnings, make sure these are fixed before proceeding with the upgrade. + +Some potentially impactful deprecations you should make sure you have addressed +are: + +- ``Table::query()`` was deprecated in 4.5.0. Use ``selectQuery()``, + ``updateQuery()``, ``insertQuery()`` and ``deleteQuery()`` instead. + +Upgrade to PHP 8.1 +================== + +If you are not running on **PHP 8.1 or higher**, you will need to upgrade PHP before updating CakePHP. + +.. note:: + CakePHP 5.0 requires **a minimum of PHP 8.1**. + +.. _upgrade-tool-use: + +Use the Upgrade Tool +==================== + +.. note:: + The upgrade tool only works on applications running on latest CakePHP 4.x. You cannot run the upgrade tool after updating to CakePHP 5.0. + +Because CakePHP 5 leverages union types and ``mixed``, there are many +backwards incompatible changes concerning method signatures and file renames. +To help expedite fixing these tedious changes there is an upgrade CLI tool: + +.. code-block:: console + + # Install the upgrade tool + git clone https://github.com/cakephp/upgrade + cd upgrade + git checkout 5.x + composer install --no-dev + +With the upgrade tool installed you can now run it on your application or +plugin:: + + bin/cake upgrade rector --rules cakephp50 + bin/cake upgrade rector --rules chronos3 + +Update CakePHP Dependency +========================= + +After applying rector refactorings you need to upgrade CakePHP, its plugins, PHPUnit +and maybe other dependencies in your ``composer.json``. +This process heavily depends on your application so we recommend you compare your +``composer.json`` with what is present in `cakephp/app +`__. + +After the version strings are adjusted in your ``composer.json`` execute +``composer update -W`` and check its output. + +Update app files based upon latest app template +=============================================== + +Next, ensure the rest of your application has been updated to be based upon the +latest version of `cakephp/app +`__. diff --git a/pt/appendices/5-1-migration-guide.rst b/pt/appendices/5-1-migration-guide.rst new file mode 100644 index 0000000000..caac02853f --- /dev/null +++ b/pt/appendices/5-1-migration-guide.rst @@ -0,0 +1,198 @@ +5.1 Migration Guide +################### + +The 5.1.0 release is a backwards compatible with 5.0. It adds new functionality +and introduces new deprecations. Any functionality deprecated in 5.x will be +removed in 6.0.0. + +Behavior Changes +================ + +- Connection now creates unique read and write drivers if the keys ``read`` or + ``write`` are present in the config regardless of values. +- FormHelper no longer generates ``aria-required`` attributes on input elements + that also have the ``required`` attribute set. The ``aria-required`` attribute + is redundant on these elements and generates HTML validation warnings. If you + are using ``aria-required`` attribute in styling or scripting you'll need to + update your application. +- Adding associations with duplicate names will now raise exceptions. You can + use ``$table->associations()->has()`` to conditionally define associations if + required. +- Text Utility and TextHelper methods around truncation and maximum length are using + a UTF-8 character for ``ellipsis`` instead of ``...`` legacy characters. +- ``TableSchema::setColumnType()`` now throws an exception if the specified column + does not exist. +- ``PluginCollection::addPlugin()`` now throws an exception if a plugin of the same + name is already added. +- ``TestCase::loadPlugins()`` will now clear out any previously loaded plugins. So + you must specify all plugins required for any subsequent tests. +- The hashing algorithm for ``Cache`` configurations that use ``groups``. Any + keys will have new group prefix hashes generated which will cause cache + misses. Consider an incremental deploy to avoid operating on an entirely cold + cache. +- ``FormHelper::getFormProtector()`` now returns ``null`` in addition to its + previous types. This allows dynamic view code to run with fewer errors and + shouldn't impact most applications. +- The default value for ``valueSeparator`` in ``Table::findList()`` is now + a single space instead of ``;``. +- ``ErrorLogger`` uses ``Psr\Log\LogTrait`` now. +- ``Database\QueryCompiler::$_orderedUnion`` was removed. + +Deprecations +============ + +I18n +---- + +- The ``_cake_core_`` cache config key has been renamed to ``_cake_translations_``. + +Mailer +------ + +- ``Mailer::setMessage()`` is deprecated. It has unintuitive behavior and very + low usage. + + +New Features +============ + +Cache +----- + +- ``RedisEngine`` now supports a ``tls`` option that enables connecting to redis + over a TLS connection. You can use the ``ssl_ca``, ``ssl_cert`` and + ``ssl_key`` options to define the TLS context for redis. + +Command +------- + +- ``bin/cake plugin list`` has been added to list all available plugins, + their load configuration and version. +- Optional ``Command`` arguments can now have a ``default`` value. +- ``BannerHelper`` was added. This command helper can format text as a banner + with a coloured background and padding. +- Additional default styles for ``info.bg``, ``warning.bg``, ``error.bg`` and + ``success.bg`` were added to ``ConsoleOutput``. + +Console +------- + +- ``Arguments::getBooleanOption()`` and ``Arguments::getMultipleOption()`` were added. +- ``Arguments::getArgument()`` will now raise an exception if an unknown + argument name is provided. This helps prevent mixing up option/argument names. + + +Controller +---------- + +- Components can now use the DI container to have dependencies resolved and + provided as constructor parameters just like Controllers and Commands do. + +Core +---- + +- ``PluginConfig`` was added. Use this class to get all available plugins, their load config and versions. +- The ``toString``, ``toInt``, ``toBool`` functions were added. They give you + a typesafe way to cast request data or other input and return ``null`` when conversion fails. +- ``pathCombine()`` was added to help build paths without worrying about duplicate and trailing slashes. +- A new ``events`` hook was added to the ``BaseApplication`` as well as the ``BasePlugin`` class. This hook + is the recommended way to register global event listeners for you application. See :ref:`Registering Listeners ` + +Database +-------- + +- Support for ``point``, ``linestring``, ``polygon`` and ``geometry`` types were + added. These types are useful when working with geospatial or cartesian + co-ordinates. Sqlite support uses text columns under the hood and lacks + functions to manipulate data as geospatial values. +- ``SelectQuery::__debugInfo()`` now includes which connection role the query + is for. +- ``SelectQuery::intersect()`` and ``SelectQuery::intersectAll()`` were added. + These methods enable queries using ``INTERSECT`` and ``INTERSECT ALL`` + conjunctions to be expressed. +- New supports features were added for ``intersect``, ``intersect-all`` and + ``set-operations-order-by`` features. +- The ability to fetch records without buffering which existed in 4.x has been restored. + Methods ``SelectQuery::enableBufferedResults()``, ``SelectQuery::disableBufferedResults()`` + and ``SelectQuery::isBufferedResultsEnabled()`` have been re-added. + +Datasource +---------- + +- ``RulesChecker::remove()``, ``removeCreate()``, ``removeUpdate()``, and + ``removeDelete()`` methods were added. These methods allow you to remove rules + by name. + + +Http +---- + +- ``SecurityHeadersMiddleware::setPermissionsPolicy()`` was added. This method + adds the ability to define ``permissions-policy`` header values. +- ``Client`` now emits ``HttpClient.beforeSend`` and ``HttpClient.afterSend`` + events when requests are sent. You can use these events to perform logging, + caching or collect telemetry. +- ``Http\Server::terminate()`` was added. This method triggers the + ``Server.terminate`` event which can be used to run logic after the response + has been sent in fastcgi environments. In other environments the + ``Server.terminate`` event runs *before* the response has been sent. + +I18n +---- + +- ``Number::formatter()`` and ``currency()`` now accept a ``roundingMode`` + option to override how rounding is done. +- The ``toDate``, and ``toDateTime`` functions were added. They give you + a typesafe way to cast request data or other input and return ``null`` when + conversion fails. + +ORM +--- + +- Setting the ``preserveKeys`` option on association finder queries. This can be + used with ``formatResults()`` to replace association finder results with an + associative array. +- SQLite columns with names containing ``json`` can now be mapped to ``JsonType``. + This is currently an opt-in feature which is enabled by setting the ``ORM.mapJsonTypeForSqlite`` + configure value to ``true`` in your app. + +TestSuite +--------- + +- CakePHP as well as the app template have been updated to use PHPUnit ``^10.5.5 || ^11.1.3"``. +- ``ConnectionHelper`` methods are now all static. This class has no state and + its methods were updated to be static. +- ``LogTestTrait`` was added. This new trait makes it easy to capture logs in + your tests and make assertions on the presence or absence of log messages. +- ``IntegrationTestTrait::replaceRequest()`` was added. + +Utility +------- + +- ``Hash::insert()`` and ``Hash::remove()`` now accept ``ArrayAccess`` objects along with ``array`` data. + +Validation +---------- + +- ``Validation::enum()`` and ``Validator::enum()`` were added. These validation + methods simplify validating backed enum values. +- ``Validation::enumOnly()`` and ``Validation::enumExcept()`` were added to check for specific cases + and further simplify validating backed enum values. + +View +---- + +- View cells now emit events around their actions ``Cell.beforeAction`` and + ``Cell.afterAction``. +- ``NumberHelper::format()`` now accepts a ``roundingMode`` option to override how + rounding is done. + +Helpers +------- + +- ``TextHelper::autoLinkUrls()`` has options added for better link label printing: + * ``stripProtocol``: Strips ``http://`` and ``https://`` from the beginning of the link. Default off. + * ``maxLength``: The maximum length of the link label. Default off. + * ``ellipsis``: The string to append to the end of the link label. Defaults to UTF8 version. +- ``HtmlHelper::meta()`` can now create a meta tag containing the current CSRF + token using ``meta('csrfToken')``. diff --git a/pt/appendices/5-2-migration-guide.rst b/pt/appendices/5-2-migration-guide.rst new file mode 100644 index 0000000000..66567f439a --- /dev/null +++ b/pt/appendices/5-2-migration-guide.rst @@ -0,0 +1,137 @@ +5.2 Migration Guide +################### + +The 5.2.0 release is a backwards compatible with 5.0. It adds new functionality +and introduces new deprecations. Any functionality deprecated in 5.x will be +removed in 6.0.0. + +Behavior Changes +================ + +- ``ValidationSet::add()`` will now raise errors when a rule is added with + a name that is already defined. This change aims to prevent rules from being + overwritten by accident. +- ``Http\Session`` will now raise an exception when an invalid session preset is + used. +- ``FormProtectionComponent`` now raises ``Cake\Controller\Exception\FormProtectionException``. This + class is a subclass of ``BadRequestException``, and offers the benefit of + being filterable from logging. +- ``NumericPaginator::paginate()`` now uses the ``finder`` option even when a ``SelectQuery`` instance is passed to it. + +Deprecations +============ + +Console +------- + +- ``Arguments::getMultipleOption()`` is deprecated. Use ``getArrayOption()`` + instead. + +Datasource +---------- + +- The ability to cast an ``EntityInterface`` instance to string has been deprecated. + You should ``json_encode()`` the entity instead. + +- Mass assigning multiple entity fields using ``EntityInterface::set()`` is deprecated. + Use ``EntityInterface::patch()`` instead. For e.g. change usage like + ``$entity->set(['field1' => 'value1', 'field2' => 'value2'])`` to + ``$entity->patch(['field1' => 'value1', 'field2' => 'value2'])``. + +Event +----- + +- Returning values from event listeners / callbacks is deprecated. Use ``$event->setResult()`` + instead or ``$event->stopPropogation()`` to just stop the event propogation. + +View +---- + +- The ``errorClass`` option of ``FormHelper`` has been deprecated in favour of + using a template string. To upgrade move your ``errorClass`` definition to + a template set. See :ref:`customizing-templates`. + + +New Features +============ + +Console +------- + +- The ``cake counter_cache`` command was added. This command can be used to + regenerate counters for models that use ``CounterCacheBehavior``. +- ``ConsoleIntegrationTestTrait::debugOutput()`` makes it easier to debug + integration tests for console commands. +- ``ConsoleInputArgument`` now supports a ``separator`` option. This option + allows positional arguments to be delimited with a character sequence like + ``,``. CakePHP will split the positional argument into an array when arguments + are parsed. +- ``Arguments::getArrayArgumentAt()``, and ``Arguments::getArrayArgument()`` + were added. These methods allow you to read ``separator`` delimitered + positional arguments as arrays. +- ``ConsoleInputOption`` now supports a ``separator`` option. This option + allows option values to be delimited with a character sequence like + ``,``. CakePHP will split the option value into an array when arguments + are parsed. +- ``Arguments::getArrayArgumentAt()``, ``Arguments::getArrayArgument()``, and + ``Arguments::getArrayOption()`` + were added. These methods allow you to read ``separator`` delimitered + positional arguments as arrays. + +Database +-------- + +- The ``nativeuuid`` type was added. This type enables ``uuid`` columns to be + used in Mysql connections with MariaDB. In all other drivers, ``nativeuuid`` + is an alias for ``uuid``. +- ``Cake\Database\Type\JsonType::setDecodingOptions()`` was added. This method + lets you define the value for the ``$flags`` argument of ``json_decode()``. +- ``CounterCacheBehavior::updateCounterCache()`` was added. This method allows + you to update the counter cache values for all records of the configured + associations. ``CounterCacheCommand`` was also added to do the same through the + console. +- ``Cake\Database\Driver::quote()`` was added. This method provides a way to + quote values to be used in SQL queries where prepared statements cannot be + used. + +Datasource +---------- + +- Application rules can now use ``Closure`` to define the validation message. + This allows you to create dynamic validation messages based on the entity + state and validation rule options. + +Error +----- + +- Custom exceptions can have specific error handling logic defined in + ``ErrorController``. + +ORM +--- + +- ``CounterCacheBehavior::updateCounterCache()`` has been added. This method + allows you to update the counter cache values for all records of the configured + associations. +- ``BelongsToMany::setJunctionProperty()`` and ``getJunctionProperty()`` were + added. These methods allow you to customize the ``_joinData`` property that is + used to hydrate junction table records. +- ``Table::findOrCreate()`` now accepts an array as second argument to directly pass data in. + +TestSuite +--------- + +- ``TestFixture::$strictFields`` was added. Enabling this property will make + fixtures raise an error if a fixture's record list contains fields that do not + exist in the schema. + +View +---- + +- ``FormHelper::deleteLink()`` has been added as convenience wrapper for delete links in + templates using ``DELETE`` method. +- ``HtmlHelper::importmap()`` was added. This method allows you to define + import maps for your JavaScript files. +- ``FormHelper`` now uses the ``containerClass`` template to apply a class to + the form control div. The default value is ``input``. + diff --git a/pt/appendices/migration-guides.rst b/pt/appendices/migration-guides.rst new file mode 100644 index 0000000000..2a86606039 --- /dev/null +++ b/pt/appendices/migration-guides.rst @@ -0,0 +1,14 @@ +Migration Guides +################ + +Migration guides contain information regarding the new features introduced in +each version and the migration path between 5.x minor releases. + +.. toctree:: + :maxdepth: 1 + + ./5-0-upgrade-guide + ./5-0-migration-guide + ./5-1-migration-guide + ./5-2-migration-guide + ./phpunit10 diff --git a/pt/appendices/phpunit10.rst b/pt/appendices/phpunit10.rst new file mode 100644 index 0000000000..1cd56d3d3a --- /dev/null +++ b/pt/appendices/phpunit10.rst @@ -0,0 +1,65 @@ +PHPUnit 10 Upgrade +################## + +With CakePHP 5 the minimum PHPUnit version has changed from ``^8.5 || ^9.3`` to ``^10.1``. +This introduces a few breaking changes from PHPUnit as well as from CakePHP's side. + +phpunit.xml adjustments +======================= + +It is recommended to let PHPUnit update its configuration file via the following command:: + + vendor/bin/phpunit --migrate-configuration + +.. note:: + + Make sure you are already on PHPUnit 10 via ``vendor/bin/phpunit --version`` before executing this command! + +With this command out of the way your ``phpunit.xml`` already has most of the recommended changes present. + +New event system +---------------- + +PHPUnit 10 removed the old hook system and introduced a new `Event system +`_ +which requires the following code in your ``phpunit.xml`` to be adjusted from:: + + + + + +to:: + + + + + +``->withConsecutive()`` has been removed +======================================== + +You can convert the removed ``->withConsecutive()`` method to a +working interim solution like you can see here:: + + ->withConsecutive(['firstCallArg'], ['secondCallArg']) + +should be converted to:: + + ->with( + ...self::withConsecutive(['firstCallArg'], ['secondCallArg']) + ) + +the static ``self::withConsecutive()`` method has been added via the ``Cake\TestSuite\PHPUnitConsecutiveTrait`` +to the base ``Cake\TestSuite\TestCase`` class so you don't have to manually add that trait to your Testcase classes. + +data providers have to be static +================================ + +If your testcases leverage the data provider feature of PHPUnit then +you have to adjust your data providers to be static:: + + public function myProvider(): array + +should be converted to:: + + public static function myProvider(): array + diff --git a/pt/console-commands.rst b/pt/console-commands.rst new file mode 100644 index 0000000000..c40f966c9c --- /dev/null +++ b/pt/console-commands.rst @@ -0,0 +1,183 @@ +Console Commands +################ + +.. php:namespace:: Cake\Console + +In addition to a web framework, CakePHP also provides a console framework for +creating command line tools & applications. Console applications are ideal for +handling a variety of background & maintenance tasks that leverage your existing +application configuration, models, plugins and domain logic. + +CakePHP provides several console tools for interacting with CakePHP features +like i18n and routing that enable you to introspect your application and +generate related files. + +The CakePHP Console +=================== + +The CakePHP Console uses a dispatcher-type system to load commands, parse +their arguments and invoke the correct command. While the examples below use +bash the CakePHP console is compatible with any \*nix shell and windows. + +A CakePHP application contains **src/Command** directory that contain its commands. +It also comes with an executable in the **bin** directory: + +.. code-block:: console + + $ cd /path/to/app + $ bin/cake + +.. note:: + + For Windows, the command needs to be ``bin\cake`` (note the backslash). + +Running the Console with no arguments will list out available commands. You +could then run the any of the listed commands by using its name: + +.. code-block:: console + + # run server command + bin/cake server + + # run migrations command + bin/cake migrations -h + + # run bake (with plugin prefix) + bin/cake bake.bake -h + +Plugin commands can be invoked without a plugin prefix if the commands's name +does not overlap with an application or framework command. In the case that two +plugins provide a command with the same name, the first loaded plugin will get +the short alias. You can always use the ``plugin.command`` format to +unambiguously reference a command. + +Console Applications +==================== + +By default CakePHP will automatically discover all the commands in your +application and its plugins. You may want to reduce the number of exposed +commands, when building standalone console applications. You can use your +``Application``'s ``console()`` hook to limit which commands are exposed and +rename commands that are exposed:: + + // in 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 + { + // Add by classname + $commands->add('user', UserCommand::class); + + // Add instance + $commands->add('version', new VersionCommand()); + + return $commands; + } + } + +In the above example, the only commands available would be ``help``, ``version`` +and ``user``. See the :ref:`plugin-commands` section for how to add commands in +your plugins. + +.. note:: + + When adding multiple commands that use the same Command class, the ``help`` + command will display the shortest option. + +.. _renaming-commands: +.. index:: nested commands, subcommands + +Renaming Commands +================= + +There are cases where you will want to rename commands, to create nested +commands or subcommands. While the default auto-discovery of commands will not +do this, you can register your commands to create any desired naming. + +You can customize the command names by defining each command in your plugin:: + + public function console(CommandCollection $commands): CommandCollection + { + // Add commands with nested naming + $commands->add('user dump', UserDumpCommand::class); + $commands->add('user:show', UserShowCommand::class); + + // Rename a command entirely + $commands->add('lazer', UserDeleteCommand::class); + + return $commands; + } + +When overriding the ``console()`` hook in your application, remember to +call ``$commands->autoDiscover()`` to add commands from CakePHP, your +application, and plugins. + +If you need to rename/remove any attached commands, you can use the +``Console.buildCommands`` event on your application event manager to modify the +available commands. + +Commands +======== + +See the :doc:`/console-commands/commands` chapter on how to create your first +command. Then learn more about commands: + +.. toctree:: + :maxdepth: 1 + + console-commands/commands + console-commands/input-output + console-commands/option-parsers + console-commands/cron-jobs + +CakePHP Provided Commands +========================= + +.. toctree:: + :maxdepth: 1 + + console-commands/cache + console-commands/completion + console-commands/counter-cache + console-commands/i18n + console-commands/plugin + console-commands/schema-cache + console-commands/routes + console-commands/server + console-commands/repl + +Routing in the Console Environment +================================== + +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=en: Shells, Tasks & Console Tools + :keywords lang=en: 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/pt/console-commands/counter-cache.rst b/pt/console-commands/counter-cache.rst new file mode 100644 index 0000000000..cc50932bcf --- /dev/null +++ b/pt/console-commands/counter-cache.rst @@ -0,0 +1,24 @@ +CounterCache Tool +################# + +The CounterCacheCommand provides a CLI tool for rebuilding the counter caches +in your application and plugin models. It can be used in maintenance and +recovery operations, or to populate new counter caches added to your +application. + +.. code-block:: console + + bin/cake counter_cache --assoc Comments Articles + +This would rebuild the ``Comments`` related counters on the ``Articles`` table. +For very large tables you may need to rebuild counters in batches. You can use +the ``--limit`` and ``--page`` options to incrementally rebuild counter state. + +.. code-block:: console + + bin/cake counter_cache --assoc Comments --limit 100 --page 2 Articles + +When ``limit`` and ``page`` are used, records will be ordered by the table's +primary key. + +.. versionadded:: 5.2.0 diff --git a/pt/console-commands/cron-jobs.rst b/pt/console-commands/cron-jobs.rst new file mode 100644 index 0000000000..6b29f9d01a --- /dev/null +++ b/pt/console-commands/cron-jobs.rst @@ -0,0 +1,43 @@ +Running Shells as Cron Jobs +########################### + +A common thing to do with a shell is making it run as a cronjob to +clean up the database once in a while or send newsletters. This is +trivial to setup, for example:: + + */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) + +You can see more info here: https://en.wikipedia.org/wiki/Cron + +.. tip:: + + Use ``-q`` (or `--quiet`) to silence any output for cronjobs. + +Cron Jobs on Shared Hosting +--------------------------- + +On some shared hostings ``cd /full/path/to/root && bin/cake mycommand myparam`` +might not work. Instead you can use +``php /full/path/to/root/bin/cake.php mycommand 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`` + +.. meta:: + :title lang=en: Running Shells as cronjobs + :keywords lang=en: cronjob,bash script,crontab diff --git a/pt/console-commands/i18n.rst b/pt/console-commands/i18n.rst new file mode 100644 index 0000000000..4cd828bfdb --- /dev/null +++ b/pt/console-commands/i18n.rst @@ -0,0 +1,92 @@ +I18N Tool +######### + +The i18n features of CakePHP use `po files `_ +as their translation source. PO files integrate with commonly used translation tools +like `Poedit `_. + +The i18n commands provides a quick way to generate po template files. +These templates files can then be given to translators so they can translate the +strings in your application. Once you have translations done, pot files can be +merged with existing translations to help update your translations. + +Generating POT Files +==================== + +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: + +.. code-block:: console + + bin/cake i18n extract + +The above will run the extraction command. The result of this command will be the +file **resources/locales/default.pot**. You use the pot file as a template for creating +po files. If you are manually creating po files from the pot file, be sure to +correctly set the ``Plural-Forms`` header line. + +Generating POT Files for Plugins +-------------------------------- + +You can generate a POT file for a specific plugin using: + +.. code-block:: console + + bin/cake i18n extract --plugin + +This will generate the required POT files used in the plugins. + +Extracting from multiple folders at once +---------------------------------------- + +Sometimes, you might need to extract strings from more than one directory of +your application. For instance, if you are defining some strings in the +``config`` directory of your application, you probably want to extract strings +from this directory as well as from the ``src`` directory. You can do it by +using the ``--paths`` option. It takes a comma-separated list of absolute paths +to extract: + +.. code-block:: console + + bin/cake i18n extract --paths /var/www/app/config,/var/www/app/src + +Excluding Folders +----------------- + +You can pass a comma separated list of folders that you wish to be excluded. +Any path containing a path segment with the provided values will be ignored: + +.. code-block:: console + + bin/cake i18n extract --exclude vendor,tests + +Skipping Overwrite Warnings for Existing POT Files +-------------------------------------------------- + +By adding ``--overwrite``, the shell script will no longer warn you if a POT +file already exists and will overwrite by default: + +.. code-block:: console + + bin/cake i18n extract --overwrite + +Extracting Messages from the CakePHP Core Libraries +--------------------------------------------------- + +By default, the extract shell script will ask you if you like to extract +the messages used in the CakePHP core libraries. Set ``--extract-core`` to yes +or no to set the default behavior: + +.. code-block:: console + + bin/cake i18n extract --extract-core yes + + // or + + bin/cake i18n extract --extract-core no + +.. meta:: + :title lang=en: I18N command + :keywords lang=en: pot files,locale default,translation tools,message string,app locale,php class,validation,i18n,translations,command,models diff --git a/pt/console-commands/input-output.rst b/pt/console-commands/input-output.rst new file mode 100644 index 0000000000..3d03c3cd03 --- /dev/null +++ b/pt/console-commands/input-output.rst @@ -0,0 +1,375 @@ +Command Input/Output +#################### + +.. php:namespace:: Cake\Console +.. php:class:: ConsoleIo + +CakePHP provides the ``ConsoleIo`` object to commands so that they can +interactively read user input and output information to the user. + +.. _command-helpers: + +Command Helpers +=============== + +Command Helpers can be accessed and used from any command:: + + // Output some data as a table. + $io->helper('Table')->output($data); + + // Get a helper from a plugin. + $io->helper('Plugin.HelperName')->output($data); + +You can also get instances of helpers and call any public methods on them:: + + // Get and use the Progress Helper. + $progress = $io->helper('Progress'); + $progress->increment(10); + $progress->draw(); + +Creating Helpers +================ + +While CakePHP comes with a few command helpers you can create more in your +application or plugins. As an example, we'll create a simple helper to generate +fancy headings. First create the **src/Command/Helper/HeadingHelper.php** and put +the following in it:: + + _io->out($marker . ' ' . $args[0] . ' ' . $marker); + } + } + +We can then use this new helper in one of our shell commands by calling it:: + + // With ### on either side + $this->helper('Heading')->output(['It works!']); + + // With ~~~~ on either side + $this->helper('Heading')->output(['It works!', '~', 4]); + +Helpers generally implement the ``output()`` method which takes an array of +parameters. However, because Console Helpers are vanilla classes they can +implement additional methods that take any form of arguments. + +.. note:: + Helpers can also live in ``src/Shell/Helper`` for backwards compatibility. + +Built-In Helpers +================ + +Table Helper +------------ + +The TableHelper assists in making well formatted ASCII art tables. Using it is +pretty 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 | + +--------------+---------------+---------------+ + +You can use the ```` formatting tag in tables to right align +content:: + + $data = [ + ['Name', 'Total Price'], + ['Cake Mix', '1.50'], + ]; + $io->helper('Table')->output($data); + + // Outputs + +----------+-------------+ + | Name 1 | Total Price | + +----------+-------------+ + | Cake Mix | 1.50 | + +----------+-------------+ + +Progress Helper +--------------- + +The ProgressHelper can be used in two different ways. The simple mode lets you +provide a callback that is invoked until the progress is complete:: + + $io->helper('Progress')->output(['callback' => function ($progress) { + // Do work here. + $progress->increment(20); + $progress->draw(); + }]); + +You can control the progress bar more by providing additional options: + +- ``total`` The total number of items in the progress bar. Defaults + to 100. +- ``width`` The width of the progress bar. Defaults to 80. +- ``callback`` The callback that will be called in a loop to advance the + progress bar. + +An example of all the options in use would be:: + + $io->helper('Progress')->output([ + 'total' => 10, + 'width' => 20, + 'callback' => function ($progress) { + $progress->increment(2); + $progress->draw(); + } + ]); + +The progress helper can also be used manually to increment and re-render the +progress bar as necessary:: + + $progress = $io->helper('Progress'); + $progress->init([ + 'total' => 10, + 'width' => 20, + ]); + + $progress->increment(4); + $progress->draw(); + +Banner Helper +------------- + +The ``BannerHelper`` can be used to format one or more lines of text into +a banner with a background and horizontal padding:: + + $io->helper('Banner') + ->withPadding(5) + ->withStyle('success.bg') + ->output(['Work complete']); + +.. versionadded:: 5.1.0 + The ``BannerHelper`` was added in 5.1 + +Getting User Input +================== + +.. php:method:: ask($question, $choices = null, $default = null) + +When building interactive console applications you'll need to get user input. +CakePHP provides a way to do this:: + + // Get arbitrary text from the user. + $color = $io->ask('What color do you like?'); + + // Get a choice from the user. + $selection = $io->askChoice('Red or Green?', ['R', 'G'], 'R'); + +Selection validation is case-insensitive. + +Creating Files +============== + +.. php:method:: createFile($path, $contents) + +Creating files is often important part of many console commands that help +automate development and deployment. The ``createFile()`` method gives you +a simple interface for creating files with interactive confirmation:: + + // Create a file with confirmation on overwrite + $io->createFile('bower.json', $stuff); + + // Force overwriting without asking + $io->createFile('bower.json', $stuff, true); + +Creating Output +=============== + +.. php:method:out($message, $newlines, $level) +.. php:method:err($message, $newlines) + +Writing to ``stdout`` and ``stderr`` is another common operation in CakePHP:: + + // Write to stdout + $io->out('Normal message'); + + // Write to stderr + $io->err('Error message'); + +In addition to vanilla output methods, CakePHP provides wrapper methods that +style output with appropriate ANSI colors:: + + // Green text on stdout + $io->success('Success message'); + + // Cyan text on stdout + $io->info('Informational text'); + + // Blue text on stdout + $io->comment('Additional context'); + + // Red text on stderr + $io->error('Error text'); + + // Yellow text on stderr + $io->warning('Warning text'); + +Color formatting will automatically be disabled if ``posix_isatty`` returns +true, or if the ``NO_COLOR`` environment variable is set. + +``ConsoleIo`` provides two convenience methods regarding the output level:: + + // Would only appear when verbose output is enabled (-v) + $io->verbose('Verbose message'); + + // Would appear at all levels. + $io->quiet('Quiet message'); + +You can also create blank lines or draw lines of dashes:: + + // Output 2 newlines + $io->out($io->nl(2)); + + // Draw a horizontal line + $io->hr(); + +Lastly, you can update the current line of text on the screen:: + + $io->out('Counting down'); + $io->out('10', 0); + for ($i = 9; $i > 0; $i--) { + sleep(1); + $io->overwrite($i, 0, 2); + } + +.. note:: + It is important to remember, that you cannot overwrite text + once a new line has been output. + +.. _shell-output-level: + +Output Levels +============= + +Console applications often need different levels of verbosity. For example, when +running as a cron job, most output is un-necessary. You can use output levels to +flag output appropriately. The user of the shell, can then decide what level of +detail they are interested in by setting the correct flag when calling the +command. There are 3 levels: + +* ``QUIET`` - Only absolutely important information should be marked for quiet + output. +* ``NORMAL`` - The default level, and normal usage. +* ``VERBOSE`` - Mark messages that may be too noisy for everyday use, but + helpful for debugging as ``VERBOSE``. + +You can mark output as follows:: + + // Would appear at all levels. + $io->out('Quiet message', 1, ConsoleIo::QUIET); + $io->quiet('Quiet message'); + + // Would not appear when quiet output is toggled. + $io->out('normal message', 1, ConsoleIo::NORMAL); + $io->out('loud message', 1, ConsoleIo::VERBOSE); + $io->verbose('Verbose output'); + + // Would only appear when verbose output is enabled. + $io->out('extra message', 1, ConsoleIo::VERBOSE); + $io->verbose('Verbose output'); + +You can control the output level of commands, by using the ``--quiet`` and +``--verbose`` options. These options are added by default, and allow you to +consistently control output levels inside your CakePHP comands. + +The ``--quiet`` and ``--verbose`` options also control how logging data is +output to stdout/stderr. Normally info and higher log messages are output to +stdout/stderr. When ``--verbose`` is used, debug logs will be output to stdout. +When ``--quiet`` is used, only warning and higher log messages will be output to +stderr. + +Styling Output +============== + +Styling output is done by including tags - just like HTML - in your output. +These tags will be replaced with the correct ansi code sequence, or +stripped if you are on a console that doesn't support ansi codes. There +are several built-in styles, and you can create more. The built-in ones are + +* ``success`` Success messages. Green text. +* ``error`` Error messages. Red text. +* ``warning`` Warning messages. Yellow text. +* ``info`` Informational messages. Cyan text. +* ``comment`` Additional text. Blue text. +* ``question`` Text that is a question, added automatically by shell. +* ``info.bg`` White background with cyan text. +* ``warning.bg`` Yellow background with black text. +* ``error.bg`` Red background with black text. +* ``success.bg`` Green background with black text. + +You can create additional styles using ``$io->setStyle()``. To declare a +new output style you could do:: + + $io->setStyle('flashy', ['text' => 'magenta', 'blink' => true]); + +This would then allow you to use a ```` tag in your shell output, and if +ansi colors 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 colors for the ``text`` and +``background`` attributes: + +* black +* blue +* cyan +* green +* magenta +* red +* white +* yellow + +You can also use the following options as boolean switches, setting them to a +truthy value enables them. + +* blink +* bold +* reverse +* underline + +Adding a style makes it available on all instances of ConsoleOutput as well, +so you don't have to redeclare styles for both stdout and stderr objects. + +.. versionchanged:: 5.1.0 + The ``info.bg``, ``warning.bg``, ``error.bg``, and ``success.bg`` were added. + +Turning Off Coloring +==================== + +Although coloring is pretty, there may be times when you want to turn it off, +or force it on:: + + $io->outputAs(ConsoleOutput::RAW); + +The above will put the output object into raw output mode. In raw output mode, +no styling is done at all. There are three modes you can use. + +* ``ConsoleOutput::COLOR`` - Output with color escape codes in place. +* ``ConsoleOutput::PLAIN`` - Plain text output, known style tags will be + stripped from the output. +* ``ConsoleOutput::RAW`` - Raw output, no styling or formatting will be done. + This is a good mode to use if you are outputting XML or, want to debug why + your styling isn't working. + +By default on \*nix systems ConsoleOutput objects default to color output. +On Windows systems, plain output is the default unless the ``ANSICON`` +environment variable is present. diff --git a/pt/console-commands/option-parsers.rst b/pt/console-commands/option-parsers.rst new file mode 100644 index 0000000000..9d5152becc --- /dev/null +++ b/pt/console-commands/option-parsers.rst @@ -0,0 +1,367 @@ +Option Parsers +############## + +.. php:namespace:: Cake\Console +.. php:class:: ConsoleOptionParser + +Console applications typically take options and arguments as the primary way to +get information from the terminal into your commands. + +Defining an OptionParser +======================== + +Commands and Shells provide a ``buildOptionParser($parser)`` hook method that +you can use to define the options and arguments for your commands:: + + protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser + { + // Define your options and arguments. + + // Return the completed parser + return $parser; + } + +Shell classes use the ``getOptionParser()`` hook method to define their option +parser:: + + public function getOptionParser() + { + // Get an empty parser from the framework. + $parser = parent::getOptionParser(); + + // Define your options and arguments. + + // Return the completed parser + return $parser; + } + + +Using Arguments +=============== + +.. php:method:: addArgument($name, $params = []) + +Positional arguments are frequently used in command line tools, +and ``ConsoleOptionParser`` allows you to define positional +arguments as well as make them required. You can add arguments +one at a time with ``$parser->addArgument();`` or multiple at once +with ``$parser->addArguments();``:: + + $parser->addArgument('model', ['help' => 'The model to bake']); + +You can use the following options when creating an argument: + +* ``help`` The help text to display for this argument. +* ``required`` Whether this parameter is required. +* ``index`` The index for the arg, if left undefined the argument will be put + onto the end of the arguments. If you define the same index twice the + first option will be overwritten. +* ``choices`` An array of valid choices for this argument. If left empty all + values are valid. An exception will be raised when parse() encounters an + invalid value. +* ``separator`` A character sequence that separates arguments that should be + parsed into an array. + +Arguments that have been marked as required will throw an exception when +parsing the command if they have been omitted. So you don't have to +handle that in your shell. + +.. versionadded:: 5.2.0 + The ``separator`` option was added. + +Adding Multiple Arguments +------------------------- + +.. php:method:: addArguments(array $args) + +If you have an array with multiple arguments you can use +``$parser->addArguments()`` to add multiple arguments at once. :: + + $parser->addArguments([ + 'node' => ['help' => 'The node to create', 'required' => true], + 'parent' => ['help' => 'The parent node', 'required' => true], + ]); + +As with all the builder methods on ConsoleOptionParser, addArguments +can be used as part of a fluent method chain. + +Validating Arguments +-------------------- + +When creating positional arguments, you can use the ``required`` flag, to +indicate that an argument must be present when a shell is called. +Additionally you can use ``choices`` to force an argument to be from a list of +valid choices:: + + $parser->addArgument('type', [ + 'help' => 'The type of node to interact with.', + 'required' => true, + 'choices' => ['aro', 'aco'], + ]); + +The above will create an argument that is required and has validation on the +input. If the argument is either missing, or has an incorrect value an exception +will be raised and the shell will be stopped. + +Using Options +============= + +.. php:method:: addOption($name, array $options = []) + +Options or flags are used in command line tools to provide unordered key/value +arguments for your commands. Options can define both verbose and short aliases. +They can accept a value (e.g ``--connection=default``) or be boolean options +(e.g ``--verbose``). Options are defined with the ``addOption()`` method:: + + $parser->addOption('connection', [ + 'short' => 'c', + 'help' => 'connection', + 'default' => 'default', + ]); + +The above would allow you to use either ``cake myshell --connection=other``, +``cake myshell --connection other``, or ``cake myshell -c other`` +when invoking the shell. + +Boolean switches do not accept or consume values, and their presence just +enables them in the parsed parameters:: + + $parser->addOption('no-commit', ['boolean' => true]); + +This option when used like ``cake mycommand --no-commit something`` would have +a value of ``true``, and 'something' would be a treated as a positional +argument. + +When creating options you can use the following options to define the behavior +of the option: + +* ``short`` - The single letter variant for this option, leave undefined for + none. +* ``help`` - Help text for this option. Used when generating help for the + option. +* ``default`` - The default value for this option. If not defined the default + will be ``true``. +* ``boolean`` - The option uses no value, it's just a boolean switch. + Defaults to ``false``. +* ``multiple`` - The option can be provided multiple times. The parsed option + will be an array of values when this option is enabled. +* ``separator`` - A character sequence that the option value is split into an + array with. +* ``choices`` - An array of valid choices for this option. If left empty all + values are valid. An exception will be raised when parse() encounters an + invalid value. + + +.. versionadded:: 5.2.0 + The ``separator`` option was added. + +Adding Multiple Options +----------------------- + +.. php:method:: addOptions(array $options) + +If you have an array with multiple options you can use ``$parser->addOptions()`` +to add multiple options at once. :: + + $parser->addOptions([ + 'node' => ['short' => 'n', 'help' => 'The node to create'], + 'parent' => ['short' => 'p', 'help' => 'The parent node'], + ]); + +As with all the builder methods on ConsoleOptionParser, addOptions can be used +as part of a fluent method chain. + +Validating Options +------------------ + +Options can be provided with a set of choices much like positional arguments +can be. When an option has defined choices, those are the only valid choices +for an option. All other values will raise an ``InvalidArgumentException``:: + + $parser->addOption('accept', [ + 'help' => 'What version to accept.', + 'choices' => ['working', 'theirs', 'mine'], + ]); + +Using Boolean Options +--------------------- + +Options can be defined as boolean options, which are useful when you need to +create some flag options. Like options with defaults, boolean options always +include themselves into the parsed parameters. When the flags are present they +are set to ``true``, when they are absent they are set to ``false``:: + + $parser->addOption('verbose', [ + 'help' => 'Enable verbose output.', + 'boolean' => true + ]); + +The following option would always have a value in the parsed parameter. When not +included its default value would be ``false``, and when defined it will be +``true``. + +Building a ConsoleOptionParser from an Array +-------------------------------------------- + +.. php:method:: buildFromArray($spec) + +Option parsers can also be defined as arrays. Within the array, you can define +keys for ``arguments``, ``options``, ``description`` and ``epilog``. The values +for arguments, and options, should follow the format that +:php:func:`Cake\\Console\\ConsoleOptionParser::addArguments()` and +:php:func:`Cake\\Console\\ConsoleOptionParser::addOptions()` use. You can also +use ``buildFromArray`` on its own, to build an option parser:: + + 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')], + ], + ]); + } + +Merging Option Parsers +---------------------- + +.. php:method:: merge($spec) + +When building a group command, you maybe want to combine several parsers for +this:: + + $parser->merge($anotherParser); + +Note that the order of arguments for each parser must be the same, and that +options must also be compatible for it work. So do not use keys for different +things. + +Getting Help from Shells +======================== + +By defining your options and arguments with the option parser CakePHP can +automatically generate rudimentary help information and add a ``--help`` and +``-h`` to each of your commands. Using one of these options will allow you to +see the generated help content: + +.. code-block:: console + + bin/cake bake --help + bin/cake bake -h + +Would both generate the help for bake. You can also get help for nested +commands: + +.. code-block:: console + + bin/cake bake model --help + bin/cake bake model -h + +The above would get you the help specific to bake's model command. + +Getting Help as XML +------------------- + +When building automated tools or development tools that need to interact with +CakePHP shell commands, it's nice to have help available in a machine parse-able format. +By providing the ``xml`` option when requesting help you can have help content +returned as XML: + +.. code-block:: console + + cake bake --help xml + cake bake -h xml + +The above would return an XML document with the generated help, options, and +arguments for the selected shell. A sample XML document would +look like: + +.. 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. + + + + + + + + + + + + + + + + + +Customizing Help Output +======================= + +You can further enrich the generated help content by adding a description, and +epilog. + +Set the Description +------------------- + +.. php:method:: setDescription($text) + +The description displays above the argument and option information. By passing +in either an array or a string, you can set the value of the description:: + + // Set multiple lines at once + $parser->setDescription(['line one', 'line two']); + + // Read the current value + $parser->getDescription(); + +Set the Epilog +-------------- + +.. php:method:: setEpilog($text) + +Gets or sets the epilog for the option parser. The epilog is displayed after the +argument and option information. By passing in either an array or a string, you +can set the value of the epilog:: + + // Set multiple lines at once + $parser->setEpilog(['line one', 'line two']); + + // Read the current value + $parser->getEpilog(); diff --git a/pt/console-commands/plugin.rst b/pt/console-commands/plugin.rst new file mode 100644 index 0000000000..4a9fe5a5b5 --- /dev/null +++ b/pt/console-commands/plugin.rst @@ -0,0 +1,65 @@ +.. _plugin-shell: + +Plugin Tool +########### + +The plugin tool allows you to load and unload plugins via the command prompt. +If you need help, run: + +.. code-block:: console + + bin/cake plugin --help + +Loading Plugins +--------------- + +Via the ``Load`` task you are able to load plugins in your +**config/bootstrap.php**. You can do this by running: + +.. code-block:: console + + bin/cake plugin load MyPlugin + +This will add the following to your **src/Application.php**:: + + // In the bootstrap method add: + $this->addPlugin('MyPlugin'); + + +Unloading Plugins +----------------- + +You can unload a plugin by specifying its name: + +.. code-block:: console + + bin/cake plugin unload MyPlugin + +This will remove the line ``$this->addPlugin('MyPlugin',...)`` from +**src/Application.php**. + +Plugin Assets +------------- + +CakePHP by default serves plugins assets using the ``AssetMiddleware`` middleware. +While this is a good convenience, it is recommended to symlink / copy +the plugin assets under app's webroot so that they can be directly served by the +web server without invoking PHP. You can do this by running: + +.. code-block:: console + + bin/cake plugin assets symlink + +Running the above command will symlink all plugins assets under app's webroot. +On Windows, which doesn't support symlinks, the assets will be copied in +respective folders instead of being symlinked. + +You can symlink assets of one particular plugin by specifying its name: + +.. code-block:: console + + bin/cake plugin assets symlink MyPlugin + +.. meta:: + :title lang=en: Plugin tool + :keywords lang=en: plugin,assets,tool,load,unload diff --git a/pt/console-commands/repl.rst b/pt/console-commands/repl.rst new file mode 100644 index 0000000000..48465ba3bd --- /dev/null +++ b/pt/console-commands/repl.rst @@ -0,0 +1,50 @@ +Interactive Console (REPL) +########################## + +CakePHP offers +`REPL(Read Eval Print Loop) plugin `__ to let +you explore some CakePHP and your application in an interactive console. + +.. note:: + + The plugin was shipped with the CakePHP app skeleton before 4.3. + +You can start the interactive console using: + +.. code-block:: console + + bin/cake console + +This will bootstrap your application and start an interactive console. At this +point you can interact with your application code and execute queries using your +application's models: + +.. code-block:: console + + bin/cake console + + >>> $articles = Cake\Datasource\FactoryLocator::get('Table')->get('Articles'); + // object(Cake\ORM\Table)( + // + // ) + >>> $articles->find()->all(); + +Since your application has been bootstrapped you can also test routing using the +REPL:: + + >>> Cake\Routing\Router::parse('/articles/view/1'); + // [ + // 'controller' => 'Articles', + // 'action' => 'view', + // 'pass' => [ + // 0 => '1' + // ], + // 'plugin' => NULL + // ] + +You can also test generating URLs:: + + >>> Cake\Routing\Router::url(['controller' => 'Articles', 'action' => 'edit', 99]); + // '/articles/edit/99' + +To quit the REPL you can use ``CTRL-C`` or by typing ``exit``. diff --git a/pt/console-commands/routes.rst b/pt/console-commands/routes.rst new file mode 100644 index 0000000000..d3c0986984 --- /dev/null +++ b/pt/console-commands/routes.rst @@ -0,0 +1,40 @@ +Routes Tool +########### + +The routes tool provides a simple to use CLI interface for testing and debugging +routes. You can use it to test how routes are parsed, and what URLs routing +parameters will generate. + +Getting a List of all Routes +---------------------------- + +.. code-block:: console + + bin/cake routes + +Testing URL parsing +------------------- + +You can quickly see how a URL will be parsed using the ``check`` method: + +.. code-block:: console + + bin/cake routes check /articles/edit/1 + +If your route contains any query string parameters remember to surround the URL +in quotes: + +.. code-block:: console + + bin/cake routes check "/articles/?page=1&sort=title&direction=desc" + +Testing URL Generation +---------------------- + +You can see the URL a :term:`routing array` will generate using the +``generate`` method: + +.. code-block:: console + + bin/cake routes generate controller:Articles action:edit 1 + diff --git a/pt/console-commands/schema-cache.rst b/pt/console-commands/schema-cache.rst new file mode 100644 index 0000000000..df891ab6a5 --- /dev/null +++ b/pt/console-commands/schema-cache.rst @@ -0,0 +1,30 @@ +Schema Cache Tool +################# + +The SchemaCacheCommand provides a simple CLI tool for managing your application's +metadata caches. In deployment situations it is helpful to rebuild the metadata +cache in-place without clearing the existing cache data. You can do this by +running: + +.. code-block:: console + + bin/cake schema_cache build --connection default + +This will rebuild the metadata cache for all tables on the ``default`` +connection. If you only need to rebuild a single table you can do that by +providing its name: + +.. code-block:: console + + bin/cake schema_cache build --connection default articles + +In addition to building cached data, you can use the SchemaCacheShell to remove +cached metadata as well: + +.. code-block:: console + + # Clear all metadata + bin/cake schema_cache clear + + # Clear a single table + bin/cake schema_cache clear articles diff --git a/pt/console-commands/server.rst b/pt/console-commands/server.rst new file mode 100644 index 0000000000..7c3eb62f6e --- /dev/null +++ b/pt/console-commands/server.rst @@ -0,0 +1,30 @@ +Server Tool +########### + +The ``ServerCommand`` lets you stand up a simple webserver using the built in PHP +webserver. While this server is *not* intended for production use it can +be handy in development when you want to quickly try an idea out and don't want +to spend time configuring Apache or Nginx. You can start the server command with: + +.. code-block:: console + + bin/cake server + +You should see the server boot up and attach to port 8765. You can visit the +CLI server by visiting ``http://localhost:8765`` +in your web-browser. You can close the server by pressing ``CTRL-C`` in your +terminal. + +.. note:: + + Try ``bin/cake server -H 0.0.0.0`` if the server is unreachable from other hosts. + +Changing the Port and Document Root +=================================== + +You can customize the port and document root using options: + +.. code-block:: console + + bin/cake server --port 8080 --document_root path/to/app + diff --git a/pt/controllers/pagination.rst b/pt/controllers/pagination.rst new file mode 100644 index 0000000000..474674a046 --- /dev/null +++ b/pt/controllers/pagination.rst @@ -0,0 +1,296 @@ +Pagination +########## + +One of the main obstacles of creating flexible and user-friendly web +applications is designing an intuitive user interface. Many applications tend to +grow in size and complexity quickly, and designers and programmers alike find +they are unable to cope with displaying hundreds or thousands of records. +Refactoring takes time, and performance and user satisfaction can suffer. + +Displaying a reasonable number of records per page has always been a critical +part of every application and used to cause many headaches for developers. +CakePHP eases the burden on the developer by providing a terse way to +paginate data. + +Pagination in CakePHP controllers is done through the ``paginate()`` method. You +then use :php:class:`~Cake\\View\\Helper\\PaginatorHelper` in your view templates +to generate pagination controls. + +Basic Usage +=========== + +You can call ``paginate()`` using an ORM table instance or ``Query`` object:: + + public function index() + { + // Paginate the ORM table. + $this->set('articles', $this->paginate($this->Articles)); + + // Paginate a select query + $query = $this->Articles->find('published')->contain('Comments'); + $this->set('articles', $this->paginate($query)); + } + +Advanced Usage +============== + +More complex use cases are supported by configuring the ``$paginate`` +controller property or as the ``$settings`` argument to ``paginate()``. These +conditions serve as the basis for you pagination queries. They are augmented +by the ``sort``, ``direction``, ``limit``, and ``page`` parameters passed in +from the URL:: + + class ArticlesController extends AppController + { + protected array $paginate = [ + 'limit' => 25, + 'order' => [ + 'Articles.title' => 'asc', + ], + ]; + } + +.. tip:: + Default ``order`` options must be defined as an array. + +You can also use :ref:`custom-find-methods` in pagination by using the ``finder`` option:: + + class ArticlesController extends AppController + { + protected array $paginate = [ + 'finder' => 'published', + ]; + } + +Note: This only works with Table as string input in ``$this->paginate('MyTable')``. Once you use ``$this->MyTable->find()`` as input for ``paginate()``, you must directly use that Query object instead. + +If your finder method requires additional options you can pass those +as values for the finder:: + + class ArticlesController extends AppController + { + // find articles by tag + public function tags() + { + $tags = $this->request->getParam('pass'); + + $customFinderOptions = [ + 'tags' => $tags + ]; + // We're using the $settings argument to paginate() here. + // But the same structure could be used in $this->paginate + // + // Our custom finder is called findTagged inside ArticlesTable.php + // which is why we're using `tagged` as the key. + // Our finder should look like: + // public function findTagged(Query $query, array $tagged = []) + $settings = [ + 'finder' => [ + 'tagged' => $customFinderOptions + ] + ]; + $articles = $this->paginate($this->Articles, $settings); + $this->set(compact('articles', 'tags')); + } + } + +In addition to defining general pagination values, you can define more than one +set of pagination defaults in the controller. The name of each model can be used +as a key in the ``$paginate`` property:: + + class ArticlesController extends AppController + { + protected array $paginate = [ + 'Articles' => [], + 'Authors' => [], + ]; + } + +The values of the ``Articles`` and ``Authors`` keys could contain all the keys +that a basic ``$paginate`` array would. + +``Controller::paginate()`` returns an instance of ``Cake\Datasource\Paging\PaginatedResultSet`` +which implements the ``Cake\Datasource\Paging\PaginatedInterface``. + +This object contains the paginated records and the paging params. + +Simple Pagination +================= + +By default ``Controller::paginate()`` uses the ``Cake\Datasource\Paging\NumericPaginator`` +class which does a ``COUNT()`` query to calculate the size of the result set so +that page number links can be rendered. On very large datasets this count query +can be very expensive. In situations where you only want to show 'Next' and 'Previous' +links you can use the 'simple' paginator which does not do a count query:: + + class ArticlesController extends AppController + { + protected array $paginate = [ + 'className' => 'Simple', // Or use Cake\Datasource\Paging\SimplePaginator::class FQCN + ]; + } + +When using the ``SimplePaginator`` you will not be able to generate page +numbers, counter data, links to the last page, or total record count controls. + +.. _paginating-multiple-queries: + +Paginating Multiple Queries +=========================== + +You can paginate multiple models in a single controller action, using the +``scope`` option both in the controller's ``$paginate`` property and in the +call to the ``paginate()`` method:: + + // Paginate property + protected array $paginate = [ + 'Articles' => ['scope' => 'article'], + 'Tags' => ['scope' => 'tag'] + ]; + + // In a controller action + $articles = $this->paginate($this->Articles, ['scope' => 'article']); + $tags = $this->paginate($this->Tags, ['scope' => 'tag']); + $this->set(compact('articles', 'tags')); + +The ``scope`` option will result in the paginator looking in +scoped query string parameters. For example, the following URL could be used to +paginate both tags and articles at the same time:: + + /dashboard?article[page]=1&tag[page]=3 + +See the :ref:`paginator-helper-multiple` section for how to generate scoped HTML +elements and URLs for pagination. + +Paginating the Same Model multiple Times +---------------------------------------- + +To paginate the same model multiple times within a single controller action you +need to define an alias for the model.:: + + // In a controller action + $this->paginate = [ + 'Articles' => [ + 'scope' => 'published_articles', + 'limit' => 10, + 'order' => [ + 'id' => 'desc', + ], + ], + 'UnpublishedArticles' => [ + 'scope' => 'unpublished_articles', + 'limit' => 10, + 'order' => [ + 'id' => 'desc', + ], + ], + ]; + + $publishedArticles = $this->paginate( + $this->Articles->find('all', scope: 'published_articles') + ->where(['published' => true]) + ); + + // Load an additional table object to allow differentiating in the paginator + $unpublishedArticlesTable = $this->fetchTable('UnpublishedArticles', [ + 'className' => 'App\Model\Table\ArticlesTable', + 'table' => 'articles', + 'entityClass' => 'App\Model\Entity\Article', + ]); + + $unpublishedArticles = $this->paginate( + $unpublishedArticlesTable->find('all', scope: 'unpublished_articles') + ->where(['published' => false]) + ); + +.. _control-which-fields-used-for-ordering: + +Control which Fields Used for Ordering +====================================== + +By default sorting can be done on any non-virtual column a table has. This is +sometimes undesirable as it allows users to sort on un-indexed columns that can +be expensive to order by. You can set the allowed list of fields that can be sorted +using the ``sortableFields`` option. This option is required when you want to +sort on any associated data, or computed fields that may be part of your +pagination query:: + + protected array $paginate = [ + 'sortableFields' => [ + 'id', 'title', 'Users.username', 'created', + ], + ]; + +Any requests that attempt to sort on fields not in the allowed list will be +ignored. + +Limit the Maximum Number of Rows per Page +========================================= + +The number of results that are fetched per page is exposed to the user as the +``limit`` parameter. It is generally undesirable to allow users to fetch all +rows in a paginated set. The ``maxLimit`` option asserts that no one can set +this limit too high from the outside. By default CakePHP limits the maximum +number of rows that can be fetched to 100. If this default is not appropriate +for your application, you can adjust it as part of the pagination options, for +example reducing it to ``10``:: + + protected array $paginate = [ + // Other keys here. + 'maxLimit' => 10 + ]; + +If the request's limit param is greater than this value, it will be reduced to +the ``maxLimit`` value. + +Out of Range Page Requests +========================== + +``Controller::paginate()`` will throw a ``NotFoundException`` when trying to +access a non-existent page, i.e. page number requested is greater than total +page count. + +So you could either let the normal error page be rendered or use a try catch +block and take appropriate action when a ``NotFoundException`` is caught:: + + use Cake\Http\Exception\NotFoundException; + + public function index() + { + try { + $this->paginate(); + } catch (NotFoundException $e) { + // Do something here like redirecting to first or last page. + // $e->getPrevious()->getAttributes('pagingParams') will give you required info. + } + } + +Using a paginator class directly +================================ + +You can also use a paginator directly.:: + + // Create a paginator + $paginator = new \Cake\Datasource\Paginator\NumericPaginator(); + + // Paginate the model + $results = $paginator->paginate( + // Query or table instance which you need to paginate + $this->fetchTable('Articles'), + // Request params + $this->request->getQueryParams(), + // Config array having the same structure as options as Controller::$paginate + [ + 'finder' => 'latest', + ] + ); + +Pagination in the View +====================== + +Check the :php:class:`~Cake\\View\\Helper\\PaginatorHelper` documentation for +how to create links for pagination navigation. + +.. meta:: + :title lang=en: Pagination + :keywords lang=en: paginate,pagination,paging diff --git a/pt/core-libraries/plugin.rst b/pt/core-libraries/plugin.rst new file mode 100644 index 0000000000..1b0065a13e --- /dev/null +++ b/pt/core-libraries/plugin.rst @@ -0,0 +1,53 @@ +Plugin Class +############ + +.. php:namespace:: Cake\Core + +.. php:class:: Plugin + +The Plugin class is responsible for resource location and path management of plugins. + +Locating Plugins +================ + +.. php:staticmethod:: path(string $plugin) + +Plugins can be located with Plugin. Using ``Plugin::path('DebugKit');`` +for example, will give you the full path to the DebugKit plugin:: + + $path = Plugin::path('DebugKit'); + +Check if a Plugin is Loaded +=========================== + +You can check dynamically inside your code if a specific plugin has been loaded:: + + $isLoaded = Plugin::isLoaded('DebugKit'); + +Use ``Plugin::loaded()`` if you want to get a list of all currently loaded plugins. + +Finding Paths to Namespaces +=========================== + +.. php:staticmethod:: classPath(string $plugin) + +Used to get the location of the plugin's class files:: + + $path = App::classPath('DebugKit'); + +Finding Paths to Resources +========================== + +.. php:staticmethod:: templatePath(string $plugin) + +The method returns the path to the plugins' templates:: + + $path = Plugin::templatePath('DebugKit'); + +The same goes for the config path:: + + $path = Plugin::configPath('DebugKit'); + +.. meta:: + :title lang=en: Plugin Class + :keywords lang=en: compatible implementation,model behaviors,path management,loading files,php class,class loading,model behavior,class location,component model,management class,autoloader,classname,directory location,override,conventions,lib,textile,cakephp,php classes,loaded diff --git a/pt/development/dependency-injection.rst b/pt/development/dependency-injection.rst new file mode 100644 index 0000000000..1fcf43615b --- /dev/null +++ b/pt/development/dependency-injection.rst @@ -0,0 +1,352 @@ +Dependency Injection +#################### + +The CakePHP service container enables you to manage class dependencies for your +application services through dependency injection. Dependency injection +automatically "injects" an object's dependencies via the constructor without +having to manually instantiate them. + +You can use the service container to define 'application services'. These +classes can use models and interact with other objects like loggers and mailers +to build re-usable workflows and business logic for your application. + +CakePHP will use the :term:`DI container` in the following situations: + +* Constructing controllers. +* Calling actions on your controllers. +* Constructing Components. +* Constructing Console Commands. +* Constructing Middleware by classname. + +Controller Example +================== + +:: + + // In src/Controller/UsersController.php + class UsersController extends AppController + { + // The $users service will be created via the service container. + public function ssoCallback(UsersService $users) + { + if ($this->request->is('post')) { + // Use the UsersService to create/get the user from a + // Single Signon Provider. + $user = $users->ensureExists($this->request->getData()); + } + } + } + + // In src/Application.php + public function services(ContainerInterface $container): void + { + $container->add(UsersService::class); + } + +In this example, the ``UsersController::ssoCallback()`` action needs to fetch +a user from a Single-Sign-On provider and ensure it exists in the local +database. Because this service is injected into our controller, we can easily +swap the implementation out with a mock object or a dummy sub-class when +testing. + +Command Example +=============== + +:: + + // In src/Command/CheckUsersCommand.php + use Cake\Console\CommandFactoryInterface; + + class CheckUsersCommand extends Command + { + public function __construct(protected UsersService $users, ?CommandFactoryInterface $factory = null) + { + parent::__construct($factory); + } + + public function execute(Arguments $args, ConsoleIo $io) + { + $valid = $this->users->check('all'); + } + + } + + // In src/Application.php + public function services(ContainerInterface $container): void + { + $container + ->add(CheckUsersCommand::class) + ->addArgument(UsersService::class) + ->addArgument(CommandFactoryInterface::class); + $container->add(UsersService::class); + } + +The injection process is a bit different here. Instead of adding the +``UsersService`` to the container we first have to add the Command as +a whole to the Container and add the ``UsersService`` as an argument. +With that you can then access that service inside the constructor +of the command. + +Component Example +================= + +:: + + // In src/Controller/Component/SearchComponent.php + class SearchComponent extends Component + { + public function __construct( + ComponentRegistry $registry, + private UserService $users, + array $config = [] + ) { + parent::__construct($registry, $config); + } + + public function something() + { + $valid = $this->users->check('all'); + } + } + + // In src/Application.php + public function services(ContainerInterface $container): void + { + $container->add(SearchComponent::class) + ->addArgument(ComponentRegistry::class) + ->addArgument(UsersService::class); + $container->add(UsersService::class); + } + +Adding Services +=============== + +In order to have services created by the container, you need to tell it which +classes it can create and how to build those classes. The +simplest definition is via a class name:: + + // Add a class by its name. + $container->add(BillingService::class); + +Your application and plugins define the services they have in the +``services()`` hook method:: + + // in src/Application.php + namespace App; + + use App\Service\BillingService; + use Cake\Core\ContainerInterface; + use Cake\Http\BaseApplication; + + class Application extends BaseApplication + { + public function services(ContainerInterface $container): void + { + $container->add(BillingService::class); + } + } + +You can define implementations for interfaces that your application uses:: + + use App\Service\AuditLogServiceInterface; + use App\Service\AuditLogService; + + // in your Application::services() method. + + // Add an implementation for an interface. + $container->add(AuditLogServiceInterface::class, AuditLogService::class); + +The container can leverage factory functions to create objects if necessary:: + + $container->add(AuditLogServiceInterface::class, function (...$args) { + return new AuditLogService(...$args); + }); + +Factory functions will receive all of the resolved dependencies for the class +as arguments. + +Once you've defined a class, you also need to define the dependencies it +requires. Those dependencies can be either objects or primitive values:: + + // Add a primitive value like a string, array or number. + $container->add('apiKey', 'abc123'); + + $container->add(BillingService::class) + ->addArgument('apiKey'); + +Your services can depend on ``ServerRequest`` in controller actions as it will +be added automatically. + +Adding Shared Services +---------------------- + +By default services are not shared. Every object (and dependencies) is created +each time it is fetched from the container. If you want to re-use a single +instance, often referred to as a singleton, you can mark a service as 'shared':: + + // in your Application::services() method. + + $container->addShared(BillingService::class); + +Extending Definitions +--------------------- + +Once a service is defined you can modify or update the service definition by +extending them. This allows you to add additional arguments to services defined +elsewhere:: + + // Add an argument to a partially defined service elsewhere. + $container->extend(BillingService::class) + ->addArgument('logLevel'); + +Tagging Services +---------------- + +By tagging services you can get all of those services resolved at the same +time. This can be used to build services that combine collections of other +services like in a reporting system:: + + $container->add(BillingReport::class)->addTag('reports'); + $container->add(UsageReport::class)->addTag('reports'); + + $container->add(ReportAggregate::class, function () use ($container) { + return new ReportAggregate($container->get('reports')); + }); + +Using Configuration Data +------------------------ + +Often you'll need configuration data in your services. While you could add +all the configuration keys your service needs into the container, that can be +tedious. To make configuration easier to work with CakePHP includes an +injectable configuration reader:: + + use Cake\Core\ServiceConfig; + + // Use a shared instance + $container->addShared(ServiceConfig::class); + +The ``ServiceConfig`` class provides a read-only view of all the data available +in ``Configure`` so you don't have to worry about accidentally changing +configuration. + +Service Providers +================= + +Service providers allow you to group related services together helping you +organize your services. Service providers can help increase your application's +performance as defined services are lazily registered after +their first use. + +Creating Service Providers +-------------------------- + +An example ServiceProvider would look like:: + + namespace App\ServiceProvider; + + use Cake\Core\ContainerInterface; + use Cake\Core\ServiceProvider; + // Other imports here. + + class BillingServiceProvider extends ServiceProvider + { + protected $provides = [ + StripeService::class, + 'configKey', + ]; + + public function services(ContainerInterface $container): void + { + $container->add(StripeService::class); + $container->add('configKey', 'some value'); + } + } + +Service providers use their ``services()`` method to define all the services they +will provide. Additionally those services **must be** defined in the ``$provides`` +property. Failing to include a service in the ``$provides`` property will result +in it not be loadable from the container. + +Using Service Providers +----------------------- + +To load a service provider add it into the container using the +``addServiceProvider()`` method:: + + // in your Application::services() method. + $container->addServiceProvider(new BillingServiceProvider()); + +Bootable ServiceProviders +------------------------- + +If your service provider needs to run logic when it is added to the container, +you can implement the ``bootstrap()`` method. This situation can come up when your +service provider needs to load additional configuration files, load additional +service providers or modify a service defined elsewhere in your application. An +example of a bootable service would be:: + + namespace App\ServiceProvider; + + use Cake\Core\ServiceProvider; + // Other imports here. + + class BillingServiceProvider extends ServiceProvider + { + protected $provides = [ + StripeService::class, + 'configKey', + ]; + + public function bootstrap($container) + { + $container->addServiceProvider(new InvoicingServiceProvider()); + } + } + + +.. _mocking-services-in-tests: + +Mocking Services in Tests +========================= + +In tests that use ``ConsoleIntegrationTestTrait`` or ``IntegrationTestTrait`` +you can replace services that are injected via the container with mocks or +stubs:: + + // In a test method or setup(). + $this->mockService(StripeService::class, function () { + return new FakeStripe(); + }); + + // If you need to remove a mock + $this->removeMockService(StripeService::class); + +Any defined mocks will be replaced in your application's container during +testing, and automatically injected into your controllers and commands. Mocks +are cleaned up at the end of each test. + +Auto Wiring +=============== + +Auto Wiring is turned off by default. To enable it:: + + // In src/Application.php + public function services(ContainerInterface $container): void + { + $container->delegate( + new \League\Container\ReflectionContainer() + ); + } + +While your dependencies will now be resolved automatically, this approach will +not cache resolutions which can be detrimental to performance. To enable +caching:: + + $container->delegate( + // or consider using the value of Configure::read('debug') + new \League\Container\ReflectionContainer(true) + ); + +Read more about auto wiring in the `PHP League Container documentation +`_. diff --git a/pt/phinx.rst b/pt/phinx.rst new file mode 100644 index 0000000000..2f0093792f --- /dev/null +++ b/pt/phinx.rst @@ -0,0 +1,4 @@ +Migrações Phinx +############### + +Esta página foi `movida `__. diff --git a/pt/release-policy.rst b/pt/release-policy.rst new file mode 100644 index 0000000000..01dc68bc81 --- /dev/null +++ b/pt/release-policy.rst @@ -0,0 +1,69 @@ +Release Policy +############## + +CakePHP follows Semantic Versioning for all releases. This follows the versioning +convention of **major.minor.patch**. + +The development team tries to guarantee each release follow the restrictions and +and guarantees below. + +Major Releases +-------------- + +Major releases are generally not backwards compatible. Although CakePHP tries +to not change many large features in major releases, there are API changes. + +Changes in major release can include almost anything but are always used to +remove deprecated features and update interfaces. + +Any behavior changes that are not backwards compatible are made in major changes. + +Each major release typically comes with an upgrade guide and many automatic +code upgrades using rector. + +Minor Releases +-------------- + +Minor release are generally backwards compatible with the previous minor and patch +release. + +Features might be deprecated, but they are never removed in a minor release. + +Interfaces are not changed, but annotations might be added for new methods exposed +in implementations provided by CakePHP. + +New features are usually only added in minor releases so users can follow migration +notes. New features can also include new exceptions thrown when behavior is fixed +or bugs are reported. + +Behavior changes that require documentation are made in minor releases, but these are +still typically backwards compatible. Some exceptions can be made if the issue is severe. + +.. note: + Minor releases are also known as point releases. + +Patch Releases +---------------- + +Patch releases are always backwards compatible. Only changes that fix broken features +are made. + +Typically, users should be able to rely on patch releases not changing behavior except +to fix an issue. + +Issues that change long-standing behavior are typically not in patch releases. These are +considered behavior changes and will go into either minor or major releases so users can +migrate. + +.. note: + Patch releases are also known as bug fix releases. + +Experimental Features +--------------------- + +When a new feature is added where the API is still changing, it can be marked **experimental**. + +Experimental features should follow the same minor and bug fix release convention. However, +API changes can go into minor releases which might significantly change behavior. + +Users should always expect an API to change before experimental features are fully released. diff --git a/pt/standalone-packages.rst b/pt/standalone-packages.rst new file mode 100644 index 0000000000..ccc0ffb66d --- /dev/null +++ b/pt/standalone-packages.rst @@ -0,0 +1,77 @@ +Standalone Packages +################### + +The CakePHP core is split into various standalone packages which can +used independently. + +`ORM `_ +--------------------------------------- + +A flexible, lightweight and powerful Object-Relational Mapper for PHP, +implemented using the DataMapper pattern. + +`Database `_ +------------------------------------------------- + +Flexible and powerful Database abstraction library with a familiar PDO-like API. + +`Datasource `_ +----------------------------------------------------- + +Provides connection managing and traits for Entities and Queries that can be +reused for different datastores. + +`HTTP `_ +----------------------------------------- + +PSR-18, PSR-15 compliant HTTP client and server libraries. + +`Console `_ +----------------------------------------------- + +A library for building command line applications from a set of commands. + +`Collection `_ +----------------------------------------------------- + +A library providing a set of tools to manipulate arrays or Traversable objects. + +`I18n `_ +----------------------------------------- + +Provides support for message translation and localization for dates and numbers. + +`Cache `_ +------------------------------------------- + +PSR-16 compliant caching library with support for multiple caching backends. + +`Log `_ +--------------------------------------- + +PSR-3 compliant logging library with support for multiple different streams. + +`Event `_ +------------------------------------------- + +The event dispatcher library. + +`Utility `_ +----------------------------------------------- + +Utility classes such as Inflector, Text, Hash, Security and Xml. + +`Validation `_ +----------------------------------------------------- + +Validation library from CakePHP. + +`Form `_ +----------------------------------------- + +Form abstraction used to create forms not tied to ORM backed models, or to other +permanent datastores. + +.. meta:: + :title lang=en: Split Packages + :keywords lang=en: packages, cakephp, orm, database, http client, http server, utility, events, log, cache diff --git a/pt/tutorials-and-examples/cms/articles-model.rst b/pt/tutorials-and-examples/cms/articles-model.rst new file mode 100644 index 0000000000..a415595707 --- /dev/null +++ b/pt/tutorials-and-examples/cms/articles-model.rst @@ -0,0 +1,88 @@ +Tutorial CMS - Criando nosso primeiro Modelo +############################################ + +Os modelos são o coração das aplicações CakePHP. Eles nos permitem ler e +modificar nossos dados. Eles nos permitem construir relações entre nossos dados, validar +dados e aplicar regras de aplicação. Os modelos fornecem a base necessária para +criar nossas ações de controller e models. + +Os modelos do CakePHP são compostos por objetos ``Table`` e ``Entity``. Os objetos ``Table`` +fornecem acesso à coleção de entidades armazenadas em uma tabela específica. +Eles são armazenados em **src/Model/Table**. O arquivo que criaremos será salvo +em **src/Model/Table/ArticlesTable.php**. O arquivo completo deverá +ficar assim:: + + addBehavior('Timestamp'); + } + } + +Anexamos o comportamento :doc:`/orm/behaviors/timestamp`, que irá +preencher automaticamente as colunas ``created`` e ``modified`` da nossa tabela. +Ao nomear nosso objeto Table como ``ArticlesTable``, o CakePHP pode usar convenções de nomenclatura +para saber que nosso modelo usa a tabela ``articles``. O CakePHP também usa +convenções para saber que a coluna ``id`` é a chave primária da nossa tabela. + +.. note:: + + O CakePHP criará dinamicamente um objeto de modelo para você caso + não encontre um arquivo correspondente em **src/Model/Table**. Isso também significa + que, se você acidentalmente nomear seu arquivo incorretamente (por exemplo, articlestable.php ou + ArticleTable.php), o CakePHP não reconhecerá nenhuma de suas configurações e + usará o modelo gerado. + +Também criaremos uma classe Entity para nossos Artigos. Entidades representam um único +registro no banco de dados e fornecem comportamento em nível de linha para nossos dados. Nossa entidade +será salva em **src/Model/Entity/Article.php**. O arquivo completo deverá +ficar assim:: + + true, + 'title' => true, + 'slug' => true, + 'body' => true, + 'published' => true, + 'created' => true, + 'modified' => true, + 'user' => true, + 'tags' => true, + ]; + } + +No momento, nossa entidade é bem enxuta; configuramos apenas a propriedade ``_accessible``, +que controla como as propriedades podem ser modificadas por +:ref:`entities-mass-assignment`. + +.. tip:: + As classes de entidade ``ArticlesTable`` e ``Article`` podem ser geradas a partir de um + terminal: + + .. code-block:: console + + bin/cake bake model articles + +Ainda não podemos fazer muita coisa com este modelo. Em seguida, criaremos nosso primeiro +:doc:`Controller e Template ` +para nos permitir interagir com nosso modelo. diff --git a/pt/tutorials-and-examples/cms/authorization.rst b/pt/tutorials-and-examples/cms/authorization.rst new file mode 100644 index 0000000000..41a0194ec5 --- /dev/null +++ b/pt/tutorials-and-examples/cms/authorization.rst @@ -0,0 +1,255 @@ +CMS Tutorial - Authorization +############################ + +With users now able to login to our CMS, we want to apply authorization rules +to ensure that each user only edits the posts they own. We'll use the +`authorization plugin `__ to do this. + +Installing Authorization Plugin +================================ + +Use composer to install the Authorization Plugin: + +.. code-block:: console + + composer require "cakephp/authorization:^3.0" + +Load the plugin by adding the following statement to the ``bootstrap()`` method in **src/Application.php**:: + + $this->addPlugin('Authorization'); + +Enabling the Authorization Plugin +================================= + +The Authorization plugin integrates into your application as a middleware layer +and optionally a component to make checking authorization easier. First, lets +apply the middleware. In **src/Application.php** add the following to the class +imports:: + + use Authorization\AuthorizationService; + use Authorization\AuthorizationServiceInterface; + use Authorization\AuthorizationServiceProviderInterface; + use Authorization\Middleware\AuthorizationMiddleware; + use Authorization\Policy\OrmResolver; + +Add the ``AuthorizationServiceProviderInterface`` to the implemented interfaces on your application:: + + class Application extends BaseApplication + implements AuthenticationServiceProviderInterface, + AuthorizationServiceProviderInterface + +Then add the following to your ``middleware()`` method:: + + // Add authorization **after** authentication + $middlewareQueue->add(new AuthorizationMiddleware($this)); + +The ``AuthorizationMiddleware`` will call a hook method on your application when +it starts handling the request. This hook method allows your application to +define the ``AuthorizationService`` it wants to use. Add the following method your +**src/Application.php**:: + + public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface + { + $resolver = new OrmResolver(); + + return new AuthorizationService($resolver); + } + +The OrmResolver lets the authorization plugin find policy classes for ORM +entities and queries. Other resolvers can be used to find policies for other +resources types. + +Next, lets add the ``AuthorizationComponent`` to ``AppController``. In +**src/Controller/AppController.php** add the following to the ``initialize()`` +method:: + + $this->loadComponent('Authorization.Authorization'); + +Lastly we'll mark the add, login, and logout actions as not requiring +authorization by adding the following to +**src/Controller/UsersController.php**:: + + // In the add, login, and logout methods + $this->Authorization->skipAuthorization(); + +The ``skipAuthorization()`` method should be called in any controller action +that should be accessible to all users even those who have not logged in yet. + +Creating our First Policy +========================= + +The Authorization plugin models authorization and permissions as Policy classes. +These classes implement the logic to check whether or not a **identity** is +allowed to **perform an action** on a given **resource**. Our **identity** is +going to be our logged in user, and our **resources** are our ORM entities and +queries. Lets use bake to generate a basic policy: + +.. code-block:: console + + bin/cake bake policy --type entity Article + +This will generate an empty policy class for our ``Article`` entity. You can +find the generated policy in **src/Policy/ArticlePolicy.php**. Next update the +policy to look like the following:: + + isAuthor($user, $article); + } + + public function canDelete(IdentityInterface $user, Article $article) + { + // logged in users can delete their own articles. + return $this->isAuthor($user, $article); + } + + protected function isAuthor(IdentityInterface $user, Article $article) + { + return $article->user_id === $user->getIdentifier(); + } + } + +While we've defined some very simple rules, you can use as complex logic as your +application requires in your policies. + +Checking Authorization in the ArticlesController +================================================ + +With our policy created we can start checking authorization in each controller +action. If we forget to check or skip authorization in an controller action the +Authorization plugin will raise an exception letting us know we forgot to apply +authorization. In **src/Controller/ArticlesController.php** add the following to +the ``add``, ``edit`` and ``delete`` methods:: + + public function add() + { + $article = $this->Articles->newEmptyEntity(); + $this->Authorization->authorize($article); + // Rest of the method + } + + public function edit($slug) + { + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') // load associated Tags + ->firstOrFail(); + $this->Authorization->authorize($article); + // Rest of the method. + } + + public function delete($slug) + { + $this->request->allowMethod(['post', 'delete']); + + $article = $this->Articles->findBySlug($slug)->firstOrFail(); + $this->Authorization->authorize($article); + // Rest of the method. + } + +The ``AuthorizationComponent::authorize()`` method will use the current +controller action name to generate the policy method to call. If you'd like to +call a different policy method you can call ``authorize`` with the operation +name:: + + $this->Authorization->authorize($article, 'update'); + +Lastly add the following to the ``tags``, ``view``, and ``index`` methods on the +``ArticlesController``:: + + // View, index and tags actions are public methods + // and don't require authorization checks. + $this->Authorization->skipAuthorization(); + +Fixing the Add & Edit Actions +============================= + +While we've blocked access to the edit action, we're still open to users +changing the ``user_id`` attribute of articles during edit. We +will solve these problems next. First up is the ``add`` action. + +When creating articles, we want to fix the ``user_id`` to be the currently +logged in user. Replace your add action with the following:: + + // in src/Controller/ArticlesController.php + + public function add() + { + $article = $this->Articles->newEmptyEntity(); + $this->Authorization->authorize($article); + + if ($this->request->is('post')) { + $article = $this->Articles->patchEntity($article, $this->request->getData()); + + // Changed: Set the user_id from the current user. + $article->user_id = $this->request->getAttribute('identity')->getIdentifier(); + + 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.')); + } + $tags = $this->Articles->Tags->find('list')->all(); + $this->set(compact('article', 'tags')); + } + +Next we'll update the ``edit`` action. Replace the edit method with the following:: + + // in src/Controller/ArticlesController.php + + public function edit($slug) + { + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') // load associated Tags + ->firstOrFail(); + $this->Authorization->authorize($article); + + if ($this->request->is(['post', 'put'])) { + $this->Articles->patchEntity($article, $this->request->getData(), [ + // Added: Disable modification of user_id. + 'accessibleFields' => ['user_id' => false] + ]); + if ($this->Articles->save($article)) { + $this->Flash->success(__('Your article has been updated.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('Unable to update your article.')); + } + $tags = $this->Articles->Tags->find('list')->all(); + $this->set(compact('article', 'tags')); + } + +Here we're modifying which properties can be mass-assigned, via the options +for ``patchEntity()``. See the :ref:`changing-accessible-fields` section for +more information. Remember to remove the ``user_id`` control from +**templates/Articles/edit.php** as we no longer need it. + +Wrapping Up +=========== + +We've built a simple CMS application that allows users to login, post articles, +tag them, explore posted articles by tag, and applied basic access control to +articles. We've also added some nice UX improvements by leveraging the +FormHelper and ORM capabilities. + +Thank you for taking the time to explore CakePHP. Next, you should learn more about +the :doc:`/orm`, or you peruse the :doc:`/topics`. diff --git a/pt/tutorials-and-examples/cms/tags-and-users.rst b/pt/tutorials-and-examples/cms/tags-and-users.rst new file mode 100644 index 0000000000..fed181f406 --- /dev/null +++ b/pt/tutorials-and-examples/cms/tags-and-users.rst @@ -0,0 +1,505 @@ +CMS Tutorial - Tags and Users +############################# + +With the basic article creation functionality built, we need to enable multiple +authors to work in our CMS. Previously, we built all the models, views and +controllers by hand. This time around we're going to use +:doc:`/bake` to create our skeleton code. Bake is a powerful +code generation :abbr:`CLI (Command Line Interface)` tool that leverages the +conventions CakePHP uses to create skeleton :abbr:`CRUD (Create, Read, Update, +Delete)` applications very efficiently. We're going to use ``bake`` to build our +users code: + +.. code-block:: console + + cd /path/to/our/app + + # You can overwrite any existing files. + bin/cake bake model users + bin/cake bake controller users + bin/cake bake template users + +These 3 commands will generate: + +* The Table, Entity, Fixture files. +* The Controller +* The CRUD templates. +* Test cases for each generated class. + +Bake will also use the CakePHP conventions to infer the associations, and +validation your models have. + +Adding Tagging to Articles +========================== + +With multiple users able to access our small :abbr:`CMS` it would be nice to +have a way to categorize our content. We'll use tags and tagging to allow users +to create free-form categories and labels for their content. Again, we'll use +``bake`` to quickly generate some skeleton code for our application: + +.. code-block:: console + + # Generate all the code at once. + bin/cake bake all tags + +Once you have the scaffold code created, create a few sample tags by going to +**http://localhost:8765/tags/add**. + +Now that we have a Tags table, we can create an association between Articles and +Tags. We can do so by adding the following to the ``initialize`` method on the +``ArticlesTable``:: + + public function initialize(array $config): void + { + $this->addBehavior('Timestamp'); + $this->belongsToMany('Tags'); // Add this line + } + +This association will work with this simple definition because we followed +CakePHP conventions when creating our tables. For more information, read +:doc:`/orm/associations`. + +Updating Articles to Enable Tagging +=================================== + +Now that our application has tags, we need to enable users to tag their +articles. First, update the ``add`` action to look like:: + + Articles->newEmptyEntity(); + if ($this->request->is('post')) { + $article = $this->Articles->patchEntity($article, $this->request->getData()); + + // Hardcoding the user_id is temporary, and will be removed later + // when we build authentication out. + $article->user_id = 1; + + 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.')); + } + // Get a list of tags. + $tags = $this->Articles->Tags->find('list')->all(); + + // Set tags to the view context + $this->set('tags', $tags); + + $this->set('article', $article); + } + + // Other actions + } + +The added lines load a list of tags as an associative array of ``id => title``. +This format will let us create a new tag input in our template. +Add the following to the PHP block of controls in **templates/Articles/add.php**:: + + echo $this->Form->control('tags._ids', ['options' => $tags]); + +This will render a multiple select element that uses the ``$tags`` variable to +generate the select box options. You should now create a couple new articles +that have tags, as in the following section we'll be adding the ability to find +articles by tags. + +You should also update the ``edit`` method to allow adding or editing tags. The +edit method should now look like:: + + public function edit($slug) + { + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') // load associated Tags + ->firstOrFail(); + if ($this->request->is(['post', 'put'])) { + $this->Articles->patchEntity($article, $this->request->getData()); + if ($this->Articles->save($article)) { + $this->Flash->success(__('Your article has been updated.')); + + return $this->redirect(['action' => 'index']); + } + $this->Flash->error(__('Unable to update your article.')); + } + + // Get a list of tags. + $tags = $this->Articles->Tags->find('list')->all(); + + // Set tags to the view context + $this->set('tags', $tags); + + $this->set('article', $article); + } + +Remember to add the new tags multiple select control we added to the **add.php** +template to the **templates/Articles/edit.php** template as well. + +Finding Articles By Tags +======================== + +Once users have categorized their content, they will want to find that content +by the tags they used. For this feature we'll implement a route, controller +action, and finder method to search through articles by tag. + +Ideally, we'd have a URL that looks like +**http://localhost:8765/articles/tagged/funny/cat/gifs**. This would let us +find all the articles that have the 'funny', 'cat' or 'gifs' tags. Before we +can implement this, we'll add a new route. Your **config/routes.php** (with +the baked comments removed) should look like:: + + setRouteClass(DashedRoute::class); + + $routes->scope('/', function (RouteBuilder $builder) { + $builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); + $builder->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); + + // Add this + // New route we're adding for our tagged action. + // The trailing `*` tells CakePHP that this action has + // passed parameters. + $builder->scope('/articles', function (RouteBuilder $builder) { + $builder->connect('/tagged/*', ['controller' => 'Articles', 'action' => 'tags']); + }); + + $builder->fallbacks(); + }); + +The above defines a new 'route' which connects the **/articles/tagged/** path, +to ``ArticlesController::tags()``. By defining routes, you can isolate how your +URLs look, from how they are implemented. If we were to visit +**http://localhost:8765/articles/tagged**, we would see a helpful error page +from CakePHP informing you that the controller action does not exist. Let's +implement that missing method now. In **src/Controller/ArticlesController.php** +add the following:: + + 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 ArticlesTable to find tagged articles. + $articles = $this->Articles->find('tagged', tags: $tags) + ->all(); + + // Pass variables into the view template context. + $this->set([ + 'articles' => $articles, + 'tags' => $tags + ]); + } + +To access other parts of the request data, consult the :ref:`cake-request` +section. + +Since passed arguments are passed as method parameters, you could also write the +action using PHP's variadic argument:: + + public function tags(...$tags) + { + // Use the ArticlesTable to find tagged articles. + $articles = $this->Articles->find('tagged', tags: $tags) + ->all(); + + // Pass variables into the view template context. + $this->set([ + 'articles' => $articles, + 'tags' => $tags + ]); + } + +Creating the Finder Method +-------------------------- + +In CakePHP we like to keep our controller actions slim, and put most of our +application's logic in the model layer. If you were to visit the +**/articles/tagged** URL now you would see an error that the ``findTagged()`` +method has not been implemented yet, so let's do that. In +**src/Model/Table/ArticlesTable.php** add the following:: + + // add this use statement right below the namespace declaration to import + // the Query class + use Cake\ORM\Query\SelectQuery; + + // The $query argument is a query builder instance. + // The $options array will contain the 'tags' option we passed + // to find('tagged') in our controller action. + public function findTagged(SelectQuery $query, array $tags = []): SelectQuery + { + $columns = [ + 'Articles.id', 'Articles.user_id', 'Articles.title', + 'Articles.body', 'Articles.published', 'Articles.created', + 'Articles.slug', + ]; + + $query = $query + ->select($columns) + ->distinct($columns); + + if (empty($tags)) { + // If there are no tags provided, find articles that have no tags. + $query->leftJoinWith('Tags') + ->where(['Tags.title IS' => null]); + } else { + // Find articles that have one or more of the provided tags. + $query->innerJoinWith('Tags') + ->where(['Tags.title IN' => $tags]); + } + + return $query->groupBy(['Articles.id']); + } + +We just implemented a :ref:`custom finder method `. This is +a very powerful concept in CakePHP that allows you to package up re-usable +queries. Finder methods always get a :doc:`/orm/query-builder` object and an +array of options as parameters. Finders can manipulate the query and add any +required conditions or criteria. When complete, finder methods must return +a modified query object. In our finder we've leveraged the ``distinct()`` and +``leftJoin()`` methods which allow us to find distinct articles that have +a 'matching' tag. + +Creating the View +----------------- + +Now if you visit the **/articles/tagged** URL again, CakePHP will show a new error +letting you know that you have not made a view file. Next, let's build the +view file for our ``tags()`` action:: + + +

+ Articles tagged with + Text->toList(h($tags), 'or') ?> +

+ +
+ +
+ +

Html->link( + $article->title, + ['controller' => 'Articles', 'action' => 'view', $article->slug] + ) ?>

+ created) ?> +
+ +
+ +In the above code we use the :doc:`/views/helpers/html` and +:doc:`/views/helpers/text` helpers to assist in generating our view output. We +also use the :php:func:`h` shortcut function to HTML encode output. You should +remember to always use ``h()`` when outputting data to prevent HTML injection +issues. + +The **tags.php** file we just created follows the CakePHP conventions for view +template files. The convention is to have the template use the lower case and +underscored version of the controller action name. + +You may notice that we were able to use the ``$tags`` and ``$articles`` +variables in our view template. When we use the ``set()`` method in our +controller, we set specific variables to be sent to the view. The View will make +all passed variables available in the template scope as local variables. + +You should now be able to visit the **/articles/tagged/funny** URL and see all +the articles tagged with 'funny'. + +Improving the Tagging Experience +================================ + +Right now, adding new tags is a cumbersome process, as authors need to +pre-create all the tags they want to use. We can improve the tag selection UI by +using a comma separated text field. This will let us give a better experience to +our users, and use some more great features in the ORM. + +Adding a Computed Field +----------------------- + +Because we'll want a simple way to access the formatted tags for an entity, we +can add a virtual/computed field to the entity. In +**src/Model/Entity/Article.php** add the following:: + + // add this use statement right below the namespace declaration to import + // the Collection class + use Cake\Collection\Collection; + + // Update the accessible property to contain `tag_string` + protected array $_accessible = [ + //other fields... + 'tag_string' => true + ]; + + 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, ', '); + } + +This will let us access the ``$article->tag_string`` computed property. We'll +use this property in controls later on. + +Updating the Views +------------------ + +With the entity updated we can add a new control for our tags. In +**templates/Articles/add.php** and **templates/Articles/edit.php**, +replace the existing ``tags._ids`` control with the following:: + + echo $this->Form->control('tag_string', ['type' => 'text']); + +We'll also need to update the article view template. In +**templates/Articles/view.php** add the line as shown:: + + + +

title) ?>

+

body) ?>

+ // Add the following line +

Tags: tag_string) ?>

+ +You should also update the view method to allow retrieving existing tags:: + + // src/Controller/ArticlesController.php file + + public function view($slug = null) + { + // Update retrieving tags with contain() + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') + ->firstOrFail(); + $this->set(compact('article')); + } + +Persisting the Tag String +------------------------- + +Now that we can view existing tags as a string, we'll want to save that data as +well. Because we marked the ``tag_string`` as accessible, the ORM will copy that +data from the request into our entity. We can use a ``beforeSave()`` hook method +to parse the tag string and find/build the related entities. Add the following +to **src/Model/Table/ArticlesTable.php**:: + + public function beforeSave(EventInterface $event, $entity, $options): void + { + if ($entity->tag_string) { + $entity->tags = $this->_buildTags($entity->tag_string); + } + + // Other code + } + + protected function _buildTags($tagString) + { + // Trim tags + $newTags = array_map('trim', explode(',', $tagString)); + // Remove all empty tags + $newTags = array_filter($newTags); + // Reduce duplicated tags + $newTags = array_unique($newTags); + + $out = []; + $tags = $this->Tags->find() + ->where(['Tags.title IN' => $newTags]) + ->all(); + + // Remove existing tags from the list of new tags. + foreach ($tags->extract('title') as $existing) { + $index = array_search($existing, $newTags); + if ($index !== false) { + unset($newTags[$index]); + } + } + // Add existing tags. + foreach ($tags as $tag) { + $out[] = $tag; + } + // Add new tags. + foreach ($newTags as $tag) { + $out[] = $this->Tags->newEntity(['title' => $tag]); + } + + return $out; + } + +If you now create or edit articles, you should be able to save tags as a comma +separated list of tags, and have the tags and linking records automatically +created. + +While this code is a bit more complicated than what we've done so far, it helps +to showcase how powerful the ORM in CakePHP is. You can manipulate query +results using the :doc:`/core-libraries/collections` methods, and handle +scenarios where you are creating entities on the fly with ease. + +Auto-populating the Tag String +============================== + +Before we finish up, we'll need a mechanism that will load the associated tags +(if any) whenever we load an article. + +In your **src/Model/Table/ArticlesTable.php**, change:: + + public function initialize(array $config): void + { + $this->addBehavior('Timestamp'); + // Change this line + $this->belongsToMany('Tags', [ + 'joinTable' => 'articles_tags', + 'dependent' => true + ]); + } + +This will tell the Articles table model that there is a join table associated +with tags. The 'dependent' option tells the table to delete any associated +records from the join table if an article is deleted. + +Lastly, update the findBySlug() method calls in +**src/Controller/ArticlesController.php**:: + + public function edit($slug) + { + // Update this line + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') + ->firstOrFail(); + ... + } + + public function view($slug = null) + { + // Update this line + $article = $this->Articles + ->findBySlug($slug) + ->contain('Tags') + ->firstOrFail(); + $this->set(compact('article')); + } + +The ``contain()`` method tells the ``ArticlesTable`` object to also populate the +Tags association when the article is loaded. Now when tag_string is called for +an Article entity, there will be data present to create the string! + +Next we'll be adding :doc:`authentication `. From e39a2e65a2f3acd13980939ed8548f632f4b11d2 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 08:58:31 -0300 Subject: [PATCH 04/46] [refact]: translated (pt/tutorials-and-examples/cms/articles-controller) --- pt/core-libraries/file-folder.rst | 444 ---------------- .../cms/articles-controller.rst | 478 +++++++++--------- 2 files changed, 239 insertions(+), 683 deletions(-) delete mode 100644 pt/core-libraries/file-folder.rst diff --git a/pt/core-libraries/file-folder.rst b/pt/core-libraries/file-folder.rst deleted file mode 100644 index f44241def5..0000000000 --- a/pt/core-libraries/file-folder.rst +++ /dev/null @@ -1,444 +0,0 @@ -Pasta & Arquivo -############### - -.. php:namespace:: Cake\Filesystem - -Os utilitários de pasta e arquivo são classes convenientes para ajudá-lo a ler -e gravar/anexar arquivos, listar arquivos dentro de uma pasta e outras tarefas -comuns relacionadas ao diretório. - -.. deprecated:: 4.0 - As classes ``File`` e ``Folder`` serão removidas na 5.0. - Use classes SPL como ``SplFileInfo`` ou ``SplFileObject`` e classes iterator - como ``RecursiveDirectoryIterator``, ``RecursiveRegexIterator`` etc. - -Uso Básico -========== - -Certifique-se de que as classes estejam carregadas:: - - use Cake\Filesystem\Folder; - use Cake\Filesystem\File; - -Com isso podemos configurar uma nova instância da pasta:: - - $dir = new Folder('/path/to/folder'); - -e então pesquise todos os arquivos *.php* dentro dessa pasta usando regex:: - - $files = $dir->find('.*\.php'); - -Agora podemos percorrer os arquivos e ler ou escrever/anexar ao -conteúdo ou simplesmente excluir o arquivo:: - - foreach ($files as $file) { - $file = new File($dir->pwd() . DS . $file); - $contents = $file->read(); - // $file->write('Estou substituindo o conteúdo deste arquivo'); - // $file->append('Estou adicionando ao final deste arquivo.'); - // $file->delete(); // Estou excluindo este arquivo - $file->close(); // Certifique-se de fechar o arquivo quando terminar - } - -API Pastas -========== - -.. php:class:: Folder(string $path = false, boolean $create = false, string|boolean $mode = false) - -:: - - // Cria uma nova pasta com as permissões 0755 - $dir = new Folder('/path/to/folder', true, 0755); - -.. php:attr:: path - - Caminho da pasta atual. :php:meth:`Folder::pwd()` retornará a mesma informação. - -.. php:attr:: sort - - Se os resultados da lista devem ou não ser classificados por nome. - -.. php:attr:: mode - - Modo a ser usado ao criar pastas. O padrão é ``0755``. Não faz nada em máquinas Windows. - -.. php:staticmethod:: addPathElement(string $path, string $element) - - Retorna $path com $elemento adicionado, com a barra correta:: - - $path = Folder::addPathElement('/a/path/for', 'testing'); - // $path é igual a /a/path/for/testing - - $element também pode ser um array:: - - $path = Folder::addPathElement('/a/path/for', ['testing', 'another']); - // $path é igual a /a/path/for/testing/another - -.. php:method:: cd( $path ) - - Mude o diretório para $path. Retorna ``false`` em caso de falha:: - - $folder = new Folder('/foo'); - echo $folder->path; // Exibe /foo - $folder->cd('/bar'); - echo $folder->path; // Exibe /bar - $false = $folder->cd('/non-existent-folder'); - -.. php:method:: chmod(string $path, integer $mode = false, boolean $recursive = true, array $exceptions = []) - - Altere o modo em uma estrutura de diretório recursivamente. Isso inclui - alterar o modo dos arquivos também:: - - $dir = new Folder(); - $dir->chmod('/path/to/folder', 0755, true, ['skip_me.php']); - -.. php:method:: copy(array|string $options = []) - - Copie recursivamente um diretório. O único parâmetro $options pode ser - um caminho para a cópia ou um conjunto de opções:: - - $folder1 = new Folder('/path/to/folder1'); - $folder1->copy('/path/to/folder2'); - // Colocará a pasta1 e todo o seu conteúdo na pasta2 - - $folder = new Folder('/path/to/folder'); - $folder->copy([ - 'to' => '/path/to/new/folder', - 'from' => '/path/to/copy/from', // Irá causar a ocorrência de um cd() - 'mode' => 0755, - 'skip' => ['skip-me.php', '.git'], - 'scheme' => Folder::SKIP // Pule diretórios/arquivos que já existem. - ]); - - Existem 3 esquemas suportados: - - * ``Folder::SKIP`` pule a cópia/movimentação de arquivos e diretórios - que existem no diretório de destino. - * ``Folder::MERGE`` mescla os diretórios de origem/destino. Os arquivos no diretório de origem - substituirão os arquivos no diretório de destino. O conteúdo do diretório será mesclado. - * ``Folder::OVERWRITE`` sobrescreve os arquivos e diretórios existentes no diretório de destino pelos do - diretório de origem. Se ambos contiverem o mesmo subdiretório, o conteúdo do diretório - de destino será removido e substituído pelo de origem. - -.. php:staticmethod:: correctSlashFor(string $path) - - Retorna um conjunto correto de barras para o $path - fornecido ('\\' para caminhos do Windows e '/' para outros caminhos). - -.. php:method:: create(string $pathname, integer $mode = false) - - Crie uma estrutura de diretório recursivamente. Pode ser usado para - criar estruturas de caminho mais profundo como `/foo/bar/baz/shoe/horn`:: - - $folder = new Folder(); - if ($folder->create('foo' . DS . 'bar' . DS . 'baz' . DS . 'shoe' . DS . 'horn')) { - // As pastas aninhadas foram criadas com sucesso - } - -.. php:method:: delete(string $path = null) - - Remova diretórios recursivamente se o sistema permitir:: - - $folder = new Folder('foo'); - if ($folder->delete()) { - // Foo foi excluído com sucesso e também suas pastas aninhadas - } - -.. php:method:: dirsize() - - Retorna o tamanho em bytes desta pasta e seu conteúdo. - -.. php:method:: errors() - - Obtenha o erro do método mais recente. - -.. php:method:: find(string $regexpPattern = '.*', boolean $sort = false) - - Retorna uma matriz de todos os arquivos correspondentes no diretório atual:: - - // Encontre todos os .png em sua pasta webroot/img/ e classifique os resultados - $dir = new Folder(WWW_ROOT . 'img'); - $files = $dir->find('.*\.png', true); - /* - Array - ( - [0] => cake.icon.png - [1] => test-error-icon.png - [2] => test-fail-icon.png - [3] => test-pass-icon.png - [4] => test-skip-icon.png - ) - */ - -.. note:: - - Os métodos find e findRecursive da pasta só encontrarão arquivos. Se você - gostaria de obter pastas e arquivos, consulte :php:meth:`Folder::read()` ou - :php:meth:`Folder::tree()` - -.. php:method:: findRecursive(string $pattern = '.*', boolean $sort = false) - - Retorna uma matriz de todos os arquivos correspondentes dentro e abaixo do diretório atual:: - - // Encontre arquivos recursivamente começando com teste ou índice - $dir = new Folder(WWW_ROOT); - $files = $dir->findRecursive('(test|index).*'); - /* - Array - ( - [0] => /var/www/cake/webroot/index.php - [1] => /var/www/cake/webroot/test.php - [2] => /var/www/cake/webroot/img/test-skip-icon.png - [3] => /var/www/cake/webroot/img/test-fail-icon.png - [4] => /var/www/cake/webroot/img/test-error-icon.png - [5] => /var/www/cake/webroot/img/test-pass-icon.png - ) - */ - -.. php:method:: inCakePath(string $path = '') - - Retorna ``true`` se o arquivo está em um determinado CakePath. - -.. php:method:: inPath(string $path = '', boolean $reverse = false) - - Retorna ``true`` se o arquivo está no caminho fornecido:: - - $Folder = new Folder(WWW_ROOT); - $result = $Folder->inPath(APP); - // $result = false, /var/www/example/src/ não está em /var/www/example/webroot/ - - $result = $Folder->inPath(WWW_ROOT . 'img' . DS, true); - // $result = true, /var/www/example/webroot/img/ está em /var/www/example/webroot/ - -.. php:staticmethod:: isAbsolute(string $path) - - Retorna ``true`` se o $path fornecido for um caminho absoluto. - -.. php:staticmethod:: isSlashTerm(string $path) - - Retorna ``true`` se o $path termina em uma barra (ou seja, termina com uma barra):: - - $result = Folder::isSlashTerm('/my/test/path'); - // $result = false - $result = Folder::isSlashTerm('/my/test/path/'); - // $result = true - -.. php:staticmethod:: isWindowsPath(string $path) - - Retorna ``true`` se o $path fornecido for um caminho do Windows. - -.. php:method:: messages() - - Obtenha as mensagens do método mais recente. - -.. php:method:: move(array $options) - - Move recursivamente o diretório. - -.. php:staticmethod:: normalizeFullPath(string $path) - - Retorna um caminho com barras normalizadas para o sistema operacional. - -.. php:method:: pwd() - - Retorna o caminho atual - -.. php:method:: read(boolean $sort = true, array|boolean $exceptions = false, boolean $fullPath = false) - - Retorna uma matriz do conteúdo do diretório atual. A matriz retornada contém - duas submatrizes e uma de diretórios e uma de arquivos:: - - $dir = new Folder(WWW_ROOT); - $files = $dir->read(true, ['files', 'index.php']); - /* - Array - ( - [0] => Array // Folders - ( - [0] => css - [1] => img - [2] => js - ) - [1] => Array // Files - ( - [0] => .htaccess - [1] => favicon.ico - [2] => test.php - ) - ) - */ - -.. php:method:: realpath(string $path) - - Pegue o caminho real (levando ".." em consideração). - -.. php:staticmethod:: slashTerm(string $path) - - Retorna $path com barra de terminação adicionada (corrigido para - Windows ou outro sistema operacional). - -.. php:method:: tree(null|string $path = null, array|boolean $exceptions = true, null|string $type = null) - - Retorna uma matriz de diretórios e arquivos aninhados em cada diretório. - -API de Arquivos -=============== - -.. php:class:: File(string $path, boolean $create = false, integer $mode = 755) - -:: - - // Cria um novo arquivo com as permissões 0644 - $file = new File('/path/to/file.php', true, 0644); - -.. php:attr:: Folder - - O objeto Folder do arquivo. - -.. php:attr:: name - - O nome do arquivo com a extensão. É diferente de :php:meth:`File::name()` que retorna o nome sem a extensão. - -.. php:attr:: info - - Uma matriz de informações do arquivo. Ao invés disso use :php:meth:`File::info()`. - -.. php:attr:: handle - - Contém o recurso de manipulador de arquivo se o arquivo for aberto. - -.. php:attr:: lock - - Habilite o bloqueio para leitura e gravação de arquivos. - -.. php:attr:: path - - O caminho absoluto do arquivo atual. - -.. php:method:: append(string $data, boolean $force = false) - - Anexe a string de dados fornecida ao arquivo atual. - -.. php:method:: close() - - Fecha o arquivo atual se estiver aberto. - -.. php:method:: copy(string $dest, boolean $overwrite = true) - - Copie o arquivo para o caminho absoluto ``$dest``. - -.. php:method:: create() - - Cria o arquivo. - -.. php:method:: delete() - - Apaga o arquivo; - -.. php:method:: executable() - - Returna ``true`` se o arquivo for executável - -.. php:method:: exists() - - Retorna ``true`` se o arquivo existe. - -.. php:method:: ext() - - Retorna a extensão do arquivo. - -.. php:method:: Folder() - - Retorna a pasta atual. - -.. php:method:: group() - - Retorna o grupo do arquivo, ou ``false`` em caso de erro. - -.. php:method:: info() - - Retorna as informações do arquivo. - -.. php:method:: lastAccess( ) - - Retorna a hora do último acesso. - -.. php:method:: lastChange() - - Retorna a hora da última modificação ou ``false`` em caso de erro. - -.. php:method:: md5(integer|boolean $maxsize = 5) - - Obtenha o MD5 Checksum do arquivo com a verificação anterior do - tamanho do arquivo, ou ``false`` no caso de um erro. - -.. php:method:: name() - - Retorna o nome do arquivo sem extensão. - -.. php:method:: offset(integer|boolean $offset = false, integer $seek = 0) - - Define ou obtém o deslocamento do arquivo aberto no momento. - -.. php:method:: open(string $mode = 'r', boolean $force = false) - - Abre o arquivo atual com o $mode fornecido. - -.. php:method:: owner() - - Retorna o proprietário do arquivo. - -.. php:method:: perms() - - Retorna o "chmod" (permissões) do arquivo. - -.. php:staticmethod:: prepare(string $data, boolean $forceWindows = false) - - Prepara uma string ascii para escrita. Converte as terminações de linha no terminador - correto para a plataforma atual. Para Windows, será usado "\\r\\n", - para todas as outras plataformas "\\ n". - -.. php:method:: pwd() - - Retorna o caminho completo do arquivo. - -.. php:method:: read(string $bytes = false, string $mode = 'rb', boolean $force = false) - - Retorne o conteúdo do arquivo atual como uma string ou retorne ``false`` em caso de falha. - -.. php:method:: readable() - - Retorna ``true`` se o arquivo é legível. - -.. php:method:: safe(string $name = null, string $ext = null) - - Torna o nome do arquivo seguro para salvar. - -.. php:method:: size() - - Retorna o tamanho do arquivo em bytes. - -.. php:method:: writable() - - Retorna ``true`` se o arquivo for gravável. - -.. php:method:: write(string $data, string $mode = 'w', boolean$force = false) - - Grave os dados fornecidos no arquivo atual. - -.. php:method:: mime() - - Pega o tipo MIME do arquivo, retorna ``false`` em caso de falha. - -.. php:method:: replaceText( $search, $replace ) - - Substitui o texto em um arquivo. Retorna ``false`` em caso de falha e ``true`` em caso de sucesso. - -.. todo:: - - Explique melhor como usar cada método com ambas as classes. - -.. meta:: - :title lang=pt: Pasta & Arquivo - :description lang=pt: Os utilitários de pasta e arquivo são classes convenientes para ajudá-lo a ler, escrever e anexar a arquivos; listar arquivos dentro de uma pasta e outras tarefas comuns relacionadas ao diretório. - :keywords lang=pt: arquivo,pasta,utilitario cakephp,ler arquivo,escrever arquivo,anexar arquivo,copia recursiva,opcoes de copia,caminho de pasta,classe de pasta,arquivo php,mudar de diretorio,utilitario de arquivo,nova pasta,estrutura de diretorio,apagar arquivo diff --git a/pt/tutorials-and-examples/cms/articles-controller.rst b/pt/tutorials-and-examples/cms/articles-controller.rst index 872668024a..7f66f80a9b 100644 --- a/pt/tutorials-and-examples/cms/articles-controller.rst +++ b/pt/tutorials-and-examples/cms/articles-controller.rst @@ -1,11 +1,12 @@ -CMS Tutorial - Creating the Articles Controller -############################################### +CMS Tutorial - Criando o Controller Articles +############################################ -With our model created, we need a controller for our articles. Controllers in -CakePHP handle HTTP requests and execute business logic contained in model -methods, to prepare the response. We'll place this new controller in a file -called **ArticlesController.php** inside the **src/Controller** directory. -Here's what the basic controller should look like:: +Com nosso modelo criado, precisamos de um controller para nossos artigos. +Os controllers no CakePHP lidam com requisições HTTP e executam a lógica +de negócios contida nos métodos do modelo para preparar a resposta. +Colocaremos esse novo controlador em um arquivo chamado **ArticlesController.php** +dentro do diretório **src/Controller**. +Veja como o controller básico deve se parecer:: Created - + @@ -99,33 +100,33 @@ application: -In the last section we assigned the 'articles' variable to the view using -``set()``. Variables passed into the view are available in the view templates as -local variables which we used in the above code. +Na última seção, atribuímos a variável 'articles' à visualização usando +``set()``. As variáveis ​​passadas para a visualização estão disponíveis nos modelos de visualização como +variáveis ​​locais que usamos no código acima. -You might have noticed the use of an object called ``$this->Html``. This is an -instance of the CakePHP :doc:`HtmlHelper `. CakePHP comes -with a set of view helpers that make tasks like creating links, forms, and -pagination buttons. You can learn more about :doc:`/views/helpers` in their -chapter, but what's important to note here is that the ``link()`` method will -generate an HTML link with the given link text (the first parameter) and URL -(the second parameter). +Você deve ter notado o uso de um objeto chamado ``$this->Html``. Esta é uma +instância do CakePHP :doc:`HtmlHelper `. O CakePHP vem +com um conjunto de assistentes de visualização que realizam tarefas como criar links, formulários e +botões de paginação. Você pode aprender mais sobre :doc:`/views/helpers` no +capítulo deles, mas o importante a ser observado aqui é que o método ``link()`` irá +gerar um link HTML com o texto do link fornecido (o primeiro parâmetro) e a URL +(o segundo parâmetro). -When specifying URLs in CakePHP, it is recommended that you use arrays or -:ref:`named routes `. These syntaxes allow you to -leverage the reverse routing features CakePHP offers. +Ao especificar URLs no CakePHP, é recomendável usar arrays ou +:ref:`rotas nomeadas `. Essas sintaxes permitem que você +aproveite os recursos de roteamento reverso oferecidos pelo CakePHP. -At this point, you should be able to point your browser to -**http://localhost:8765/articles/index**. You should see your list view, -correctly formatted with the title and table listing of the articles. +Neste ponto, você deve conseguir acessar seu navegador em +**http://localhost:8765/articles/index**. Você deverá ver sua visualização de lista, +formatada corretamente com o título e a lista de artigos na tabela. -Create the View Action -====================== +Criando a Ação de Visualização +============================== -If you were to click one of the 'view' links in our Articles list page, you'd -see an error page saying that action hasn't been implemented. Lets fix that now:: +Se você clicar em um dos links "visualizar" na nossa página de lista de artigos, +verá uma página de erro informando que a ação não foi implementada. Vamos corrigir isso agora:: - // Add to existing src/Controller/ArticlesController.php file + // Adicione ao arquivo existente src/Controller/ArticlesController.php public function view($slug = null) { @@ -133,22 +134,22 @@ see an error page saying that action hasn't been implemented. Lets fix that now: $this->set(compact('article')); } -While this is a simple action, we've used some powerful CakePHP features. We -start our action off by using ``findBySlug()`` which is -a :ref:`Dynamic Finder `. This method allows us to create a basic query that -finds articles by a given slug. We then use ``firstOrFail()`` to either fetch -the first record, or throw a ``\Cake\Datasource\Exception\RecordNotFoundException``. +Embora esta seja uma ação simples, utilizamos alguns recursos poderosos do CakePHP. +Iniciamos nossa ação usando ``findBySlug()``, que é +um :ref:`Dynamic Finder `. Este método nos permite criar uma consulta básica que +encontra artigos de um determinado slug. Em seguida, usamos ``firstOrFail()`` para buscar +o primeiro registro ou lançar uma ``\Cake\Datasource\Exception\RecordNotFoundException``. -Our action takes a ``$slug`` parameter, but where does that parameter come from? -If a user requests ``/articles/view/first-post``, then the value 'first-post' is -passed as ``$slug`` by CakePHP's routing and dispatching layers. If we -reload our browser with our new action saved, we'd see another CakePHP error -page telling us we're missing a view template; let's fix that. +Nossa ação recebe um parâmetro ``$slug``, mas de onde vem esse parâmetro? +Se um usuário solicitar ``/articles/view/first-post``, o valor 'first-post' será +passado como ``$slug`` pelas camadas de roteamento e despacho do CakePHP. Se +recarregarmos nosso navegador com nossa nova ação salva, veremos outra página de erro do CakePHP +nos informando que falta um modelo de visualização; vamos corrigir isso. -Create the View Template -======================== +Criando o Template View +======================= -Let's create the view for our new 'view' action and place it in +Vamos criar a visualização para nossa nova ação 'view' e colocá-la em **templates/Articles/view.php** .. code-block:: php @@ -160,16 +161,16 @@ Let's create the view for our new 'view' action and place it in

Created: created->format(DATE_RFC850) ?>

Html->link('Edit', ['action' => 'edit', $article->slug]) ?>

-You can verify that this is working by trying the links at ``/articles/index`` or -manually requesting an article by accessing URLs like +Você pode verificar se isso está funcionando tentando os links em ``/articles/index`` ou +solicitando manualmente um artigo acessando URLs como ``/articles/view/first-post``. -Adding Articles -=============== +Adicionando Artigos +=================== -With the basic read views created, we need to make it possible for new articles -to be created. Start by creating an ``add()`` action in the -``ArticlesController``. Our controller should now look like:: +Com as views de leitura básicas criadas, precisamos possibilitar a criação de novos artigos. +Comece criando uma ação ``add()`` no +``ArticlesController``. Nosso controller agora deve se parecer com:: request->is('post')) { $article = $this->Articles->patchEntity($article, $this->request->getData()); - // Hardcoding the user_id is temporary, and will be removed later - // when we build authentication out. + // A codificação do user_id é temporária e será removida posteriormente + // quando construirmos a autenticação. $article->user_id = 1; if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been saved.')); + $this->Flash->success(__('Seu artigo foi salvo.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to add your article.')); + $this->Flash->error(__('Não é possível adicionar seu artigo.')); } $this->set('article', $article); } @@ -214,46 +215,45 @@ to be created. Start by creating an ``add()`` action in the .. note:: - You need to include the :doc:`/controllers/components/flash` component in - any controller where you will use it. Often it makes sense to include it in - your ``AppController``, which is there already for this tutorial. - -Here's what the ``add()`` action does: - -* If the HTTP method of the request was POST, try to save the data using the Articles model. -* If for some reason it doesn't save, just render the view. This gives us a - chance to show the user validation errors or other warnings. - -Every CakePHP request includes a request object which is accessible using -``$this->request``. The request object contains information regarding the -request that was just received. We use the -:php:meth:`Cake\\Http\\ServerRequest::is()` method to check that the request -is a HTTP POST request. - -Our POST data is available in ``$this->request->getData()``. You can use the -:php:func:`pr()` or :php:func:`debug()` functions to print it out if you want to -see what it looks like. To save our data, we first 'marshal' the POST data into -an Article Entity. The Entity is then persisted using the ArticlesTable we -created earlier. - -After saving our new article we use FlashComponent's ``success()`` method to set -a message into the session. The ``success`` method is provided using PHP's -`magic method features -`_. Flash -messages will be displayed on the next page after redirecting. In our layout we have -``Flash->render() ?>`` which displays flash messages and clears the -corresponding session variable. Finally, after saving is complete, we use -:php:meth:`Cake\\Controller\\Controller::redirect` to send the user back to the -articles list. The param ``['action' => 'index']`` translates to URL -``/articles`` i.e the index action of the ``ArticlesController``. You can refer -to :php:func:`Cake\\Routing\\Router::url()` function on the `API -`_ to see the formats in which you can specify a URL -for various CakePHP functions. - -Create Add Template -=================== + Você precisa incluir o componente :doc:`/controllers/components/flash` em + qualquer controlador onde for usá-lo. Muitas vezes, faz sentido incluí-lo em + seu ``AppController``, que já está lá para este tutorial. + +Veja o que a ação ``add()`` faz: + +* Se o método HTTP da solicitação for POST, tente salvar os dados usando o modelo Articles. +* Se, por algum motivo, não for possível salvar, basta renderizar a visualização. Isso nos dá a chance de mostrar ao usuário erros de validação ou outros avisos. + +Cada requisição do CakePHP inclui um objeto de requisição que pode ser acessado usando +``$this->request``. O objeto de requisição contém informações sobre a +requisição que acabou de ser recebida. Usamos o método +:php:meth:`Cake\\Http\\ServerRequest::is()` para verificar se a requisição +é uma requisição HTTP POST. + +Nossos dados POST estão disponíveis em ``$this->request->getData()``. Você pode usar as funções +:php:func:`pr()` ou :php:func:`debug()` para imprimi-los, se quiser +ver como ficam. Para salvar nossos dados, primeiro 'marshal' os dados POST em +uma Entidade de Artigo. A Entidade é então persistida usando a Tabela de Artigos que +criamos anteriormente. + +Após salvar nosso novo artigo, usamos o método ``success()`` do FlashComponent para definir +uma mensagem na sessão. O método ``success`` é fornecido usando os +`recursos do método mágico do PHP +`_. As mensagens em Flash +serão exibidas na próxima página após o redirecionamento. Em nosso layout, temos +``Flash->render() ?>``, que exibe mensagens em Flash e limpa a +variável de sessão correspondente. Finalmente, após a conclusão do salvamento, usamos +:php:meth:`Cake\\Controller\\Controller::redirect` para enviar o usuário de volta à +lista de artigos. O parâmetro ``['action' => 'index']`` traduz para a URL +``/articles``, ou seja, a ação de índice do ``ArticlesController``. Você pode consultar +a função :php:func:`Cake\\Routing\\Router::url()` na `API +`_ para ver os formatos nos quais você pode especificar uma URL +para várias funções do CakePHP. + +Criando o Template Add +====================== -Here's our add view template: +Aqui está nosso template de visualização para 'add': .. code-block:: php @@ -262,47 +262,47 @@ Here's our add view template:

Add Article

Form->create($article); - // Hard code the user for now. + // Codifique o usuário por enquanto. echo $this->Form->control('user_id', ['type' => 'hidden', 'value' => 1]); echo $this->Form->control('title'); echo $this->Form->control('body', ['rows' => '3']); - echo $this->Form->button(__('Save Article')); + echo $this->Form->button(__('Salvar Artigo')); echo $this->Form->end(); ?> -We use the FormHelper to generate the opening tag for an HTML -form. Here's the HTML that ``$this->Form->create()`` generates: +Usamos o FormHelper para gerar a tag de abertura para um formulário HTML. +Aqui está o HTML que ``$this->Form->create()`` gera: .. code-block:: html -Because we called ``create()`` without a URL option, ``FormHelper`` assumes we -want the form to submit back to the current action. +Como chamamos ``create()`` sem uma opção de URL, ``FormHelper`` assume que +queremos que o formulário seja enviado de volta para a ação atual. -The ``$this->Form->control()`` method is used to create form elements -of the same name. The first parameter tells CakePHP which field -they correspond to, and the second parameter allows you to specify -a wide array of options - in this case, the number of rows for the -textarea. There's a bit of introspection and conventions used here. The -``control()`` will output different form elements based on the model -field specified, and use inflection to generate the label text. You can -customize the label, the input or any other aspect of the form controls using -options. The ``$this->Form->end()`` call closes the form. +O método ``$this->Form->control()`` é usado para criar elementos de formulário +com o mesmo nome. O primeiro parâmetro informa ao CakePHP a qual campo +eles correspondem, e o segundo parâmetro permite especificar +uma ampla gama de opções - neste caso, o número de linhas para a +textarea. Há um pouco de introspecção e convenções usadas aqui. O método ``control()`` +gerará diferentes elementos de formulário com base no campo +do modelo especificado e usará a inflexão para gerar o texto do rótulo(label). Você pode +personalizar o rótulo(label), a entrada ou qualquer outro aspecto dos controles do formulário usando +opções. A chamada ``$this->Form->end()`` fecha o formulário. -Now let's go back and update our **templates/Articles/index.php** -view to include a new "Add Article" link. Before the ````, add -the following line:: +Agora, vamos voltar e atualizar nossa view **templates/Articles/index.php** +para incluir um novo link "Adicionar Artigo". Antes de ``
``, adicione +a seguinte linha:: - Html->link('Add Article', ['action' => 'add']) ?> + Html->link('Adicionar Artigo', ['action' => 'add']) ?> -Adding Simple Slug Generation -============================= +Adicionando Geração Simples de Slug +=================================== -If we were to save an Article right now, saving would fail as we are not -creating a slug attribute, and the column is ``NOT NULL``. Slug values are -typically a URL-safe version of an article's title. We can use the -:ref:`beforeSave() callback ` of the ORM to populate our slug:: +Se salvássemos um artigo agora, o salvamento falharia, pois não estamos +criando um atributo slug e a coluna é ``NOT NULL``. Valores slug são +tipicamente uma versão segura para URL do título de um artigo. Podemos usar o retorno de chamada +:ref:`beforeSave() ` do ORM para preencher nosso slug:: isNew() && !$entity->slug) { $sluggedTitle = Text::slug($entity->title); - // trim slug to maximum length defined in schema + // aparar o slug até o comprimento máximo definido no esquema $entity->slug = substr($sluggedTitle, 0, 191); } } -This code is simple, and doesn't take into account duplicate slugs. But we'll -fix that later on. +Este código é simples e não leva em conta slugs duplicados. Mas vamos +consertar isso mais tarde. -Add Edit Action -=============== +Adicionar Ação de Edição +======================== -Our application can now save articles, but we can't edit them. Lets rectify that -now. Add the following action to your ``ArticlesController``:: +Nossa aplicação agora pode salvar artigos, mas não podemos editá-los. Vamos corrigir isso +agora. Adicione a seguinte ação ao seu ``ArticlesController``:: // in src/Controller/ArticlesController.php - // Add the following method. + // Adicione o seguinte método. public function edit($slug) { @@ -347,30 +347,30 @@ now. Add the following action to your ``ArticlesController``:: if ($this->request->is(['post', 'put'])) { $this->Articles->patchEntity($article, $this->request->getData()); if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been updated.')); + $this->Flash->success(__('Seu artigo foi atualizado.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to update your article.')); + $this->Flash->error(__('Não é possível atualizar seu artigo.')); } $this->set('article', $article); } -This action first ensures that the user has tried to access an existing record. -If they haven't passed in an ``$slug`` parameter, or the article does not exist, -a ``RecordNotFoundException`` will be thrown, and the CakePHP ErrorHandler will render -the appropriate error page. +Esta ação primeiro garante que o usuário tentou acessar um registro existente. +Se o parâmetro ``$slug`` não tiver sido passado ou o artigo não existir, +uma ``RecordNotFoundException`` será lançada e o ErrorHandler do CakePHP renderizará +a página de erro apropriada. -Next the action checks whether the request is either a POST or a PUT request. If -it is, then we use the POST/PUT data to update our article entity by using the -``patchEntity()`` method. Finally, we call ``save()``, set the appropriate flash -message, and either redirect or display validation errors. +Em seguida, a ação verifica se a solicitação é uma solicitação POST ou PUT. Se for, +usamos os dados POST/PUT para atualizar nossa entidade de artigo usando o método +``patchEntity()``. Por fim, chamamos ``save()``, definimos a mensagem flash apropriada +e redirecionamos ou exibimos os erros de validação. -Create Edit Template -==================== +Criando o Template Edit +======================= -The edit template should look like this: +O template de edição deve ficar assim: .. code-block:: php @@ -382,15 +382,15 @@ The edit template should look like this: echo $this->Form->control('user_id', ['type' => 'hidden']); echo $this->Form->control('title'); echo $this->Form->control('body', ['rows' => '3']); - echo $this->Form->button(__('Save Article')); + echo $this->Form->button(__('Salvar Artigo')); echo $this->Form->end(); ?> -This template outputs the edit form (with the values populated), along -with any necessary validation error messages. +Este modelo exibe o formulário de edição (com os valores preenchidos), juntamente +com quaisquer mensagens de erro de validação necessárias. -You can now update your index view with links to edit specific -articles: +Agora você pode atualizar sua visualização de índice com links para editar artigos +específicos: .. code-block:: php @@ -405,7 +405,7 @@ articles: - + @@ -423,19 +423,19 @@ articles:
Action
-Update Validation Rules for Articles -==================================== +Atualizar Regras de Validação para Artigos +========================================== -Up until this point our Articles had no input validation done. Lets fix that by -using :ref:`a validator `:: +Até este ponto, nossos artigos não tiveram nenhuma validação de entrada realizada. Vamos corrigir isso +usando :ref:`um validador `:: // src/Model/Table/ArticlesTable.php - // add this use statement right below the namespace declaration to import - // the Validator class + // adicione esta instrução use logo abaixo da declaração do namespace para importar + // a classe Validator use Cake\Validation\Validator; - // Add the following method. + // Adicione o seguinte método. public function validationDefault(Validator $validator): Validator { $validator @@ -449,30 +449,30 @@ using :ref:`a validator `:: return $validator; } -The ``validationDefault()`` method tells CakePHP how to validate your data when -the ``save()`` method is called. Here, we've specified that both the title, and -body fields must not be empty, and have certain length constraints. +O método ``validationDefault()`` informa ao CakePHP como validar seus dados quando +o método ``save()`` é chamado. Aqui, especificamos que os campos ``title`` e ``body`` +não devem estar vazios e têm certas restrições de comprimento. -CakePHP's validation engine is powerful and flexible. It provides a suite of -frequently used rules for tasks like email addresses, IP addresses etc. and the -flexibility for adding your own validation rules. For more information on that -setup, check the :doc:`/core-libraries/validation` documentation. +O mecanismo de validação do CakePHP é poderoso e flexível. Ele oferece um conjunto de +regras frequentemente usadas para tarefas como endereços de e-mail, endereços IP, etc., além da +flexibilidade para adicionar suas próprias regras de validação. Para mais informações sobre essa +configuração, consulte a documentação :doc:`/core-libraries/validation`. -Now that your validation rules are in place, use the app to try to add -an article with an empty title or body to see how it works. Since we've used the -:php:meth:`Cake\\View\\Helper\\FormHelper::control()` method of the FormHelper to -create our form elements, our validation error messages will be shown -automatically. +Agora que suas regras de validação estão definidas, use o aplicativo para tentar adicionar +um artigo com título ou corpo vazio para ver como funciona. Como usamos o método +:php:meth:`Cake\\View\\Helper\\FormHelper::control()` do FormHelper para +criar nossos elementos de formulário, nossas mensagens de erro de validação serão exibidas +automaticamente. -Add Delete Action -================= +Adicionar Ação de Delete +========================= -Next, let's make a way for users to delete articles. Start with a -``delete()`` action in the ``ArticlesController``:: +Em seguida, vamos criar uma maneira para os usuários excluírem artigos. Comece com uma ação +``delete()`` no ``ArticlesController``:: // src/Controller/ArticlesController.php - // Add the following method. + // Adicione o seguinte método. public function delete($slug) { @@ -480,37 +480,37 @@ Next, let's make a way for users to delete articles. Start with a $article = $this->Articles->findBySlug($slug)->firstOrFail(); if ($this->Articles->delete($article)) { - $this->Flash->success(__('The {0} article has been deleted.', $article->title)); + $this->Flash->success(__('O artigo {0} foi excluído.', $article->title)); return $this->redirect(['action' => 'index']); } } -This logic deletes the article specified by ``$slug``, and uses -``$this->Flash->success()`` to show the user a confirmation -message after redirecting them to ``/articles``. If the user attempts to -delete an article using a GET request, ``allowMethod()`` will throw an exception. -Uncaught exceptions are captured by CakePHP's exception handler, and a nice -error page is displayed. There are many built-in -:doc:`Exceptions ` that can be used to indicate the various -HTTP errors your application might need to generate. +Esta lógica exclui o artigo especificado por ``$slug`` e usa +``$this->Flash->success()`` para mostrar ao usuário uma mensagem de confirmação +após redirecioná-lo para ``/articles``. Se o usuário tentar +excluir um artigo usando uma requisição GET, ``allowMethod()`` lançará uma exceção. +Exceções não capturadas são capturadas pelo manipulador de exceções do CakePHP e uma bela +página de erro é exibida. Há muitas +:doc:`Exceções ` integradas que podem ser usadas para indicar os vários +erros HTTP que seu aplicativo pode precisar gerar. .. warning:: - Allowing content to be deleted using GET requests is *very* dangerous, as web - crawlers could accidentally delete all your content. That is why we used - ``allowMethod()`` in our controller. + Permitir que conteúdo seja excluído usando solicitações GET é *muito* perigoso, pois rastreadores + da web podem excluir acidentalmente todo o seu conteúdo. É por isso que usamos + ``allowMethod()`` em nosso controlador. -Because we're only executing logic and redirecting to another action, this -action has no template. You might want to update your index template with links -that allow users to delete articles: +Como estamos apenas executando a lógica e redirecionando para outra ação, esta +ação não tem modelo. Você pode querer atualizar seu modelo de índice com links +que permitam aos usuários excluir artigos: .. code-block:: php

Articles

-

Html->link("Add Article", ['action' => 'add']) ?>

+

Html->link("Adicionar Artigo", ['action' => 'add']) ?>

@@ -518,7 +518,7 @@ that allow users to delete articles: - + @@ -531,7 +531,7 @@ that allow users to delete articles:
TitleAction
Html->link('Edit', ['action' => 'edit', $article->slug]) ?> Form->deleteLink( - 'Delete', + 'Deletar', ['action' => 'delete', $article->slug], ['confirm' => 'Are you sure?']) ?> @@ -541,25 +541,25 @@ that allow users to delete articles:
-Using :php:meth:`~Cake\\View\\Helper\\FormHelper::deleteLink()` will create a link -that uses JavaScript to do a DELETE request deleting our article. -Prior to CakePHP 5.2 you need to use ``postLink()`` instead. +Usar :php:meth:`~Cake\\View\\Helper\\FormHelper::deleteLink()` criará um link +que usa JavaScript para fazer uma solicitação DELETE, excluindo nosso artigo. +Anterior ao CakePHP 5.2, você precisa usar ``postLink()``. .. note:: - This view code also uses the ``FormHelper`` to prompt the user with a - JavaScript confirmation dialog before they attempt to delete an - article. + Este código de visualização também usa o ``FormHelper`` para exibir ao usuário uma + caixa de diálogo de confirmação em JavaScript antes que ele tente excluir um + artigo. .. tip:: - The ``ArticlesController`` can also be built with ``bake``: + O ``ArticlesController`` também pode ser construído com ``bake``: .. code-block:: console /bin/cake bake controller articles - However, this does not build the **templates/Articles/*.php** files. + Entretanto, isso não cria os arquivos **templates/Articles/*.php**. -With a basic articles management setup, we'll create the :doc:`basic actions -for our Tags and Users tables `. +Com uma configuração básica de gerenciamento de artigos, criaremos as :doc:`ações básicas +para nossas tabelas de Tags e Usuários `. From 9c9d2f5f2fba1d9489bb64f9c0b3c9e81edf9c94 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 17:02:37 -0300 Subject: [PATCH 05/46] [refact]: translated (pt/appendices/5-0-migration-guide.rst) --- pt/appendices/5-0-migration-guide.rst | 443 +++++++++++++------------- 1 file changed, 221 insertions(+), 222 deletions(-) diff --git a/pt/appendices/5-0-migration-guide.rst b/pt/appendices/5-0-migration-guide.rst index d81e7561de..6342ced76d 100644 --- a/pt/appendices/5-0-migration-guide.rst +++ b/pt/appendices/5-0-migration-guide.rst @@ -1,34 +1,33 @@ -5.0 Migration Guide -################### +5.0 Guia de Migração +#################### -CakePHP 5.0 contains breaking changes, and is not backwards compatible with 4.x -releases. Before attempting to upgrade to 5.0, first upgrade to 4.5 and resolve -all deprecation warnings. +O CakePHP 5.0 contém mudanças drásticas e não é compatível com versões anteriores da versão 4.x. +Antes de tentar atualizar para a versão 5.0, atualize primeiro para a 4.5 e resolva +todos os avisos de descontinuação. -Refer to the :doc:`/appendices/5-0-upgrade-guide` for step by step instructions -on how to upgrade to 5.0. +Consulte o :doc:`/appendices/5-0-upgrade-guide` para obter instruções passo a passo +sobre como atualizar para a versão 5.0. -Deprecated Features Removed -=========================== +Recursos obsoletos removidos +============================ -All methods, properties and functionality that were emitting deprecation warnings -as of 4.5 have been removed. +Todos os métodos, propriedades e funcionalidades que estavam emitindo avisos de descontinuação +a partir da versão 4.5 foram removidos. -Breaking Changes -================ +Mudanças Drásticas +================== -In addition to the removal of deprecated features there have been breaking -changes made: +Além da remoção de recursos obsoletos, foram feitas alterações drásticas: Global ------ -- Type declarations were added to all function parameter and returns where possible. These are intended - to match the docblock annotations, but include fixes for incorrect annotations. -- Type declarations were added to all class properties where possible. These also include some fixes for - incorrect annotations. -- The ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``, ``WEEK``, ``MONTH``, ``YEAR`` constants were removed. -- Use of ``#[\AllowDynamicProperties]`` removed everywhere. It was used for the following classes: +- Declarações de tipo foram adicionadas a todos os parâmetros e retornos de funções, sempre que possível. O objetivo é + corresponder às anotações do docblock, mas incluem correções para anotações incorretas. +- Declarações de tipo foram adicionadas a todas as propriedades de classe sempre que possível. + Isso também inclui algumas correções para anotações incorretas. +- As constantes ``SECOND``, ``MINUTE``, ``HOUR``, ``DAY``, ``WEEK``, ``MONTH``, ``YEAR`` foram removidas. +- O uso de ``#[\AllowDynamicProperties]`` foi removido em todos os lugares. Era usado para as seguintes classes: - ``Command/Command`` - ``Console/Shell`` - ``Controller/Component`` @@ -37,261 +36,262 @@ Global - ``View/Cell`` - ``View/Helper`` - ``View/View`` -- The supported database engine versions were updated: - - MySQL (5.7 or higher) - - MariaDB (10.1 or higher) - - PostgreSQL (9.6 or higher) - - Microsoft SQL Server (2012 or higher) - - SQLite 3 (3.16 or higher) +- As versões do mecanismo de banco de dados suportado foram atualizadas: + - MySQL (5.7 ou superior) + - MariaDB (10.1 ou superior) + - PostgreSQL (9.6 ou superior) + - Microsoft SQL Server (2012 ou superior) + - SQLite 3 (3.16 ou superior) Auth ---- -- `Auth` has been removed. Use the `cakephp/authentication `__ and - `cakephp/authorization `__ plugins instead. +- `Auth` foi removido. Em vez disso, use os plugins `cakephp/authentication `__ e + `cakephp/authorization `__. Cache ----- -- The ``Wincache`` engine was removed. The wincache extension is not supported - on PHP 8. +- O mecanismo ``Wincache`` foi removido. A extensão wincache não é suportada + no PHP 8. Collection ---------- -- ``combine()`` now throws an exception if the key path or group path doesn't exist or contains a null value. - This matches the behavior of ``indexBy()`` and ``groupBy()``. +- ``combine()`` agora lança uma exceção se o caminho da chave ou do grupo não existir ou contiver um valor nulo. + Isso corresponde ao comportamento de ``indexBy()`` e ``groupBy()``. Console ------- -- ``BaseCommand::__construct()`` was removed. -- ``ConsoleIntegrationTestTrait::useCommandRunner()`` was removed since it's no longer needed. -- ``Shell`` has been removed and should be replaced with `Command `__ -- ``ConsoleOptionParser::addSubcommand()`` was removed alongside the removal of - ``Shell``. Subcommands should be replaced with ``Command`` classes that - implement ``Command::defaultName()`` to define the necessary command name. -- ``BaseCommand`` now emits ``Command.beforeExecute`` and - ``Command.afterExecute`` events around the command's ``execute()`` method - being invoked by the framework. +- ``BaseCommand::__construct()`` foi removido. +- ``ConsoleIntegrationTestTrait::useCommandRunner()`` foi removido porque não é mais necessário. +- ``Shell`` foi removido e deve ser substituído por `Command `__ +- ``ConsoleOptionParser::addSubcommand()`` foi removido juntamente com a remoção de + ``Shell``. Subcomandos devem ser substituídos por classes ``Command`` que + implementam ``Command::defaultName()`` para definir o nome do comando necessário. +- ``BaseCommand`` agora emite eventos ``Command.beforeExecute`` e + ``Command.afterExecute`` em torno do método ``execute()`` do comando + sendo invocado pelo framework. Connection ---------- -- ``Connection::prepare()`` has been removed. You can use ``Connection::execute()`` - instead to execute a SQL query by specifing the SQL string, params and types in a single call. -- ``Connection::enableQueryLogging()`` has been removed. If you haven't enabled logging - through the connection config then you can later set the logger instance for the - driver to enable query logging ``$connection->getDriver()->setLogger()``. +- ``Connection::prepare()`` foi removido. Você pode usar ``Connection::execute()`` + para executar uma consulta SQL especificando a string SQL, os parâmetros e os tipos em uma única chamada. +- ``Connection::enableQueryLogging()`` foi removido. Se você não habilitou o registro + através da configuração de conexão, poderá posteriormente definir a instância do registrador para o + driver para habilitar o registro de consulta ``$connection->getDriver()->setLogger()``. Controller ---------- -- The method signature for ``Controller::__construct()`` has changed. - So you need to adjust your code accordingly if you are overriding the constructor. -- After loading components are no longer set as dynamic properties. Instead - ``Controller`` uses ``__get()`` to provide property access to components. This - change can impact applications that use ``property_exists()`` on components. -- The components' ``Controller.shutdown`` event callback has been renamed from - ``shutdown`` to ``afterFilter`` to match the controller one. This makes the callbacks more consistent. -- ``PaginatorComponent`` has been removed and should be replaced by calling ``$this->paginate()`` in your controller or - using ``Cake\Datasource\Paging\NumericPaginator`` directly -- ``RequestHandlerComponent`` has been removed. See the `4.4 migration `__ guide for how to upgrade -- ``SecurityComponent`` has been removed. Use ``FormProtectionComponent`` for form tampering protection - or ``HttpsEnforcerMiddleware`` to enforce use of HTTPS for requests instead. -- ``Controller::paginate()`` no longer accepts query options like ``contain`` for - its ``$settings`` argument. You should instead use the ``finder`` option - ``$this->paginate($this->Articles, ['finder' => 'published'])``. Or you can - create required select query before hand and then pass it to ``paginate()`` +- A assinatura do método ``Controller::__construct()`` foi alterada. + Portanto, você precisa ajustar seu código adequadamente se estiver substituindo o construtor. +- Após o carregamento, os componentes não são mais definidos como propriedades dinâmicas. Em vez disso, + ``Controller`` usa ``__get()`` para fornecer acesso de propriedade aos componentes. Esta + alteração pode impactar aplicativos que usam ``property_exists()`` em componentes. +- O retorno de chamada do evento ``Controller.shutdown`` dos componentes foi renomeado de + ``shutdown`` para ``afterFilter`` para corresponder ao do controller. Isso torna os retornos de chamada mais consistentes. +- ``PaginatorComponent`` foi removido e deve ser substituído chamando ``$this->paginate()`` no seu controller ou + usando ``Cake\Datasource\Paging\NumericPaginator`` diretamente. +- ``RequestHandlerComponent`` foi removido. Consulte o guia + `migração 4.4 `__ para saber como atualizar. +- ``SecurityComponent`` foi removido. Use ``FormProtectionComponent`` para proteção contra adulteração de formulários + ou ``HttpsEnforcerMiddleware`` para impor o uso de HTTPS para solicitações. +- ``Controller::paginate()`` não aceita mais opções de consulta como ``contain`` para + seu argumento ``$settings``. Em vez disso, você deve usar a opção ``finder`` + ``$this->paginate($this->Articles, ['finder' => 'published'])``. Ou você pode + criar a consulta de seleção necessária com antecedência e passá-la para ``paginate()`` ``$query = $this->Articles->find()->where(['is_published' => true]); $this->paginate($query);``. Core ---- -- The function ``getTypeName()`` has been dropped. Use PHP's ``get_debug_type()`` instead. -- The dependency on ``league/container`` was updated to ``4.x``. This will - require the addition of typehints to your ``ServiceProvider`` implementations. -- ``deprecationWarning()`` now has a ``$version`` parameter. -- The ``App.uploadedFilesAsObjects`` configuration option has been removed - alongside of support for PHP file upload shaped arrays throughout the +- A função ``getTypeName()`` foi descontinuada. Em vez disso, use ``get_debug_type()`` do PHP. +- A dependência de ``league/container`` foi atualizada para ``4.x``. Isso + exigirá a adição de dicas de tipo às suas implementações de ``ServiceProvider``. +- ``deprecationWarning()`` agora possui um parâmetro ``$version``. +- A opção de configuração ``App.uploadedFilesAsObjects`` foi removida, + juntamente com o suporte a arrays em formato de upload de arquivos PHP em todo o framework. -- ``ClassLoader`` has been removed. Use composer to generate autoload files instead. +- ``ClassLoader`` foi removido. Em vez disso, use o composer para gerar arquivos de carregamento automático. Database -------- -- The ``DateTimeType`` and ``DateType`` now always return immutable objects. - Additionally the interface for ``Date`` objects reflects the ``ChronosDate`` - interface which lacks all of the time related methods that were present in - CakePHP 4.x. -- ``DateType::setLocaleFormat()`` no longer accepts an array. -- ``Query`` now accepts only ``\Closure`` parameters instead of ``callable``. Callables can be converted - to closures using the new first-class array syntax in PHP 8.1. -- ``Query::execute()`` no longer runs results decorator callbacks. You must use ``Query::all()`` instead. -- ``TableSchemaAwareInterface`` was removed. -- ``Driver::quote()`` was removed. Use prepared statements instead. -- ``Query::orderBy()`` was added to replace ``Query::order()``. -- ``Query::groupBy()`` was added to replace ``Query::group()``. -- ``SqlDialectTrait`` has been removed and all its functionality has been moved - into the ``Driver`` class itself. -- ``CaseExpression`` has been removed and should be replaced with - ``QueryExpression::case()`` or ``CaseStatementExpression`` -- ``Connection::connect()`` has been removed. Use - ``$connection->getDriver()->connect()`` instead. -- ``Connection::disconnect()`` has been removed. Use - ``$connection->getDriver()->disconnect()`` instead. -- ``cake.database.queries`` has been added as an alternative to the ``queriesLog`` scope -- The ability to enable/disable ResultSet buffering has been removed. Results are always buffered. +- ``DateTimeType`` e ``DateType`` agora sempre retornam objetos imutáveis. + Além disso, a interface para objetos ``Date`` reflete a interface ``ChronosDate``, + que não possui todos os métodos relacionados a tempo presentes no CakePHP 4.x. +- ``DateType::setLocaleFormat()`` não aceita mais um array. +- ``Query`` agora aceita apenas parâmetros ``\Closure`` em vez de ``callable``. Callables podem ser convertidos + em closures usando a nova sintaxe de array de primeira classe do PHP 8.1. +- ``Query::execute()`` não executa mais callbacks do decorador de resultados. Você deve usar ``Query::all()`` em seu lugar. +- ``TableSchemaAwareInterface`` foi removido. +- ``Driver::quote()`` foi removido. Use instruções preparadas em seu lugar. +- ``Query::orderBy()`` foi adicionado para substituir ``Query::order()``. +- ``Query::groupBy()`` foi adicionado para substituir ``Query::group()``. +- ``SqlDialectTrait`` foi removido e toda a sua funcionalidade foi movida + para a própria classe ``Driver``. +- ``CaseExpression`` foi removido e deve ser substituído por + ``QueryExpression::case()`` ou ``CaseStatementExpression`` +- ``Connection::connect()`` foi removido. Use + ``$connection->getDriver()->connect()`` em vez disso. +- ``Connection::disconnect()`` foi removido. Use + ``$connection->getDriver()->disconnect()`` em vez disso. +- ``cake.database.queries`` foi adicionado como alternativa ao escopo ``queriesLog`` +- A capacidade de habilitar/desabilitar o buffer do ResultSet foi removida. Os resultados são sempre armazenados em buffer. Datasource ---------- -- The ``getAccessible()`` method was added to ``EntityInterface``. Non-ORM - implementations need to implement this method now. -- The ``aliasField()`` method was added to ``RepositoryInterface``. Non-ORM - implementations need to implement this method now. +- O método ``getAccessible()`` foi adicionado a ``EntityInterface``. Implementações não-ORM + precisam implementar este método agora. +- O método ``aliasField()`` foi adicionado a ``RepositoryInterface``. Implementações não-ORM + precisam implementar este método agora. Event ----- -- Event payloads must be an array. Other object such as ``ArrayAccess`` are no longer cast to array and will raise a ``TypeError`` now. -- It is recommended to adjust event handlers to be void methods and use ``$event->setResult()`` instead of returning the result +- Os payloads de eventos devem ser um array. Outros objetos, como ``ArrayAccess``, não são mais convertidos para array e agora gerarão um ``TypeError``. +- Recomenda-se ajustar os manipuladores de eventos para que sejam métodos nulos e usem ``$event->setResult()`` em vez de retornar o resultado. Error ----- -- ``ErrorHandler`` and ``ConsoleErrorHandler`` have been removed. See the `4.4 migration `__ guide for how to upgrade -- ``ExceptionRenderer`` has been removed and should be replaced with ``WebExceptionRenderer`` -- ``ErrorLoggerInterface::log()`` has been removed and should be replaced with ``ErrorLoggerInterface::logException()`` -- ``ErrorLoggerInterface::logMessage()`` has been removed and should be replaced with ``ErrorLoggerInterface::logError()`` +- ``ErrorHandler`` e ``ConsoleErrorHandler`` foram removidos. Consulte o guia + `migração 4.4 `__ para saber como atualizar. +- ``ExceptionRenderer`` foi removido e deve ser substituído por ``WebExceptionRenderer`` +- ``ErrorLoggerInterface::log()`` foi removido e deve ser substituído por ``ErrorLoggerInterface::logException()`` +- ``ErrorLoggerInterface::logMessage()`` foi removido e deve ser substituído por ``ErrorLoggerInterface::logError()`` Filesystem ---------- -- The Filesystem package was removed, and ``Filesystem`` class was moved to the Utility package. +- O pacote Filesystem foi removido, e a classe ``Filesystem`` foi movida para o pacote Utility. Http ---- -- ``ServerRequest`` is no longer compatible with ``files`` as arrays. This - behavior has been disabled by default since 4.1.0. The ``files`` data will now - always contain ``UploadedFileInterfaces`` objects. +- ``ServerRequest`` não é mais compatível com ``files`` como arrays. Este + comportamento foi desabilitado por padrão desde a versão 4.1.0. Os dados de ``files`` agora + sempre conterão objetos ``UploadedFileInterfaces``. I18n ---- -- ``FrozenDate`` was renamed to `Date` and ``FrozenTime`` was renamed to `DateTime`. -- ``Time`` now extends ``Cake\Chronos\ChronosTime`` and is therefore immutable. -- ``Date`` objects do not extend ``DateTimeInterface`` anymore - therefore you can't compare them with ``DateTime`` objects. - See the `cakephp/chronos release documentation `__ for more information. -- ``Date::parseDateTime()`` was removed. -- ``Date::parseTime()`` was removed. -- ``Date::setToStringFormat()`` and ``Date::setJsonEncodeFormat()`` no longer accept an array. -- ``Date::i18nFormat()`` and ``Date::nice()`` no longer accept a timezone parameter. -- Translation files for plugins with vendor prefixed names (``FooBar/Awesome``) will now have that - prefix in the file name, e.g. ``foo_bar_awesome.po`` to avoid collision with a ``awesome.po`` file - from a corresponding plugin (``Awesome``). +- ``FrozenDate`` foi renomeado para `Date` e ``FrozenTime`` foi renomeado para ``DateTime``. +- ``Time`` agora estende ``Cake\Chronos\ChronosTime`` e, portanto, é imutável. +- Objetos ``Date`` não estendem mais ``DateTimeInterface`` - portanto, você não pode compará-los com objetos ``DateTime``. + Consulte a documentação da versão `cakephp/chronos `__ para mais informações. +- ``Date::parseDateTime()`` foi removido. +- ``Date::parseTime()`` foi removido. +- ``Date::setToStringFormat()`` e ``Date::setJsonEncodeFormat()`` não aceitam mais um array. +- ``Date::i18nFormat()`` e ``Date::nice()`` não aceitam mais um parâmetro de fuso horário. +- Arquivos de tradução para plugins com nomes prefixados pelo fornecedor (``FooBar/Awesome``) agora terão esse + prefixo no nome do arquivo, por exemplo, ``foo_bar_awesome.po`` para evitar colisão com um arquivo ``awesome.po`` + de um plugin correspondente (``Awesome``). Log --- -- Log engine config now uses ``null`` instead of ``false`` to disable scopes. - So instead of ``'scopes' => false`` you need to use ``'scopes' => null`` in your log config. +- A configuração do mecanismo de log agora usa ``null`` em vez de ``false`` para desabilitar escopos. + Portanto, em vez de ``'scopes' => false``, você precisa usar ``'scopes' => null`` na sua configuração de log. Mailer ------ -- ``Email`` has been removed. Use `Mailer `__ instead. -- ``cake.mailer`` has been added as an alternative to the ``email`` scope +- ``Email`` foi removido. Em vez disso, use `Mailer `__. +- ``cake.mailer`` foi adicionado como alternativa ao escopo ``email``. ORM --- -- ``EntityTrait::has()`` now returns ``true`` when an attribute exists and is - set to ``null``. In previous versions of CakePHP this would return ``false``. - See the release notes for 4.5.0 for how to adopt this behavior in 4.x. -- ``EntityTrait::extractOriginal()`` now returns only existing fields, similar to ``extractOriginalChanged()``. -- Finder arguments are now required to be associative arrays as they were always expected to be. -- ``TranslateBehavior`` now defaults to the ``ShadowTable`` strategy. If you are - using the ``Eav`` strategy you will need to update your behavior configuration - to retain the previous behavior. -- ``allowMultipleNulls`` option for ``isUnique`` rule now default to true matching - the original 3.x behavior. -- ``Table::query()`` has been removed in favor of query-type specific functions. -- ``Table::updateQuery()``, ``Table::selectQuery()``, ``Table::insertQuery()``, and - ``Table::deleteQuery()``) were added and return the new type-specific query objects below. -- ``SelectQuery``, ``InsertQuery``, ``UpdateQuery`` and ``DeleteQuery`` were added - which represent only a single type of query and do not allow switching between query types nor - calling functions unrelated to the specific query type. -- ``Table::_initializeSchema()`` has been removed and should be replaced by calling - ``$this->getSchema()`` inside the ``initialize()`` method. -- ``SaveOptionsBuilder`` has been removed. Use a normal array for options instead. +- ``EntityTrait::has()`` agora retorna ``true`` quando um atributo existe e está + definido como ``null``. Em versões anteriores do CakePHP, isso retornava ``false``. + Consulte as notas de lançamento da versão 4.5.0 para saber como adotar esse comportamento na versão 4.x. +- ``EntityTrait::extractOriginal()`` agora retorna apenas campos existentes, semelhante a ``extractOriginalChanged()``. +- Os argumentos do Finder agora precisam ser arrays associativos, como sempre foi esperado. +- ``TranslateBehavior`` agora usa como padrão a estratégia ``ShadowTable``. Se você estiver + usando a estratégia ``Eav``, precisará atualizar sua configuração de comportamento + para manter o comportamento anterior. +- A opção ``allowMultipleNulls`` para a regra ``isUnique`` agora usa como padrão true, correspondendo + ao comportamento original da versão 3.x. +- ``Table::query()`` foi removido em favor de funções específicas do tipo de consulta. +- ``Table::updateQuery()``, ``Table::selectQuery()``, ``Table::insertQuery()`` e + ``Table::deleteQuery()`` foram adicionados e retornam os novos objetos de consulta específicos do tipo abaixo. +- ``SelectQuery``, ``InsertQuery``, ``UpdateQuery`` e ``DeleteQuery`` foram adicionados, + que representam apenas um único tipo de consulta e não permitem alternar entre tipos de consulta nem + chamar funções não relacionadas ao tipo de consulta específico. +- ``Table::_initializeSchema()`` foi removido e deve ser substituído pela chamada de + ``$this->getSchema()`` dentro do método ``initialize()``. +- ``SaveOptionsBuilder`` foi removido. Use um array normal para opções. Routing ------- -- Static methods ``connect()``, ``prefix()``, ``scope()`` and ``plugin()`` of the ``Router`` have been removed and - should be replaced by calling their non-static method variants via the ``RouteBuilder`` instance. -- ``RedirectException`` has been removed. Use ``\Cake\Http\Exception\RedirectException`` instead. +- Os métodos estáticos ``connect()``, ``prefix()``, ``scope()`` e ``plugin()`` do ``Router`` foram removidos e + devem ser substituídos chamando suas variantes de método não estático por meio da instância ``RouteBuilder``. +- ``RedirectException`` foi removido. Em vez disso, use ``\Cake\Http\Exception\RedirectException``. TestSuite --------- -- ``TestSuite`` was removed. Users should use environment variables to customize - unit test settings instead. -- ``TestListenerTrait`` was removed. PHPUnit dropped support for these listeners. - See :doc:`/appendices/phpunit10` -- ``IntegrationTestTrait::configRequest()`` now merges config when called multiple times - instead of replacing the currently present config. +- ``TestSuite`` foi removido. Os usuários devem usar variáveis ​​de ambiente para personalizar + as configurações de teste unitário. +- ``TestListenerTrait`` foi removido. O PHPUnit removeu o suporte para esses ouvintes. + Consulte :doc:`/appendices/phpunit10` +- ``IntegrationTestTrait::configRequest()`` agora mescla a configuração quando chamado várias vezes + em vez de substituir a configuração atual. Validation ---------- -- ``Validation::isEmpty()`` is no longer compatible with file upload shaped - arrays. Support for PHP file upload arrays has been removed from - ``ServerRequest`` as well so you should not see this as a problem outside of - tests. -- Previously, most data validation error messages were simply ``The provided value is invalid``. - Now, the data validation error messages are worded more precisely. - For example, ``The provided value must be greater than or equal to \`5\```. +- ``Validation::isEmpty()`` não é mais compatível com arrays + de upload de arquivos. O suporte para arrays de upload de arquivos PHP também foi removido de + ``ServerRequest``, portanto, você não deve ver isso como um problema fora dos testes. +- Anteriormente, a maioria das mensagens de erro de validação de dados era simplesmente ``O valor fornecido é inválido``. + Agora, as mensagens de erro de validação de dados são redigidas com mais precisão. + Por exemplo, ``O valor fornecido deve ser maior ou igual a \`5\```. View ---- -- ``ViewBuilder`` options are now truly associative (string keys). -- ``NumberHelper`` and ``TextHelper`` no longer accept an ``engine`` config. -- ``ViewBuilder::setHelpers()`` parameter ``$merge`` was removed. Use ``ViewBuilder::addHelpers()`` instead. -- Inside ``View::initialize()``, prefer using ``addHelper()`` instead of ``loadHelper()``. - All configured helpers will be loaded afterwards, anyway. -- ``View\Widget\FileWidget`` is no longer compatible with PHP file upload shaped - arrays. This is aligned with ``ServerRequest`` and ``Validation`` changes. -- ``FormHelper`` no longer sets ``autocomplete=off`` on CSRF token fields. This - was a workaround for a Safari bug that is no longer relevant. +- As opções ``ViewBuilder`` agora são verdadeiramente associativas (chaves de string). +- ``NumberHelper`` e ``TextHelper`` não aceitam mais a configuração ``engine``. +- O parâmetro ``$merge`` do ``ViewBuilder::setHelpers()`` foi removido. Em vez disso, use ``ViewBuilder::addHelpers()``. +- Dentro de ``View::initialize()``, prefira usar ``addHelper()`` em vez de ``loadHelper()``. + Todos os auxiliares configurados serão carregados posteriormente, de qualquer forma. +- ``View\Widget\FileWidget`` não é mais compatível com arrays em formato de upload de arquivos PHP. + Isso está alinhado com as alterações em ``ServerRequest`` e ``Validation``. +- ``FormHelper`` não define mais ``autocomplete=off`` em campos de token CSRF. Esta + foi uma solução alternativa para um bug do Safari que não é mais relevante. Deprecations ============ -The following is a list of deprecated methods, properties and behaviors. These -features will continue to function in 5.x and will be removed in 6.0. +A seguir, uma lista de métodos, propriedades e comportamentos obsoletos. +Esses recursos continuarão funcionando na versão 5.x e serão removidos na versão 6.0. Database -------- -- ``Query::order()`` was deprecated. Use ``Query::orderBy()`` instead now that - ``Connection`` methods are no longer proxied. This aligns the function name - with the SQL statement. -- ``Query::group()`` was deprecated. Use ``Query::groupBy()`` instead now that - ``Connection`` methods are no longer proxied. This aligns the function name - with the SQL statement. +- ``Query::order()`` foi descontinuado. Use ``Query::orderBy()`` em vez disso, agora que + os métodos ``Connection`` não são mais proxy. Isso alinha o nome da função + com a instrução SQL. +- ``Query::group()`` foi descontinuado. Use ``Query::groupBy()`` em vez disso, agora que + os métodos ``Connection`` não são mais proxy. Isso alinha o nome da função + com a instrução SQL. ORM --- -- Calling ``Table::find()`` with options array is deprecated. Use `named arguments `__ - instead. For e.g. instead of ``find('all', ['conditions' => $array])`` use - ``find('all', conditions: $array)``. Similarly for custom finder options, instead - of ``find('list', ['valueField' => 'name'])`` use ``find('list', valueField: 'name')`` - or multiple named arguments like ``find(type: 'list', valueField: 'name', conditions: $array)``. +- Chamar ``Table::find()`` com array de opções está obsoleto. Use + `argumentos nomeados `__ + em vez disso. Por exemplo, em vez de ``find('all', ['conditions' => $array])``, use + ``find('all', conditions: $array)``. Da mesma forma, para opções personalizadas do localizador, em vez + de ``find('list', ['valueField' => 'name'])``, use ``find('list', valueField: 'name')`` + ou múltiplos argumentos nomeados como ``find(type: 'list', valueField: 'name', conditions: $array)``. New Features ============ @@ -299,46 +299,45 @@ New Features Improved type checking ----------------------- -CakePHP 5 leverages the expanded type system feature available in PHP 8.1+. -CakePHP also uses ``assert()`` to provide improved error messages and additional -type soundness. In production mode, you can configure PHP to not generate -code for ``assert()`` yielding improved application performance. See the -:ref:`symlink-assets` for how to do this. +O CakePHP 5 aproveita o recurso de sistema de tipos expandido disponível no PHP 8.1+. +O CakePHP também utiliza ``assert()`` para fornecer mensagens de erro aprimoradas e maior solidez de tipos. +No modo de produção, você pode configurar o PHP para não gerar código para ``assert()``, +resultando em melhor desempenho da aplicação. Consulte o arquivo +:ref:`symlink-assets` para saber como fazer isso. Collection ---------- -- Added ``unique()`` which filters out duplicate value specified by provided callback. -- ``reject()`` now supports a default callback which filters out truthy values which is - the inverse of the default behavior of ``filter()`` +- Adicionado ``unique()``, que filtra valores duplicados especificados pelo retorno de chamada fornecido. +- ``reject()`` agora suporta um retorno de chamada padrão que filtra valores verdadeiros, o que é + o inverso do comportamento padrão de ``filter()``. Core ---- -- The ``services()`` method was added to ``PluginInterface``. -- ``PluginCollection::addFromConfig()`` has been added to :ref:`simplify plugin loading `. +- O método ``services()`` foi adicionado a ``PluginInterface``. +- ``PluginCollection::addFromConfig()`` foi adicionado para :ref:`simplificar o carregamento de plugins `. Database -------- -- ``ConnectionManager`` now supports read and write connection roles. Roles can be configured - with ``read`` and ``write`` keys in the connection config that override the shared config. -- ``Query::all()`` was added which runs result decorator callbacks and returns a result set for select queries. -- ``Query::comment()`` was added to add a SQL comment to the executed query. This makes it easier to debug queries. -- ``EnumType`` was added to allow mapping between PHP backed enums and a string or integer column. -- ``getMaxAliasLength()`` and ``getConnectionRetries()`` were added - to ``DriverInterface``. -- Supported drivers now automatically add auto-increment only to integer primary keys named "id" instead - of all integer primary keys. Setting 'autoIncrement' to false always disables on all supported drivers. +- ``ConnectionManager`` agora suporta funções de conexão de leitura e gravação. As funções podem ser configuradas + com as chaves ``read`` e ``write`` na configuração de conexão que substituem a configuração compartilhada. +- ``Query::all()`` foi adicionado, o que executa callbacks do decorador de resultados e retorna um conjunto de resultados para consultas selecionadas. +- ``Query::comment()`` foi adicionado para adicionar um comentário SQL à consulta executada. Isso facilita a depuração de consultas. +- ``EnumType`` foi adicionado para permitir o mapeamento entre enumerações baseadas em PHP e uma coluna de string ou inteiro. +- ``getMaxAliasLength()`` e ``getConnectionRetries()`` foram adicionados à ``DriverInterface``. +- Os drivers suportados agora adicionam automaticamente o incremento automático apenas às chaves primárias inteiras denominadas "id" em vez + de todas as chaves primárias inteiras. Definir 'autoIncrement' como falso sempre desabilita em todos os drivers suportados. Http ---- -- Added support for `PSR-17 `__ factories - interface. This allows ``cakephp/http`` to provide a client implementation to - libraries that allow automatic interface resolution like php-http. -- Added ``CookieCollection::__get()`` and ``CookieCollection::__isset()`` to add - ergonomic ways to access cookies without exceptions. +- Adicionado suporte para a interface `PSR-17 `__factorys. + Isso permite que ``cakephp/http`` forneça uma implementação cliente para + bibliotecas que permitem resolução automática de interface, como php-http. +- Adicionados ``CookieCollection::__get()`` e ``CookieCollection::__isset()`` para adicionar + maneiras ergonômicas de acessar cookies sem exceções. ORM --- @@ -346,10 +345,10 @@ ORM Required Entity Fields ---------------------- -Entities have a new opt-in functionality that allows making entities handle -properties more strictly. The new behavior is called 'required fields'. When -enabled, accessing properties that are not defined in the entity will raise -exceptions. This impacts the following usage:: +As entidades têm uma nova funcionalidade de opt-in que permite que as entidades tratem +propriedades de forma mais rigorosa. O novo comportamento é chamado de "campos obrigatórios". Quando +habilitado, o acesso a propriedades não definidas na entidade gerará +exceções. Isso afeta o seguinte uso:: $entity->get(); $entity->has(); @@ -357,17 +356,16 @@ exceptions. This impacts the following usage:: isset($entity->attribute); $entity->attribute; -Fields are considered defined if they pass ``array_key_exists``. This includes -null values. Because this can be a tedious to enable feature, it was deferred to -5.0. We'd like any feedback you have on this feature as we're considering making -this the default behavior in the future. - +Os campos são considerados definidos se passarem por ``array_key_exists``. Isso inclui +valores nulos. Como esse recurso pode ser trabalhoso de habilitar, ele foi adiado para a versão 5.0. +Gostaríamos de receber seu feedback sobre esse recurso, pois estamos considerando torná-lo +o comportamento padrão no futuro. Typed Finder Parameters ----------------------- -Table finders can now have typed arguments as required instead of an options array. -For e.g. a finder for fetching posts by category or user:: +Os localizadores de tabela agora podem ter argumentos digitados, conforme necessário, em vez de uma matriz de opções. +Por exemplo, um localizador para buscar postagens por categoria ou usuário:: public function findByCategoryOrUser(SelectQuery $query, array $options) { @@ -381,7 +379,7 @@ For e.g. a finder for fetching posts by category or user:: return $query; } -can now be written as:: +agora pode ser escrito como:: public function findByCategoryOrUser(SelectQuery $query, ?int $categoryId = null, ?int $userId = null) { @@ -395,11 +393,11 @@ can now be written as:: return $query; } -The finder can then be called as ``find('byCategoryOrUser', userId: $somevar)``. -You can even include the special named arguments for setting query clauses. +O localizador pode então ser chamado como ``find('byCategoryOrUser', userId: $somevar)``. +Você pode até incluir argumentos nomeados especiais para definir cláusulas de consulta. ``find('byCategoryOrUser', userId: $somevar, conditions: ['enabled' => true])``. -A similar change has been applied to the ``RepositoryInterface::get()`` method:: +Uma alteração semelhante foi aplicada ao método ``RepositoryInterface::get()``:: public function view(int $id) { @@ -409,7 +407,7 @@ A similar change has been applied to the ``RepositoryInterface::get()`` method:: ]); } -can now be written as:: +agora pode ser escrito como:: public function view(int $id) { @@ -419,10 +417,11 @@ can now be written as:: TestSuite --------- -- ``IntegrationTestTrait::requestAsJson()`` has been added to set JSON headers for the next request. +- ``IntegrationTestTrait::requestAsJson()`` foi adicionado para definir cabeçalhos JSON para a próxima solicitação. Plugin Installer ---------------- -- The plugin installer has been updated to automatically handle class autoloading - for your app plugins. So you can remove the namespace to path mappings for your - plugins from your ``composer.json`` and just run ``composer dumpautoload``. + +- O instalador de plugins foi atualizado para lidar automaticamente com o carregamento automático de classes + para os plugins do seu aplicativo. Assim, você pode remover os mapeamentos de namespace para caminho dos seus + plugins do seu ``composer.json`` e simplesmente executar ``composer dumpautoload``. From de20c6b441c4762ec48ba5327561e19cd10701d6 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 17:03:08 -0300 Subject: [PATCH 06/46] [refact]: translated (pt/appendices/migration-guides.rst) --- pt/appendices/migration-guides.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pt/appendices/migration-guides.rst b/pt/appendices/migration-guides.rst index 2a86606039..a898a1241c 100644 --- a/pt/appendices/migration-guides.rst +++ b/pt/appendices/migration-guides.rst @@ -1,8 +1,8 @@ -Migration Guides -################ +Guias de Migração +################# -Migration guides contain information regarding the new features introduced in -each version and the migration path between 5.x minor releases. +Os guias de migração contêm informações sobre os novos recursos introduzidos em +cada versão e o caminho de migração entre as versões secundárias 5.x. .. toctree:: :maxdepth: 1 From 9494458db47542198dda314538927d9cf8a7472d Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 17:03:35 -0300 Subject: [PATCH 07/46] [refact]: translated (pt/appendices/5-0-upgrade-guide.rst) --- pt/appendices/5-0-upgrade-guide.rst | 70 +++++++++++++++-------------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/pt/appendices/5-0-upgrade-guide.rst b/pt/appendices/5-0-upgrade-guide.rst index 5f34ec911c..fc14a4a31b 100644 --- a/pt/appendices/5-0-upgrade-guide.rst +++ b/pt/appendices/5-0-upgrade-guide.rst @@ -1,44 +1,46 @@ -5.0 Upgrade Guide -################# +5.0 Guia de atualização +####################### -First, check that your application is running on latest CakePHP 4.x version. +Primeiro, verifique se seu aplicativo está sendo executado na versão mais recente do CakePHP 4.x. -Fix Deprecation Warnings -======================== +Corrigir Avisos de Descontinuação +================================= -Once your application is running on latest CakePHP 4.x, enable deprecation warnings in **config/app.php**:: +Depois que seu aplicativo estiver sendo executado na versão mais recente do CakePHP 4.x, +habilite os avisos de descontinuação em **config/app.php**:: 'Error' => [ 'errorLevel' => E_ALL, ] -Now that you can see all the warnings, make sure these are fixed before proceeding with the upgrade. +Agora que você pode ver todos os avisos, certifique-se de que eles sejam corrigidos antes de prosseguir com a atualização. -Some potentially impactful deprecations you should make sure you have addressed -are: +Algumas descontinuações potencialmente impactantes que você deve ter certeza de ter abordado +são: -- ``Table::query()`` was deprecated in 4.5.0. Use ``selectQuery()``, - ``updateQuery()``, ``insertQuery()`` and ``deleteQuery()`` instead. +- ``Table::query()`` foi descontinuado na versão 4.5.0. Em vez disso, use ``selectQuery()``, + ``updateQuery()``, ``insertQuery()`` e ``deleteQuery()``. -Upgrade to PHP 8.1 -================== +Atualizar para PHP 8.1 +====================== -If you are not running on **PHP 8.1 or higher**, you will need to upgrade PHP before updating CakePHP. +Se você não estiver usando **PHP 8.1 ou superior**, será necessário atualizar o PHP antes de atualizar o CakePHP. .. note:: - CakePHP 5.0 requires **a minimum of PHP 8.1**. + O CakePHP 5.0 requer **no mínimo PHP 8.1**. .. _upgrade-tool-use: -Use the Upgrade Tool -==================== +Use a Ferramenta de Atualização +=============================== .. note:: - The upgrade tool only works on applications running on latest CakePHP 4.x. You cannot run the upgrade tool after updating to CakePHP 5.0. + A ferramenta de atualização só funciona em aplicativos que executam a versão mais recente do CakePHP 4.x. + Você não pode executar a ferramenta de atualização após atualizar para o CakePHP 5.0. -Because CakePHP 5 leverages union types and ``mixed``, there are many -backwards incompatible changes concerning method signatures and file renames. -To help expedite fixing these tedious changes there is an upgrade CLI tool: +Como o CakePHP 5 utiliza tipos de união e ``mixed``, há muitas +mudanças incompatíveis com versões anteriores relacionadas a assinaturas de métodos e renomeação de arquivos. +Para ajudar a agilizar a correção dessas mudanças tediosas, existe uma ferramenta de CLI de atualização: .. code-block:: console @@ -48,27 +50,27 @@ To help expedite fixing these tedious changes there is an upgrade CLI tool: git checkout 5.x composer install --no-dev -With the upgrade tool installed you can now run it on your application or +Com a ferramenta de atualização instalada, você pode executá-la em seu aplicativo ou plugin:: bin/cake upgrade rector --rules cakephp50 bin/cake upgrade rector --rules chronos3 -Update CakePHP Dependency -========================= +Atualizar Dependência do CakePHP +================================ -After applying rector refactorings you need to upgrade CakePHP, its plugins, PHPUnit -and maybe other dependencies in your ``composer.json``. -This process heavily depends on your application so we recommend you compare your -``composer.json`` with what is present in `cakephp/app +Após aplicar as refatorações do rector, você precisa atualizar o CakePHP, seus plugins, o PHPUnit +e talvez outras dependências no seu ``composer.json``. +Este processo depende muito da sua aplicação, por isso recomendamos que você compare o seu +``composer.json`` com o que está presente em `cakephp/app `__. -After the version strings are adjusted in your ``composer.json`` execute -``composer update -W`` and check its output. +Após as strings de versão serem ajustadas em seu ``composer.json``, execute +``composer update -W`` e verifique sua saída. -Update app files based upon latest app template -=============================================== +Atualizar arquivos do aplicativo com base no modelo de aplicativo mais recente +============================================================================== -Next, ensure the rest of your application has been updated to be based upon the -latest version of `cakephp/app +Em seguida, certifique-se de que o restante do seu aplicativo foi atualizado para se basear na +versão mais recente de `cakephp/app `__. From 86095eb392d4883a84124d6693254d6481f67511 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 2 Sep 2025 17:04:06 -0300 Subject: [PATCH 08/46] [refact]: linked (pt/plugins.rst) --- pt/plugins.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pt/plugins.rst b/pt/plugins.rst index fdda612489..b431d33011 100644 --- a/pt/plugins.rst +++ b/pt/plugins.rst @@ -44,6 +44,8 @@ plugins quando são carregados com ``load ()`` ou ``loadAll ()``. Você geralmen não precisará editar este arquivo à mão, com Composer e ``plugin-installer`` O pacote o gerenciará para você. +.. _loading-a-plugin: + Carregando um Plugin ==================== From df93a4d2c8006e4348667e71997f6f0b4b04d75b Mon Sep 17 00:00:00 2001 From: kromodoro Date: Wed, 3 Sep 2025 06:02:41 -0300 Subject: [PATCH 09/46] [refact]: translated (pt/appendices/5-1-migration-guide.rst) --- pt/appendices/5-1-migration-guide.rst | 233 +++++++++++++------------- 1 file changed, 116 insertions(+), 117 deletions(-) diff --git a/pt/appendices/5-1-migration-guide.rst b/pt/appendices/5-1-migration-guide.rst index caac02853f..dd79c84073 100644 --- a/pt/appendices/5-1-migration-guide.rst +++ b/pt/appendices/5-1-migration-guide.rst @@ -1,42 +1,42 @@ -5.1 Migration Guide -################### +5.1 Guia de Migração +#################### -The 5.1.0 release is a backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. +A versão 5.1.0 é compatível com a versão 5.0. Ela adiciona novas funcionalidades +e introduz novas descontinuações. Qualquer funcionalidade descontinuada na versão 5.x será +removida na versão 6.0.0. Behavior Changes ================ -- Connection now creates unique read and write drivers if the keys ``read`` or - ``write`` are present in the config regardless of values. -- FormHelper no longer generates ``aria-required`` attributes on input elements - that also have the ``required`` attribute set. The ``aria-required`` attribute - is redundant on these elements and generates HTML validation warnings. If you - are using ``aria-required`` attribute in styling or scripting you'll need to - update your application. -- Adding associations with duplicate names will now raise exceptions. You can - use ``$table->associations()->has()`` to conditionally define associations if - required. -- Text Utility and TextHelper methods around truncation and maximum length are using - a UTF-8 character for ``ellipsis`` instead of ``...`` legacy characters. -- ``TableSchema::setColumnType()`` now throws an exception if the specified column - does not exist. -- ``PluginCollection::addPlugin()`` now throws an exception if a plugin of the same - name is already added. -- ``TestCase::loadPlugins()`` will now clear out any previously loaded plugins. So - you must specify all plugins required for any subsequent tests. -- The hashing algorithm for ``Cache`` configurations that use ``groups``. Any - keys will have new group prefix hashes generated which will cause cache - misses. Consider an incremental deploy to avoid operating on an entirely cold - cache. -- ``FormHelper::getFormProtector()`` now returns ``null`` in addition to its - previous types. This allows dynamic view code to run with fewer errors and - shouldn't impact most applications. -- The default value for ``valueSeparator`` in ``Table::findList()`` is now - a single space instead of ``;``. -- ``ErrorLogger`` uses ``Psr\Log\LogTrait`` now. -- ``Database\QueryCompiler::$_orderedUnion`` was removed. +- A conexão agora cria drivers de leitura e gravação exclusivos se as chaves ``read`` ou + ``write`` estiverem presentes na configuração, independentemente dos valores. +- O FormHelper não gera mais atributos ``aria-required`` em elementos de entrada + que também possuem o atributo ``required`` definido. O atributo ``aria-required`` + é redundante nesses elementos e gera avisos de validação HTML. Se você + estiver usando o atributo ``aria-required`` em estilização ou script, precisará + atualizar seu aplicativo. +- Adicionar associações com nomes duplicados agora gerará exceções. Você pode + usar ``$table->associations()->has()`` para definir associações condicionalmente, + se necessário. +- Os métodos Text Utility e TextHelper sobre truncamento e comprimento máximo estão usando + um caractere UTF-8 para ``ellipsis`` em vez de caracteres legados ``...``. +- ``TableSchema::setColumnType()`` agora gera uma exceção se a coluna especificada + não existir. +- ``PluginCollection::addPlugin()`` agora gera uma exceção se um plugin com o mesmo + nome já tiver sido adicionado. +- ``TestCase::loadPlugins()`` agora limpará todos os plugins carregados anteriormente. Portanto, + você deve especificar todos os plugins necessários para quaisquer testes subsequentes. +- O algoritmo de hash para configurações de ``Cache`` que usam ``groups``. Quaisquer + chaves terão novos hashes de prefixo de grupo gerados, o que causará + falhas de cache. Considere uma implementação incremental para evitar operar em um cache + totalmente frio. +- ``FormHelper::getFormProtector()`` agora retorna ``null`` além de seus + tipos anteriores. Isso permite que o código de visualização dinâmica seja executado com menos erros e + não deve impactar a maioria dos aplicativos. +- O valor padrão para ``valueSeparator`` em ``Table::findList()`` agora é + um único espaço em vez de ``;``. +- ``ErrorLogger`` agora usa ``Psr\Log\LogTrait``. +- ``Database\QueryCompiler::$_orderedUnion`` foi removido. Deprecations ============ @@ -44,13 +44,13 @@ Deprecations I18n ---- -- The ``_cake_core_`` cache config key has been renamed to ``_cake_translations_``. +- A chave de configuração de cache ``_cake_core_`` foi renomeada para ``_cake_translations_``. Mailer ------ -- ``Mailer::setMessage()`` is deprecated. It has unintuitive behavior and very - low usage. +- ``Mailer::setMessage()`` está obsoleto. Seu comportamento é pouco intuitivo e seu uso é + muito baixo. New Features @@ -59,140 +59,139 @@ New Features Cache ----- -- ``RedisEngine`` now supports a ``tls`` option that enables connecting to redis - over a TLS connection. You can use the ``ssl_ca``, ``ssl_cert`` and - ``ssl_key`` options to define the TLS context for redis. +- ``RedisEngine`` agora suporta a opção ``tls`` que permite a conexão ao Redis + por meio de uma conexão TLS. Você pode usar as opções ``ssl_ca``, ``ssl_cert`` e + ``ssl_key`` para definir o contexto TLS para o Redis. Command ------- -- ``bin/cake plugin list`` has been added to list all available plugins, - their load configuration and version. -- Optional ``Command`` arguments can now have a ``default`` value. -- ``BannerHelper`` was added. This command helper can format text as a banner - with a coloured background and padding. -- Additional default styles for ``info.bg``, ``warning.bg``, ``error.bg`` and - ``success.bg`` were added to ``ConsoleOutput``. +- ``bin/cake plugin list`` foi adicionado para listar todos os plugins disponíveis, + sua configuração de carregamento e versão. +- Argumentos opcionais ``Command`` agora podem ter um valor ``default``. +- ``BannerHelper`` foi adicionado. Este auxiliar de comando pode formatar texto como um banner + com fundo colorido e preenchimento. +- Estilos padrão adicionais para ``info.bg``, ``warning.bg``, ``error.bg`` e + ``success.bg`` foram adicionados a ``ConsoleOutput``. Console ------- -- ``Arguments::getBooleanOption()`` and ``Arguments::getMultipleOption()`` were added. -- ``Arguments::getArgument()`` will now raise an exception if an unknown - argument name is provided. This helps prevent mixing up option/argument names. - +- ``Arguments::getBooleanOption()`` e ``Arguments::getMultipleOption()`` foram adicionados. +- ``Arguments::getArgument()`` agora gerará uma exceção se um nome de argumento + desconhecido for fornecido. Isso ajuda a evitar a confusão de nomes de opções/argumentos. Controller ---------- -- Components can now use the DI container to have dependencies resolved and - provided as constructor parameters just like Controllers and Commands do. +- Os componentes agora podem usar o contêiner DI para ter dependências resolvidas e + fornecidas como parâmetros do construtor, assim como Controllers e Comandos. Core ---- -- ``PluginConfig`` was added. Use this class to get all available plugins, their load config and versions. -- The ``toString``, ``toInt``, ``toBool`` functions were added. They give you - a typesafe way to cast request data or other input and return ``null`` when conversion fails. -- ``pathCombine()`` was added to help build paths without worrying about duplicate and trailing slashes. -- A new ``events`` hook was added to the ``BaseApplication`` as well as the ``BasePlugin`` class. This hook - is the recommended way to register global event listeners for you application. See :ref:`Registering Listeners ` +- ``PluginConfig`` foi adicionado. Use esta classe para obter todos os plugins disponíveis, suas configurações de carga e versões. +- As funções ``toString``, ``toInt`` e ``toBool`` foram adicionadas. Elas oferecem + uma maneira segura de converter dados de requisição ou outras entradas e retornar ``null`` quando a conversão falha. +- ``pathCombine()`` foi adicionado para ajudar a construir caminhos sem se preocupar com barras duplicadas e finais. +- Um novo hook ``events`` foi adicionado à classe ``BaseApplication``, bem como à classe ``BasePlugin``. Este hook + é a maneira recomendada de registrar ouvintes de eventos globais para sua aplicação. + Consulte :ref:`Registrando Ouvintes ` Database -------- -- Support for ``point``, ``linestring``, ``polygon`` and ``geometry`` types were - added. These types are useful when working with geospatial or cartesian - co-ordinates. Sqlite support uses text columns under the hood and lacks - functions to manipulate data as geospatial values. -- ``SelectQuery::__debugInfo()`` now includes which connection role the query - is for. -- ``SelectQuery::intersect()`` and ``SelectQuery::intersectAll()`` were added. - These methods enable queries using ``INTERSECT`` and ``INTERSECT ALL`` - conjunctions to be expressed. -- New supports features were added for ``intersect``, ``intersect-all`` and - ``set-operations-order-by`` features. -- The ability to fetch records without buffering which existed in 4.x has been restored. - Methods ``SelectQuery::enableBufferedResults()``, ``SelectQuery::disableBufferedResults()`` - and ``SelectQuery::isBufferedResultsEnabled()`` have been re-added. +- Suporte para os tipos ``point``, ``linestring``, ``polygon`` e ``geometry`` foi + adicionado. Esses tipos são úteis ao trabalhar com coordenadas geoespaciais ou cartesianas. + O suporte ao SQLite usa colunas de texto por baixo dos panos e não possui + funções para manipular dados como valores geoespaciais. +- ``SelectQuery::__debugInfo()`` agora inclui a função de conexão para a qual a consulta + se destina. +- ``SelectQuery::intersect()`` e ``SelectQuery::intersectAll()`` foram adicionados. + Esses métodos permitem que consultas usando as conjunções ``INTERSECT`` e ``INTERSECT ALL`` + sejam expressas. +- Novos recursos de suporte foram adicionados para os recursos ``intersect``, ``intersect-all`` e + ``set-operations-order-by``. +- A capacidade de buscar registros sem buffer, que existia na versão 4.x, foi restaurada. + Os métodos ``SelectQuery::enableBufferedResults()``, ``SelectQuery::disableBufferedResults()`` + e ``SelectQuery::isBufferedResultsEnabled()`` foram adicionados novamente. Datasource ---------- -- ``RulesChecker::remove()``, ``removeCreate()``, ``removeUpdate()``, and - ``removeDelete()`` methods were added. These methods allow you to remove rules - by name. - +- Os métodos ``RulesChecker::remove()``, ``removeCreate()``, ``removeUpdate()`` e + ``removeDelete()`` foram adicionados. Esses métodos permitem remover regras + por nome. Http ---- -- ``SecurityHeadersMiddleware::setPermissionsPolicy()`` was added. This method - adds the ability to define ``permissions-policy`` header values. -- ``Client`` now emits ``HttpClient.beforeSend`` and ``HttpClient.afterSend`` - events when requests are sent. You can use these events to perform logging, - caching or collect telemetry. -- ``Http\Server::terminate()`` was added. This method triggers the - ``Server.terminate`` event which can be used to run logic after the response - has been sent in fastcgi environments. In other environments the - ``Server.terminate`` event runs *before* the response has been sent. +- ``SecurityHeadersMiddleware::setPermissionsPolicy()`` foi adicionado. Este método + adiciona a capacidade de definir valores de cabeçalho ``permissions-policy``. +- ``Client`` agora emite os eventos ``HttpClient.beforeSend`` e ``HttpClient.afterSend`` + quando as solicitações são enviadas. Você pode usar esses eventos para executar logs, + armazenamento em cache ou coletar telemetria. +- ``Http\Server::terminate()`` foi adicionado. Este método aciona o + evento ``Server.terminate``, que pode ser usado para executar lógica após a resposta + ter sido enviada em ambientes fastcgi. Em outros ambientes, o + evento ``Server.terminate`` é executado *antes* do envio da resposta. I18n ---- -- ``Number::formatter()`` and ``currency()`` now accept a ``roundingMode`` - option to override how rounding is done. -- The ``toDate``, and ``toDateTime`` functions were added. They give you - a typesafe way to cast request data or other input and return ``null`` when - conversion fails. +- ``Number::formatter()`` e ``currency()`` agora aceitam a opção ``roundingMode`` + para substituir a forma como o arredondamento é feito. +- As funções ``toDate`` e ``toDateTime`` foram adicionadas. Elas oferecem + uma maneira segura de converter dados de solicitação ou outra entrada e retornar ``null`` quando + a conversão falhar. ORM --- -- Setting the ``preserveKeys`` option on association finder queries. This can be - used with ``formatResults()`` to replace association finder results with an - associative array. -- SQLite columns with names containing ``json`` can now be mapped to ``JsonType``. - This is currently an opt-in feature which is enabled by setting the ``ORM.mapJsonTypeForSqlite`` - configure value to ``true`` in your app. +- Definindo a opção ``preserveKeys`` em consultas do localizador de associações. Isso pode ser + usado com ``formatResults()`` para substituir os resultados do localizador de associações por um + array associativo. +- Colunas SQLite com nomes contendo ``json`` agora podem ser mapeadas para ``JsonType``. + Este é atualmente um recurso opcional que é habilitado definindo o valor de configuração ``ORM.mapJsonTypeForSqlite`` + como ``true`` em seu aplicativo. TestSuite --------- -- CakePHP as well as the app template have been updated to use PHPUnit ``^10.5.5 || ^11.1.3"``. -- ``ConnectionHelper`` methods are now all static. This class has no state and - its methods were updated to be static. -- ``LogTestTrait`` was added. This new trait makes it easy to capture logs in - your tests and make assertions on the presence or absence of log messages. -- ``IntegrationTestTrait::replaceRequest()`` was added. +- O CakePHP e o template do aplicativo foram atualizados para usar o PHPUnit ``^10.5.5 || ^11.1.3"``. +- Os métodos ``ConnectionHelper`` agora são todos estáticos. Esta classe não possui estado e + seus métodos foram atualizados para serem estáticos. +- ``LogTestTrait`` foi adicionado. Este novo trait facilita a captura de logs + em seus testes e a realização de asserções sobre a presença ou ausência de mensagens de log. +- ``IntegrationTestTrait::replaceRequest()`` foi adicionado. Utility ------- -- ``Hash::insert()`` and ``Hash::remove()`` now accept ``ArrayAccess`` objects along with ``array`` data. +- ``Hash::insert()`` e ``Hash::remove()`` agora aceitam objetos ``ArrayAccess`` junto com dados ``array``. Validation ---------- -- ``Validation::enum()`` and ``Validator::enum()`` were added. These validation - methods simplify validating backed enum values. -- ``Validation::enumOnly()`` and ``Validation::enumExcept()`` were added to check for specific cases - and further simplify validating backed enum values. +- ``Validation::enum()`` e ``Validator::enum()`` foram adicionados. Esses métodos de + validação simplificam a validação de valores de enumeração com suporte. +- ``Validation::enumOnly()`` e ``Validation::enumExcept()`` foram adicionados para verificar casos específicos + e simplificar ainda mais a validação de valores de enumeração com suporte. View ---- -- View cells now emit events around their actions ``Cell.beforeAction`` and +- As células de visualização agora emitem eventos em torno de suas ações ``Cell.beforeAction`` e ``Cell.afterAction``. -- ``NumberHelper::format()`` now accepts a ``roundingMode`` option to override how - rounding is done. +- ``NumberHelper::format()`` agora aceita a opção ``roundingMode`` para substituir como + o arredondamento é feito. Helpers ------- -- ``TextHelper::autoLinkUrls()`` has options added for better link label printing: - * ``stripProtocol``: Strips ``http://`` and ``https://`` from the beginning of the link. Default off. - * ``maxLength``: The maximum length of the link label. Default off. - * ``ellipsis``: The string to append to the end of the link label. Defaults to UTF8 version. -- ``HtmlHelper::meta()`` can now create a meta tag containing the current CSRF - token using ``meta('csrfToken')``. +- ``TextHelper::autoLinkUrls()`` possui opções adicionadas para melhorar a impressão do rótulo do link: + * ``stripProtocol``: Remove ``http://`` e ``https://`` do início do link. Padrão desativado. + * ``maxLength``: O comprimento máximo do rótulo do link. Padrão desativado. + * ``ellipsis``: A string a ser anexada ao final do rótulo do link. Padrão para a versão UTF8. +- ``HtmlHelper::meta()`` agora pode criar uma meta tag contendo o token CSRF + atual usando ``meta('csrfToken')``. From 5b4b3ce0f2ccda97a4c86c1e0eafa79791a98fb3 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Wed, 3 Sep 2025 06:03:04 -0300 Subject: [PATCH 10/46] [refact]: translated (pt/appendices/5-2-migration-guide.rst) --- pt/appendices/5-2-migration-guide.rst | 146 +++++++++++++------------- 1 file changed, 71 insertions(+), 75 deletions(-) diff --git a/pt/appendices/5-2-migration-guide.rst b/pt/appendices/5-2-migration-guide.rst index 66567f439a..fa54745ec6 100644 --- a/pt/appendices/5-2-migration-guide.rst +++ b/pt/appendices/5-2-migration-guide.rst @@ -1,22 +1,21 @@ 5.2 Migration Guide ################### -The 5.2.0 release is a backwards compatible with 5.0. It adds new functionality -and introduces new deprecations. Any functionality deprecated in 5.x will be -removed in 6.0.0. +A versão 5.2.0 é compatível com a versão 5.0. Ela adiciona novas funcionalidades +e introduz novas descontinuações. Qualquer funcionalidade descontinuada na versão 5.x será +removida na versão 6.0.0. Behavior Changes ================ -- ``ValidationSet::add()`` will now raise errors when a rule is added with - a name that is already defined. This change aims to prevent rules from being - overwritten by accident. -- ``Http\Session`` will now raise an exception when an invalid session preset is - used. -- ``FormProtectionComponent`` now raises ``Cake\Controller\Exception\FormProtectionException``. This - class is a subclass of ``BadRequestException``, and offers the benefit of - being filterable from logging. -- ``NumericPaginator::paginate()`` now uses the ``finder`` option even when a ``SelectQuery`` instance is passed to it. +- ``ValidationSet::add()`` agora gerará erros quando uma regra for adicionada com + um nome já definido. Essa alteração visa evitar que as regras sejam + sobrescritas acidentalmente. +- ``Http\Session`` agora gerará uma exceção quando uma predefinição de sessão inválida for usada. +- ``FormProtectionComponent`` agora gera ``Cake\Controller\Exception\FormProtectionException``. Esta + classe é uma subclasse de ``BadRequestException`` e oferece o benefício de + ser filtrável a partir de registros. +- ``NumericPaginator::paginate()`` agora usa a opção ``finder`` mesmo quando uma instância de ``SelectQuery`` é passada a ela. Deprecations ============ @@ -24,32 +23,31 @@ Deprecations Console ------- -- ``Arguments::getMultipleOption()`` is deprecated. Use ``getArrayOption()`` - instead. +- ``Arguments::getMultipleOption()`` está obsoleto. Use ``getArrayOption()`` em seu lugar. Datasource ---------- -- The ability to cast an ``EntityInterface`` instance to string has been deprecated. - You should ``json_encode()`` the entity instead. +- A capacidade de converter uma instância de ``EntityInterface`` para uma string foi descontinuada. + Em vez disso, você deve usar ``json_encode()`` para a entidade. -- Mass assigning multiple entity fields using ``EntityInterface::set()`` is deprecated. - Use ``EntityInterface::patch()`` instead. For e.g. change usage like - ``$entity->set(['field1' => 'value1', 'field2' => 'value2'])`` to +- A atribuição em massa de múltiplos campos de entidade usando ``EntityInterface::set()`` foi descontinuada. + Em vez disso, use ``EntityInterface::patch()``. Por exemplo, altere o uso de + ``$entity->set(['field1' => 'value1', 'field2' => 'value2'])`` para ``$entity->patch(['field1' => 'value1', 'field2' => 'value2'])``. Event ----- -- Returning values from event listeners / callbacks is deprecated. Use ``$event->setResult()`` - instead or ``$event->stopPropogation()`` to just stop the event propogation. +- Retornar valores de ouvintes de eventos/retornos de chamada está obsoleto. Use ``$event->setResult()`` + em vez disso ou ``$event->stopPropogation()`` para simplesmente interromper a propagação do evento. View ---- -- The ``errorClass`` option of ``FormHelper`` has been deprecated in favour of - using a template string. To upgrade move your ``errorClass`` definition to - a template set. See :ref:`customizing-templates`. +- A opção ``errorClass`` de ``FormHelper`` foi descontinuada em favor do uso de + uma string de modelo. Para atualizar, mova sua definição de ``errorClass`` para + um conjunto de modelos. Consulte :ref:`customizing-templates`. New Features @@ -58,80 +56,78 @@ New Features Console ------- -- The ``cake counter_cache`` command was added. This command can be used to - regenerate counters for models that use ``CounterCacheBehavior``. -- ``ConsoleIntegrationTestTrait::debugOutput()`` makes it easier to debug - integration tests for console commands. -- ``ConsoleInputArgument`` now supports a ``separator`` option. This option - allows positional arguments to be delimited with a character sequence like - ``,``. CakePHP will split the positional argument into an array when arguments - are parsed. -- ``Arguments::getArrayArgumentAt()``, and ``Arguments::getArrayArgument()`` - were added. These methods allow you to read ``separator`` delimitered - positional arguments as arrays. -- ``ConsoleInputOption`` now supports a ``separator`` option. This option - allows option values to be delimited with a character sequence like - ``,``. CakePHP will split the option value into an array when arguments - are parsed. -- ``Arguments::getArrayArgumentAt()``, ``Arguments::getArrayArgument()``, and - ``Arguments::getArrayOption()`` - were added. These methods allow you to read ``separator`` delimitered - positional arguments as arrays. +- O comando ``cake counter_cache`` foi adicionado. Este comando pode ser usado para + regenerar contadores para modelos que usam ``CounterCacheBehavior``. +- ``ConsoleIntegrationTestTrait::debugOutput()`` facilita a depuração + de testes de integração para comandos de console. +- ``ConsoleInputArgument`` agora suporta a opção ``separator``. Esta opção + permite que argumentos posicionais sejam delimitados com uma sequência de caracteres como + ``,``. O CakePHP dividirá o argumento posicional em um array quando os argumentos + forem analisados. +- ``Arguments::getArrayArgumentAt()`` e ``Arguments::getArrayArgument()`` + foram adicionados. Esses métodos permitem que você leia argumentos posicionais delimitados + por ``separator`` como arrays. +- ``ConsoleInputOption`` agora suporta a opção ``separator``. Esta opção + permite que valores de opção sejam delimitados com uma sequência de caracteres como + ``,``. O CakePHP dividirá o valor da opção em um array quando os argumentos + forem analisados. +- ``Arguments::getArrayArgumentAt()``, ``Arguments::getArrayArgument()`` e ``Arguments::getArrayOption()`` + foram adicionados. Esses métodos permitem que você leia argumentos posicionais delimitados + por ``separator`` como arrays. Database -------- -- The ``nativeuuid`` type was added. This type enables ``uuid`` columns to be - used in Mysql connections with MariaDB. In all other drivers, ``nativeuuid`` - is an alias for ``uuid``. -- ``Cake\Database\Type\JsonType::setDecodingOptions()`` was added. This method - lets you define the value for the ``$flags`` argument of ``json_decode()``. -- ``CounterCacheBehavior::updateCounterCache()`` was added. This method allows - you to update the counter cache values for all records of the configured - associations. ``CounterCacheCommand`` was also added to do the same through the +- O tipo ``nativeuuid`` foi adicionado. Este tipo permite que colunas ``uuid`` sejam + usadas em conexões MySQL com MariaDB. Em todos os outros drivers, ``nativeuuid`` + é um alias para ``uuid``. +- ``Cake\Database\Type\JsonType::setDecodingOptions()`` foi adicionado. Este método + permite definir o valor para o argumento ``$flags`` de ``json_decode()``. +- ``CounterCacheBehavior::updateCounterCache()`` foi adicionado. Este método permite + atualizar os valores do cache do contador para todos os registros das associações + configuradas. ``CounterCacheCommand`` também foi adicionado para fazer o mesmo através do console. -- ``Cake\Database\Driver::quote()`` was added. This method provides a way to - quote values to be used in SQL queries where prepared statements cannot be - used. +- ``Cake\Database\Driver::quote()`` foi adicionado. Este método fornece uma maneira de + citar valores a serem usados ​​em consultas SQL onde instruções preparadas não podem + ser usadas. Datasource ---------- -- Application rules can now use ``Closure`` to define the validation message. - This allows you to create dynamic validation messages based on the entity - state and validation rule options. +- As regras de aplicação agora podem usar ``Closure`` para definir a mensagem de validação. + Isso permite criar mensagens de validação dinâmicas com base no estado da entidade + e nas opções da regra de validação. Error ----- -- Custom exceptions can have specific error handling logic defined in +- Exceções personalizadas podem ter lógica específica de tratamento de erros definida em ``ErrorController``. ORM --- -- ``CounterCacheBehavior::updateCounterCache()`` has been added. This method - allows you to update the counter cache values for all records of the configured - associations. -- ``BelongsToMany::setJunctionProperty()`` and ``getJunctionProperty()`` were - added. These methods allow you to customize the ``_joinData`` property that is - used to hydrate junction table records. -- ``Table::findOrCreate()`` now accepts an array as second argument to directly pass data in. +- ``CounterCacheBehavior::updateCounterCache()`` foi adicionado. Este método + permite atualizar os valores do cache do contador para todos os registros das associações + configuradas. +- ``BelongsToMany::setJunctionProperty()`` e ``getJunctionProperty()`` foram + adicionados. Esses métodos permitem personalizar a propriedade ``_joinData`` que é + usada para hidratar os registros da tabela de junção. +- ``Table::findOrCreate()`` agora aceita um array como segundo argumento para passar dados diretamente. TestSuite --------- -- ``TestFixture::$strictFields`` was added. Enabling this property will make - fixtures raise an error if a fixture's record list contains fields that do not - exist in the schema. +- ``TestFixture::$strictFields`` foi adicionado. Habilitar esta propriedade fará com que + os fixtures gerem um erro se a lista de registros de um fixture contiver campos que não + existam no esquema. View ---- -- ``FormHelper::deleteLink()`` has been added as convenience wrapper for delete links in - templates using ``DELETE`` method. -- ``HtmlHelper::importmap()`` was added. This method allows you to define - import maps for your JavaScript files. -- ``FormHelper`` now uses the ``containerClass`` template to apply a class to - the form control div. The default value is ``input``. - +- ``FormHelper::deleteLink()`` foi adicionado como um wrapper conveniente para links de exclusão em + modelos usando o método ``DELETE``. +- ``HtmlHelper::importmap()`` foi adicionado. Este método permite definir + mapas de importação para seus arquivos JavaScript. +- ``FormHelper`` agora usa o modelo ``containerClass`` para aplicar uma classe + à div de controle do formulário. O valor padrão é ``input``. From 0f058b6fd42afd865029c09b913a9d5831a754fe Mon Sep 17 00:00:00 2001 From: kromodoro Date: Wed, 3 Sep 2025 06:03:34 -0300 Subject: [PATCH 11/46] [refact]: translated (pt/appendices/phpunit10.rst) --- pt/appendices/phpunit10.rst | 52 ++++++++++++++++++------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/pt/appendices/phpunit10.rst b/pt/appendices/phpunit10.rst index 1cd56d3d3a..8c3256460d 100644 --- a/pt/appendices/phpunit10.rst +++ b/pt/appendices/phpunit10.rst @@ -1,65 +1,65 @@ -PHPUnit 10 Upgrade -################## +Atualização do PHPUnit 10 +######################### -With CakePHP 5 the minimum PHPUnit version has changed from ``^8.5 || ^9.3`` to ``^10.1``. -This introduces a few breaking changes from PHPUnit as well as from CakePHP's side. +Com o CakePHP 5, a versão mínima do PHPUnit mudou de ``^8.5 || ^9.3`` para ``^10.1``. +Isso introduz algumas mudanças significativas tanto no PHPUnit quanto no CakePHP. -phpunit.xml adjustments -======================= +ajustes de phpunit.xml +====================== -It is recommended to let PHPUnit update its configuration file via the following command:: +É recomendável deixar o PHPUnit atualizar seu arquivo de configuração por meio do seguinte comando:: vendor/bin/phpunit --migrate-configuration .. note:: - Make sure you are already on PHPUnit 10 via ``vendor/bin/phpunit --version`` before executing this command! + Certifique-se de que você já está no PHPUnit 10 via ``vendor/bin/phpunit --version`` antes de executar este comando! -With this command out of the way your ``phpunit.xml`` already has most of the recommended changes present. +Com esse comando concluído, seu ``phpunit.xml`` já terá a maioria das alterações recomendadas presentes. -New event system ----------------- +Novo sistema de eventos +----------------------- -PHPUnit 10 removed the old hook system and introduced a new `Event system +O PHPUnit 10 removeu o antigo sistema de hooks e introduziu um novo `Sistema de eventos `_ -which requires the following code in your ``phpunit.xml`` to be adjusted from:: +que requer que o seguinte código no seu ``phpunit.xml`` seja ajustado de:: -to:: +para:: -``->withConsecutive()`` has been removed -======================================== +``->withConsecutive()`` foi removido +==================================== -You can convert the removed ``->withConsecutive()`` method to a -working interim solution like you can see here:: +Você pode converter o método removido ``->withConsecutive()`` em uma +solução provisória funcional, como você pode ver aqui:: ->withConsecutive(['firstCallArg'], ['secondCallArg']) -should be converted to:: +deve ser convertido para:: ->with( ...self::withConsecutive(['firstCallArg'], ['secondCallArg']) ) -the static ``self::withConsecutive()`` method has been added via the ``Cake\TestSuite\PHPUnitConsecutiveTrait`` -to the base ``Cake\TestSuite\TestCase`` class so you don't have to manually add that trait to your Testcase classes. +O método estático ``self::withConsecutive()`` foi adicionado por meio de ``Cake\TestSuite\PHPUnitConsecutiveTrait`` +à classe base ``Cake\TestSuite\TestCase`` para que você não precise adicionar manualmente essa característica às suas classes Testcase. -data providers have to be static -================================ +os provedores de dados precisam ser estáticos +============================================= -If your testcases leverage the data provider feature of PHPUnit then -you have to adjust your data providers to be static:: +Se seus casos de teste utilizam o recurso de provedor de dados do PHPUnit, +você precisa ajustar seus provedores de dados para que sejam estáticos:: public function myProvider(): array -should be converted to:: +deve ser convertido para:: public static function myProvider(): array From ee94528c7eda2d367c983e87fff26a9a5baf3972 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Wed, 3 Sep 2025 06:09:22 -0300 Subject: [PATCH 12/46] [refact]: translated (pt/release-policy.rst) --- pt/release-policy.rst | 88 +++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/pt/release-policy.rst b/pt/release-policy.rst index 01dc68bc81..db0ef58b52 100644 --- a/pt/release-policy.rst +++ b/pt/release-policy.rst @@ -1,69 +1,69 @@ -Release Policy -############## +Política de Lançamento +###################### -CakePHP follows Semantic Versioning for all releases. This follows the versioning -convention of **major.minor.patch**. +O CakePHP segue o Versionamento Semântico para todos os lançamentos. Isso segue a convenção de versionamento +de **major.minor.patch**. -The development team tries to guarantee each release follow the restrictions and -and guarantees below. +A equipe de desenvolvimento tenta garantir que cada lançamento siga as restrições e +garantias abaixo. -Major Releases --------------- +Lançamentos Principais +---------------------- -Major releases are generally not backwards compatible. Although CakePHP tries -to not change many large features in major releases, there are API changes. +Lançamentos principais geralmente não são compatíveis com versões anteriores. Embora o CakePHP tente +não alterar muitos recursos importantes em lançamentos principais, há mudanças na API. -Changes in major release can include almost anything but are always used to -remove deprecated features and update interfaces. +As mudanças em lançamentos principais podem incluir quase tudo, mas são sempre usadas para +remover recursos obsoletos e atualizar interfaces. -Any behavior changes that are not backwards compatible are made in major changes. +Quaisquer mudanças de comportamento que não sejam compatíveis com versões anteriores são feitas em mudanças principais. -Each major release typically comes with an upgrade guide and many automatic -code upgrades using rector. +Cada lançamento principal normalmente vem com um guia de atualização e muitas atualizações +de código automáticas usando o rector. -Minor Releases --------------- +Lançamentos Menores +------------------- -Minor release are generally backwards compatible with the previous minor and patch -release. +Lançamentos menores geralmente são compatíveis com versões anteriores com o lançamento menor e o patch +anteriores. -Features might be deprecated, but they are never removed in a minor release. +Recursos podem ser descontinuados, mas nunca são removidos em uma versão menor. -Interfaces are not changed, but annotations might be added for new methods exposed -in implementations provided by CakePHP. +As interfaces não são alteradas, mas anotações podem ser adicionadas para novos métodos expostos +em implementações fornecidas pelo CakePHP. -New features are usually only added in minor releases so users can follow migration -notes. New features can also include new exceptions thrown when behavior is fixed -or bugs are reported. +Novos recursos geralmente são adicionados apenas em versões menores para que os usuários possam acompanhar as notas de migração. +Novos recursos também podem incluir novas exceções lançadas quando o comportamento é corrigido +ou bugs são relatados. -Behavior changes that require documentation are made in minor releases, but these are -still typically backwards compatible. Some exceptions can be made if the issue is severe. +Mudanças de comportamento que exigem documentação são feitas em versões menores, mas estas +ainda são normalmente compatíveis com versões anteriores. Algumas exceções podem ser feitas se o problema for grave. .. note: - Minor releases are also known as point releases. - -Patch Releases + Versões menores também são conhecidas como versões pontuais. + +Versões de Patch ---------------- -Patch releases are always backwards compatible. Only changes that fix broken features -are made. +Versões de patch são sempre compatíveis com versões anteriores. Somente alterações que corrigem recursos quebrados +são feitas. -Typically, users should be able to rely on patch releases not changing behavior except -to fix an issue. +Normalmente, os usuários devem poder confiar que as versões de patch não alteram o comportamento, exceto +para corrigir um problema. -Issues that change long-standing behavior are typically not in patch releases. These are -considered behavior changes and will go into either minor or major releases so users can -migrate. +Problemas que alteram comportamentos antigos normalmente não estão em versões de patch. Estas são +consideradas mudanças de comportamento e serão incluídas em versões menores ou maiores para que os usuários possam +migrar. .. note: - Patch releases are also known as bug fix releases. + Lançamentos de patches também são conhecidos como lançamentos de correção de bugs. -Experimental Features ---------------------- +Recursos Experimentais +---------------------- -When a new feature is added where the API is still changing, it can be marked **experimental**. +Quando um novo recurso é adicionado onde a API ainda está mudando, ele pode ser marcado como **experimental**. -Experimental features should follow the same minor and bug fix release convention. However, -API changes can go into minor releases which might significantly change behavior. +Recursos experimentais devem seguir a mesma convenção de lançamentos menores e de correção de bugs. No entanto, +alterações na API podem ser incluídas em lançamentos menores, o que pode alterar significativamente o comportamento. -Users should always expect an API to change before experimental features are fully released. +Os usuários devem sempre esperar que uma API mude antes que os recursos experimentais sejam totalmente lançados. \ No newline at end of file From 535bec761b24ba5935917c9b1aa5cb68f4a4d110 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:39:47 -0300 Subject: [PATCH 13/46] [refact]: translated (pt/contributing/code.rst) --- pt/contributing/code.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pt/contributing/code.rst b/pt/contributing/code.rst index 3cbc818f7d..65a7d0df4a 100644 --- a/pt/contributing/code.rst +++ b/pt/contributing/code.rst @@ -13,7 +13,7 @@ ambiente. Você vai precisar do seguinte *software*: * Git * PHP |minphpversion| ou maior -* PHPUnit 3.7.0 ou maior +* PHPUnit 5.7.0 ou maior Defina suas informações de usuário com seu nome e endereço de email:: @@ -129,12 +129,12 @@ Ao fazer *pull requests* você deve ter certeza que selecionou o *branch* corret atual, escolhe o *branch* **master** como seu alvo. * Se sua alteração for uma **feature**, então você deve escolher o *branch* referente ao próximo número de versão. Por exemplo, se o *branch* atual - estável for ``3.2.10``, o *branch* a receber novas funcionalidades será o - ``3.next``. + estável for ``4.0.0``, o *branch* a receber novas funcionalidades será o + ``4.next``. * Se sua alteração quebra funcionalidades existentes, ou API's, então você deverá escolher o próximo *major release*. Por exemplo, se o branch estável - atual for ``3.2.2``, então a versão na qual o comportamento pode ser quebrado - será na versão ``4.x``. + atual for ``4.0.0``, então a versão na qual o comportamento pode ser quebrado + será na versão ``5.x``. .. note:: From 419a06f67fc4cba6048386133d726fb84201d364 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:41:14 -0300 Subject: [PATCH 14/46] [refact]: translated (pt/contributing/documentation.rst) --- pt/contributing/documentation.rst | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pt/contributing/documentation.rst b/pt/contributing/documentation.rst index 08e8d46da8..b4e338483e 100644 --- a/pt/contributing/documentation.rst +++ b/pt/contributing/documentation.rst @@ -10,8 +10,7 @@ direita em qualquer página vai direcioná-lo para o editor online do Github. A documentação do CakePHP é `continuamente integrada `_, -sendo assim, você pode checar o status de -`várias builds `_ no servidor Jenkins a qualquer momento. +e implantada após cada pull request ser mesclada. Traduções ========= @@ -53,8 +52,8 @@ Por exemplo, se um novo arquivo é criado em **en/file.rst**, nós devemos: elementos ``toc-tree``. A nota a seguir será adicionada até que alguém traduza o arquivo:: - File Title - ########## + Título do Arquivo + ################# .. note:: Atualmente, a documentação desta página não é suportada em português. @@ -70,9 +69,8 @@ Por exemplo, se um novo arquivo é criado em **en/file.rst**, nós devemos: .. toctree:: :maxdepth: 1 - toc-file-x - toc-file-y - toc-file-z + one-toc-file + other-toc-file .. meta:: :title lang=pt: Título do arquivo From 754d5f78ae4cb279f1988915c5297b9b4c70f771 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:41:30 -0300 Subject: [PATCH 15/46] [refact]: translated (pt/contributing/tickets.rst) --- pt/contributing/tickets.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pt/contributing/tickets.rst b/pt/contributing/tickets.rst index 80388ce0d7..8b29448252 100644 --- a/pt/contributing/tickets.rst +++ b/pt/contributing/tickets.rst @@ -20,10 +20,10 @@ ajudam a criar relatórios de erro melhores: seja corrigido. * **Faça**: Dê o máximo de detalhes sobre o seu ambiente: (SO, versão do PHP, versão do CakePHP). -* **Não faça**: Não use o sistema de *tickets* para sanar dúvidas. O canal de - IRC #cakephp na `Freenode `__ possui muitos - desenvolvedores dispníveis para ajudar a responder suas dúvidas. Também dê uma - olhada no `Stack Overflow `__. +* **Não faça**: Por favor, não use o sistema de *tickets* para fazer perguntas de suporte. Tanto o canal de suporte no + `CakePHP Slack workspace `__ quanto o canal IRC #cakephp no `Freenode `__ têm muitos + desenvolvedores disponíveis para ajudar a responder às suas perguntas. Consulte também o + `Stack Overflow `__ ou o `fórum oficial do CakePHP `__. Reportando problemas de segurança ================================= From 893b4fdcd21051f0f1371091b6990867fd4ec33a Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:41:58 -0300 Subject: [PATCH 16/46] [refact]: translated (pt/tutorials-and-examples/cms/articles-model.rst) --- pt/tutorials-and-examples/cms/articles-model.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/tutorials-and-examples/cms/articles-model.rst b/pt/tutorials-and-examples/cms/articles-model.rst index a415595707..492debd4a8 100644 --- a/pt/tutorials-and-examples/cms/articles-model.rst +++ b/pt/tutorials-and-examples/cms/articles-model.rst @@ -1,4 +1,4 @@ -Tutorial CMS - Criando nosso primeiro Modelo +CMS Tutorial - Criando nosso primeiro Modelo ############################################ Os modelos são o coração das aplicações CakePHP. Eles nos permitem ler e From c72fd4c553cec6c7807797369cf78a7f4559d395 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:42:29 -0300 Subject: [PATCH 17/46] [refact]: translated (pt/tutorials-and-examples/cms/authentication.rst) --- .../cms/authentication.rst | 316 +++++++++++++++++- 1 file changed, 315 insertions(+), 1 deletion(-) diff --git a/pt/tutorials-and-examples/cms/authentication.rst b/pt/tutorials-and-examples/cms/authentication.rst index 497ced0825..37eefc7678 100644 --- a/pt/tutorials-and-examples/cms/authentication.rst +++ b/pt/tutorials-and-examples/cms/authentication.rst @@ -1,4 +1,318 @@ CMS Tutorial - Authentication ############################# -TODO +Agora que nosso CMS possui usuários, podemos habilitá-los a fazer login usando o plugin +`cakephp/authentication `__ +. Começaremos garantindo que as senhas estejam armazenadas com segurança em +nosso banco de dados. Em seguida, forneceremos um login e logout funcionais e +permitiremos que novos usuários se registrem. + +Instalando o Plugin Authentication +================================== + +Use o composer para instalar o Plugin Authentication: + +.. code-block:: console + + composer require "cakephp/authentication:~3.0" + + +Adicionando Password Hashing +---------------------------- + +Você precisa ter criado ``Controller``, ``Table``, ``Entity`` e +os templates para a tabela ``users`` no seu banco de dados. Você pode fazer isso manualmente, +como fez antes para o ArticlesController, ou pode usar o shell bake +para gerar as classes para você usando: + +.. code-block:: console + + bin/cake bake all users + +Se você criar ou atualizar um usuário com essa configuração, poderá notar que +as senhas são armazenadas em texto simples. Isso é muito ruim do ponto de vista da segurança, +então vamos corrigir isso. + +Este também é um bom momento para falar sobre a camada de modelo no CakePHP. No CakePHP, +usamos classes diferentes para operar em coleções de registros e registros individuais. +Métodos que operam na coleção de entidades são colocados na classe ``Table``, +enquanto recursos pertencentes a um único registro são colocados na classe ``Entity``. + +Por exemplo, o hash de senha é feito no registro individual, então +implementaremos esse comportamento no objeto entidade. Como queremos fazer o hash da +senha sempre que ela for definida, usaremos um método mutador/setter. O CakePHP +chamará um método setter baseado em convenção sempre que uma propriedade for definida em uma de suas +entidades. Vamos adicionar um setter para a senha. Em **src/Model/Entity/User.php** +adicione o seguinte:: + + 0) { + return (new DefaultPasswordHasher())->hash($password); + } + return null; + } + } + +Agora, acesse **http://localhost:8765/users** no seu navegador para ver uma lista de usuários. +Lembre-se de que você precisa ter seu servidor local em execução. Inicie um servidor PHP autônomo +usando ``bin/cake server``. + +Você pode editar o usuário padrão criado durante a +:doc:`Instalação `. Se você alterar a senha desse usuário, +você deverá ver uma senha com hash em vez do valor original na lista ou +nas páginas de visualização. O CakePHP usa o hash de senhas com `bcrypt +`_ por padrão. Recomendamos +bcrypt para todos os novos aplicativos para manter seus padrões de segurança elevados. Este +é o `algoritmo de hash de senha recomendado para PHP `_. + +.. note:: + + Crie uma senha com hash para pelo menos uma das contas de usuário agora! + Ela será necessária nas próximas etapas. + Após atualizar a senha, você verá uma longa string armazenada na coluna de senhas. + Observe que o bcrypt gerará um hash diferente mesmo para a mesma senha salva duas vezes. + + +Adicionando Login +================= + +Agora é hora de configurar o plugin de autenticação. +O plugin lidará com o processo de autenticação usando 3 classes diferentes: + +* ``Application`` usará o Middleware de Autenticação e fornecerá um + AuthenticationService, contendo toda a configuração que queremos definir como + vamos verificar as credenciais e onde encontrá-las. +* ``AuthenticationService`` será uma classe utilitária que permitirá a configuração do + processo de autenticação. +* ``AuthenticationMiddleware`` será executado como parte da fila de middleware, + isso antes que seus controladores sejam processados ​​pelo framework, e selecionará as + credenciais e as processará para verificar se o usuário está autenticado. + +Se você se lembra, usamos `AuthComponent` +antes para lidar com todas essas etapas. Agora, a lógica é dividida em classes específicas e +o processo de autenticação acontece antes da camada do controller. Primeiro, ele verifica se o usuário +está autenticado (com base na configuração fornecida) e insere o usuário e +os resultados da autenticação na solicitação para referência futura. + +Em **src/Application.php**, adicione as seguintes importações:: + + // In src/Application.php add the following imports + use Authentication\AuthenticationService; + use Authentication\AuthenticationServiceInterface; + use Authentication\AuthenticationServiceProviderInterface; + use Authentication\Middleware\AuthenticationMiddleware; + use Cake\Routing\Router; + use Psr\Http\Message\ServerRequestInterface; + +Em seguida, implemente a interface de autenticação na sua classe ``Application``:: + + // in src/Application.php + class Application extends BaseApplication + implements AuthenticationServiceProviderInterface + { + +Em seguida adicione o seguinte:: + + // src/Application.php + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue + // ... other middleware added before + ->add(new RoutingMiddleware($this)) + ->add(new BodyParserMiddleware()) + // Add the AuthenticationMiddleware. It should be after routing and body parser. + ->add(new AuthenticationMiddleware($this)); + + return $middlewareQueue; + } + + public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface + { + $authenticationService = new AuthenticationService([ + 'unauthenticatedRedirect' => Router::url('/users/login'), + 'queryParam' => 'redirect', + ]); + + // Carregue os autenticadores, você quer a sessão primeiro + $authenticationService->loadAuthenticator('Authentication.Session'); + // Configurar verificação de dados do formulário para escolher e-mail e senha + $authenticationService->loadAuthenticator('Authentication.Form', [ + 'fields' => [ + 'username' => 'email', + 'password' => 'password', + ], + 'loginUrl' => Router::url('/users/login'), + 'identifier' => [ + 'Authentication.Password' => [ + 'fields' => [ + 'username' => 'email', + 'password' => 'password', + ], + ], + ], + ]); + + return $authenticationService; + } + +Na sua classe ``AppController`` adicione o seguinte código:: + + // src/Controller/AppController.php + public function initialize(): void + { + parent::initialize(); + $this->loadComponent('Flash'); + + // Adicione esta linha para verificar o resultado da autenticação e bloquear seu site + $this->loadComponent('Authentication.Authentication'); + +Agora, em cada requisição, o ``AuthenticationMiddleware`` inspecionará +a sessão de requisição para procurar um usuário autenticado. Se estivermos carregando a página ``/users/login``, +ele também inspecionará os dados do formulário publicado (se houver) para extrair as credenciais. +Por padrão, as credenciais serão extraídas dos campos ``username`` e ``password`` +nos dados da requisição. +O resultado da autenticação será injetado em um atributo de requisição chamado ``authentication``. +Você pode inspecionar o resultado a qualquer momento usando +``$this->request->getAttribute('authentication')`` nas ações do seu concontrollertrolador. +Todas as suas páginas serão restritas, pois o ``AuthenticationComponent`` verifica o +resultado em cada requisição. Quando não encontrar nenhum usuário autenticado, ele redirecionará +o usuário para a página ``/users/login``. +Observe que, neste ponto, o site não funcionará, pois ainda não temos uma página de login. +Se você visitar seu site, verá um "loop de redirecionamento infinito", então vamos consertar isso. + +.. note:: + + Se sua aplicação serve tanto em protocolos SSL quanto não SSL, você poderá ter problemas + com perda de sessões, caso sua aplicação esteja em protocolo não SSL. Você precisa habilitar + o acesso definindo session.cookie_secure como false em sua configuração config/app.php ou config/app_local.php. + (Veja :doc:`Padrões do CakePHP para session.cookie_secure `) + +No seu ``UsersController``, adicione o seguinte código:: + + public function beforeFilter(\Cake\Event\EventInterface $event): void + { + parent::beforeFilter($event); + // Configure a ação de login para não exigir autenticação, evitando + // o problema do loop de redirecionamento infinito + $this->Authentication->addUnauthenticatedActions(['login']); + } + + public function login() + { + $this->request->allowMethod(['get', 'post']); + $result = $this->Authentication->getResult(); + // independentemente de POST ou GET, redirecionar se o usuário estiver logado + if ($result && $result->isValid()) { + // redirecionar para /articles após login bem-sucedido + $redirect = $this->request->getQuery('redirect', [ + 'controller' => 'Articles', + 'action' => 'index', + ]); + + return $this->redirect($redirect); + } + // exibir erro se o usuário enviou e a autenticação falhou + if ($this->request->is('post') && !$result->isValid()) { + $this->Flash->error(__('Nome de usuário ou senha inválidos')); + } + } + +Adicione a lógica do modelo para sua ação de login:: + + +
+ Flash->render() ?> +

Login

+ Form->create() ?> +
+ + Form->control('email', ['required' => true]) ?> + Form->control('password', ['required' => true]) ?> +
+ Form->submit(__('Login')); ?> + Form->end() ?> + + Html->link("Add User", ['action' => 'add']) ?> +
+ +Agora, a página de login nos permitirá efetuar login corretamente no aplicativo. +Teste solicitando qualquer página do seu site. Após ser redirecionado +para a página ``/users/login``, insira o e-mail e a senha que você +escolheu anteriormente ao criar seu usuário. Você deverá ser redirecionado +com sucesso após o login. + +Precisamos adicionar mais alguns detalhes para configurar nosso aplicativo. +Queremos que todas as páginas ``view`` e ``index`` sejam acessíveis sem login, então adicionaremos esta +configuração específica no AppController:: + + // in src/Controller/AppController.php + public function beforeFilter(\Cake\Event\EventInterface $event): void + { + parent::beforeFilter($event); + // para todos os controladores em nossa aplicação, tornar o índice e a visualização + // ações públicas, ignorando a verificação de autenticação + $this->Authentication->addUnauthenticatedActions(['index', 'view']); + } + +.. note:: + + Se você ainda não possui um usuário com senha hash, comente a linha + ``$this->loadComponent('Authentication.Authentication')`` no seu + AppController e todas as outras linhas onde a autenticação é usada. Em seguida, acesse + ``/users/add`` para criar um novo usuário, escolhendo e-mail e senha. Depois, + certifique-se de descomenter as linhas que acabamos de comentar temporariamente! + +Experimente visitar ``/articles/add`` antes de fazer login! Como esta ação não é +permitida, você será redirecionado para a página de login. Após o login +com sucesso, o CakePHP o redirecionará automaticamente para ``/articles/add``. + +Logout +====== + +Adicione a ação de logout à classe ``UsersController``:: + + // in src/Controller/UsersController.php + public function logout() + { + $result = $this->Authentication->getResult(); + // independentemente de POST ou GET, redirecionar se o usuário estiver logado + if ($result && $result->isValid()) { + $this->Authentication->logout(); + + return $this->redirect(['controller' => 'Users', 'action' => 'login']); + } + } + +Agora você pode acessar ``/users/logout`` para sair. Você será direcionado para a página de login. + +Habilitando Registrations +========================= + +Se você tentar visitar **/users/add** sem estar logado, será +redirecionado para a página de login. Devemos corrigir isso, pois queremos permitir que as pessoas +se inscrevam em nosso aplicativo. No ``UsersController``, corrija a seguinte linha:: + + // Adicionar ao método beforeFilter do UsersController + $this->Authentication->addUnauthenticatedActions(['login', 'add']); + +O texto acima informa ao ``AuthenticationComponent`` que a ação ``add()`` do +``UsersController`` *não* requer autenticação ou autorização. Você pode +dedicar um tempo para limpar o arquivo **Users/add.php** e remover os +links enganosos ou prosseguir para a próxima seção. Não desenvolveremos +a edição, visualização ou listagem de usuários neste tutorial, mas este é um exercício que você +pode realizar sozinho. + +Agora que os usuários podem efetuar login, queremos limitar os usuários a editar apenas os artigos +que eles criaram :doc:`aplicando políticas de autorização <./authorization>`. From 8a7d0beb24a7920381e394454d300ff6c8f4f633 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:42:46 -0300 Subject: [PATCH 18/46] [refact]: translated (pt/tutorials-and-examples/cms/authorization.rst) --- .../cms/authorization.rst | 170 +++++++++--------- 1 file changed, 83 insertions(+), 87 deletions(-) diff --git a/pt/tutorials-and-examples/cms/authorization.rst b/pt/tutorials-and-examples/cms/authorization.rst index 41a0194ec5..2af491eb4d 100644 --- a/pt/tutorials-and-examples/cms/authorization.rst +++ b/pt/tutorials-and-examples/cms/authorization.rst @@ -1,29 +1,29 @@ CMS Tutorial - Authorization ############################ -With users now able to login to our CMS, we want to apply authorization rules -to ensure that each user only edits the posts they own. We'll use the -`authorization plugin `__ to do this. +Agora que os usuários podem fazer login em nosso CMS, queremos aplicar regras de autorização +para garantir que cada usuário edite apenas as postagens que lhe pertencem. Usaremos o +`plugin de autorização `__ para fazer isso. -Installing Authorization Plugin -================================ +Instalando o Plugin Authorization +================================= -Use composer to install the Authorization Plugin: +Use o composer para instalar o Plugin Authorization: .. code-block:: console composer require "cakephp/authorization:^3.0" -Load the plugin by adding the following statement to the ``bootstrap()`` method in **src/Application.php**:: +Carregue o plugin adicionando a seguinte declaração ao método ``bootstrap()`` em **src/Application.php**:: $this->addPlugin('Authorization'); -Enabling the Authorization Plugin -================================= +Habilitando o Plugin Authorization +================================== -The Authorization plugin integrates into your application as a middleware layer -and optionally a component to make checking authorization easier. First, lets -apply the middleware. In **src/Application.php** add the following to the class +O plugin Authorization integra-se à sua aplicação como uma camada de middleware +e, opcionalmente, como um componente para facilitar a verificação da autorização. Primeiro, vamos +aplicar o middleware. Em **src/Application.php**, adicione o seguinte à classe imports:: use Authorization\AuthorizationService; @@ -32,20 +32,20 @@ imports:: use Authorization\Middleware\AuthorizationMiddleware; use Authorization\Policy\OrmResolver; -Add the ``AuthorizationServiceProviderInterface`` to the implemented interfaces on your application:: +Adicione ``AuthorizationServiceProviderInterface`` às interfaces implementadas em seu aplicativo:: class Application extends BaseApplication implements AuthenticationServiceProviderInterface, AuthorizationServiceProviderInterface -Then add the following to your ``middleware()`` method:: +Em seguida, adicione o seguinte ao seu método ``middleware()``:: - // Add authorization **after** authentication + // Adicionar autorização **após** a autenticação $middlewareQueue->add(new AuthorizationMiddleware($this)); -The ``AuthorizationMiddleware`` will call a hook method on your application when -it starts handling the request. This hook method allows your application to -define the ``AuthorizationService`` it wants to use. Add the following method your +O ``AuthorizationMiddleware`` chamará um método de gancho em sua aplicação quando +começar a processar a requisição. Este método de gancho permite que sua aplicação +defina o ``AuthorizationService`` que deseja usar. Adicione o seguinte método ao seu **src/Application.php**:: public function getAuthorizationService(ServerRequestInterface $request): AuthorizationServiceInterface @@ -55,42 +55,40 @@ define the ``AuthorizationService`` it wants to use. Add the following method yo return new AuthorizationService($resolver); } -The OrmResolver lets the authorization plugin find policy classes for ORM -entities and queries. Other resolvers can be used to find policies for other -resources types. +O OrmResolver permite que o plugin de autorização encontre classes de políticas para entidades e consultas ORM. +Outros resolvedores podem ser usados ​​para encontrar políticas para outros tipos de recursos. -Next, lets add the ``AuthorizationComponent`` to ``AppController``. In -**src/Controller/AppController.php** add the following to the ``initialize()`` -method:: +Em seguida, vamos adicionar o ``AuthorizationComponent`` ao ``AppController``. Em +**src/Controller/AppController.php**, adicione o seguinte ao método ``initialize()``:: $this->loadComponent('Authorization.Authorization'); -Lastly we'll mark the add, login, and logout actions as not requiring -authorization by adding the following to +Por fim, marcaremos as ações de adicionar, fazer login e sair como não exigindo +autorização, adicionando o seguinte a **src/Controller/UsersController.php**:: - // In the add, login, and logout methods + // Nos métodos add, login e logout $this->Authorization->skipAuthorization(); -The ``skipAuthorization()`` method should be called in any controller action -that should be accessible to all users even those who have not logged in yet. +O método ``skipAuthorization()`` deve ser chamado em qualquer ação do controlador +que deve ser acessível a todos os usuários, mesmo aqueles que ainda não efetuaram login. -Creating our First Policy -========================= +Criando Nossa Primeira Política +=============================== -The Authorization plugin models authorization and permissions as Policy classes. -These classes implement the logic to check whether or not a **identity** is -allowed to **perform an action** on a given **resource**. Our **identity** is -going to be our logged in user, and our **resources** are our ORM entities and -queries. Lets use bake to generate a basic policy: +O plugin Authorization modela autorização e permissões como classes de Política. +Essas classes implementam a lógica para verificar se uma **identidade** tem ou não +permissão para **executar uma ação** em um determinado **recurso**. Nossa **identidade** será +nosso usuário logado, e nossos **recursos** serão nossas entidades ORM e +consultas. Vamos usar o bake para gerar uma política básica: .. code-block:: console bin/cake bake policy --type entity Article -This will generate an empty policy class for our ``Article`` entity. You can -find the generated policy in **src/Policy/ArticlePolicy.php**. Next update the -policy to look like the following:: +Isso gerará uma classe de política vazia para nossa entidade ``Article``. Você pode +encontrar a política gerada em **src/Policy/ArticlePolicy.php**. Em seguida, atualize a +política para que fique semelhante à seguinte:: isAuthor($user, $article); } public function canDelete(IdentityInterface $user, Article $article) { - // logged in users can delete their own articles. + // usuários logados podem excluir seus próprios artigos. return $this->isAuthor($user, $article); } @@ -124,23 +122,23 @@ policy to look like the following:: } } -While we've defined some very simple rules, you can use as complex logic as your -application requires in your policies. +Embora tenhamos definido algumas regras muito simples, você pode usar uma lógica tão +complexa quanto seu aplicativo exigir em suas políticas. -Checking Authorization in the ArticlesController -================================================ +Verificando a Autorização no ArticlesController +=============================================== -With our policy created we can start checking authorization in each controller -action. If we forget to check or skip authorization in an controller action the -Authorization plugin will raise an exception letting us know we forgot to apply -authorization. In **src/Controller/ArticlesController.php** add the following to -the ``add``, ``edit`` and ``delete`` methods:: +Com nossa política criada, podemos começar a verificar a autorização em cada ação +do controller. Se esquecermos de verificar ou pularmos a autorização em uma ação do controller, o plugin +Authorization lançará uma exceção nos informando que esquecemos de aplicar +a autorização. Em **src/Controller/ArticlesController.php**, adicione o seguinte aos +métodos ``add``, ``edit`` e ``delete``:: public function add() { $article = $this->Articles->newEmptyEntity(); $this->Authorization->authorize($article); - // Rest of the method + // Resto do método } public function edit($slug) @@ -150,7 +148,7 @@ the ``add``, ``edit`` and ``delete`` methods:: ->contain('Tags') // load associated Tags ->firstOrFail(); $this->Authorization->authorize($article); - // Rest of the method. + // Resto do método } public function delete($slug) @@ -159,32 +157,31 @@ the ``add``, ``edit`` and ``delete`` methods:: $article = $this->Articles->findBySlug($slug)->firstOrFail(); $this->Authorization->authorize($article); - // Rest of the method. + // Resto do método } -The ``AuthorizationComponent::authorize()`` method will use the current -controller action name to generate the policy method to call. If you'd like to -call a different policy method you can call ``authorize`` with the operation -name:: +O método ``AuthorizationComponent::authorize()`` usará o nome da ação +do controller atual para gerar o método de política a ser chamado. Se desejar +chamar um método de política diferente, você pode chamar ``authorize`` com o nome da operação:: $this->Authorization->authorize($article, 'update'); -Lastly add the following to the ``tags``, ``view``, and ``index`` methods on the +Por fim, adicione o seguinte aos métodos ``tags``, ``view`` e ``index`` no ``ArticlesController``:: - // View, index and tags actions are public methods - // and don't require authorization checks. + // As ações de visualização, índice e tags são métodos públicos + // e não requerem verificações de autorização. $this->Authorization->skipAuthorization(); -Fixing the Add & Edit Actions -============================= +Corrigindo as Ações de Adicionar e Editar +========================================= -While we've blocked access to the edit action, we're still open to users -changing the ``user_id`` attribute of articles during edit. We -will solve these problems next. First up is the ``add`` action. +Embora tenhamos bloqueado o acesso à ação de edição, ainda estamos abertos a usuários +que alterem o atributo ``user_id`` dos artigos durante a edição. +Resolveremos esses problemas a seguir. A primeira é a ação ``add``. -When creating articles, we want to fix the ``user_id`` to be the currently -logged in user. Replace your add action with the following:: +Ao criar artigos, queremos corrigir o ``user_id`` para que seja o usuário +atualmente conectado. Substitua sua ação de adição pelo seguinte:: // in src/Controller/ArticlesController.php @@ -196,21 +193,21 @@ logged in user. Replace your add action with the following:: if ($this->request->is('post')) { $article = $this->Articles->patchEntity($article, $this->request->getData()); - // Changed: Set the user_id from the current user. + // Alterado: Defina o user_id do usuário atual. $article->user_id = $this->request->getAttribute('identity')->getIdentifier(); if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been saved.')); + $this->Flash->success(__('Seu artigo foi salvo.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to add your article.')); + $this->Flash->error(__('Não é possível adicionar seu artigo.')); } $tags = $this->Articles->Tags->find('list')->all(); $this->set(compact('article', 'tags')); } -Next we'll update the ``edit`` action. Replace the edit method with the following:: +Em seguida, atualizaremos a ação ``edit``. Substitua o método de edição pelo seguinte:: // in src/Controller/ArticlesController.php @@ -218,38 +215,37 @@ Next we'll update the ``edit`` action. Replace the edit method with the followin { $article = $this->Articles ->findBySlug($slug) - ->contain('Tags') // load associated Tags + ->contain('Tags') // carrega as Tags associadas ->firstOrFail(); $this->Authorization->authorize($article); if ($this->request->is(['post', 'put'])) { $this->Articles->patchEntity($article, $this->request->getData(), [ - // Added: Disable modification of user_id. + // Adicionado: desabilitar modificação do user_id. 'accessibleFields' => ['user_id' => false] ]); if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been updated.')); + $this->Flash->success(__('Seu artigo foi atualizado.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to update your article.')); + $this->Flash->error(__('Não é possível atualizar seu artigo.')); } $tags = $this->Articles->Tags->find('list')->all(); $this->set(compact('article', 'tags')); } -Here we're modifying which properties can be mass-assigned, via the options -for ``patchEntity()``. See the :ref:`changing-accessible-fields` section for -more information. Remember to remove the ``user_id`` control from -**templates/Articles/edit.php** as we no longer need it. +Aqui, estamos modificando quais propriedades podem ser atribuídas em massa, por meio das opções +para ``patchEntity()``. Consulte a seção ``changing-accessible-fields`` para +mais informações. Lembre-se de remover o controle ``user_id`` de +**templates/Articles/edit.php**, pois não precisamos mais dele. -Wrapping Up +Concluindo =========== -We've built a simple CMS application that allows users to login, post articles, -tag them, explore posted articles by tag, and applied basic access control to -articles. We've also added some nice UX improvements by leveraging the -FormHelper and ORM capabilities. +Criamos um aplicativo CMS simples que permite aos usuários fazer login, publicar artigos, +marcá-los, explorar artigos publicados por tag e aplicar controle de acesso básico a +artigos. Também adicionamos algumas melhorias interessantes na UX, aproveitando os recursos do FormHelper e do ORM. -Thank you for taking the time to explore CakePHP. Next, you should learn more about -the :doc:`/orm`, or you peruse the :doc:`/topics`. +Obrigado por dedicar seu tempo para explorar o CakePHP. Em seguida, você deve aprender mais sobre +o :doc:`/orm` ou consultar o :doc:`/topics`. From 0b56f934034409c92c3c4c903c35541dc2685b40 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:43:05 -0300 Subject: [PATCH 19/46] [refact]: translated (pt/tutorials-and-examples/cms/database.rst) --- pt/tutorials-and-examples/cms/database.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pt/tutorials-and-examples/cms/database.rst b/pt/tutorials-and-examples/cms/database.rst index 5ba82fe955..cdb4a1aae1 100644 --- a/pt/tutorials-and-examples/cms/database.rst +++ b/pt/tutorials-and-examples/cms/database.rst @@ -1,5 +1,5 @@ -Tutorial - Gerenciador de Conteúdo - Criando o Banco de Dados -############################################################# +CMS Tutorial - Criando o Banco de Dados +####################################### Agora que temos o CakePHP instalado, vamos configurar o banco de dados para nossa aplicação :abbr:`CMS (Sistema Gerenciador de Conteúdo)`. Se você ainda não tiver From 9a833b5590a421be67bb7e14b67e9bb848ed16a0 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:43:25 -0300 Subject: [PATCH 20/46] [refact]: translated (pt/tutorials-and-examples/cms/installation.rst) --- pt/tutorials-and-examples/cms/installation.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pt/tutorials-and-examples/cms/installation.rst b/pt/tutorials-and-examples/cms/installation.rst index 35de1b3b23..ac53d16c7c 100644 --- a/pt/tutorials-and-examples/cms/installation.rst +++ b/pt/tutorials-and-examples/cms/installation.rst @@ -1,5 +1,5 @@ -Tutorial - Gerenciador de Conteúdo -################################## +CMS Tutorial - Gerenciador de Conteúdo +###################################### Este tutorial irá orientá-lo através da criação de uma simples aplicação do tipo :abbr:`CMS (Sistema Gerenciador de Conteúdo)`. Para começar, nós iremos From 2aecbde5f5086700a4204ddc2985b550a20fc86b Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 4 Sep 2025 04:43:57 -0300 Subject: [PATCH 21/46] [refact]: translated (pt/tutorials-and-examples/cms/tags-and-users.rst) --- .../cms/tags-and-users.rst | 380 +++++++++--------- 1 file changed, 188 insertions(+), 192 deletions(-) diff --git a/pt/tutorials-and-examples/cms/tags-and-users.rst b/pt/tutorials-and-examples/cms/tags-and-users.rst index fed181f406..16294bf4a5 100644 --- a/pt/tutorials-and-examples/cms/tags-and-users.rst +++ b/pt/tutorials-and-examples/cms/tags-and-users.rst @@ -1,69 +1,67 @@ -CMS Tutorial - Tags and Users +CMS Tutorial - Tags e Users ############################# -With the basic article creation functionality built, we need to enable multiple -authors to work in our CMS. Previously, we built all the models, views and -controllers by hand. This time around we're going to use -:doc:`/bake` to create our skeleton code. Bake is a powerful -code generation :abbr:`CLI (Command Line Interface)` tool that leverages the -conventions CakePHP uses to create skeleton :abbr:`CRUD (Create, Read, Update, -Delete)` applications very efficiently. We're going to use ``bake`` to build our -users code: +Com a funcionalidade básica de criação de artigos construída, precisamos permitir que múltiplos +autores trabalhem em nosso CMS. Anteriormente, construíamos todos os models, views e +controllers manualmente. Desta vez, usaremos o Bake para criar nosso esqueleto de código. +O Bake é uma poderosa ferramenta de geração de código CLI (Interface de Linha de Comando) +que utiliza as convenções que o CakePHP usa para criar esqueletos de aplicações CRUD (Criar, Ler, Atualizar, +Excluir) de forma muito eficiente. Usaremos o Bake para construir o código dos nossos usuários: .. code-block:: console cd /path/to/our/app - # You can overwrite any existing files. + # Você pode substituir qualquer arquivo existente. bin/cake bake model users bin/cake bake controller users bin/cake bake template users -These 3 commands will generate: +Estes 3 comandos gerarão: -* The Table, Entity, Fixture files. -* The Controller -* The CRUD templates. -* Test cases for each generated class. +* Os arquivos de Tabela, Entidade e Fixture. +* O Controller +* Os templates CRUD. +* Casos de teste para cada classe gerada. -Bake will also use the CakePHP conventions to infer the associations, and -validation your models have. +O Bake também usará as convenções do CakePHP para inferir as associações e +a validação que seus modelos possuem. -Adding Tagging to Articles -========================== +Adicionando Tags aos Artigos +============================ -With multiple users able to access our small :abbr:`CMS` it would be nice to -have a way to categorize our content. We'll use tags and tagging to allow users -to create free-form categories and labels for their content. Again, we'll use -``bake`` to quickly generate some skeleton code for our application: +Com vários usuários acessando nosso pequeno :abbr:`CMS`, seria ótimo +ter uma maneira de categorizar nosso conteúdo. Usaremos tags e marcação para permitir que os usuários +criem categorias e rótulos livres para seu conteúdo. Novamente, usaremos +``bake`` para gerar rapidamente um esqueleto de código para nossa aplicação: .. code-block:: console - # Generate all the code at once. + # Gere todo o código de uma só vez. bin/cake bake all tags -Once you have the scaffold code created, create a few sample tags by going to +Após criar o código do scaffold, crie algumas tags de exemplo acessando **http://localhost:8765/tags/add**. -Now that we have a Tags table, we can create an association between Articles and -Tags. We can do so by adding the following to the ``initialize`` method on the +Agora que temos uma tabela de Tags, podemos criar uma associação entre Artigos e +Tags. Podemos fazer isso adicionando o seguinte ao método ``initialize`` na ``ArticlesTable``:: public function initialize(array $config): void { $this->addBehavior('Timestamp'); - $this->belongsToMany('Tags'); // Add this line + $this->belongsToMany('Tags'); // Adicione esta linha } -This association will work with this simple definition because we followed -CakePHP conventions when creating our tables. For more information, read +Esta associação funcionará com esta definição simples porque seguimos as convenções +do CakePHP ao criar nossas tabelas. Para mais informações, leia :doc:`/orm/associations`. -Updating Articles to Enable Tagging -=================================== +Atualizando Artigos para Habilitar o Tagueamento +================================================ -Now that our application has tags, we need to enable users to tag their -articles. First, update the ``add`` action to look like:: +Agora que nosso aplicativo possui tags, precisamos permitir que os usuários marquem seus +artigos. Primeiro, atualize a ação ``add`` para que fique assim:: request->is('post')) { $article = $this->Articles->patchEntity($article, $this->request->getData()); - // Hardcoding the user_id is temporary, and will be removed later - // when we build authentication out. + // A codificação do user_id é temporária e será removida posteriormente + // quando construirmos a autenticação. $article->user_id = 1; if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been saved.')); + $this->Flash->success(__('Seu artigo foi salvo.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to add your article.')); + $this->Flash->error(__('Não é possível adicionar seu artigo.')); } - // Get a list of tags. + // Obtenha uma lista de tags. $tags = $this->Articles->Tags->find('list')->all(); - // Set tags to the view context + // Definir tags para o contexto de visualização $this->set('tags', $tags); $this->set('article', $article); } - // Other actions + // Outras ações } -The added lines load a list of tags as an associative array of ``id => title``. -This format will let us create a new tag input in our template. -Add the following to the PHP block of controls in **templates/Articles/add.php**:: +As linhas adicionadas carregam uma lista de tags como um array associativo de ``id => title``. +Este formato nos permitirá criar uma nova entrada de tag em nosso modelo. +Adicione o seguinte ao bloco de controles PHP em **templates/Articles/add.php**:: echo $this->Form->control('tags._ids', ['options' => $tags]); -This will render a multiple select element that uses the ``$tags`` variable to -generate the select box options. You should now create a couple new articles -that have tags, as in the following section we'll be adding the ability to find -articles by tags. +Isso renderizará um elemento de seleção múltipla que usa a variável ``$tags`` para +gerar as opções da caixa de seleção. Agora você deve criar alguns novos artigos +com tags, pois na seção a seguir adicionaremos a capacidade de encontrar +artigos por tags. -You should also update the ``edit`` method to allow adding or editing tags. The -edit method should now look like:: +Você também deve atualizar o método ``edit`` para permitir a adição ou edição de tags. O método +edit agora deve se parecer com:: public function edit($slug) { @@ -125,37 +123,37 @@ edit method should now look like:: if ($this->request->is(['post', 'put'])) { $this->Articles->patchEntity($article, $this->request->getData()); if ($this->Articles->save($article)) { - $this->Flash->success(__('Your article has been updated.')); + $this->Flash->success(__('Seu artigo foi atualizado.')); return $this->redirect(['action' => 'index']); } - $this->Flash->error(__('Unable to update your article.')); + $this->Flash->error(__('Não é possível atualizar seu artigo.')); } - // Get a list of tags. + // Obtenha uma lista de tags. $tags = $this->Articles->Tags->find('list')->all(); - // Set tags to the view context + // Definir tags para o contexto de visualização $this->set('tags', $tags); $this->set('article', $article); } -Remember to add the new tags multiple select control we added to the **add.php** -template to the **templates/Articles/edit.php** template as well. +Lembre-se de adicionar o novo controle de seleção múltipla de tags que adicionamos ao template **add.php** +também ao template **templates/Articles/edit.php**. -Finding Articles By Tags -======================== +Encontrar Artigos por Tags +========================== -Once users have categorized their content, they will want to find that content -by the tags they used. For this feature we'll implement a route, controller -action, and finder method to search through articles by tag. +Depois que os usuários categorizarem seu conteúdo, eles desejarão encontrá-lo +pelas tags que usaram. Para este recurso, implementaremos uma rota, uma ação +do controller e um método finder para pesquisar artigos por tag. -Ideally, we'd have a URL that looks like -**http://localhost:8765/articles/tagged/funny/cat/gifs**. This would let us -find all the articles that have the 'funny', 'cat' or 'gifs' tags. Before we -can implement this, we'll add a new route. Your **config/routes.php** (with -the baked comments removed) should look like:: +O ideal seria ter uma URL semelhante a +**http://localhost:8765/articles/tagged/funny/cat/gifs**. Isso nos permitiria +encontrar todos os artigos que possuem as tags 'engraçado', 'gato' ou 'gifs'. Antes de +implementarmos isso, adicionaremos uma nova rota. Seu **config/routes.php** (com +os comentários removidos) deve se parecer com:: connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); $builder->connect('/pages/*', ['controller' => 'Pages', 'action' => 'display']); - // Add this - // New route we're adding for our tagged action. - // The trailing `*` tells CakePHP that this action has - // passed parameters. + // Adicione isto + // Nova rota que estamos adicionando para nossa ação marcada. + // O `*` final informa ao CakePHP que esta ação tem + // parâmetros passados. $builder->scope('/articles', function (RouteBuilder $builder) { $builder->connect('/tagged/*', ['controller' => 'Articles', 'action' => 'tags']); }); @@ -178,66 +176,65 @@ the baked comments removed) should look like:: $builder->fallbacks(); }); -The above defines a new 'route' which connects the **/articles/tagged/** path, -to ``ArticlesController::tags()``. By defining routes, you can isolate how your -URLs look, from how they are implemented. If we were to visit -**http://localhost:8765/articles/tagged**, we would see a helpful error page -from CakePHP informing you that the controller action does not exist. Let's -implement that missing method now. In **src/Controller/ArticlesController.php** -add the following:: +O exemplo acima define uma nova 'rota' que conecta o caminho **/articles/tagged/** +a ``ArticlesController::tags()``. Ao definir rotas, você pode isolar a aparência das suas +URLs de como elas são implementadas. Se visitássemos +**http://localhost:8765/articles/tagged**, veríamos uma página de erro útil +do CakePHP informando que a ação do controlador não existe. Vamos +implementar esse método ausente agora. Em **src/Controller/ArticlesController.php** +adicione o seguinte:: public function tags() { - // The 'pass' key is provided by CakePHP and contains all - // the passed URL path segments in the request. + // A chave 'pass' é fornecida pelo CakePHP e contém todos os + // segmentos de caminho de URL passados ​​na solicitação. $tags = $this->request->getParam('pass'); - // Use the ArticlesTable to find tagged articles. + // Use a ArticlesTable para encontrar artigos marcados. $articles = $this->Articles->find('tagged', tags: $tags) ->all(); - // Pass variables into the view template context. + // Passe variáveis ​​para o contexto do template de visualização. $this->set([ 'articles' => $articles, 'tags' => $tags ]); } -To access other parts of the request data, consult the :ref:`cake-request` -section. +Para acessar outras partes dos dados da solicitação, consulte a seção :ref:`cake-request`. -Since passed arguments are passed as method parameters, you could also write the -action using PHP's variadic argument:: +Como os argumentos passados ​​são passados ​​como parâmetros do método, você também pode escrever a +ação usando o argumento variável do PHP:: public function tags(...$tags) { - // Use the ArticlesTable to find tagged articles. + // Use a ArticlesTable para encontrar artigos marcados. $articles = $this->Articles->find('tagged', tags: $tags) ->all(); - // Pass variables into the view template context. + // Passe variáveis ​​para o contexto do template de visualização. $this->set([ 'articles' => $articles, 'tags' => $tags ]); } -Creating the Finder Method --------------------------- +Criando o Método Finder +----------------------- -In CakePHP we like to keep our controller actions slim, and put most of our -application's logic in the model layer. If you were to visit the -**/articles/tagged** URL now you would see an error that the ``findTagged()`` -method has not been implemented yet, so let's do that. In -**src/Model/Table/ArticlesTable.php** add the following:: +No CakePHP, gostamos de manter as ações do nosso controller enxutas e colocar a maior parte da lógica da nossa +aplicação na camada de modelo. Se você acessasse a URL +**/articles/tagged** agora, veria um erro informando que o método ``findTagged()`` +ainda não foi implementado, então vamos fazer isso. Em +**src/Model/Table/ArticlesTable.php** adicione o seguinte:: - // add this use statement right below the namespace declaration to import - // the Query class + // adicione esta instrução use logo abaixo da declaração de namespace para importar + // a classe Query use Cake\ORM\Query\SelectQuery; - // The $query argument is a query builder instance. - // The $options array will contain the 'tags' option we passed - // to find('tagged') in our controller action. + // O argumento $query é uma instância do construtor de consultas. + // O array $options conterá a opção 'tags' que passamos + // para find('tagged') em nossa ação do controlador. public function findTagged(SelectQuery $query, array $tags = []): SelectQuery { $columns = [ @@ -251,11 +248,11 @@ method has not been implemented yet, so let's do that. In ->distinct($columns); if (empty($tags)) { - // If there are no tags provided, find articles that have no tags. + // Se não houver tags fornecidas, localize artigos que não tenham tags. $query->leftJoinWith('Tags') ->where(['Tags.title IS' => null]); } else { - // Find articles that have one or more of the provided tags. + // Encontre artigos que tenham uma ou mais das tags fornecidas. $query->innerJoinWith('Tags') ->where(['Tags.title IN' => $tags]); } @@ -263,21 +260,21 @@ method has not been implemented yet, so let's do that. In return $query->groupBy(['Articles.id']); } -We just implemented a :ref:`custom finder method `. This is -a very powerful concept in CakePHP that allows you to package up re-usable -queries. Finder methods always get a :doc:`/orm/query-builder` object and an -array of options as parameters. Finders can manipulate the query and add any -required conditions or criteria. When complete, finder methods must return -a modified query object. In our finder we've leveraged the ``distinct()`` and -``leftJoin()`` methods which allow us to find distinct articles that have -a 'matching' tag. +Acabamos de implementar um :ref:`método localizador personalizado `. Este é +um conceito muito poderoso no CakePHP que permite empacotar consultas +reutilizáveis. Os métodos localizadores sempre recebem um objeto :doc:`/orm/query-builder` e um +array de opções como parâmetros. Os localizadores podem manipular a consulta e adicionar quaisquer +condições ou critérios necessários. Quando concluídos, os métodos localizadores devem retornar +um objeto de consulta modificado. Em nosso localizador, utilizamos os métodos ``distinct()`` e +``leftJoin()``, que nos permitem encontrar artigos distintos que tenham +uma tag 'matching'. -Creating the View ------------------ +Criando a Visualização +---------------------- -Now if you visit the **/articles/tagged** URL again, CakePHP will show a new error -letting you know that you have not made a view file. Next, let's build the -view file for our ``tags()`` action:: +Agora, se você visitar a URL **/articles/tagged** novamente, o CakePHP mostrará um novo erro +informando que você não criou um arquivo de visualização. Em seguida, vamos construir o +arquivo de visualização para nossa ação ``tags()``::

@@ -288,7 +285,7 @@ view file for our ``tags()`` action::
- +

Html->link( $article->title, ['controller' => 'Articles', 'action' => 'view', $article->slug] @@ -298,46 +295,45 @@ view file for our ``tags()`` action::

-In the above code we use the :doc:`/views/helpers/html` and -:doc:`/views/helpers/text` helpers to assist in generating our view output. We -also use the :php:func:`h` shortcut function to HTML encode output. You should -remember to always use ``h()`` when outputting data to prevent HTML injection -issues. +No código acima, usamos os auxiliares :doc:`/views/helpers/html` e +:doc:`/views/helpers/text` para auxiliar na geração da saída da nossa view. +Também usamos a função de atalho :php:func:`h` para codificar a saída em HTML. Você deve +lembrar-se de sempre usar ``h()`` ao gerar dados para evitar problemas de injeção de HTML. -The **tags.php** file we just created follows the CakePHP conventions for view -template files. The convention is to have the template use the lower case and -underscored version of the controller action name. +O arquivo **tags.php** que acabamos de criar segue as convenções do CakePHP para arquivos de template de view. +A convenção é que o template use a versão em letras minúsculas e +sublinhada do nome da ação do controller. -You may notice that we were able to use the ``$tags`` and ``$articles`` -variables in our view template. When we use the ``set()`` method in our -controller, we set specific variables to be sent to the view. The View will make -all passed variables available in the template scope as local variables. +Você pode notar que conseguimos usar as variáveis ​​``$tags`` e ``$articles`` +em nosso template de view. Quando usamos o método ``set()`` em nosso +controller, definimos variáveis ​​específicas a serem enviadas para a view. A View tornará +todas as variáveis ​​passadas disponíveis no escopo do template como variáveis ​​locais. -You should now be able to visit the **/articles/tagged/funny** URL and see all -the articles tagged with 'funny'. +Agora você poderá visitar a URL **/articles/tagged/funny** e ver todos +os artigos marcados com 'funny'. -Improving the Tagging Experience -================================ +Melhorando a Experiência de Tagueamento +======================================= -Right now, adding new tags is a cumbersome process, as authors need to -pre-create all the tags they want to use. We can improve the tag selection UI by -using a comma separated text field. This will let us give a better experience to -our users, and use some more great features in the ORM. +No momento, adicionar novas tags é um processo trabalhoso, pois os autores precisam +pré-criar todas as tags que desejam usar. Podemos melhorar a interface de seleção de tags +usando um campo de texto separado por vírgula. Isso nos permitirá oferecer uma experiência melhor aos +nossos usuários e usar mais alguns recursos excelentes do ORM. -Adding a Computed Field ------------------------ +Adicionando um Campo Computado +------------------------------ -Because we'll want a simple way to access the formatted tags for an entity, we -can add a virtual/computed field to the entity. In -**src/Model/Entity/Article.php** add the following:: +Como queremos uma maneira simples de acessar as tags formatadas de uma entidade, +podemos adicionar um campo virtual/computado à entidade. Em +**src/Model/Entity/Article.php**, adicione o seguinte:: - // add this use statement right below the namespace declaration to import - // the Collection class + // adicione esta instrução use logo abaixo da declaração de namespace para importar + // a classe Collection use Cake\Collection\Collection; - // Update the accessible property to contain `tag_string` + // Atualiza a propriedade acessível para conter `tag_string` protected array $_accessible = [ - //other fields... + //outros campos... 'tag_string' => true ]; @@ -357,35 +353,35 @@ can add a virtual/computed field to the entity. In return trim($str, ', '); } -This will let us access the ``$article->tag_string`` computed property. We'll -use this property in controls later on. +Isso nos permitirá acessar a propriedade computada ``$article->tag_string``. +Usaremos essa propriedade em controles posteriormente. -Updating the Views ------------------- +Atualizando as Views +-------------------- -With the entity updated we can add a new control for our tags. In -**templates/Articles/add.php** and **templates/Articles/edit.php**, -replace the existing ``tags._ids`` control with the following:: +Com a entidade atualizada, podemos adicionar um novo controle para nossas tags. Em +**templates/Articles/add.php** e **templates/Articles/edit.php**, +substitua o controle ``tags._ids`` existente pelo seguinte:: echo $this->Form->control('tag_string', ['type' => 'text']); -We'll also need to update the article view template. In -**templates/Articles/view.php** add the line as shown:: +Também precisaremos atualizar o template de visualização do artigo. Em +**templates/Articles/view.php**, adicione a linha conforme mostrado::

title) ?>

body) ?>

- // Add the following line + // Adicione a seguinte linha

Tags: tag_string) ?>

-You should also update the view method to allow retrieving existing tags:: +Você também deve atualizar o método de visualização para permitir a recuperação de tags existentes:: // src/Controller/ArticlesController.php file public function view($slug = null) { - // Update retrieving tags with contain() + // Atualizar a recuperação de tags com contain() $article = $this->Articles ->findBySlug($slug) ->contain('Tags') @@ -393,14 +389,14 @@ You should also update the view method to allow retrieving existing tags:: $this->set(compact('article')); } -Persisting the Tag String -------------------------- +Persistindo a Sequência de Tags +------------------------------- -Now that we can view existing tags as a string, we'll want to save that data as -well. Because we marked the ``tag_string`` as accessible, the ORM will copy that -data from the request into our entity. We can use a ``beforeSave()`` hook method -to parse the tag string and find/build the related entities. Add the following -to **src/Model/Table/ArticlesTable.php**:: +Agora que podemos visualizar as tags existentes como uma string, também queremos salvar esses dados. +Como marcamos a ``tag_string`` como acessível, o ORM copiará esses dados da solicitação para a nossa +entidade. Podemos usar um método de gancho ``beforeSave()`` +para analisar a string da tag e encontrar/construir as entidades relacionadas. Adicione o seguinte +a **src/Model/Table/ArticlesTable.php**:: public function beforeSave(EventInterface $event, $entity, $options): void { @@ -408,16 +404,16 @@ to **src/Model/Table/ArticlesTable.php**:: $entity->tags = $this->_buildTags($entity->tag_string); } - // Other code + // Outro código } protected function _buildTags($tagString) { // Trim tags $newTags = array_map('trim', explode(',', $tagString)); - // Remove all empty tags + // Removendo todas as tags vazias $newTags = array_filter($newTags); - // Reduce duplicated tags + // Removendo tags duplicadas $newTags = array_unique($newTags); $out = []; @@ -425,18 +421,18 @@ to **src/Model/Table/ArticlesTable.php**:: ->where(['Tags.title IN' => $newTags]) ->all(); - // Remove existing tags from the list of new tags. + // Remova as tags existentes da lista de novas tags. foreach ($tags->extract('title') as $existing) { $index = array_search($existing, $newTags); if ($index !== false) { unset($newTags[$index]); } } - // Add existing tags. + // Adicionando tags existentes. foreach ($tags as $tag) { $out[] = $tag; } - // Add new tags. + // Adicionando novas tags. foreach ($newTags as $tag) { $out[] = $this->Tags->newEntity(['title' => $tag]); } @@ -444,43 +440,43 @@ to **src/Model/Table/ArticlesTable.php**:: return $out; } -If you now create or edit articles, you should be able to save tags as a comma -separated list of tags, and have the tags and linking records automatically -created. +Se você agora cria ou edita artigos, poderá salvar tags como uma lista de tags +separadas por vírgulas e ter as tags e os registros de vinculação +criados automaticamente. -While this code is a bit more complicated than what we've done so far, it helps -to showcase how powerful the ORM in CakePHP is. You can manipulate query -results using the :doc:`/core-libraries/collections` methods, and handle -scenarios where you are creating entities on the fly with ease. +Embora este código seja um pouco mais complexo do que o que fizemos até agora, ele ajuda +a demonstrar o quão poderoso é o ORM no CakePHP. Você pode manipular resultados de consulta +usando os métodos :doc:`/core-libraries/collections` e lidar +com cenários em que você cria entidades dinamicamente com facilidade. -Auto-populating the Tag String -============================== +Preenchimento Automático da Sequência de Tags +============================================= -Before we finish up, we'll need a mechanism that will load the associated tags -(if any) whenever we load an article. +Antes de terminarmos, precisaremos de um mecanismo que carregue as tags associadas +(se houver) sempre que carregarmos um artigo. -In your **src/Model/Table/ArticlesTable.php**, change:: +Em seu **src/Model/Table/ArticlesTable.php**, altere:: public function initialize(array $config): void { $this->addBehavior('Timestamp'); - // Change this line + // Altere essa linha $this->belongsToMany('Tags', [ 'joinTable' => 'articles_tags', 'dependent' => true ]); } -This will tell the Articles table model that there is a join table associated -with tags. The 'dependent' option tells the table to delete any associated -records from the join table if an article is deleted. +Isso informará ao modelo de tabela "Articles" que há uma tabela de junção associada +com tags. A opção "dependent" informa à tabela para excluir quaisquer registros +associados da tabela de junção se um artigo for excluído. -Lastly, update the findBySlug() method calls in +Por fim, atualize as chamadas do método findBySlug() em **src/Controller/ArticlesController.php**:: public function edit($slug) { - // Update this line + // Atualize está linha $article = $this->Articles ->findBySlug($slug) ->contain('Tags') @@ -490,7 +486,7 @@ Lastly, update the findBySlug() method calls in public function view($slug = null) { - // Update this line + // Atualize está linha $article = $this->Articles ->findBySlug($slug) ->contain('Tags') @@ -498,8 +494,8 @@ Lastly, update the findBySlug() method calls in $this->set(compact('article')); } -The ``contain()`` method tells the ``ArticlesTable`` object to also populate the -Tags association when the article is loaded. Now when tag_string is called for -an Article entity, there will be data present to create the string! +O método ``contain()`` informa ao objeto ``ArticlesTable`` para também preencher a +associação Tags quando o artigo for carregado. Agora, quando tag_string for chamada para +uma entidade Article, haverá dados presentes para criar a string! -Next we'll be adding :doc:`authentication `. +Em seguida, adicionaremos :doc:`authentication `. From a07fb519e3bca5c36aa72979567d7ed4509297db Mon Sep 17 00:00:00 2001 From: kromodoro Date: Sun, 7 Sep 2025 08:47:45 -0300 Subject: [PATCH 22/46] [refact]: translated (pt/controllers/components.rst) --- pt/controllers/components.rst | 303 ++++++++++++++++++++-------------- 1 file changed, 181 insertions(+), 122 deletions(-) diff --git a/pt/controllers/components.rst b/pt/controllers/components.rst index 411fd7910f..266db61506 100644 --- a/pt/controllers/components.rst +++ b/pt/controllers/components.rst @@ -1,128 +1,128 @@ Componentes ########### -Componentes são pacotes de lógica compartilhados entre controladores. O CakePHP -vem com um conjunto fantástico de componentes principais que você pode usar para -ajudar em várias tarefas comuns. Você também pode criar seus próprios componentes. -Se você deseja copiar e colar coisas entre controladores, considere criar seu próprio -componente para conter a funcionalidade. A criação de componentes mantém o código do -controlador limpo e permite reutilizar o código entre diferentes controladores. +Componentes são pacotes de lógica compartilhados entre controllers. +O CakePHP vem com um conjunto fantástico de componentes principais que você pode usar para auxiliar em +várias tarefas comuns. Você também pode criar seus próprios componentes. Se você +quiser copiar e colar coisas entre controllers, deve +considerar criar seu próprio componente para conter a funcionalidade. Criar +componentes mantém o código do controller limpo e permite que você reutilize código entre +diferentes controllers. -Para mais informações sobre os componentes incluídos no CakePHP, consulte o -capítulo para cada componente: +Para mais informações sobre os componentes incluídos no CakePHP, confira o +capítulo de cada componente: .. toctree:: :maxdepth: 1 - /controllers/components/authentication /controllers/components/flash - /controllers/components/security - /controllers/pagination - /controllers/components/request-handling + /controllers/components/form-protection + /controllers/components/check-http-cache .. _configuring-components: Configurando Componentes ======================== -Muitos dos componentes principais requerem configuração. Alguns exemplos de componentes -que requerem configuração são :doc:`/controllers/components/security` e -:doc:`/controllers/components/request-handling`. A configuração desses componentes e dos -componentes em geral é geralmente feita via ``loadComponent()`` no método ``initialize()`` -do seu Controlador ou através do array ``$components``:: +Muitos dos componentes principais exigem configuração. Um exemplo seria +o :doc:`/controllers/components/form-protection`. A configuração desses componentes, +e de componentes em geral, geralmente é feita via ``loadComponent()`` no método ``initialize()`` do seu +Controller ou via array ``$components``:: class PostsController extends AppController { public function initialize(): void { parent::initialize(); - $this->loadComponent('RequestHandler', [ - 'viewClassMap' => ['json' => 'AppJsonView'], + $this->loadComponent('FormProtection', [ + 'unlockedActions' => ['index'], ]); - $this->loadComponent('Security', ['blackholeCallback' => 'blackhole']); + $this->loadComponent('Flash'); } - } -Você pode configurar componentes em tempo de execução usando o método ``setConfig()``. -Muitas vezes, isso é feito no método ``beforeFilter()`` do seu controlador. O exemplo acima -também pode ser expresso como:: +Você pode configurar componentes em tempo de execução usando o método ``setConfig()``. Frequentemente, +isso é feito no método ``beforeFilter()`` do seu controller. O exemplo acima +também poderia ser expresso como:: - public function beforeFilter(EventInterface $event) + public function beforeFilter(EventInterface $event): void { - $this->RequestHandler->setConfig('viewClassMap', ['rss' => 'MyRssView']); + $this->FormProtection->setConfig('unlockedActions', ['index']); } -Como os auxiliares, os componentes implementam os métodos ``getConfig()`` e -``setConfig()`` para ler e gravar dados de configuração:: +Assim como os helpers, os componentes implementam os métodos ``getConfig()`` e ``setConfig()`` +para ler e escrever dados de configuração:: - // Leia os dados de configuração. - $this->RequestHandler->getConfig('viewClassMap'); + // Ler dados de configuração. + $this->FormProtection->getConfig('unlockedActions'); // Definir configuração - $this->Csrf->setConfig('cookieName', 'token'); + $this->Flash->setConfig('key', 'myFlash'); -Assim como os auxiliares, os componentes mesclam automaticamente sua propriedade -``$ _defaultConfig`` com a configuração do construtor para criar a propriedade -``$_config`` que pode ser acessada com ``getConfig()`` e ``setConfig()``. +Assim como os auxiliares, os componentes mesclarão automaticamente sua propriedade ``$_defaultConfig`` +com a configuração do construtor para criar a propriedade ``$_config`` +que pode ser acessada com ``getConfig()`` e ``setConfig()``. -Alias em Componentes +Aliasing Componentes -------------------- -Uma configuração comum a ser usada é a opção ``className``, que permite o alias -de componentes. Esse recurso é útil quando você deseja substituir ``$this->Auth`` -ou outra referência de componente comum por uma implementação personalizada:: +Uma configuração comum é a opção ``className``, que permite +criar alias para componentes. Este recurso é útil quando você deseja +substituir ``$this->Flash`` ou outra referência comum a Componentes por uma +implementação personalizada:: // src/Controller/PostsController.php class PostsController extends AppController { 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 { - // Adicione seu código para substituir o principal AuthComponent + // Adicione seu código para substituir o FlashComponent principal } -O exemplo acima seria *alias* ``MyAuthComponent`` para ``$this->Auth`` em seus controladores. +O comando acima seria um *alias* de ``MyFlashComponent`` para ``$this->Flash`` em seus +controllers. .. note:: - O alias de um componente substitui essa instância em qualquer lugar em que esse componente - seja usado, inclusive dentro de outros componentes. + Criar um alias para um componente substitui essa instância em qualquer lugar em + que o componente seja usado, inclusive dentro de outros componentes. -Carregando Componentes em Tempo Real ------------------------------------- +Carregando Componentes em Rempo Real +----------------------------- -Você pode não precisar de todos os seus componentes disponíveis em todas as ações do controlador. +Você pode não precisar de todos os seus componentes disponíveis em todas as ações do controller. Em situações como essa, você pode carregar um componente em tempo de execução usando o método -``loadComponent()`` no seu controlador:: +``loadComponent()`` no seu controller: - // Em um método do controlador + // Na ação do controller $this->loadComponent('OneTimer'); $time = $this->OneTimer->getTime(); .. note:: - Lembre-se de que os componentes carregados em tempo real não terão retornos de chamada perdidos. - Se você confiar nos retornos de chamada ``beforeFilter`` ou ``startup`` que estão sendo chamados, - pode ser necessário chamá-los manualmente, dependendo de quando você carregar o componente. + Lembre-se de que os componentes carregados em tempo real não terão chamadas de retorno de chamada + perdidas. Se você depender da chamada dos retornos de chamada ``beforeFilter`` ou ``startup`` + , poderá ser necessário chamá-los manualmente, dependendo de quando + você carregar seu componente. Usando Componentes ================== -Depois de incluir alguns componentes no seu controlador, usá-los é bastante simples. -Cada componente usado é exposto como uma propriedade no seu controlador. Se você -carregou a classe :php:class:`Cake\\Controller\\Component\\FlashComponent` no seu -controlador, é possível acessá-lo da seguinte maneira:: +Depois de incluir alguns componentes no seu controller, usá-los é bem +simples. Cada componente usado é exposto como uma propriedade no seu controller. Se +você tivesse carregado a :php:class:`Cake\\Controller\\Component\\FlashComponent` +no seu controller, você poderia acessá-lo assim:: class PostsController extends AppController { @@ -135,7 +135,7 @@ controlador, é possível acessá-lo da seguinte maneira:: public function delete() { if ($this->Post->delete($this->request->getData('Post.id')) { - $this->Flash->success('Post deleted.'); + $this->Flash->success('Post deletado.'); return $this->redirect(['action' => 'index']); } @@ -143,22 +143,25 @@ controlador, é possível acessá-lo da seguinte maneira:: .. note:: - Como os Modelos e os Componentes são adicionados aos Controladores - como propriedades, eles compartilham o mesmo 'namespace'. Certifique-se - de não dar o mesmo nome a um componente de um modelo. + Como Modelos e Componentes são adicionados aos Controllers como + propriedades, eles compartilham o mesmo "namespace". Certifique-se de não dar a um + componente e a um modelo o mesmo nome. + +.. versionchanged:: 5.1.0 + Os componentes podem usar :doc:`/development/dependency-injection` para receber serviços. .. _creating-a-component: Criando um Componente ===================== -Suponha que nosso aplicativo precise executar uma operação matemática complexa -em muitas partes diferentes do aplicativo. Poderíamos criar um componente para -hospedar essa lógica compartilhada para uso em muitos controladores diferentes. +Suponha que nossa aplicação precise realizar uma operação matemática complexa em +várias partes diferentes da aplicação. Poderíamos criar um componente para abrigar +essa lógica compartilhada para uso em diversos controllers. -O primeiro passo é criar um novo arquivo e classe de componente. Crie o arquivo em -**src/Controller/Component/MathComponent.php**. A estrutura básica do componente -será semelhante a isso:: +O primeiro passo é criar um novo arquivo de componente e uma nova classe. Crie o arquivo em +**src/Controller/Component/MathComponent.php**. A estrutura básica do +componente seria algo como isto:: namespace App\Controller\Component; @@ -174,52 +177,74 @@ será semelhante a isso:: .. note:: - Todos os componentes devem estender :php:class:`Cake\\Controller\\Component`. - Não fazer isso acionará uma exceção. + Todos os componentes devem estender :php:class:`Cake\\Controller\\Component`. Caso contrário, + uma exceção será acionada. + +Os componentes podem usar :doc:`/development/dependency-injection` para receber serviços +como parâmetros do construtor:: + + namespace App\Controller\Component; + + use Cake\Controller\Component; + use App\Service\UserService; + + class SsoComponent extends Component + { + public function __construct( + ComponentRegistry $registry, + array $config = [], + UserService $users + ) { + parent::__construct($registry, $config); + $this->users = $users; + } + } + +.. versionadded: 5.1.0 + Foi adicionado suporte ao contêiner DI para componentes. -Incluindo seu Componente em seus Controladores ----------------------------------------------- +Incluindo seu Componente no seu Controller +------------------------------------------ -Depois que nosso componente é concluído, podemos usá-lo nos controladores -do aplicativo carregando-o durante o método ``initialize()`` do controlador. -Uma vez carregado, o controlador receberá um novo atributo com o nome do componente, -através do qual podemos acessar uma instância dele:: +Assim que nosso componente estiver pronto, podemos usá-lo nos controllers +da aplicação, carregando-o durante o método ``initialize()`` do controllers. +Uma vez carregado, o controllers receberá um novo atributo com o nome do +componente, por meio do qual podemos acessar uma instância dele:: // Em um controlador // Disponibilize o novo componente em $this->Math, - // bem como o padrão $this->Csrf + // assim como o $this->Flash padrão public function initialize(): void { parent::initialize(); $this->loadComponent('Math'); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } -Ao incluir componentes em um controlador, você também pode declarar -um conjunto de parâmetros que serão passados para o construtor do componente. -Esses parâmetros podem ser manipulados pelo componente:: +Ao incluir Componentes em um Controller, você também pode declarar um +conjunto de parâmetros que serão passados ​​ao construtor +do Componente. Esses parâmetros podem então ser manipulados +pelo Componente:: - // Em seu controlador + // No seu controller. public function initialize(): void { parent::initialize(); $this->loadComponent('Math', [ 'precision' => 2, - 'randomGenerator' => 'srand' + 'randomGenerator' => 'srand', ]); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); } -O exemplo acima passaria um array contendo precision e randomGenerator -para ``MathComponent::initialize()`` no parâmetro ``$config``. +O exemplo acima passaria o array contendo precision e randomGenerator para +``MathComponent::initialize()`` no parâmetro ``$config``. -Usando Outros Componentes em seu Componente +Usando Outros Componentes no seu Componente ------------------------------------------- -Às vezes, um de seus componentes pode precisar usar outro componente. -Nesse caso, você pode incluir outros componentes no seu componente -exatamente da mesma maneira que os inclui nos controladores - usando o -atributo ``$components``:: +Às vezes, um dos seus componentes pode precisar usar outro componente. +Você pode carregar outros componentes adicionando-os à propriedade `$components`:: // src/Controller/Component/CustomComponent.php namespace App\Controller\Component; @@ -229,9 +254,9 @@ atributo ``$components``:: class CustomComponent extends Component { // O outro componente que seu componente usa - public $components = ['Existing']; + protected array $components = ['Existing']; - // Execute qualquer outra configuração adicional para o seu componente. + // Execute qualquer outra configuração adicional para seu componente. public function initialize(array $config): void { $this->Existing->foo(); @@ -250,7 +275,6 @@ atributo ``$components``:: class ExistingComponent extends Component { - public function foo() { // ... @@ -259,54 +283,89 @@ atributo ``$components``:: .. note:: - Ao contrário de um componente incluído em um controlador, + Ao contrário de um componente incluído em um controller, nenhum retorno de chamada será acionado no componente de um componente. -Acessando o Controlador de um Componente ----------------------------------------- +Acessando um Componente de Controller +------------------------------------- -De dentro de um componente, você pode acessar o controlador atual através do +De dentro de um Componente, você pode acessar o controller atual por meio do registro:: - $controller = $this->_registry->getController(); - -Você pode acessar o controlador em qualquer método de retorno de chamada do objeto de -evento:: - - $controller = $event->getSubject(); + $controller = $this->getController(); -Callback de Componentes -======================= +Retornos de Chamada de Componentes +================================== -Os componentes também oferecem alguns retornos de chamada do ciclo de vida da solicitação que -permitem aumentar o ciclo da solicitação. +Os componentes também oferecem alguns retornos de chamada do ciclo de vida da +solicitação que lhes permitem aumentar o ciclo da solicitação. .. php:method:: beforeFilter(EventInterface $event) - É chamado antes do método beforeFilter do controlador, - mas *após* o método initialize() do controlador. + É chamado antes do método + beforeFilter() do controller, mas *depois* do método initialize() do controller. .. php:method:: startup(EventInterface $event) - É chamado após o método beforeFilter do controlador, - mas antes que o controlador execute o manipulador de ações atual. + É chamado após o método beforeFilter() do controller, + mas antes que o controller execute o manipulador de ação + atual. .. php:method:: beforeRender(EventInterface $event) - É chamado após o controlador executar a lógica da ação solicitada, - mas antes de o controlador renderizar visualizações e layout. + É chamado após o controller executar a lógica da ação solicitada, + mas antes do controller renderizar as visualizações e o layout. -.. php:method:: shutdown(EventInterface $event) +.. php:method:: afterFilter(EventInterface $event) - É chamado antes que a saída seja enviada ao navegador. + É chamado durante o evento ``Controller.shutdown``, antes da saída ser enviada ao navegador. .. php:method:: beforeRedirect(EventInterface $event, $url, Response $response) - É chamado quando o método de redirecionamento do controlador é chamado, - mas antes de qualquer ação adicional. Se esse método retornar ``false``, - o controlador não continuará redirecionando a solicitação. Os parâmetros - $url e $response permitem inspecionar e modificar o local ou qualquer outro - cabeçalho na resposta. + É invocado quando o método de redirecionamento + do controller é chamado, mas antes de qualquer ação adicional. Se este método + retornar ``false``, o controller não continuará redirecionando a + solicitação. Os parâmetros $url e $response permitem que você inspecione e modifique + a localização ou quaisquer outros cabeçalhos na resposta. + +.. _redirect-component-events: + +Usando Redirecionamentos em Eventos de Componentes +================================================== + +Para redirecionar de dentro de um método de retorno de chamada de componente, você pode usar o seguinte:: + + public function beforeFilter(EventInterface $event): void + { + if (...) { + $event->setResult($this->getController()->redirect('/')); + + return; + } + + ... + } + +Ao definir um redirecionamento como resultado do evento, você informa ao CakePHP que não deseja que nenhum outro +retorno de chamada de componente seja executado e que o controller não deve mais manipular a ação. +A partir da versão 4.1.0, você pode lançar uma ``RedirectException`` para sinalizar +um redirecionamento:: + + use Cake\Http\Exception\RedirectException; + use Cake\Routing\Router; + + public function beforeFilter(EventInterface $event): void + { + throw new RedirectException(Router::url('/')) + } + +Gerar uma exceção interromperá todos os outros ouvintes de eventos e criará uma nova +resposta que não retém ou herda nenhum dos cabeçalhos da resposta atual. +Ao gerar uma ``RedirectException``, você pode incluir cabeçalhos adicionais:: + + throw new RedirectException(Router::url('/'), 302, [ + 'Header-Key' => 'value', + ]); .. meta:: :title lang=pt: Componentes From 36c8b3ab152f59a7c10edcfc9e65bd3710ab5592 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Sun, 7 Sep 2025 08:50:50 -0300 Subject: [PATCH 23/46] [refact]: translated (pt/controllers/components/check-http-cache.rst) --- .../components/check-http-cache.rst | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 pt/controllers/components/check-http-cache.rst diff --git a/pt/controllers/components/check-http-cache.rst b/pt/controllers/components/check-http-cache.rst new file mode 100644 index 0000000000..d4fd94360e --- /dev/null +++ b/pt/controllers/components/check-http-cache.rst @@ -0,0 +1,31 @@ +Checando o Cache HTTP +===================== + +.. php:class:: CheckHttpCacheComponent(ComponentCollection $collection, array $config = []) + +O modelo de validação de cache HTTP é um dos processos usados ​​por gateways de cache, +também conhecidos como proxies reversos, para determinar se eles podem fornecer uma cópia armazenada de +uma resposta ao cliente. Com esse modelo, você economiza principalmente largura de banda, mas, quando +usado corretamente, também pode economizar algum processamento de CPU, reduzindo +os tempos de resposta:: + + // No Controller + public function initialize(): void + { + parent::initialize(); + + $this->addComponent('CheckHttpCache'); + } + +Habilitar o ``CheckHttpCacheComponent`` no seu controlador ativa automaticamente uma verificação +``beforeRender``. Essa verificação compara os cabeçalhos de cache definidos no objeto de resposta +com os cabeçalhos de cache enviados na solicitação para determinar se a resposta não foi modificada +desde a última vez que o cliente a solicitou. Os seguintes cabeçalhos de solicitação são usados: + +* ``If-None-Match`` é comparado com o cabeçalho ``Etag`` da resposta. +* ``If-Modified-Since`` é comparado com o cabeçalho ``Last-Modified`` da resposta. + +Se os cabeçalhos de resposta corresponderem aos critérios do cabeçalho da solicitação, a renderização da visualização será +ignorada. Isso evita que seu aplicativo gere uma visualização, economizando largura de banda e +tempo. Quando os cabeçalhos de resposta correspondem, uma resposta vazia é retornada com um código de status ``304 +Not Modified``. From 85d4717a6e39853238191d87a5dd841e55fb9f9a Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:21:09 -0300 Subject: [PATCH 24/46] [refact]: translated (pt/plugins.rst) --- pt/plugins.rst | 792 +++++++++++++++++++++++++++++-------------------- 1 file changed, 470 insertions(+), 322 deletions(-) diff --git a/pt/plugins.rst b/pt/plugins.rst index b431d33011..7db95f10be 100644 --- a/pt/plugins.rst +++ b/pt/plugins.rst @@ -1,227 +1,240 @@ Plugins ####### -O CakePHP permite que você configure uma combinação de controllers, models e views, que são os plugins de aplicativo empacotado que outros podem usar em suas aplicações CakePHP. - -Se você criou um módulo de gerenciamento de usuários, blog ou serviços da Web em uma das suas aplicações, por que não torná-lo um plugin CakePHP? Desta forma, você pode reutilizá-lo em seus outros aplicativos e compartilhar com a comunidade! - -Um plugin do CakePHP é, em última instância, separado do próprio aplicativo host e, geralmente, oferece algumas funcionalidades bem definidas que podem ser embaladas de maneira ordenada e reutilizadas com pouco esforço em outras aplicações. O aplicativo e o plugin operam em seus respectivos espaços, mas compartilham propriedades específicas da aplicação (parâmetros de conectividade de banco de dados) que são definidos e compartilhados através da configuração do aplicativo. - -No CakePHP 3.0 cada plugin define seu próprio namespace de nível superior. Por exemplo: ``DebugKit``. -Por convenção, os plugins usam o nome do pacote como seu namespace. Se você quiser usar um espaço para nome diferente, você pode configurar o espaço para nome do plugin, quando os plugins são carregados. +O CakePHP permite que você configure uma combinação de controllers, models e views, +e os libere como um plugin de aplicativo pré-empacotado que +outros podem usar em seus aplicativos CakePHP. Se você criou +um ótimo gerenciamento de usuários, um blog simples ou adaptadores de serviços web em um de +seus aplicativos, por que não empacotá-lo como um plugin do CakePHP? Dessa forma, você +pode reutilizá-lo em seus outros aplicativos e compartilhar com a comunidade! + +Um plugin do CakePHP é separado do próprio aplicativo host e geralmente +fornece algumas funcionalidades bem definidas que podem ser empacotadas de forma organizada e +reutilizadas com pouco esforço em outros aplicativos. O aplicativo e o plugin +operam em seus respectivos espaços, mas compartilham os dados de configuração do aplicativo +(por exemplo, conexões de banco de dados, transportes de e-mail). + +O plugin deve definir seu próprio namespace de nível superior. Por exemplo: +``DebugKit``. Por convenção, os plugins usam o nome do pacote como seu namespace. +Se desejar usar um namespace diferente, você pode configurar o namespace do plugin +quando os plugins forem carregados. Instalando um Plugin com Composer ================================= Muitos plugins estão disponíveis no `Packagist ` _ E podem ser instalados com o ``Composer``. Para instalar o DebugKit, você -deve fazer assim o assim:: +deve fazer assim o assim: + +.. code-block:: console php composer.phar require cakephp/debug_kit -Ou se o composer tiver sido instalado globalmente assim: - composer require cakephp/debug_kit +Isso instalaria a versão mais recente do DebugKit e atualizaria seus arquivos **composer.json**, **composer.lock**, +atualiza **vendor/cakephp-plugins.php** e atualize seu autoloader. -Isso instalaria a versão mais recente do DebugKit e atualizaria seus arquivos **composer.json**, **composer.lock**, atualiza **vendor/cakephp-plugins.php** e atualize seu autoloader. +Instalando um Plugin Manualmente +================================ -Se o plugin que deseja instalar não estiver disponível em -Packagist.org, você pode clonar ou copiar o código do plugin para seu diretório **plugins**, no raiz do aplicativo. -Supondo que você deseja instalar um plugin chamado 'ContactManager', você -deve ter uma pasta em **plugins** chamado 'ContactManager'. Neste diretório -existe o src, o plugin, testes e outros diretórios. +Se o plugin que você deseja instalar não estiver disponível em +packagist.org, você pode clonar ou copiar o código do plugin para o seu diretório **plugins** +. Supondo que você queira instalar um plugin chamado 'ContactManager', você +deve ter uma pasta em **plugins** chamada 'ContactManager'. Neste diretório +estão os diretórios src, tests e quaisquer outros diretórios do plugin. -.. index:: vendor/cakephp-plugins.php +.. _autoloading-plugin-classes: -Plugin Map File ---------------- +Autoloading Manual das Classes do Plugin +---------------------------------------- -Ao instalar plugins através do Composer, você pode notar que -**vendor/cakephp-plugins.php** é criado. Este arquivo de configuração contém -um mapa de nomes de plugins e seus caminhos no sistema de arquivos. Isso torna possível -que os plugins sejam instalados no diretório padrão do vendor que está fora -dos caminhos de pesquisa normais. A classe ``Plugin`` usará este arquivo para localizar -plugins quando são carregados com ``load ()`` ou ``loadAll ()``. Você geralmente -não precisará editar este arquivo à mão, com Composer e ``plugin-installer`` -O pacote o gerenciará para você. +Se você instalar seus plugins via ``composer`` ou ``bake``, não precisará +configurar o carregamento automático de classes para seus plugins. -.. _loading-a-plugin: +Se você criar um plugin manualmente na pasta ``plugins``, precisará +informar ao ``composer`` para atualizar o cache de carregamento automático: -Carregando um Plugin -==================== +.. code-block:: console -Depois de instalar um plugin e configurar o autoloader, você deve carregar -O plugin. Você pode carregar plugins um a um, ou todos eles com um único -método:: + php composer.phar dumpautoload - // In config/bootstrap.php - // Or in Application::bootstrap() +Se estiver usando namespaces de fornecedores para seus plugins, você terá que adicionar o +namespace ao mapeamento de caminho para o ``composer.json``, semelhante ao seguinte, +antes de executar o comando composer acima: - // Carrega um único plugin - Plugin::load('ContactManager'); +.. code-block:: json - // Carrega um plugin com um namespace no nível superior. - Plugin::load('AcmeCorp/ContactManager'); + { + "autoload": { + "psr-4": { + "AcmeCorp\\Users\\": "plugins/AcmeCorp/Users/src/", + } + }, + "autoload-dev": { + "psr-4": { + "AcmeCorp\\Users\\Test\\": "plugins/AcmeCorp/Users/tests/" + } + } + } - // Carrega todos os plugins de uma só vez - Plugin::loadAll(); +.. _loading-a-plugin: -``loadAll()`` carrega todos os plugins disponíveis, permitindo que você especifique determinadas -configurações para plugins. ``load()`` funciona de forma semelhante, mas apenas carrega o -Plugins que você especifica explicitamente. +Carregando um Plugin +==================== -.. note:: +Se você quiser usar as rotas, comandos de console, middlewares, middlewares, event +listeners, templates or webroot assets de um plugin, precisará carregar o plugin. - ``Plugin::loadAll()`` não irá carregar os plugins vendor namespaced que não são - Definido em **vendor/cakephp-plugins.php**. +Se você quiser usar apenas auxiliares, comportamentos ou componentes de um plugin, +não precisa carregá-lo explicitamente, mas é recomendável sempre fazê-lo. -Há também um comando de shell acessível para habilitar o plugin. Execute a seguinte linha: +Há também um comando de console útil para carregar o plugin. Execute a seguinte +linha: .. code-block:: console bin/cake plugin load ContactManager -Isso colocará o plugin ``Plugin::load('ContactManager');`` no bootstrap para você. +Isso atualizará o array no arquivo ``config/plugins.php`` do seu aplicativo com +uma entrada semelhante a ``'ContactManager' => []``. -.. _autoloading-plugin-classes: - -Autoloading Plugin Classes --------------------------- - -Ao usar ``bake`` para criar um plugin ou quando instalar um plugin usando o -Composer, você normalmente não precisa fazer alterações em seu aplicativo para -faça com que o CakePHP reconheça as classes que vivem dentro dele. +.. _plugin-configuration: -Em qualquer outro caso, você precisará modificar o arquivo do composer.json do seu aplicativo. -Para conter as seguintes informações:: +Configuração do Hook do Plugin +============================== - "psr-4": { - (...) - "MyPlugin\\": "./plugins/MyPlugin/src", - "MyPlugin\\Test\\": "./plugins/MyPlugin/tests" - } +Os plugins oferecem vários hooks que permitem que um plugin se injete nas +partes apropriadas da sua aplicação. Os hooks são: -Se você estiver usando o vendor namespaces para seus plugins, o espaço para nome para mapeamento de caminho -deve se parecer com o seguinte:: +* ``bootstrap`` Usado para carregar os arquivos de configuração padrão do plugin, definir + constantes e outras funções globais. O método ``bootstrap`` recebe a + instância atual de ``Application``, dando a você amplo acesso ao contêiner DI + e à configuração. +* ``routes`` Usado para carregar rotas para um plugin. Disparado após as rotas do aplicativo + serem carregadas. +* ``middleware`` Usado para adicionar o middleware do plugin à fila de middleware + de um aplicativo. +* ``console`` Usado para adicionar comandos de console à coleção de comandos + de um aplicativo. +* ``services`` Usado para registrar o serviço do contêiner do aplicativo. Esta é uma boa + oportunidade para configurar objetos adicionais que precisam de acesso ao contêiner. - "psr-4": { - (...) - "AcmeCorp\\Users\\": "./plugins/AcmeCorp/Users/src", - "AcmeCorp\\Users\\Test\\": "./plugins/AcmeCorp/Users/tests" - } +Por padrão, todos os hooks de plugins estão habilitados. Você pode desabilitá-los usando as +opções relacionadas do comando ``plugin load``: -Além disso, você precisará dizer ao Composer para atualizar o cache de autoloading:: +.. code-block:: console - $ php composer.phar dumpautoload + bin/cake plugin load ContactManager --no-routes -Se você não conseguir usar o Composer por qualquer outro motivo, você também pode usar um recurso alternativo -Autoloading para o seu plugin:: +Isso atualizaria o array no ``config/plugins.php`` do seu aplicativo com +uma entrada semelhante a ``'ContactManager' => ['routes' => false]``. - Plugin::load('ContactManager', ['autoload' => true]); +Opções de Carregamento de Plugins +================================= -.. _plugin-configuration: +Além das opções para ganchos de plugins, o comando ``plugin load`` possui as seguintes opções +para controlar o carregamento de plugins: -Configuração do Plugin -====================== +- ``--only-debug`` Carrega o plugin somente quando o modo de depuração estiver habilitado. +- ``--only-cli`` Carrega o plugin somente para CLI. +- ``--optional`` Não gera erro se o plugin não estiver disponível. -Os métodos ``load()`` e ``loadAll()`` podem ajudar na configuração do plugin -E roteamento. Talvez você queira carregar todos os plugins automaticamente enquanto especifica -Rotas personalizadas e arquivos bootstrap para determinados plugins:: +Carregando plugins através de ``Application::bootstrap()`` +========================================================== - // No config/bootstrap.php, - // ou in Application::bootstrap() +Além do array de configuração em ``config/plugins.php``, os plugins também podem ser +carregados no método ``bootstrap()`` do seu aplicativo:: - // Usando loadAll() - Plugin::loadAll([ - 'Blog' => ['routes' => true], - 'ContactManager' => ['bootstrap' => true], - 'WebmasterTools' => ['bootstrap' => true, 'routes' => true], - ]); + // In src/Application.php + use Cake\Http\BaseApplication; + use ContactManager\ContactManagerPlugin; -Ou você pode carregar os plugins individualmente:: + class Application extends BaseApplication + { + public function bootstrap() + { + parent::bootstrap(); - // Carregando apenas o blog e inclui rotas - Plugin::load('Blog', ['routes' => true]); + // Carregue o plugin do contact manager pelo nome da classe + $this->addPlugin(ContactManagerPlugin::class); - // Inclua o arquivo configuration/initializer do bootstrap. - Plugin::load('ContactManager', ['bootstrap' => true]); + // Carregar um plugin com um namespace de fornecedor por 'short name' com opções + $this->addPlugin('AcmeCorp/ContactManager', ['console' => false]); -Com qualquer uma das abordagens, você não precisa mais manualmente ``include()`` ou -``require()`` configuração de um plugin ou arquivo de rotas - acontece -Automaticamente no momento e no lugar certos. + // Carregue uma dependência de desenvolvimento que não existirá em compilações de produção. + $this->addOptionalPlugin('AcmeCorp/ContactManager'); + } + } -Você pode especificar um conjunto de padrões para ``loadAll()`` que irá -aplicar a cada plugin que não tenha uma configuração mais específica. +Você pode configurar hooks com opções de array ou com os métodos fornecidos pelo plugin +classes:: -O seguinte exemplo irá carregar o arquivo bootstrap de todos os plugins e -além disso, as rotas do Blog Plugin:: + // In Application::bootstrap() + use ContactManager\ContactManagerPlugin; - Plugin::loadAll([ - ['bootstrap' => true], - 'Blog' => ['routes' => true] - ]); + // Use disable/enable para configurar os hooks. + $plugin = new ContactManagerPlugin(); -Tenha em atenção que todos os arquivos especificados deveriam existir na configuração -o(s) plugin(s) ou PHP dará avisos para cada arquivo que não pode carregar. Você pode evitar -potenciais avisos usando a opção ``ignoreMissing``:: + $plugin->disable('bootstrap'); + $plugin->enable('routes'); + $this->addPlugin($plugin); - Plugin::loadAll([ - ['ignoreMissing' => true, 'bootstrap' => true], - 'Blog' => ['routes' => true] - ]); +As classes de plugin também conhecem seus nomes e informações de caminho:: -Ao carregar os plugins, o nome do plugin usado deve corresponder ao namespace. Para -por exemplo, se você tiver um plugin com o namespace de nível superior ``Users`` você carregaria -Usando:: + $plugin = new ContactManagerPlugin(); - Plugin::load('User'); + // Obtenha o nome do plugin. + $name = $plugin->getName(); -Se você preferir ter seu nome vendor como nível superior e ter um espaço para nome como -``AcmeCorp/Users``, então você carregaria o plugin como:: + // Caminho para raiz do plugin e outros caminhos. + $path = $plugin->getPath(); + $path = $plugin->getConfigPath(); + $path = $plugin->getClassPath(); - Plugin::load('AcmeCorp/Users'); +Usando Plugin Classes +===================== -Isso garantirá que os nomes das classes sejam resolvidos corretamente ao usar -:term:`sintaxe plugin`. +Você pode referenciar controllers, models, components, behaviors e +helpers de um plugin prefixando o nome do plugin. -A maioria dos plugins indicará o procedimento adequado para configurá-los e configurar -até o banco de dados em sua documentação. Alguns plugins exigirão mais configuração -do que outros. +Por exemplo, digamos que você queira usar o +``ContactInfoHelper`` do plugin ContactManager para gerar informações de contato formatadas em +uma de suas visualizações. No seu controller, usar ``addHelper()`` +poderia ter a seguinte aparência:: -Usando Plugins -============== + $this->viewBuilder()->addHelper('ContactManager.ContactInfo'); -Você pode fazer referência aos controllers, models, components, -behaviors, e helpers, prefixando o nome do plugin antes +.. note:: + Este nome de classe separado por pontos é chamado de :term:`sintaxe plugin`. -Por exemplo, vamos supor que você queria usar o plugin do ContactManager -ContactInfoHelper para produzir algumas informações de contato legítimas em -uma das suas opiniões. No seu controller, o ``$helpers`` array -poderia ficar assim:: +Você poderá então acessar o ``ContactInfoHelper`` como qualquer outro auxiliar +em sua visualização, como:: - public $helpers = ['ContactManager.ContactInfo']; + echo $this->ContactInfo->address($contact); -.. note:: - Esse nome de classe separado por pontos é denominado :term:`sintaxe plugin`. +Os plugins podem usar os models, components, behaviors and helpers fornecidos +pelo aplicativo ou outros plugins, se necessário:: -Você poderia então acessar o ``ContactInfoHelper`` como -qualquer outro helper em sua view, como:: + // Use um componente de applicação + $this->loadComponent('AppFlash'); - echo $this->ContactInfo->address($contact); + // Use o comportamento de outro plugin + $this->addBehavior('OtherPlugin.AuditLog'); .. _plugin-create-your-own: -Criando seus próprios complementos -================================== +Criando seus Próprios Plugins +============================= -Apenas como um exemplo, vamos começar a criar o ContactManager -plugin referenciado acima. Para começar, vamos configurar o nosso plugin -estrutura de diretório básico. Deve ser assim:: +Como exemplo prático, vamos começar a criar o plugin ContactManager +referenciado acima. Para começar, vamos configurar a +estrutura básica de diretórios do nosso plugin. Deve ficar assim:: /src /plugins /ContactManager /config /src + /ContactManagerPlugin.php /Controller /Component /Model @@ -230,109 +243,181 @@ estrutura de diretório básico. Deve ser assim:: /Behavior /View /Helper - /Template - /Layout + /templates + /layout /tests /TestCase /Fixture /webroot Observe o nome da pasta do plugin, '**ContactManager**'. É importante -Que esta pasta tem o mesmo nome que o plugin. +que esta pasta tenha o mesmo nome do plugin. -Dentro da pasta do plugin, você notará que se parece muito com um aplicativo -CakePHP, e é basicamente isso. Você não precisa incluir qualquer uma das pastas -que você não está usando, ou seja, pode remover o que não for usar. -Alguns plugins podem apenas define um Component e um Behavior, e nesse -caso eles podem completamente omitir o diretório 'Template'. +Dentro da pasta do plugin, você notará que ela se parece muito com um aplicativo CakePHP, +e é basicamente isso que ele é. Só que em vez de ``Application.php``, +você tem um ``ContactManagerPlugin.php``. Você não precisa +incluir nenhuma das pastas que não estiver usando. Alguns plugins podem +definir apenas um Componente e um Comportamento e, nesse caso, podem +omitir completamente o diretório 'templates'. -Um plugin também pode ter basicamente qualquer um dos outros diretórios de seu -aplicativo, como Config, Console, webroot, etc. +Um plugin também pode ter basicamente qualquer um dos outros diretórios que seu +aplicativo pode ter, como Config, Console, webroot, etc. -Criando um plugin usando bake +Criando um Plugin Usando Bake ----------------------------- -O processo de criação de plugins pode ser bastante simplificado usando o bake -shell. +O processo de criação de plugins pode ser bastante simplificado usando o bake. -.. note:: - Use sempre o bake para gerar código, isso evitará muitas dores de cabeça. - -Para criar um plugin com o bake, use o seguinte comando: +Para criar um plugin, use o seguinte comando: .. code-block:: console bin/cake bake plugin ContactManager -Agora você pode user o bake com as mesmas convenções que se aplicam ao resto -do seu aplicativo. Por exemplo - baking controllers: +O Bake pode ser usado para criar classes no seu plugin. Por exemplo, para gerar +um controller de plugin, você pode executar: .. code-block:: console bin/cake bake controller --plugin ContactManager Contacts Consulte o capítulo -:doc:`/bake/usage` se você -tiver problemas para usar a linha de comando. Certifique-se de voltar a gerar o seu -autoloader uma vez que você criou seu plugin: +:doc:`/bake/usage` se tiver +algum problema ao usar a linha de comando. Certifique-se de gerar novamente o seu +carregador automático após criar o plugin: .. code-block:: console - $ php composer.phar dumpautoload + php composer.phar dumpautoload -.. _plugin-routes: +.. _plugin-objects: -Rotas para Plugin +Classes do Plugin ================= +As classes de plugin permitem que o autor defina a lógica de configuração, defina hooks +padrão, rotas de carregamento, middleware e comandos de console. As classes de plugin estão em +**src/{PluginName}Plugin.php**. Para o nosso plugin ContactManager, +nossa classe de plugin poderia ter a seguinte aparência:: + + 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 + { + // Coloque o middleware aqui. + $middleware = parent::middleware($middleware); + + return $middleware; + } + + /** + * @inheritDoc + */ + public function console(CommandCollection $commands): CommandCollection + { + // Coloque os comandos de console aqui. + $commands = parent::console($commands); + + return $commands; + } + + /** + * @inheritDoc + */ + public function bootstrap(PluginApplicationInterface $app): void + { + // Adicionar constantes, carregar padrões de configuração. + // Por padrão, carregará `config/bootstrap.php` no plugin. + parent::bootstrap($app); + } + + /** + * @inheritDoc + */ + public function routes(RouteBuilder $routes): void + { + // Adicionar rotas. + // Por padrão, carregará `config/routes.php` no plugin. + parent::routes($routes); + } + + /** + * Registrar serviços de contêiner de aplicativos. + * + * @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 + { + // Adicione seus serviços aqui. + } + } + +.. _plugin-routes: + +Rotas do Plugin +=============== + Os plugins podem fornecer arquivos de rotas contendo suas rotas. Cada plugin pode -conter um arquivo **config/routes.php**. Este arquivo de rotas pode ser carregado quando o -complemento é adicionado ou no arquivo de rotas do aplicativo. Para criar as -rotas de plugins do ContactManager, coloque o seguinte +conter um arquivo **config/routes.php**. Este arquivo de rotas pode ser carregado quando o plugin +é adicionado ou no arquivo de rotas do aplicativo. Para criar as rotas do plugin +ContactManager, insira o seguinte em **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']); + $routes->get('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'view']); + $routes->put('/contacts/{id}', ['controller' => 'Contacts', 'action' => 'update']); } ); -O código acima irá conectar as rotas padrão para o seu plugin. Você pode personalizar isso -no arquivo com rotas mais específicas mais tarde:: - - Plugin::load('ContactManager', ['routes' => true]); +O exemplo acima conectará as rotas padrão do seu plugin. Você pode personalizar este +arquivo com rotas mais específicas posteriormente. -Você também pode carregar rotas de plugins na lista de rotas do seu aplicativo. Fazendo isso -fornece mais controle sobre como as rotas do plugin são carregadas e permite que você envolva +Você também pode carregar rotas de plugin na lista de rotas do seu aplicativo. Isso +fornece mais controle sobre como as rotas de plugin são carregadas e permite que você envolva as rotas de plugin em escopos ou prefixos adicionais:: - Router::scope('/', function ($routes) { + $routes->scope('/', function ($routes) { // Connect other routes. $routes->scope('/backend', function ($routes) { $routes->loadPlugin('ContactManager'); }); }); -O código acima resultaria em URLs como ``/backend/contact_manager/contacts``. +A URL acima resultaria em algo como ``/backend/contact-manager/contacts``. -Plugin Controllers -================== +Controllers do Plugin +===================== -Os Controllers para o nosso plug-in do ContactManager serão armazenados em -**plugins/ContactManager/src/Controller/**. Como a principal coisa que vamos -estar fazendo gerenciar contatos, precisaremos de um ContactsController para +Os controllers para o nosso plugin ContactManager serão armazenados em +**plugins/ContactManager/src/Controller/**. Como a principal tarefa que faremos +é gerenciar contatos, precisaremos de um ContactsController para este plugin. -Então, colocamos nosso new ContactsController em -**plugins/ContactManager/src/Controller** e parece ser assim:: +Então, colocamos nosso novo ContactsController em +**plugins/ContactManager/src/Controller** e ele ficará assim:: // plugins/ContactManager/src/Controller/ContactsController.php namespace ContactManager\Controller; @@ -341,14 +426,13 @@ Então, colocamos nosso new ContactsController em class ContactsController extends AppController { - public function index() { //... } } -Também faça o ``AppController`` se você não possuir um já:: +Crie também o ``AppController`` se você ainda não tiver um:: // plugins/ContactManager/src/Controller/AppController.php namespace ContactManager\Controller; @@ -359,43 +443,39 @@ Também faça o ``AppController`` se você não possuir um já:: { } -Um ``AppController`` do plugin pode manter a lógica do controller comum a todos os controllers -em um plugin, mas não é necessário se você não quiser usar um. +O ``AppController`` de um plugin pode conter a lógica do controller comum a todos os controllers +de um plugin, mas não é necessário se você não quiser usar um. -Se você deseja acessar o que temos chegado até agora, visite -``/contact-manager/contacts``. Você deve obter um erro "Missing Model" -porque ainda não temos um model de Contact definido. +Se quiser acessar o que fizemos até agora, visite +``/contact-manager/contacts``. Você deverá receber um erro "Modelo Ausente" +porque ainda não temos um modelo de Contato definido. -Se o seu aplicativo incluir o roteamento padrão do CakePHP, você será -capaz de acessar seus controllers de plugins usando URLs como:: +Se sua aplicação incluir o roteamento padrão fornecido pelo CakePHP, você poderá +acessar os controllers do seu plugin usando URLs como:: // Acesse a rota de índice de um controller de plugin. /contact-manager/contacts - // Qualquer ação em um controller de plug-in. + // Qualquer ação em um controller de plugin. /contact-manager/contacts/view/1 -Se o seu aplicativo definir prefixos de roteamento, o roteamento padrão do CakePHP -também conecte rotas que usam o seguinte padrão:: +Se sua aplicação definir prefixos de roteamento, o roteamento padrão do CakePHP +também conectará rotas que usam o seguinte padrão:: - /:prefix/:plugin/:controller - /:prefix/:plugin/:controller/:action + /{prefix}/{plugin}/{controller} + /{prefix}/{plugin}/{controller}/{action} -Consulte a seção em :ref:`plugin-configuration` para obter informações sobre como carregar -qrquivos de rota específicos do plugin. - -Para os plugins que você não criou com bake, você também precisará editar o -**composer.json** para adicionar seu plugin às classes de autoload, isso pode ser -feito conforme a documentação :ref:`autoloading-plugin-classes`. +Consulte a seção :ref:`plugin-configuration` para obter informações sobre como carregar +arquivos de rota específicos do plugin. .. _plugin-models: -Plugin Models -============= +Modelos do Plugin +================= -Os models para o plugin são armazenados em **plugins/ContactManager/src/Model**. -Nós já definimos um ContactsController para este plugin, então vamos -criar a tabela e a entidade para esse controlador:: +Os modelos para o plugin são armazenados em **plugins/ContactManager/src/Model**. +Já definimos um ContactsController para este plugin, então vamos +criar a tabela e a entidade para esse controller:: // plugins/ContactManager/src/Model/Entity/Contact.php: namespace ContactManager\Model\Entity; @@ -415,9 +495,9 @@ criar a tabela e a entidade para esse controlador:: { } -Se você precisa fazer referência a um modelo no seu plugin ao criar associações -ou definindo classes de entidade, você precisa incluir o nome do plugin com a class -name, separado com um ponto. Por exemplo:: +Se você precisar referenciar um modelo dentro do seu plugin ao construir associações +ou definir classes de entidade, você precisa incluir o nome do plugin com o nome da classe +separados por um ponto. Por exemplo:: // plugins/ContactManager/src/Model/Table/ContactsTable.php: namespace ContactManager\Model\Table; @@ -426,14 +506,14 @@ name, separado com um ponto. Por exemplo:: class ContactsTable extends Table { - public function initialize(array $config) + public function initialize(array $config): void { $this->hasMany('ContactManager.AltName'); } } -Se você preferir que as chaves da array para a associação não tenham o prefixo plugin -sobre eles, use a sintaxe alternativa:: +Se você preferir que as chaves do array para a associação não tenham o prefixo +do plugin, use a sintaxe alternativa:: // plugins/ContactManager/src/Model/Table/ContactsTable.php: namespace ContactManager\Model\Table; @@ -442,7 +522,7 @@ sobre eles, use a sintaxe alternativa:: class ContactsTable extends Table { - public function initialize(array $config) + public function initialize(array $config): void { $this->hasMany('AltName', [ 'className' => 'ContactManager.AltName', @@ -450,70 +530,96 @@ sobre eles, use a sintaxe alternativa:: } } -Você pode usar ``TableRegistry`` para carregar suas tabelas de plugins usando o familiar +Você pode usar ``Cake\ORM\Locator\LocatorAwareTrait`` para carregar suas tabelas de plugins usando a conhecida :term:`sintaxe plugin`:: - use Cake\ORM\TableRegistry; + // Os controladores já usam LocatorAwareTrait, então você não precisa disso. + use Cake\ORM\Locator\LocatorAwareTrait; - // Prior to 3.6 use TableRegistry::get('ContactManager.Contacts') - $contacts = TableRegistry::getTableLocator()->get('ContactManager.Contacts'); + $contacts = $this->fetchTable('ContactManager.Contacts'); -Alternativamente, a partir de um contexto de controller, você pode usar:: +Templates do Plugin +=================== - $this->loadModel('ContactsMangager.Contacts'); +As visualizações se comportam exatamente como em aplicativos normais. Basta colocá-las na +pasta correta dentro da pasta ``plugins/[NomeDoPlugin]/templates/``. Para o nosso +plugin ContactManager, precisaremos de uma visualização para a nossa ação ``ContactsController::index()``, +então vamos incluí-la também:: -Plugin Views -============ + //plugins/ContactManager/templates/Contacts/index.php: +

Contatos

+

A seguir, uma lista classificável dos seus contatos

+ -As views se comportam exatamente como ocorrem em aplicações normais. Basta colocá-los na -pasta ``plugins/[PluginName]/templates/``. Para nós -o plugin ContactManager, precisamos de uma view para o nosso ``ContactsController::index()`` -action, então incluamos isso também:: +Os plugins podem fornecer seus próprios layouts. Para adicionar layouts de plugin, +coloque seus arquivos de template dentro de ``plugins/[NomeDoPlugin]/templates/layout``. +Para usar um layout de plugin no seu controller você pode fazer o seguinte:: - // plugins/ContactManager/templates/Contacts/index.php: -

Contacts

-

Following is a sortable list of your contacts

- + $this->viewBuilder()->setLayout('ContactManager.admin'); -Os plugins podem fornecer seus próprios layouts. Para adicionar layouts em plugins, coloque seus arquivos de template dentro -``plugins/[PluginName]/templates/layout``. Para usar um layout de plug-in em seu controller -você pode fazer o seguinte:: +Se o prefixo do plugin for omitido, o arquivo de layout/visualização será localizado normalmente. - public $layout = 'ContactManager.admin'; +Elementos do Plugin +------------------- -Se o prefixo do plugin for omitido, o arquivo layout/view será localizado normalmente. +Para renderizar um elemento de um plugin, use o :term:`sintaxe plugin` para referenciar +um plugin. Você não precisa usar a sintaxe plugin para elementos no plugin ativo no momento. -.. note:: +Se o elemento não existir no plugin, ele procurará na pasta principal do APP:: + + echo $this->element('Contacts.helpbox'); + +Se a sua visualização fizer parte de um plugin, você pode omitir o nome do plugin. Por exemplo, +se você estiver no ``ContactsController`` do plugin Contacts, o seguinte:: + + echo $this->element('helpbox'); + // e + echo $this->element('Contacts.helpbox'); + +são equivalentes e resultarão na renderização do mesmo elemento. - Para obter informações sobre como usar elementos de um plugin, procure - :ref:`view-elements` +Para elementos dentro de uma subpasta de um plugin +(por exemplo, **plugins/Contacts/Template/element/sidebar/helpbox.php**), use o +seguinte:: -Substituindo Templates de plugins do na sua aplicação ------------------------------------------------------ + echo $this->element('Contacts.sidebar/helpbox'); -Você pode substituir todas as view do plugin do seu aplicativo usando caminhos especiais. E se -você tem um plugin chamado 'ContactManager', você pode substituir os arquivos do template do -plugin com lógica de visualização específica da aplicação criando arquivos usando o seguinte -template **templates/plugin/[Plugin]/[Controller]/[view].php**. Para o -controller Contacts você pode fazer o seguinte arquivo:: +.. note:: + Veja :ref:`view-elements` para mais informações sobre renderização de elementos. + +Substituindo Templates de Plugin de Dentro do seu Aplicativo +------------------------------------------------------------ + +Você pode sobrescrever qualquer visualização de plugin de dentro do seu aplicativo usando caminhos especiais. Se +você tiver um plugin chamado "ContactManager", poderá sobrescrever os arquivos de template do +plugin com a lógica de visualização específica do aplicativo, criando arquivos usando o +seguinte template: **templates/plugin/[Plugin]/[Controller]/[view].php**. Para o +controller de contatos, você pode criar o seguinte arquivo:: templates/plugin/ContactManager/Contacts/index.php -Criar este arquivo permitiria que você substituir +A criação deste arquivo permitirá que você substitua **plugins/ContactManager/templates/Contacts/index.php**. -Se o seu plugin estiver em uma dependência no composer (ou seja, 'TheVendor/ThePlugin'), o -caminho para da view 'index' do controller personalizado será:: +Para substituir elementos do plugin, crie um elemento com o mesmo nome em:: + + templates/plugin/ContactManager/element/helpbox.php + +Este arquivo substituirá +**plugins/ContactManager/templates/element/helpbox.ctp**. + +Se o seu plugin estiver em uma dependência do composer (por exemplo, 'Empresa/ContactManager'), o +caminho para a visualização 'index' do controller de contatos será:: templates/plugin/TheVendor/ThePlugin/Custom/index.php -Criar este arquivo permitiria que você substituir +A criação deste arquivo permitirá que você sobrescreva **vendor/thevendor/theplugin/templates/Custom/index.php**. -Se o plugin implementar um prefixo de roteamento, você deve incluir o prefixo de roteamento em seu -O template para substitui. +Se o plugin implementar um prefixo de roteamento, você deverá incluí-lo +nas substituições de modelo do seu aplicativo. Por exemplo, se o plugin "ContactManager" -Se o plugin 'Contact Manager' implementou um prefixo 'admin', o caminho principal seria:: +implementou um prefixo "Admin", o caminho de sobreposição seria:: templates/plugin/ContactManager/Admin/ContactManager/index.php @@ -522,8 +628,8 @@ Se o plugin 'Contact Manager' implementou um prefixo 'admin', o caminho principa Plugin Assets ============= -Os recursos da web de um plugin (mas não arquivos PHP) podem ser atendidos através do plugin no -diretório ``webroot``, assim como os assets da aplicação principal:: +Os recursos da web de um plugin (mas não os arquivos PHP) podem ser servidos por meio do diretório +``webroot`` do plugin, assim como os recursos do aplicativo principal:: /plugins/ContactManager/webroot/ css/ @@ -532,54 +638,53 @@ diretório ``webroot``, assim como os assets da aplicação principal:: flash/ pdf/ -Você pode colocar qualquer tipo de arquivo em qualquer no diretório webroot. +Você pode colocar qualquer tipo de arquivo em qualquer diretório, como um webroot comum. .. warning:: - Manipulação de assets estáticos (como imagens, JavaScript e arquivos CSS) - Através do Dispatcher é muito ineficiente. Ver :ref:`symlink-assets` - Para maiores informações. + Lidar com ativos estáticos (como imagens, arquivos JavaScript e CSS) + por meio do Dispatcher é muito ineficiente. Consulte :ref:`symlink-assets` + para mais informações. -Linking to Assets in Plugins +Linkando os Assets no Plugin ---------------------------- -Você pode usar o :term:`sintaxe plugin` ao vincular aos recursos do plugin -usando o :php:class:`~Cake\\View\\Helper\\HtmlHelper` script, image ou css -methods:: +Você pode usar o plugin :term:`sintaxe plugin` ao vincular aos recursos do plugin usando os +métodos de script, imagem ou css do :php:class:`~Cake\\View\\Helper\\HtmlHelper`:: - // Gera a URL /contact_manager/css/styles.css + // Gera uma URL de /contact_manager/css/styles.css echo $this->Html->css('ContactManager.styles'); - // Gera a URL /contact_manager/js/widget.js + // Gera uma URL de /contact_manager/js/widget.js echo $this->Html->script('ContactManager.widget'); - // Gera a URL /contact_manager/img/logo.jpg + // Gera uma URL de /contact_manager/img/logo.jpg echo $this->Html->image('ContactManager.logo'); -Os recursos do plugin são servidos usando o filtro ``AssetFilter`` dispatcher por padrão. -Isso só é recomendado para o desenvolvimento. Na produção, você deve -:ref:`symlink do plugin symlink ` para melhorar o desempenho. +Os recursos do plugin são servidos usando o middleware ``AssetMiddleware`` por padrão. +Isso é recomendado apenas para desenvolvimento. Em produção, você deve +:ref:`symlink plugin assets ` para melhorar o desempenho. -Se você não estiver usando os helpers, você pode /plugin_name/ para o início -da URL para um recurso dentro desse plugin para atendê-lo. Ligando para -'/contact_manager/js/some_file.js' serviria o asset +Se não estiver usando os helpers, você pode adicionar /nome-do-plugin/ ao início +da URL de um recurso dentro desse plugin para servi-lo. Vincular a +'/contact_manager/js/some_file.js' serviria o recurso **plugins/ContactManager/webroot/js/some_file.js**. -Components, Helpers and Behaviors -================================= +Componentes, Helpers e Behaviors +================================ -Um plugin pode ter Components, Helpers e Behaviors, como uma aplicação CakePHP -normal. Você pode até criar plugins que consistem apenas em Componentes, -Helpers ou Behaviors que podem ser uma ótima maneira de construir componentes reutilizáveis que -pode ser lançado em qualquer projeto. +Um plugin pode ter Componentes, Helpers e Behaviors, assim como uma aplicação CakePHP. +Você pode até criar plugins que consistem apenas em Componentes, +Helpers ou Behaviors, o que pode ser uma ótima maneira de construir componentes reutilizáveis ​​que +podem ser inseridos em qualquer projeto. -Construir esses componentes é exatamente o mesmo que construí-lo dentro de uma aplicacao -normal, sem convenção de nome especial. +Construir esses componentes é exatamente o mesmo que criá-los dentro de uma aplicação +normal, sem nenhuma convenção de nomenclatura especial. -Referir-se ao seu componente de dentro ou fora do seu plugin requer apenas -que você prefixa o nome do plugin antes do nome do componente. Por exemplo:: +Referir-se ao seu componente de dentro ou de fora do seu plugin requer apenas +que você prefixe o nome do plugin antes do nome do componente. Por exemplo:: - // Component definido no 'ContactManager' plugin + // Componente definido no plugin 'ContactManager' namespace ContactManager\Controller\Component; use Cake\Controller\Component; @@ -589,50 +694,93 @@ que você prefixa o nome do plugin antes do nome do componente. Por exemplo:: } // Dentro de seus controllers - public function initialize() + public function initialize(): void { parent::initialize(); $this->loadComponent('ContactManager.Example'); } -A mesma técnica se aplica aos Helpers e Behaviors. +A mesma técnica se aplica a Helpers e Behaviors. -Expanda seu plugin -================== +.. _plugin-commands: -Este exemplo criou um bom começo para um plugin, mas há muito -mais que você pode fazer. Como regra geral, qualquer coisa que você possa fazer com o seu -aplicativo que você pode fazer dentro de um plugin também. +Commands +======== -Vá em frente - inclua algumas bibliotecas de terceiros em 'vendor', adicione algumas -novas shells para o cake console e não se esqueça de criar os testes -então seus usuários de plugins podem testar automaticamente a funcionalidade do seu plugin! +Os plugins podem registrar seus comandos dentro do hook ``console()``. Por padrão, +todos os comandos de console no plugin são descobertos automaticamente e adicionados à +lista de comandos do aplicativo. Os comandos do plugin são prefixados com o nome do plugin. +Por exemplo, o ``UserCommand`` fornecido pelo plugin ``ContactManager`` seria +registrado como ``contact_manager.user`` e ``user``. O nome sem prefixo +só será usado por um plugin se não for usado pelo aplicativo ou +por outro plugin. -Em nosso exemplo do ContactManager, podemos criar as actions add/remove/edit/delete -no ContactsController, implementar a validação no model e implementar a funcionalidade -que se poderia esperar ao gerenciar seus contatos. -Depende de você decidir o que implementar no seu Plugins. Apenas não esqueça de compartilhar seu código com a comunidade, então -que todos possam se beneficiar de seus componentes incríveis e reutilizáveis! +Você pode personalizar os nomes dos comandos definindo cada comando no seu plugin:: -Publique seu plugin + public function console($commands) + { + // Criar comandos aninhados + $commands->add('bake model', ModelCommand::class); + $commands->add('bake controller', ControllerCommand::class); + + return $commands; + } + + +Testando seu Plugin =================== -Certifique-se de adicionar o seu plug-in para `Plugins.cakephp.org `_. Desta forma, outras pessoas podem -Use-o como dependência do compositor. -Você também pode propor seu plugin para o -Lista de `awesome-cakephp list `_. +Se você estiver testando controllers ou gerando URLs, certifique-se de que seu +plugin conecte as rotas ``tests/bootstrap.php``. + +Para mais informações, consulte a página :doc:`testando plugins `. -Escolha um nome semanticamente significativo para o nome do pacote. Isso deve ser ideal -prefixado com a dependência, neste caso "cakephp" como o framework. -O nome do vendor geralmente será seu nome de usuário do GitHub. -Não **não** use o espaço de nome CakePHP (cakephp), pois este é reservado ao CakePHP -Plugins de propriedade. +Publicando seu Plugin +===================== -A convenção é usar letras minúsculas e traços como separador. +Os plugins do CakePHP devem ser publicados no `packagist +`__. Dessa forma, outras pessoas podem usá-lo como dependência do composer. +Você também pode propor seu plugin à lista `awesome-cakephp +`_. + +Escolha um nome semanticamente significativo para o nome do pacote. Idealmente, este deve +ser prefixado com a dependência, neste caso "cakephp" como o framework. +O nome do fornecedor geralmente será seu nome de usuário do GitHub. +**Não** use o namespace CakePHP (cakephp), pois ele é reservado para plugins +de propriedade do CakePHP. A convenção é usar letras minúsculas e hífens como separadores. + +Portanto, se você criou um plugin "Logging" com sua conta do GitHub "FooBar", um bom +nome seria `foo-bar/cakephp-logging`. +E o plugin "Localized", de propriedade do CakePHP, pode ser encontrado em `cakephp/localized` +respectivamente. + +.. index:: vendor/cakephp-plugins.php + +Arquivo de Mapa de Plugin +========================= + +Ao instalar plugins via Composer, você pode notar que +**vendor/cakephp-plugins.php** é criado. Este arquivo de configuração contém +um mapa de nomes de plugins e seus caminhos no sistema de arquivos. Ele possibilita +que plugins sejam instalados no diretório padrão do vendor, que está fora +dos caminhos de busca normais. A classe ``Plugin`` usará este arquivo para localizar +plugins quando eles forem carregados com ``addPlugin()``. Geralmente, +você não precisará editar este arquivo manualmente, pois o Composer e o pacote ``plugin-installer`` +o gerenciarão para você. + + +Gerencie Seus Plugins Usando o Mixer +==================================== + +Outra maneira de descobrir e gerenciar plugins em sua aplicação CakePHP é o +`Mixer `_. É um plugin do CakePHP que ajuda +você a instalar plugins do Packagist. Ele também ajuda a gerenciar seus plugins +existentes. + +.. note:: -Então, se você criou um plugin "Logging" com sua conta do GitHub "FooBar", um bom nome seria `foo-bar/cakephp-logging`. -E o plugin "Localized" do CakePHP pode ser encontrado em ``cakephp/localized`` respectivamente. + IMPORTANTE: Não use isso em ambiente de produção. .. meta:: - :title lang=en: Plugins - :keywords lang=en: plugin folder,plugins,controllers,models,views,package,application,database connection,little space + :title lang=pt: Plugins + :keywords lang=pt: pasta de plugins, plugins, controllers, models, views, pacote, aplicativo, conexão de banco de dados, pouco espaço \ No newline at end of file From e7a1bf45a75d5c2b0a34a1515d282f00f7002693 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:22:09 -0300 Subject: [PATCH 25/46] [feat]: del old files --- pt/controllers/components/authentication.rst | 1082 ----------------- .../components/request-handling.rst | 274 ----- pt/controllers/components/security.rst | 253 ---- 3 files changed, 1609 deletions(-) delete mode 100644 pt/controllers/components/authentication.rst delete mode 100644 pt/controllers/components/request-handling.rst delete mode 100644 pt/controllers/components/security.rst diff --git a/pt/controllers/components/authentication.rst b/pt/controllers/components/authentication.rst deleted file mode 100644 index 2e9eb11b09..0000000000 --- a/pt/controllers/components/authentication.rst +++ /dev/null @@ -1,1082 +0,0 @@ -AuthComponent -############## - -.. php:class:: AuthComponent(ComponentCollection $collection, array $config = []) - -Identificar, autenticar e autorizar usuários é uma parte comum de -quase todos os aplicativos da web. No CakePHP, o AuthComponent fornece -uma maneira conectável de executar essas tarefas. AuthComponent permite -combinar objetos de autenticação e objetos de autorização para criar maneiras -flexíveis de identificar e verificar a autorização do usuário. - -.. deprecated:: 4.0.0 - O AuthComponent está obsoleto a partir da versão 4.0.0 e será - substituído pelos plugins `authorization `__ e `authentication `__ . - -.. _authentication-objects: - -Sugestão de Leitura Antes de Continuar -====================================== - -A configuração da autenticação requer várias etapas, incluindo a definição de uma -tabela de usuários, a criação de um modelo, controlador e visualizações, etc. - -Tudo isso é abordado passo a passo em :doc:`CMS Tutorial `. - -Se você está procurando soluções de autenticação e/ou autorização existentes para o CakePHP, -consulte a seção `Authentication and Authorization `_ da lista Awesome CakePHP. - -Autenticação -============ - -Autenticação é o processo de identificar usuários pelas credenciais fornecidas -e garantir que os usuários sejam quem eles dizem que são. Geralmente, isso é -feito através de um nome de usuário e senha, que são verificados em uma lista -conhecida de usuários. No CakePHP, existem várias maneiras internas de autenticar -usuários armazenados no seu aplicativo. - -* ``FormAuthenticate`` permite autenticar usuários com base nos dados do formulário POST. - Geralmente, este é um formulário de login no qual os usuários inserem informações. -* ``BasicAuthenticate`` permite autenticar usuários usando a autenticação HTTP básica. -* ``DigestAuthenticate`` permite autenticar usuários usando o Diges Autenticação HTTP. - -Por padrão, ``AuthComponent`` usa ``FormAuthenticate``. - -Escolhendo um Tipo de Autenticação ----------------------------------- - -Geralmente, você deseja oferecer autenticação baseada em formulário. É o mais fácil para os -usuários que usam um navegador da web. Se você estiver criando uma API ou serviço da web, -convém considerar a autenticação básica ou digerir a autenticação. As principais diferenças -entre Digest e autenticação básica estão relacionadas principalmente à maneira como as senhas -são tratadas. Na autenticação básica, o nome de usuário e a senha são transmitidos como texto -sem formatação para o servidor. Isso torna a autenticação básica inadequada para aplicativos -sem SSL, pois você acabaria expondo senhas confidenciais. A autenticação Digest usa um hash de -resumo do nome de usuário, senha e alguns outros detalhes. Isso torna a autenticação Digest mais -apropriada para aplicativos sem criptografia SSL. - -Você também pode usar sistemas de autenticação como o OpenID também; no entanto, o OpenID -não faz parte do núcleo do CakePHP. - -Configurando Manipuladores de Autenticação ------------------------------------------- - -Você configura manipuladores de autenticação usando a configuração ``authenticate``. -Você pode configurar um ou muitos manipuladores para autenticação. O uso de vários manipuladores -permite oferecer suporte a diferentes maneiras de efetuar logon nos usuários. Ao efetuar logon -nos usuários, os manipuladores de autenticação são verificados na ordem em que são declarados. -Quando um manipulador conseguir identificar o usuário, nenhum outro manipulador será verificado. -Por outro lado, você pode interromper toda a autenticação lançando uma exceção. Você precisará -capturar todas as exceções lançadas e manipulá-las conforme necessário. - -Você pode configurar manipuladores de autenticação nos métodos ``beforeFilter()`` ou -``initialize()`` do seu controlador. Você pode passar informações de configuração para -cada objeto de autenticação usando uma matriz:: - - // Configuração simples - $this->Auth->setConfig('authenticate', ['Form']); - - // Passando as configurações - $this->Auth->setConfig('authenticate', [ - 'Basic' => ['userModel' => 'Members'], - 'Form' => ['userModel' => 'Members'] - ]); - -No segundo exemplo, você notará que tivemos que declarar a chave -``userModel`` duas vezes. Para ajudá-lo a manter seu código DRY, você pode usar a -chave ``all``. Essa chave especial permite definir configurações que são passadas -para todos os objetos anexados. A chave ``all`` também é exposta como ``AuthComponent::ALL``:: - - // Passando configurações usando 'all' - $this->Auth->setConfig('authenticate', [ - AuthComponent::ALL => ['userModel' => 'Members'], - 'Basic', - 'Form' - ]); - -No exemplo acima, ``Form`` e ``Basic`` obterão as configurações -definido para a chave 'all'. Quaisquer configurações passadas para -um objeto de autenticação específico substituirão a chave correspondente -na chave 'all'. Os objetos de autenticação principal suportam as seguintes -chaves de configuração. - -- ``fields`` Os campos a serem usados para identificar um usuário. Você pode usar as chaves - ``username`` e ``password`` para especificar seus campos de nome de usuário e senha, respectivamente. -- ``userModel`` O nome do modelo da tabela users; o padrão é Users. -- ``finder`` O método finder a ser usado para buscar um registro do usuário. O padrão é 'all'. -- ``passwordHasher`` Classe de hasher de senha; O padrão é ``Default``. - -Para configurar campos diferentes para o usuário no seu método ``initialize()``:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Form' => [ - 'fields' => ['username' => 'email', 'password' => 'passwd'] - ] - ] - ]); - } - -Não coloque outras chaves de configuração ``Auth``, como ``authError``, ``loginAction``, etc., -dentro do elemento ``authenticate`` ou ``Form``. Eles devem estar no mesmo nível da chave de -autenticação. A configuração acima com outro exemplo de configuração para autenticação deve -se parecer com:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'loginAction' => [ - 'controller' => 'Users', - 'action' => 'login', - 'plugin' => 'Users' - ], - 'authError' => 'Did you really think you are allowed to see that?', - 'authenticate' => [ - 'Form' => [ - 'fields' => ['username' => 'email'] - ] - ], - 'storage' => 'Session' - ]); - } - -Além da configuração comum, a autenticação básica suporta as seguintes chaves: - -- ``realm`` O domínio a ser autenticado. O padrão é ``env('SERVER_NAME')``. - -Além da configuração comum, a autenticação Digest suporta as seguintes -chaves: - -- ``realm`` Para autenticação de domínio. O padrão é o nome do servidor. -- ``nonce`` Um nonce usado para autenticação. O padrão é ``uniqid()``. -- ``qop`` O padrão é auth; nenhum outro valor é suportado no momento. -- ``opaque`` Uma sequência que deve ser retornada inalterada pelos clientes. O padrão é ``md5($config['realm']))``. - -.. note:: - Para encontrar o registro do usuário, o banco de dados é consultado apenas - usando o nome de usuário. A verificação da senha é feita em PHP. Isso é necessário - porque algoritmos de hash como bcrypt (que é usado por padrão) geram um novo hash a - cada vez, mesmo para a mesma string e você não pode simplesmente fazer uma comparação - simples de strings no SQL para verificar se a senha corresponde. - -Personalizando a Consulta de Localização ----------------------------------------- - -Você pode personalizar a consulta usada para buscar o registro do usuário usando a opção -``finder`` na opção de autenticação da classe:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Form' => [ - 'finder' => 'auth' - ] - ], - ]); - } - -Isso exigirá que seu ``UsersTable`` tenha o método localizador ``findAuth()``. -No exemplo mostrado abaixo, a consulta é modificada para buscar apenas os campos -obrigatórios e adicionar uma condição. Você deve garantir que você selecione os -campos necessários para autenticar um usuário, como ``username`` e ``password``:: - - public function findAuth(\Cake\ORM\Query $query, array $options) - { - $query - ->select(['id', 'username', 'password']) - ->where(['Users.active' => 1]); - - return $query; - } - -Identificando Usuários e Efetuando Login ----------------------------------------- - -.. php:method:: identify() - -Você precisa chamar manualmente ``$this->Auth->identity()`` para -identificar o usuário usando as credenciais fornecidas na solicitação. -Em seguida, use ``$this->Auth->setUser()`` para conectar o usuário, -ou seja, salve as informações do usuário na sessão. - -Ao autenticar usuários, os objetos de autenticação anexados são verificados -na ordem em que estão. Depois que um dos objetos pode identificar o -usuário, nenhum outro objeto é verificado. Uma função de login como exemplo para -trabalhar com um formulário de login pode se parecer com:: - - public function login() - { - if ($this->request->is('post')) { - $user = $this->Auth->identify(); - if ($user) { - $this->Auth->setUser($user); - - return $this->redirect($this->Auth->redirectUrl()); - } else { - $this->Flash->error(__('Username or password is incorrect')); - } - } - } - -O código acima tentará primeiro identificar um usuário usando os dados do POST. -Se for bem-sucedido, definimos as informações do usuário para a sessão, para que -elas persistam nas solicitações e, em seguida, redirecionamos para a última página -que eles estavam visitando ou para uma URL especificada na configuração ``loginRedirect``. -Se o logon não for bem-sucedido, uma mensagem flash será definida. - -.. warning:: - - ``$this->Auth->setUser($data)`` registrará o usuário com todos os dados - passados para o método. Na verdade, ele não verifica as credenciais em - uma classe de autenticação. - -Redirecionando Usuários após o Login ------------------------------------- - -.. php:method:: redirectUrl - -Depois de fazer o login de um usuário, você geralmente desejará redirecioná-lo -de volta para onde eles vieram. Passe um URL para definir o destino ao qual um -usuário deve ser redirecionado após o login. - -Se nenhum parâmetro for passado, a URL retornada usará as seguintes regras: - -- Retorna a URL normalizada do valor da string de consulta ``redirect``, - se estiver presente e no mesmo domínio em que o aplicativo atual estiver sendo executado. -- Se não houver um valor de string/sessão de consulta e houver uma configuração com - ``loginRedirect``, o valor ``loginRedirect`` será retornado. -- Se não houver valor de redirecionamento e nenhum ``loginRedirect``, ``/`` será retornado. - -Criando Sistemas de Autenticação sem Estado -------------------------------------------- - -Basic e Digest são esquemas de autenticação sem estado e não requerem um POST -ou um formulário inicial. Se você estiver usando apenas autenticadores basic/digest, -não precisará de uma ação de login no seu controlador. A autenticação sem estado -verificará novamente as credenciais do usuário em cada solicitação, isso cria uma -pequena quantidade de sobrecarga adicional, mas permite que os clientes efetuem login -sem usar cookies e torna o AuthComponent mais adequado para a criação de APIs. - -Para autenticadores sem estado, a configuração `` storage`` deve ser definida como -``Memory`` para que o AuthComponent não use uma sessão para armazenar o registro do -usuário. Você também pode querer configurar config ``unauthorizedRedirect`` para -``false``, para que AuthComponent gere uma ``ForbiddenException`` em vez do comportamento -padrão de redirecionar para o referenciador. - -A opção ``unauthorizedRedirect`` se aplica apenas a usuários autenticados. Quando um usuário -ainda não está autenticado e você não deseja que ele seja redirecionado, será necessário -carregar um ou mais autenticadores sem estado, como ``Basic`` ou ``Digest``. - -Objetos de autenticação podem implementar um método ``getUser()`` que pode ser usado para -oferecer suporte a sistemas de login de usuário que não dependem de cookies. Um método -getUser típico examina a solicitação/ambiente e usa as informações para confirmar a -identidade do usuário. A autenticação HTTP Basic, por exemplo, usa ``$_SERVER['PHP_AUTH_USER']`` -e ``$_SERVER['PHP_AUTH_PW']`` para os campos de nome de usuário e senha. - -.. note:: - - Caso a autenticação não funcione como o esperado, verifique se as consultas são executadas - (consulte ``BaseAuthenticate::_query($username)``). Caso nenhuma consulta seja executada, - verifique se ``$_SERVER['PHP_AUTH_USER']`` e ``$_SERVER['PHP_AUTH_PW']`` são preenchidos - pelo servidor web. Se você estiver usando o Apache com FastCGI-PHP, poderá ser necessário - adicionar esta linha ao seu arquivo **.htaccess** no webroot:: - - RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] - -Em cada solicitação, esses valores, ``PHP_AUTH_USER`` e ``PHP_AUTH_PW``, são usados -para identificar novamente o usuário e garantir que ele seja o usuário válido. Assim -como no método ``authenticate()`` do objeto de autenticação, o método ``getUser()`` -deve retornar uma matriz de informações do usuário sobre o sucesso ou ``false`` em -caso de falha. :: - - public function getUser(ServerRequest $request) - { - $username = env('PHP_AUTH_USER'); - $pass = env('PHP_AUTH_PW'); - - if (empty($username) || empty($pass)) { - return false; - } - - return $this->_findUser($username, $pass); - } - -A seguir, é apresentado como você pode implementar o método getUser para -autenticação HTTP básica. O método ``_findUser()`` faz parte de ``BaseAuthenticate`` -e identifica um usuário com base em um nome de usuário e senha. - -.. _basic-authentication: - -Usando Autenticação Básica --------------------------- - -A autenticação básica permite criar uma autenticação sem estado que pode -ser usada em aplicativos de intranet ou em cenários simples da API. As -credenciais de autenticação básica serão verificadas novamente em cada solicitação. - -.. warning:: - A autenticação básica transmite credenciais em texto sem formatação. - Você deve usar HTTPS ao usar a autenticação básica. - -Para usar a autenticação básica, você precisará configurar o AuthComponent:: - - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Basic' => [ - 'fields' => ['username' => 'username', 'password' => 'api_key'], - 'userModel' => 'Users' - ], - ], - 'storage' => 'Memory', - 'unauthorizedRedirect' => false - ]); - -Aqui, usamos o nome de usuário + chave da API como nossos campos e usamos o modelo Usuários. - -Criando Chaves de API para Autenticação Básica -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Como o HTTP básico envia credenciais em texto sem formatação, não é aconselhável -que os usuários enviem sua senha de login. Em vez disso, geralmente é usada uma -chave de API. Você pode gerar esses tokens de API aleatoriamente usando bibliotecas -do CakePHP:: - - namespace App\Model\Table; - - use Cake\Auth\DefaultPasswordHasher; - use Cake\Utility\Text; - use Cake\Event\EventInterface; - use Cake\ORM\Table; - use Cake\Utility\Security; - - class UsersTable extends Table - { - public function beforeSave(EventInterface $event) - { - $entity = $event->getData('entity'); - - if ($entity->isNew()) { - $hasher = new DefaultPasswordHasher(); - - // Gera uma API 'token' - $entity->api_key_plain = Security::hash(Security::randomBytes(32), 'sha256', false); - - // Criptografe o token para que BasicAuthenticate - // possa verificá-lo durante o login. - $entity->api_key = $hasher->hash($entity->api_key_plain); - } - - return true; - } - } - -O exemplo acima gera um hash aleatório para cada usuário conforme eles são salvos. -O código acima assume que você tem duas colunas ``api_key`` - para armazenar a chave -da API hash e ``api_key_plain`` - para a versão em texto sem formatação da chave da -API, para que possamos exibi-la ao usuário posteriormente. Usar uma chave em vez de -uma senha significa que, mesmo em HTTP simples, seus usuários podem usar um token simples -em vez da senha original. Também é aconselhável incluir lógica que permita que as chaves -da API sejam regeneradas a pedido de um usuário. - -Usando Autenticação Digest --------------------------- - -A autenticação Digest oferece um modelo de segurança aprimorado em relação à -autenticação básica, pois as credenciais do usuário nunca são enviadas no cabeçalho -da solicitação. Em vez disso, um hash é enviado. - -Para usar a autenticação Digest, você precisará configurar o ``AuthComponent``:: - - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Digest' => [ - 'fields' => ['username' => 'username', 'password' => 'digest_hash'], - 'userModel' => 'Users' - ], - ], - 'storage' => 'Memory', - 'unauthorizedRedirect' => false - ]); - -Aqui, estamos usando o nome de usuário + digest_hash como nossos campos e também -usamos o modelo Users. - -Hashing de Senhas para Autenticação Digest -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Como a autenticação Digest requer um hash de senha no formato definido pelo -RFC, para hash corretamente uma senha para uso com a autenticação Digest, -você deve usar a função de hash de senha especial em ``DigestAuthenticate``. -Se você combinar a autenticação digest com outras estratégias de autenticação, -também é recomendável que você armazene a senha digest em uma coluna separada, -a partir do hash da senha normal:: - - namespace App\Model\Table; - - use Cake\Auth\DigestAuthenticate; - use Cake\Event\EventInterface; - use Cake\ORM\Table; - - class UsersTable extends Table - { - public function beforeSave(EventInterface $event) - { - $entity = $event->getData('entity'); - - // Fazendo a senha para autenticação digest - $entity->digest_hash = DigestAuthenticate::password( - $entity->username, - $entity->plain_password, - env('SERVER_NAME') - ); - - return true; - } - } - -As senhas para autenticação Digest precisam de um pouco mais de informações do -que outros hashes de senha, com base no RFC para autenticação Digest. - -.. note:: - - O terceiro parâmetro de ``DigestAuthenticate::password()`` deve - corresponder ao valor de configuração 'realm' definido quando - DigestAuthentication foi configurado em ``AuthComponent::$authenticate``. - O padrão é ``env('SCRIPT_NAME')``. Você pode usar uma string estática se - desejar hashes consistentes em vários ambientes. - -Criando Objetos de Autenticação Personalizados ----------------------------------------------- - -Como os objetos de autenticação são conectáveis, você pode criar objetos -de autenticação personalizados em seu aplicativo ou plug-in. Se, por exemplo, -você desejasse criar um objeto de autenticação OpenID. Em **src/Auth/OpenidAuthenticate.php**, -você pode colocar o seguinte:: - - namespace App\Auth; - - use Cake\Auth\BaseAuthenticate; - use Cake\Http\ServerRequest; - use Cake\Http\Response; - - class OpenidAuthenticate extends BaseAuthenticate - { - public function authenticate(ServerRequest $request, Response $response) - { - // Faça coisas para o OpenID aqui. - // Retorne uma matriz do usuário se eles puderem autenticar o usuário, - // retorne false se não. - } - } - -Os objetos de autenticação devem retornar ``false`` se não puderem identificar -o usuário e uma matriz de informações do usuário, se puderem. Não é necessário -que você estenda ``BaseAuthenticate``, apenas que seu objeto de autenticação -implemente ``Cake\Event\EventListenerInterface``. A classe ``BaseAuthenticate`` -fornece vários métodos úteis que são comumente usados. Você também pode implementar -um método ``getUser()`` se o seu objeto de autenticação precisar suportar -autenticação sem estado ou sem cookie. Consulte as seções sobre autenticação -básica e digest abaixo para obter mais informações. - -``AuthComponent`` dispara dois eventos, ``Auth.afterIdentify`` e ``Auth.logout``, -depois que um usuário é identificado e antes que o usuário seja desconectado, -respectivamente. Você pode definir funções de retorno de chamada para esses eventos -retornando uma matriz de mapeamento do método ``managedEvents()`` da sua classe de -autenticação:: - - public function implementedEvents() - { - return [ - 'Auth.afterIdentify' => 'afterIdentify', - 'Auth.logout' => 'logout' - ]; - } - -Usando Objetos de Autenticação Personalizados ---------------------------------------------- - -Depois de criar seus objetos de autenticação personalizados, você pode usá-los -incluindo-os na matriz de autenticação do ``AuthComponent``:: - - $this->Auth->setConfig('authenticate', [ - 'Openid', // app authentication object. - 'AuthBag.Openid', // plugin authentication object. - ]); - -.. note:: - Observe que, ao usar notação simples, não há palavra 'Authenticate' ao iniciar o - objeto de autenticação. Em vez disso, se você estiver usando namespace, precisará - definir o namespace completo da classe, incluindo a palavra 'Authenticate'. - -Manipulando Solicitações Não Autenticadas ------------------------------------------ - -Quando um usuário não autenticado tenta acessar uma página protegida primeiro, -o método ``unauthenticated()`` do último autenticador da cadeia é chamado. -O objeto de autenticação pode lidar com o envio de resposta ou redirecionamento -retornando um objeto de resposta para indicar que nenhuma ação adicional é necessária. -Devido a isso, é importante a ordem na qual você especifica o provedor de autenticação -na configuração ``authenticate``. - -Se o autenticador retornar nulo, ``AuthComponent`` redirecionará o usuário para a -ação de login. Se for uma solicitação AJAX e a configuração ``ajaxLogin`` for -especificada, esse elemento será renderizado, caso contrário, um código de status -HTTP 403 será retornado. - -Exibindo Mensagens Flash Relacionadas à Autenticação ----------------------------------------------------- - -Para exibir as mensagens de erro da sessão que o Auth gera, você precisa -adicionar o seguinte código ao seu layout. Adicione as duas linhas a seguir -ao arquivo **templates/layout/default.php** na seção body:: - - echo $this->Flash->render(); - -Você pode personalizar as mensagens de erro e as configurações do flash que o -``AuthComponent`` usa. Usando a configuração ``flash``, você pode configurar -os parâmetros que o ``AuthComponent`` usa para definir mensagens em flash. -As chaves disponíveis são - -- ``key`` - A chave a ser usada é padronizada como 'default'. -- ``element`` - O nome do elemento a ser usado para renderização, o padrão é null. -- ``params`` - A matriz de parâmetros adicionais a serem usados, o padrão é ``[]``. - -Além das configurações de mensagens flash, você pode personalizar outras -mensagens de erro que o ``AuthComponent`` usa. Nas configurações ``beforeFilter()`` -do seu controlador ou componente, você pode usar ``authError`` para personalizar -o erro usado quando a autorização falha:: - - $this->Auth->setConfig('authError', "Woopsie, you are not authorized to access this area."); - -Às vezes, você deseja exibir o erro de autorização somente após o usuário já estar conectado. -Você pode suprimir esta mensagem definindo seu valor como booleano ``false``. - -Nas configurações ``beforeFilter()`` ou no componente do seu controlador:: - - if (!$this->Auth->user()) { - $this->Auth->setConfig('authError', false); - } - -.. _hashing-passwords: - -Hashing de Senhas ------------------ - -Você é responsável por fazer o hash das senhas antes que elas persistam no -banco de dados, a maneira mais fácil é usar uma função setter na sua entidade -User:: - - namespace App\Model\Entity; - - use Cake\Auth\DefaultPasswordHasher; - use Cake\ORM\Entity; - - class User extends Entity - { - - // ... - - protected function _setPassword($password) - { - if (strlen($password) > 0) { - return (new DefaultPasswordHasher)->hash($password); - } - } - - // ... - } - -``AuthComponent`` é configurado por padrão para usar o ``DefaultPasswordHasher`` -ao validar credenciais do usuário, portanto, nenhuma configuração adicional é -necessária para autenticar usuários. - -``AuthComponent`` é configurado como padrão para usar o ``DefaultPasswordHasher`` -para validar credenciais do usuário, portanto, nenhuma configuração adicional é -necessária para autenticação de usuários. - -O ``DefaultPasswordHasher`` usa o algoritmo de hash bcrypt internamente, que é -uma das soluções mais fortes de hash de senha usadas no setor. Embora seja -recomendável usar essa classe de hasher de senha, pode ser que você esteja -gerenciando um banco de dados de usuários cuja senha foi usada um tipo de hash diferente. - -Criando Classes Personalizadas de Hasher de Senha -------------------------------------------------- - -Para usar um hasher de senha diferente, você precisa criar a classe em -**src/Auth/LegacyPasswordHasher.php** e implementar os métodos ``hash()`` -e ``check()``. Esta classe precisa estender a classe ``AbstractPasswordHasher``:: - - namespace App\Auth; - - use Cake\Auth\AbstractPasswordHasher; - - class LegacyPasswordHasher extends AbstractPasswordHasher - { - - public function hash($password) - { - return sha1($password); - } - - public function check($password, $hashedPassword) - { - return sha1($password) === $hashedPassword; - } - } - -Em seguida, você deve configurar o ``AuthComponent`` para usar o seu hasher de senha -customizado:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Form' => [ - 'passwordHasher' => [ - 'className' => 'Legacy', - ] - ] - ] - ]); - } - -Oferecer suporte a sistemas legados é uma boa idéia, mas é ainda melhor manter seu -banco de dados com os mais recentes avanços de segurança. A seção a seguir explica -como migrar de um algoritmo de hash para o padrão do CakePHP. - -Alterando Algoritmos de Hash ----------------------------- - -O CakePHP fornece uma maneira limpa de migrar as senhas de seus usuários de um -algoritmo para outro, isso é alcançado através da classe ``FallbackPasswordHasher``. -Supondo que você esteja migrando seu aplicativo do CakePHP 2.x que usa hashes de -senha ``sha1``, você pode configurar o ``AuthComponent`` da seguinte forma:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'authenticate' => [ - 'Form' => [ - 'passwordHasher' => [ - 'className' => 'Fallback', - 'hashers' => [ - 'Default', - 'Weak' => ['hashType' => 'sha1'] - ] - ] - ] - ] - ]); - } - -O primeiro nome que aparece na chave ``hashers`` indica qual das classes é a -preferida, mas retornará para as outras na lista se a verificação não tiver êxito. - -Ao usar o ``WeakPasswordHasher``, você precisará definir o ``Security.salt`` -para configurar o valor para garantir que as senhas sejam transformadas. - -Para atualizar as senhas de usuários antigos rapidamente, você pode alterar -a função de login de acordo:: - - public function login() - { - if ($this->request->is('post')) { - $user = $this->Auth->identify(); - if ($user) { - $this->Auth->setUser($user); - if ($this->Auth->authenticationProvider()->needsPasswordRehash()) { - $user = $this->Users->get($this->Auth->user('id')); - $user->password = $this->request->getData('password'); - $this->Users->save($user); - } - - return $this->redirect($this->Auth->redirectUrl()); - } - ... - } - } - -Como você pode ver, estamos apenas definindo a senha simples novamente, -para que a função setter na entidade faça hash na senha, como mostrado no -exemplo anterior, e salve a entidade. - -Logon Manual de Usuários ------------------------- - -.. php:method:: setUser(array $user) - -Às vezes, surge a necessidade de fazer o login manual de um usuário, como -logo após ele se registrar no seu aplicativo. Você pode fazer isso chamando -``$this->Auth->setUser()`` com os dados do usuário que deseja 'logar':: - - public function register() - { - $user = $this->Users->newEntity($this->request->getData()); - if ($this->Users->save($user)) { - $this->Auth->setUser($user->toArray()); - - return $this->redirect([ - 'controller' => 'Users', - 'action' => 'home' - ]); - } - } - -.. warning:: - - Certifique-se de adicionar manualmente o novo ID do usuário à matriz - passada para o método ``setUser()``. Caso contrário, você não terá o ID - do usuário disponível. - -Acessando o Usuário Conectado ------------------------------ - -.. php:method:: user($key = null) - -Depois que um usuário está logado, muitas vezes você precisará de algumas -informações específicas sobre o usuário atual. Você pode acessar o usuário -conectado no momento usando ``AuthComponent::user()``:: - - // De dentro de um controlador ou outro componente. - $this->Auth->user('id'); - -Se o usuário atual não estiver conectado ou a chave não existir, o valor nulo -será retornado. - -Logout de Usuários ------------------- - -.. php:method:: logout() - -Eventualmente, você desejará uma maneira rápida de autenticar alguém e redirecioná-lo -para onde ele precisa ir. Esse método também é útil se você deseja fornecer um link -'Desconectar-se' dentro da área de um membro do seu aplicativo:: - - public function logout() - { - return $this->redirect($this->Auth->logout()); - } - -É difícil realizar logoff de usuários que efetuaram logon com autenticação -Digest ou Basic para todos os clientes. A maioria dos navegadores retém -credenciais pelo período em que ainda estão abertos. Alguns clientes podem -ser forçados a sair, enviando um código de status 401. Alterar o domínio de -autenticação é outra solução que funciona para alguns clientes. - -Decidindo Quando Executar a Autenticação ----------------------------------------- - -Em alguns casos, você pode querer usar ``$this->Auth->user()`` no método -``beforeFilter()``. Isso é possível usando a chave de configuração ``checkAuthIn``. -As alterações a seguir, para o qual as verificações de autenticação -inicial devem ser feitas:: - - // Configure AuthComponent para autenticar em initialize () - $this->Auth->setConfig('checkAuthIn', 'Controller.initialize'); - -O valor padrão para ``checkAuthIn`` é ``'Controller.startup'``, mas usando a -autenticação inicial ``'Controller.initialize'`` é feita antes do método ``beforeFilter()``. - -.. _authorization-objects: - -Autorização -=========== - -Autorização é o processo de garantir que um usuário identificado/autenticado -tenha permissão para acessar os recursos que está solicitando. Se ativado, o -``AuthComponent`` pode verificar automaticamente os manipuladores de autorização -e garantir que os usuários conectados tenham permissão para acessar os recursos -que estão solicitando. Existem vários manipuladores de autorização internos e você -pode criar personalizações para o seu aplicativo ou como parte de um plug-in. - -- ``ControllerAuthorize`` Chama ``isAuthorized()`` no controlador ativo e - usa o retorno para autorizar um usuário. Geralmente, é a maneira mais - simples de autorizar usuários. - -.. note:: - - O adaptador ``ActionsAuthorize`` e ``CrudAuthorize`` disponível no CakePHP 2.x - foram agora movidos para um plugin separado `cakephp/acl `_. - -Configurando Manipuladores de Autorização ------------------------------------------ - -Você configura manipuladores de autorização usando a chave de configuração -``authorize``. Você pode configurar um ou muitos manipuladores para autorização. -O uso de vários manipuladores permite oferecer suporte a diferentes maneiras de -verificar a autorização. Quando os manipuladores de autorização são verificados, -eles serão chamados na ordem em que são declarados. Os manipuladores devem retornar -``false``, se não conseguirem verificar a autorização ou se a verificação falhar. -Os manipuladores devem retornar ``true`` se puderem verificar a autorização com -êxito. Os manipuladores serão chamados em sequência até que um passe. Se todas as -verificações falharem, o usuário será redirecionado para a página de onde veio. Além -disso, você pode interromper toda a autorização lançando uma exceção. Você precisará -capturar todas as exceções lançadas e lidar com elas. - -Você pode configurar manipuladores de autorização nos métodos ``beforeFilter()`` ou -``initialize()`` do seu controlador. Você pode passar informações de configuração -para cada objeto de autorização, usando uma matriz:: - - // Configuração básica - $this->Auth->setConfig('authorize', ['Controller']); - - // Passando configurações - $this->Auth->setConfig('authorize', [ - 'Actions' => ['actionPath' => 'controllers/'], - 'Controller' - ]); - -Assim como ``authenticate``, ``authorize``, ajuda a manter seu código DRY, -usando a chave ``all``. Essa chave especial permite definir configurações -que são passadas para todos os objetos anexados. A chave ``all`` também é -exposta como ``AuthComponent::ALL``:: - - // Passando as configurações usando 'all' - $this->Auth->setConfig('authorize', [ - AuthComponent::ALL => ['actionPath' => 'controllers/'], - 'Actions', - 'Controller' - ]); - -No exemplo acima, as ações ``Actions`` e `` Controller`` receberão as -configurações definidas para a chave 'all'. Quaisquer configurações -passadas para um objeto de autorização específico substituirão a chave -correspondente na chave 'all'. - -Se um usuário autenticado tentar acessar uma URL que ele não está autorizado -a acessar, ele será redirecionado de volta ao referenciador. Se você não desejar -esse redirecionamento (principalmente necessário ao usar o adaptador de autenticação -sem estado), defina a opção de configuração ``unauthorizedRedirect`` para ``false``. -Isso faz com que o ``AuthComponent`` gere uma ``ForbiddenException`` em vez de redirecionar. - -Criando Objetos de Autorização Personalizados ---------------------------------------------- - -Como os objetos de autorização são conectáveis, você pode criar objetos -de autorização personalizados em seu aplicativo ou plug-in. Se, por exemplo, -você desejasse criar um objeto de autorização LDAP. Em **src/Auth/LdapAuthorize.php**, -você pode colocar o seguinte:: - - namespace App\Auth; - - use Cake\Auth\BaseAuthorize; - use Cake\Http\ServerRequest; - - class LdapAuthorize extends BaseAuthorize - { - public function authorize($user, ServerRequest $request) - { - // Faça coisas para o LDAP aqui. - } - } - -Os objetos de autorização devem retornar ``false`` se o acesso do usuário -for negado ou se o objeto não puder executar uma verificação. Se o objeto -puder verificar o acesso do usuário, ``true`` deve ser retornado. Não é -necessário que você estenda ``BaseAuthorize``, apenas que seu objeto de -autorização implemente um método ``authorize()``. A classe ``BaseAuthorize`` -fornece vários métodos úteis que são comumente usados. - -Usando Objetos de Autorização Personalizados -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Depois de criar seu objeto de autorização personalizado, você pode usá-lo -incluindo-o na matriz de autorização do ``AuthComponent``:: - - $this->Auth->setConfig('authorize', [ - 'Ldap', // objeto de autorização do aplicativo. - 'AuthBag.Combo', // plugin autoriza objeto. - ]); - -Usando Sem Autorização ----------------------- - -Se você não quiser usar nenhum dos objetos de autorização internos e quiser -lidar com coisas totalmente fora de ``AuthComponent``, poderá definir -``$this->Auth->setConfig('authorize', false);``. Por padrão, ``AuthComponent`` -começa com ``authorize`` definido como ``false``. Se você não usar um esquema -de autorização, verifique você mesmo a autorização no ``beforeFilter()`` do -seu controlador ou com outro componente. - -Tornando Métodos Públicos -------------------------- - -.. php:method:: allow($actions = null) - -Muitas vezes, há ações do controlador que você deseja manter totalmente -públicas ou que não exigem que os usuários façam login. ``AuthComponent`` -é pessimista no padrão para negar acesso. Você pode marcar métodos como métodos -públicos usando ``AuthComponent::allow()``. Ao marcar ações como públicas, o -``AuthComponent`` não procurará um usuário conectado nem autorizará a verificação -de objetos:: - - // Permitir todas as ações - $this->Auth->allow(); - - // Permitir apenas a ação index. - $this->Auth->allow('index'); - - // Permitir apenas as ações de view e index. - $this->Auth->allow(['view', 'index']); - -Ao chamá-lo de vazio, você permite que todas as ações sejam públicas. Para uma única -ação, você pode fornecer o nome da ação como uma sequência. Caso contrário, use uma matriz. - -.. note:: - - Você não deve adicionar a ação "login" do seu ``UsersController`` na lista de permissões. - Fazer isso causaria problemas com o funcionamento normal de ``AuthComponent``. - -Fazendo Métodos Exigir Autorização ----------------------------------- - -.. php:method:: deny($actions = null) - -Por padrão, todas as ações requerem autorização. No entanto, depois de tornar -os métodos públicos, você deseje revogar o acesso público. -Você pode fazer isso usando ``AuthComponent::deny()``:: - - // Negar todas as ações. - $this->Auth->deny(); - - // Negar uma ação - $this->Auth->deny('add'); - - // Nega um grupo de ações. - $this->Auth->deny(['add', 'edit']); - -Ao chamá-lo de vazio, você nega todas as ações. Para um único método, -você pode fornecer o nome da ação como uma sequência. Caso contrário, use uma matriz. - -Usando ControllerAuthorize --------------------------- - -ControllerAuthorize permite manipular verificações de autorização em um -retorno de chamada do controlador. Isso é ideal quando você possui uma -autorização muito simples ou precisa usar uma combinação de modelos e -componentes para fazer sua autorização e não deseja criar um objeto de -autorização personalizado. - -O retorno de chamada é sempre chamado de ``isAuthorized()`` e deve -retornar um valor booleano para permitir ou não ao usuário acessar -recursos na solicitação. O retorno de chamada é passado ao usuário -ativo para que possa ser verificado:: - - class AppController extends Controller - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Auth', [ - 'authorize' => 'Controller', - ]); - } - - public function isAuthorized($user = null) - { - // Qualquer usuário registrado pode acessar funções públicas - if (!$this->request->getParam('prefix')) { - return true; - } - - // Somente administradores podem acessar funções administrativas - if ($this->request->getParam('prefix') === 'admin') { - return (bool)($user['role'] === 'admin'); - } - - // Negação padrão - return false; - } - } - -O retorno de chamada acima forneceria um sistema de autorização muito simples, -no qual apenas usuários com role = admin poderiam acessar ações que estavam no -prefixo do administrador. - -Opções de configuração -====================== - -Todas as configurações a seguir podem ser definidas no método ``initialize()`` do seu -controlador ou usando ``$this->Auth->setConfig()`` no seu ``beforeFilter()``: - -ajaxLogin - O nome de um elemento de exibição opcional a ser renderizado quando uma - solicitação AJAX é feita com uma sessão inválida ou expirada. -allowedActions - Ações do controlador para as quais a validação do usuário não é necessária. -authenticate - Defina como uma matriz de objetos de autenticação que você deseja - usar ao fazer logon de usuários. Existem vários objetos de autenticação - principais; veja a seção :ref:`authentication-objects`. -authError - Erro para exibir quando o usuário tenta acessar um objeto ou ação ao qual não tem acesso. - - Você pode impedir que a mensagem authError seja exibida definindo esse valor como - booleano ``false``. -authorize - Defina como uma matriz de objetos de Autorização que você deseja - usar ao autorizar usuários em cada solicitação; veja a seção :ref:`authorization-objects`. -flash - Configurações a serem usadas quando o Auth precisar enviar - uma mensagem flash com ``FlashComponent::set()``. - - As chaves disponíveis são: - - ``element`` - O elemento a ser usado; o padrão é 'default'. - - ``key`` - A chave para usar; o padrão é 'auth'. - - ``params`` - A matriz de parâmetros adicionais a serem usados; o padrão é '[]'. -loginAction - Uma URL (definida como uma sequência ou matriz) para a ação do controlador - que lida com logins. O padrão é ``/users/login``. -loginRedirect - A URL (definida como uma sequência ou matriz) para os usuários da - ação do controlador deve ser redirecionada após o login. Esse valor - será ignorado se o usuário tiver um valor ``Auth.redirect`` em sua sessão. -logoutRedirect - A ação padrão a ser redirecionada após o logout do usuário. Enquanto - ``AuthComponent`` não lida com o redirecionamento pós-logout, uma URL - de redirecionamento será retornada de :php:meth:`AuthComponent::logout()`. - O padrão é ``loginAction``. -unauthorizedRedirect - Controla a manipulação do acesso não autorizado. Por padrão, o usuário não - autorizado é redirecionado para o URL do referenciador ``loginAction`` - ou '/'. Se definido como ``false``, uma exceção ForbiddenException é - lançada em vez de redirecionar. -storage - Classe de armazenamento a ser usada para o registro persistente do usuário. - Ao usar o autenticador sem estado, defina-o como ``Memory``. O padrão é ``Session``. - Você pode passar as opções de configuração para a classe de armazenamento usando o - formato de matriz. Por ex. Para usar uma chave de sessão personalizada, - você pode definir ``storage`` como ``['className' => 'Session', 'key' => 'Auth.Admin']``. -checkAuthIn - Nome do evento no qual as verificações de autenticação iniciais devem ser - feitas. O padrão é ``Controller.startup``. Você pode configurá-lo para - ``Controller.initialize`` se você quiser que a verificação seja feita antes - que o método ``beforeFilter()`` do controlador seja executado. - -Você pode obter os valores atuais da configuração chamando ``$this->Auth->getConfig()``:: -apenas a opção de configuração:: - - $this->Auth->getConfig('loginAction'); - - $this->redirect($this->Auth->getConfig('loginAction')); - -Isso é útil se você deseja redirecionar um usuário para a rota ``login``, por exemplo. -Sem um parâmetro, a configuração completa será retornada. - -Testando Ações Protegidas por AuthComponent -=========================================== - -Veja a seção :ref:`testing-authentication` para obter dicas sobre como testar -ações do controlador protegidas por ``AuthComponent``. - -.. meta:: - :title lang=pt: Autenticação - :keywords lang=pt: manipuladores de autenticação, array php, autenticação básica, aplicativo da web, maneiras diferentes, credenciais, exceções, cakephp, logging diff --git a/pt/controllers/components/request-handling.rst b/pt/controllers/components/request-handling.rst deleted file mode 100644 index 5adebed49b..0000000000 --- a/pt/controllers/components/request-handling.rst +++ /dev/null @@ -1,274 +0,0 @@ -Request Handler (Tratamento de Requisições) -########################################### - -.. php:class:: RequestHandlerComponent(ComponentCollection $collection, array $config = []) - -O componente Request Handler é usado no CakePHP para obter informações adicionais -sobre as solicitações HTTP feitas para sua aplicação. Você pode usá-lo para ver quais -tipos de conteúdo os clientes preferem, analisar automaticamente a entrada da solicitação, -definir como os tipos de conteúdo são mapeados para exibir classes ou caminhos de modelo. - -Por padrão, o RequestHandler detectará automaticamente solicitações AJAX com base no -cabeçalho HTTP ``X-Requested-With`` que muitas bibliotecas JavaScript usam. Quando usado -em conjunto com :php:meth:`Cake\\Routing\\Router::extensions()`, o RequestHandler muda -automaticamente os arquivos de layout e modelo para aqueles que correspondem aos tipos -de mídia não HTML. Além disso, se existir um auxiliar com o mesmo nome que a extensão -solicitada, ele será adicionado à matriz Auxiliar de Controladores. Por fim, se os -dados XML/JSON forem enviados para seus controladores, eles serão analisados em -uma matriz atribuída a ``$this->request->getData()`` e, e pode ser acessado como -faria com os dados POST padrão. Para fazer uso do RequestHandler, ele deve ser incluído no -seu método ``initialize()``:: - - class WidgetsController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('RequestHandler'); - } - - // Resto do controller - } - -Obtenção de Informações de Solicitação -====================================== - -O manipulador de solicitações possui vários métodos que fornecem informações sobre o -cliente e sua solicitação. - -.. php:method:: accepts($type = null) - - $type pode ser uma string, uma matriz ou nulo. Se uma string, ``accept()`` - retornará ``true`` se o cliente aceitar o tipo de conteúdo. Se uma matriz - for especificada, ``accept()`` retorna ``true`` se qualquer um dos tipos de - conteúdo for aceito pelo cliente. Se null, retorna uma matriz dos tipos de - conteúdo que o cliente aceita. Por exemplo:: - - class ArticlesController extends AppController - { - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('RequestHandler'); - } - - public function beforeFilter(EventInterface $event) - { - if ($this->RequestHandler->accepts('html')) { - // Execute o código apenas se o cliente aceitar uma resposta - // HTML (text/html). - } elseif ($this->RequestHandler->accepts('xml')) { - // Executar código somente XML - } - if ($this->RequestHandler->accepts(['xml', 'rss', 'atom'])) { - // Executa se o cliente aceita qualquer um dos itens acima: XML, RSS - // ou Atom. - } - } - } - -Outros métodos de detecção de 'type' de solicitação incluem: - -.. php:method:: isXml() - - Retorna ``true`` se a solicitação atual aceitar XML como resposta. - -.. php:method:: isRss() - - Retorna ``true`` se a solicitação atual aceitar RSS como resposta. - -.. php:method:: isAtom() - - Retorna ``true`` se a chamada atual aceitar uma resposta Atom, caso contrário, false. - -.. php:method:: isMobile() - - Retorna ``true`` se a sequência do agente do usuário corresponder a um - navegador da Web móvel ou se o cliente aceitar conteúdo WAP. As sequências - suportadas do Mobile User Agent são: - - - Android - - AvantGo - - BlackBerry - - DoCoMo - - Fennec - - iPad - - iPhone - - iPod - - J2ME - - MIDP - - NetFront - - Nokia - - Opera Mini - - Opera Mobi - - PalmOS - - PalmSource - - portalmmm - - Plucker - - ReqwirelessWeb - - SonyEricsson - - Symbian - - UP.Browser - - webOS - - Windows CE - - Windows Phone OS - - Xiino - -.. php:method:: isWap() - - Retorna ``true`` se o cliente aceitar conteúdo WAP. - -Todos os métodos de detecção de solicitação acima podem ser usados de -maneira semelhante para filtrar a funcionalidade destinada a tipos de -conteúdo específicos. Por exemplo, ao responder a solicitações AJAX, -geralmente você deseja desativar o cache do navegador e alterar o nível -de depuração. No entanto, você deseja permitir o armazenamento em cache -para solicitações não-AJAX. O seguinte exemplo faria isso:: - - if ($this->request->is('ajax')) { - $this->response->disableCache(); - } - // Continua ação do controlador - -Decodificação Automática de Dados de Solicitação -================================================ - -Adicione um decodificador de dados de solicitação. O manipulador deve conter -um retorno de chamada e quaisquer argumentos adicionais para o retorno de -chamada. O retorno de chamada deve retornar uma matriz de dados contidos na -entrada da solicitação. Por exemplo, adicionar um manipulador de CSV pode -parecer:: - - class ArticlesController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $parser = function ($data) { - $rows = str_getcsv($data, "\n"); - foreach ($rows as &$row) { - $row = str_getcsv($row, ','); - } - - return $rows; - }; - $this->loadComponent('RequestHandler', [ - 'inputTypeMap' => [ - 'csv' => [$parser] - ] - ]); - } - } - -Você pode usar qualquer `callable `_ para a função -de manipulação. Você também pode passar argumentos adicionais para o retorno de -chamada, isso é útil para retornos de chamada como ``json_decode``:: - - $this->RequestHandler->setConfig('inputTypeMap.json', ['json_decode', true]); - -O exemplo acima tornará ``$this->request->getData()`` uma matriz dos dados de entrada JSON, -sem o ``true`` adicional, você obteria um conjunto de objetos ``stdClass``. - -.. versionchanged:: 3.6.0 - Você deve preferir usar :ref:`body-parser-middleware` em vez de - RequestHandlerComponent. - -VerificandoPreferências de Tipo de Conteúdo -=========================================== - -.. php:method:: prefers($type = null) - -Determina quais tipos de conteúdo o cliente prefere. Se nenhum parâmetro -for fornecido, o tipo de conteúdo mais provável será retornado. Se $type -for uma matriz, o primeiro tipo aceito pelo cliente será retornado. A -preferência é determinada principalmente pela extensão do arquivo analisada -pelo roteador, se houver, e por uma lista de tipos de conteúdo em ``HTTP_ACCEPT``:: - - $this->RequestHandler->prefers('json'); - -Respondendo a Solicitações -========================== - -.. php:method:: renderAs($controller, $type) - -Altere o modo de renderização de um controlador para o tipo especificado. -Também anexará o auxiliar apropriado à matriz auxiliar do controlador, se -disponível e ainda não estiver na matriz:: - - // Force o controlador a renderizar uma resposta xml. - $this->RequestHandler->renderAs($this, 'xml'); - -Este método também tentará adicionar um auxiliar que corresponda ao seu tipo de -conteúdo atual. Por exemplo, se você renderizar como ``rss``, o ``RssHelper`` -será adicionado. - -.. php:method:: respondAs($type, $options) - -Define o cabeçalho da resposta com base nos nomes do mapa do tipo de conteúdo. -Este método permite definir várias propriedades de resposta de uma só vez:: - - $this->RequestHandler->respondAs('xml', [ - // Força o download - 'attachment' => true, - 'charset' => 'UTF-8' - ]); - -.. php:method:: responseType() - -Retorna o tipo de resposta atual com o Cabeçalho do tipo de conteúdo ou nulo se -ainda não tiver sido definido. - -Aproveitando a Validação de Cache HTTP -====================================== - -O modelo de validação de cache HTTP é um dos processos usados para gateways de cache, -também conhecidos como proxies reversos, para determinar se eles podem servir uma cópia -armazenada de uma resposta ao cliente. Sob esse modelo, você economiza principalmente -largura de banda, mas, quando usado corretamente. Também é possível economizar algum -processamento da CPU, reduzindo assim os tempos de resposta. - -A ativação do RequestHandlerComponent no seu controlador ativa automaticamente uma -verificação feita antes de renderizar a exibição. Essa verificação compara o objeto de -resposta com a solicitação original para determinar se a resposta não foi modificada -desde a última vez que o cliente solicitou. - -Se a resposta for avaliada como não modificada, o processo de renderização da visualização -será interrompido, economizando tempo de processamento, economizando largura de banda e nenhum -conteúdo será retornado ao cliente. O código de status da resposta é então definido como ``304 Not Modified``. - -Você pode desativar essa verificação automática definindo a configuração ``checkHttpCache`` -como ``false``:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('RequestHandler', [ - 'checkHttpCache' => false - ]); - } - -Usando ViewClasses Customizadas -=============================== - -Ao usar o JsonView/XmlView, você pode substituir a serialização padrão por uma -classe View personalizada ou adicionar classes View para outros tipos. - -Você pode mapear tipos novos e existentes para suas classes personalizadas. Você -também pode definir isso automaticamente usando a configuração ``viewClassMap``:: - - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('RequestHandler', [ - 'viewClassMap' => [ - 'json' => 'ApiKit.MyJson', - 'xml' => 'ApiKit.MyXml', - 'csv' => 'ApiKit.Csv' - ] - ]); - } - -.. meta:: - :title lang=pt: Request Handling - :keywords lang=pt: componente manipulador, bibliotecas javascript, componentes públicos, retornos nulos, dados do modelo, dados de solicitação, tipos de conteúdo, extensões de arquivo, ajax, meth, tipo de conteúdo, matriz, conjunção, cakephp, insight, php diff --git a/pt/controllers/components/security.rst b/pt/controllers/components/security.rst deleted file mode 100644 index a66821ebc0..0000000000 --- a/pt/controllers/components/security.rst +++ /dev/null @@ -1,253 +0,0 @@ -Security (Segurança) -#################### - -.. php:class:: SecurityComponent(ComponentCollection $collection, array $config = []) - -O componente de segurança cria uma maneira fácil de integrar maior segurança ao -seu aplicativo. Ele fornece métodos para várias tarefas, como: - -* Restringindo quais métodos HTTP seu aplicativo aceita. -* Proteção contra violação de formulário -* Exigindo que o SSL seja usado. -* Limitar a comunicação entre controladores. - -Como todos os componentes, ele é ajustado através de vários parâmetros configuráveis. -Todas essas propriedades podem ser definidas diretamente ou através de métodos setter -com o mesmo nome no ``beforeFilter()`` do seu controlador. - -Ao usar o componente de segurança, você obtém automaticamente proteção -contra violação de formulários. Os campos de token oculto são inseridos -automaticamente nos formulários e verificados pelo componente Security. - -Se você estiver usando os recursos de proteção de formulário do componente -Security e outros componentes que processam dados do formulário em seus -retornos de chamada ``startup()``, certifique-se de colocar o Componente -de Segurança antes desses componentes no método ``initialize()``. - -.. note:: - - Ao usar o componente de segurança, você deve usar o FormHelper para criar seus - formulários. Além disso, você não deve substituir nenhum dos atributos "name" dos - campos. O componente de segurança procura determinados indicadores criados e - gerenciados pelo FormHelper (especialmente aqueles criados em - :php:meth:`~Cake\\View\\Helper\\FormHelper::create()` e :php:meth:`~Cake\\View\\Helper\\FormHelper::end()`). - Alterar dinamicamente os campos que são enviados em uma solicitação POST (por exemplo, - desativar, excluir ou criar novos campos via JavaScript) provavelmente fará com - que a solicitação seja enviada como retorno a um blackhole. - - Você sempre deve verificar o método HTTP que está sendo usado antes da execução - para evitar efeitos colaterais. Você deve :ref:`check the HTTP method ` - ou usar :php:meth:`Cake\\Http\\ServerRequest::allowMethod()` para garantir que - o método HTTP correto seja usado. - -Como Lidar com Retornos de Chamada Blackhole -============================================ - -.. php:method:: blackHole(Controller $controller, string $error = '', ?SecurityException $exception = null) - -Se uma ação for restringida pelo Componente de Segurança, ela será 'ocultada em preto' -como uma solicitação inválida que resultará em um erro 400 por padrão. Você pode configurar -esse comportamento definindo a opção de configuração ``blackHoleCallback`` como uma função -de retorno de chamada no controlador. - -Ao configurar um método de retorno de chamada, você pode personalizar como o processo do -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') { - // Reescreva a mensagem de exceção com uma sequência traduzível. - $exception->setMessage(__('Please access the requested page through HTTPS')); - } - - // Lance novamente a exceção reformulada condicionalmente. - throw $exception; - - // Como alternativa, lide com o erro, ex.: defina uma mensagem flash e - // redirecione para a versão HTTPS da página solicitada. - } - -O parâmetro ``$type`` pode ter os seguintes valores: - -* 'auth' Indica um erro de validação do formulário ou um erro de incompatibilidade de controlador/ação. -* 'secure' Indica uma falha de restrição do método SSL. - -Restringir Ações ao SSL -======================= - -.. php:method:: requireSecure() - - Define as ações que requerem uma solicitação protegida por SSL. - Leva qualquer número de argumentos. Pode ser chamado sem argumentos - para forçar todas as ações a exigir um SSL protegido. - -.. php:method:: requireAuth() - - Define as ações que requerem um token válido gerado pelo Componente de segurança. - Leva qualquer número de argumentos. Pode ser chamado sem argumentos para forçar - todas as ações a exigir uma autenticação válida. - -Restringindo a Comunicação entre Controladores -============================================== - -allowedControllers - Uma lista de controladores que podem enviar solicitações para esse controlador. - Isso pode ser usado para controlar solicitações entre controladores. -allowedActions - Uma lista de ações que têm permissão para enviar solicitações para as ações deste controlador. - Isso pode ser usado para controlar solicitações entre controladores. - -Essas opções de configuração permitem restringir a comunicação entre controladores. - -Prevenção de Adulteração de Formulários -======================================= - -Por padrão, o ``SecurityComponent`` impede que os usuários adulterem formulários de -maneiras específicas. O `` SecurityComponent`` impedirá o seguinte: - -* Campos desconhecidos não podem ser adicionados ao formulário. -* Os campos não podem ser removidos do formulário. -* Os valores nas entradas ocultas não podem ser modificados. - -A prevenção desses tipos de adulteração é realizada trabalhando com o ``FormHelper`` e -rastreando quais campos estão em um formulário. Os valores para campos ocultos também -são rastreados. Todos esses dados são combinados e transformados em um hash. Quando um -formulário é enviado, o ``SecurityComponent`` usará os dados do POST para criar a mesma -estrutura e comparar o hash. - -.. note:: - - O SecurityComponent **não** impede que as opções selecionadas sejam adicionadas/alteradas. - Nem impedirá que as opções de rádio sejam adicionadas/alteradas. - -unlockedFields - Defina para uma lista de campos de formulário a serem excluídos da validação - do POST. Os campos podem ser desbloqueados no Component ou com :php:meth:`FormHelper::unlockField()`. - Os campos que foram desbloqueados não precisam fazer parte do POST e os - campos desbloqueados ocultos não têm seus valores verificados. - -validatePost - Defina como ``false`` para ignorar completamente a validação - de solicitações POST, essencialmente desativando a validação de formulário. - -Uso -=== - -Geralmente, o uso do componente de segurança é feito no ``beforeFilter()`` do -controlador. Você especificaria as restrições de segurança que deseja e o -Componente de Segurança as aplicará em sua inicialização:: - - 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('admin')) { - $this->Security->requireSecure(); - } - } - } - -O exemplo acima forçaria todas as ações que tinham roteamento de -administrador a exigir solicitações SSL seguras:: - - namespace App\Controller; - - use App\Controller\AppController; - use Cake\Event\EventInterface; - - class WidgetsController extends AppController - { - public function initialize(): void - { - parent::initialize(); - $this->loadComponent('Security', ['blackHoleCallback' => 'forceSSL']); - } - - public function beforeFilter(EventInterface $event) - { - parent::beforeFilter($event); - - if ($this->request->getParam('admin')) { - $this->Security->requireSecure(); - } - } - - public function forceSSL($error = '', SecurityException $exception = null) - { - if ($exception instanceof SecurityException && $exception->getType() === 'secure') { - return $this->redirect('https://' . env('SERVER_NAME') . Router::url($this->request->getRequestTarget())); - } - - throw $exception; - } - } - -Este exemplo forçaria todas as ações que tinham roteamento de administrador a -exigir solicitações SSL seguras. Quando a solicitação é ocultada em preto, ele -chama o retorno de chamada ``forceSSL()``, que redirecionará solicitações não -seguras para proteger solicitações automaticamente. - -.. _security-csrf: - -Proteção CSRF -============= - -CSRF ou falsificação de solicitação entre sites é uma vulnerabilidade comum em -aplicativos da web. Ele permite que um invasor capture e reproduza uma solicitação -anterior e, às vezes, envie solicitações de dados usando tags ou recursos de imagem -em outros domínios. Para habilitar os recursos de proteção CSRF, use :ref:`csrf-middleware`. - -Desabilitando o Componente de Segurança para Ações Específicas -============================================================== - -Pode haver casos em que você deseja desativar todas as verificações de -segurança de uma ação (por exemplo, solicitações AJAX). Você pode "desbloquear" -essas ações listando-as em ``$this->Security->unlockedActions`` em seu ``beforeFilter()``. -A propriedade ``unlockedActions`` **não** afeta outros recursos do ``SecurityComponent``:: - - 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 exemplo desabilitaria todas as verificações de segurança da ação de edição. - -.. meta:: - :title lang=pt: Segurança - :keywords lang=pt: parâmetros configuráveis, componente de segurança, parâmetros de configuração, solicitação inválida, recursos de proteção, segurança mais rígida, holing, classe php, meth, erro 404, período de inatividade, csrf, matriz, envio, classe de segurança, desativar segurança, unlockActions From 2b5c2503aeb5b3afcc6f049fe9ae10839965b6a9 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:26:10 -0300 Subject: [PATCH 26/46] [refact]: translated (pt/appendices/glossary.rst) --- pt/appendices/glossary.rst | 117 ++++++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 41 deletions(-) diff --git a/pt/appendices/glossary.rst b/pt/appendices/glossary.rst index 5559f7e471..b150094f89 100644 --- a/pt/appendices/glossary.rst +++ b/pt/appendices/glossary.rst @@ -1,47 +1,38 @@ Glossário ######### -.. glossary:: - - routing array - Uma série de atributos que são passados para :php:meth:`Router::url()`. - Eles normalmente parecem:: - - ['controller' => 'Posts', 'action' => 'view', 5] - - HTML attributes - Uma série de arrays key => values que são compostos em atributos HTML. Por Exemplo:: - // Tendo isso - ['class' => 'my-class', 'target' => '_blank'] - - // Geraria isto - class="my-class" target="_blank" - - Se uma opção pode ser minimizada ou aceitar seu nome como valor, então ``true`` - pode ser usado:: +.. glossary:: - // Tendo isso - ['checked' => true] + CDN + Content Delivery Network. Um fornecedor de código de terceiros que você + pode pagar para ajudar a distribuir seu conteúdo para centros de dados + em todo o mundo. Isso ajuda a colocar seus ativos estáticos mais próximos + dos usuários distribuídos geograficamente. - // Geraria isto - checked="checked" + columns + Usado no ORM ao se referir às colunas de uma tabela em um banco de dados. - sintaxe plugin - A sintaxe do plugin refere-se ao nome da classe separada por pontos que indica classes:: + CSRF + Cross Site Request Forgery. Impede ataques de repetição, + envios duplos e solicitações forjadas de outros domínios. - // O plugin "DebugKit", e o nome da "Toolbar". - 'DebugKit.Toolbar' + DI Container + Em ``Application::services()`` você pode configurar serviços de aplicação + e suas dependências. Os serviços de aplicação são injetados automaticamente + em ações do Controller e Construtores de Comando. Consulte + :doc:`/development/dependency-injection`. - // O plugin "AcmeCorp/Tools", e o nome da class "Toolbar". - 'AcmeCorp/Tools.Toolbar' + DSN + Nome da Fonte de Dados. Um formato de string de conexão que é formado como um URI. + O CakePHP suporta DSNs para conexões de Cache, Banco de Dados, Log e E-mail. dot notation - A notação de ponto define um caminho do array, separando níveis aninhados com ``.`` + A notação de ponto define um caminho de array, separando os níveis aninhados com ``.`` Por exemplo:: Cache.default.engine - Geraria o seguinte valor:: + Apontaria para o seguinte valor:: [ 'Cache' => [ @@ -51,20 +42,64 @@ Glossário ] ] - CSRF - Cross Site Request Forgery. Impede ataques de repetição, envios duplos e solicitações forjadas de outros domínios. + DRY + Não se repita. É um princípio de desenvolvimento de software que visa + reduzir a repetição de informações de todos os tipos. No CakePHP, o DRY é usado + para permitir que você codifique as coisas uma vez e as reutilize em toda a sua + aplicação. - CDN - Content Delivery Network. Um fornecedor de código de terceiros que você pode pagar para ajudar a distribuir seu conteúdo para centros de dados em todo o mundo. Isso ajuda a colocar seus ativos estáticos mais próximos dos usuários distribuídos geograficamente. + campos + Um termo genérico usado para descrever propriedades de entidades ou colunas + de banco de dados. Frequentemente usado em conjunto com o FormHelper. - routes.php - O arquivo ``config`` diretório que contém configuração de roteamento. Este arquivo está incluído antes de cada solicitação ser processada. Ele deve conectar todas as rotas que seu aplicativo precisa para que as solicitações possam ser encaminhadas para a ação correta do controlador. + attributos HTML + Uma chave de array => valores que são compostos em atributos HTML. Por exemplo:: - DRY - Não se repita. É um princípio de desenvolvimento de software destinado a reduzir a repetição de informações de todos os tipos. No CakePHP DRY é usado para permitir codificar coisas uma vez e reutilizá-las em toda a sua aplicação. + // Considerando + ['class' => 'my-class', 'target' => '_blank'] + + // Geraria + class="my-class" target="_blank" + + Se uma opção puder ser minimizada ou aceitar seu nome como valor, então ``true`` + pode ser usado:: + + // Considerando + ['checked' => true] + + // Geraria + checked="checked" PaaS - Plataforma como um serviço. A plataforma como um provedor de serviços fornecerá recursos baseados em nuvem de hospedagem, banco de dados e armazenamento em cache. Alguns provedores populares incluem Heroku, EngineYard e PagodaBox. + Plataforma como Serviço. Os provedores de Plataforma como Serviço fornecerão + recursos de hospedagem, banco de dados e cache baseados em nuvem. Alguns provedores + populares incluem Heroku, EngineYard e PagodaBox. - DSN - Data Source Name. Um formato de seqüência de conexão que é formado como um URI. O CakePHP suporta DSN para conexões Cache, Database, Log e Email. + propriedades + Usado ao referenciar colunas mapeadas em uma entidade ORM. + + sintaxe plugin + Sintaxe plugin refere-se ao nome da classe separado por pontos, indicando que as classes + fazem parte de um plugin:: + + // O plugin é "DebugKit" e o nome da classe é "Toolbar". + 'DebugKit.Toolbar' + + // O plugin é "AcmeCorp/Tools" e o nome da classe é "Toolbar". + 'AcmeCorp/Tools.Toolbar' + + routes.php + Um arquivo no diretório ``config`` que contém a configuração de roteamento. + Este arquivo é incluído antes do processamento de cada solicitação. + Ele deve conectar todas as rotas que sua aplicação precisa para que + as solicitações possam ser roteadas para o controller + ação corretos. + + routing array + Um conjunto de atributos que são passados ​​para :php:meth:`Router::url()`. + Eles normalmente se parecem com:: + + ['controller' => 'Posts', 'action' => 'view', 5] + +.. meta:: + :title lang=pt: Glossário + :keywords lang=pt: atributos HTML, classe de array, controller de array, glossário,target blank, campos, propriedades, colunas, notação de ponto, configuração de roteamento, falsificação, repetição, roteador, sintaxe, configuração, envios \ No newline at end of file From 221925263335897dea4a7dfdab663024c7ebd270 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:28:10 -0300 Subject: [PATCH 27/46] [fix]: short title in (pt/controllers/components.rst) --- pt/controllers/components.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/controllers/components.rst b/pt/controllers/components.rst index 266db61506..c7f6383963 100644 --- a/pt/controllers/components.rst +++ b/pt/controllers/components.rst @@ -99,7 +99,7 @@ controllers. que o componente seja usado, inclusive dentro de outros componentes. Carregando Componentes em Rempo Real ------------------------------ +------------------------------------ Você pode não precisar de todos os seus componentes disponíveis em todas as ações do controller. Em situações como essa, você pode carregar um componente em tempo de execução usando o método From 40e26599e8c52bc90fd81eb06eb6015e26949ad7 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:34:00 -0300 Subject: [PATCH 28/46] [feat]: new image --- pt/_static/img/cakefest-2022-banner.png | Bin 0 -> 158728 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 pt/_static/img/cakefest-2022-banner.png diff --git a/pt/_static/img/cakefest-2022-banner.png b/pt/_static/img/cakefest-2022-banner.png new file mode 100644 index 0000000000000000000000000000000000000000..ce074d12425e79f20001f76f406011ccafe9a98e GIT binary patch literal 158728 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 literal 0 HcmV?d00001 From c06de83debcf1bcf1990a77c8c83ef7a28c05df8 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:37:32 -0300 Subject: [PATCH 29/46] [refact]: new link (pt/bake/usage.rst) --- pt/bake/usage.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/bake/usage.rst b/pt/bake/usage.rst index 03762dd4c7..be237d2d5c 100644 --- a/pt/bake/usage.rst +++ b/pt/bake/usage.rst @@ -1,4 +1,4 @@ Geração de código com bake ########################## -Esta página foi `movida `__. +Esta página foi `movida `__. From 49c176392b26b158c6c91640c2a23191d06d814d Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:37:52 -0300 Subject: [PATCH 30/46] [refact]: new link (pt/bake/development.rst) --- pt/bake/development.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/bake/development.rst b/pt/bake/development.rst index ea02f8c8bc..7a7b33232c 100644 --- a/pt/bake/development.rst +++ b/pt/bake/development.rst @@ -1,4 +1,4 @@ Extendendo o Bake ################# -Esta página foi `movida `__. +Esta página foi `movida `__. From e178a6e9e2018daebf4b54080690feee8041f429 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 04:40:34 -0300 Subject: [PATCH 31/46] [refact]: translated (pt/console-commands/cache.rst) --- pt/console-commands/cache.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pt/console-commands/cache.rst b/pt/console-commands/cache.rst index 4bb1ea3761..ce307f163e 100644 --- a/pt/console-commands/cache.rst +++ b/pt/console-commands/cache.rst @@ -1,14 +1,14 @@ -Cache Tool -########## +Ferramenta de Cache +################### -To help you better manage cached data from a CLI environment, a console command -is available for clearing cached data your application has:: +Para ajudá-lo a gerenciar melhor os dados armazenados em cache de um ambiente CLI, um comando de console +está disponível para limpar os dados armazenados em cache do seu aplicativo:: - // Clear one cache config + // Limpar uma configuração de cache bin/cake cache clear - // Clear all cache configs + // Limpar todas as configurações de cache bin/cake cache clear_all - // Clear one cache group + // Limpar um grupo de cache bin/cake cache clear_group From d14802b6e884f039dc9e9ca7faff034d876b0fc4 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 06:43:10 -0300 Subject: [PATCH 32/46] [refact]: translated (pt/console-commands/commands.rst) --- pt/console-commands/commands.rst | 303 +++++++++++++++---------------- 1 file changed, 151 insertions(+), 152 deletions(-) diff --git a/pt/console-commands/commands.rst b/pt/console-commands/commands.rst index 9493a24b5c..95331da84c 100644 --- a/pt/console-commands/commands.rst +++ b/pt/console-commands/commands.rst @@ -1,19 +1,19 @@ -Command Objects -############### +Objetos de Comando +################## .. php:namespace:: Cake\Console .. php:class:: Command -CakePHP comes with a number of built-in commands for speeding up your -development, and automating routine tasks. You can use these same libraries to -create commands for your application and plugins. +O CakePHP vem com uma série de comandos integrados para acelerar seu +desenvolvimento e automatizar tarefas rotineiras. Você pode usar essas mesmas bibliotecas para +criar comandos para sua aplicação e plugins. -Creating a Command +Criando um Comando ================== -Let's create our first Command. For this example, we'll create a -simple Hello world command. In your application's **src/Command** directory create -**HelloCommand.php**. Put the following code inside it:: +Vamos criar nosso primeiro Comando. Para este exemplo, criaremos um +comando simples "Olá, mundo". No diretório **src/Command** do seu aplicativo, crie +**HelloCommand.php**. Insira o seguinte código dentro dele:: out('Hello world.'); + $io->out('Olá, mundo.'); return static::CODE_SUCCESS; } } -Command classes must implement an ``execute()`` method that does the bulk of -their work. This method is called when a command is invoked. Lets call our first -command application directory, run: +As classes de comando devem implementar um método ``execute()`` que realiza a maior parte +do seu trabalho. Este método é chamado quando um comando é invocado. Vamos chamar nosso primeiro +diretório de aplicativo de comando, execute: .. code-block:: console bin/cake hello -You should see the following output:: +Você deverá ver a seguinte saída:: - Hello world. + Olá, mundo. -Our ``execute()`` method isn't very interesting let's read some input from the -command line:: +Nosso método ``execute()`` não é muito interessante, vamos ler algumas entradas da +linha de comando:: fetchTable()`` since command use the ``LocatorAwareTrait``:: +Frequentemente, você precisará acessar a lógica de negócios do seu aplicativo em comandos +de console. Você pode carregar modelos em comandos, assim como faria em um controller +usando ``$this->fetchTable()``, já que o comando usa o ``LocatorAwareTrait``:: fetchTable()`` since command use the ``LocatorAwareTrait``:: class UserCommand extends Command { - // Define the default table. This allows you to use `fetchTable()` without any argument. + // Defina a tabela padrão. Isso permite que você use `fetchTable()` sem nenhum argumento. protected $defaultTable = 'Users'; protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser @@ -185,86 +184,86 @@ using ``$this->fetchTable()`` since command use the ``LocatorAwareTrait``:: } } -The above command, will fetch a user by username and display the information -stored in the database. +O comando acima buscará um usuário pelo nome de usuário e exibirá as informações +armazenadas no banco de dados. -Exit Codes and Stopping Execution -================================= +Códigos de Saída e Interrupção da Execução +========================================== -When your commands hit an unrecoverable error you can use the ``abort()`` method -to terminate execution:: +Quando seus comandos apresentam um erro irrecuperável, você pode usar o método ``abort()`` +para encerrar a execução:: // ... public function execute(Arguments $args, ConsoleIo $io): int { $name = $args->getArgument('name'); if (strlen($name) < 5) { - // Halt execution, output to stderr, and set exit code to 1 - $io->error('Name must be at least 4 characters long.'); + // Interrompa a execução, envie para stderr e defina o código de saída como 1 + $io->error('O nome deve ter pelo menos 4 caracteres.'); $this->abort(); } return static::CODE_SUCCESS; } -You can also use ``abort()`` on the ``$io`` object to emit a message and code:: +Você também pode usar ``abort()`` no objeto ``$io`` para emitir uma mensagem e código:: public function execute(Arguments $args, ConsoleIo $io): int { $name = $args->getArgument('name'); if (strlen($name) < 5) { - // Halt execution, output to stderr, and set exit code to 99 - $io->abort('Name must be at least 4 characters long.', 99); + // Interrompa a execução, envie para stderr e defina o código de saída como 99 + $io->abort('O nome deve ter pelo menos 4 caracteres.', 99); } return static::CODE_SUCCESS; } -You can pass any desired exit code into ``abort()``. +Você pode passar qualquer código de saída desejado para ``abort()``. .. tip:: - Avoid exit codes 64 - 78, as they have specific meanings described by - ``sysexits.h``. Avoid exit codes above 127, as these are used to indicate - process exit by signal, such as SIGKILL or SIGSEGV. + Evite os códigos de saída 64 a 78, pois eles têm significados específicos descritos por + ``sysexits.h``. Evite códigos de saída acima de 127, pois eles são usados ​​para indicar + a saída do processo por sinal, como SIGKILL ou SIGSEGV. - You can read more about conventional exit codes in the sysexit manual page - on most Unix systems (``man sysexits``), or the ``System Error Codes`` help - page in Windows. + Você pode ler mais sobre códigos de saída convencionais na página do manual do sysexit + na maioria dos sistemas Unix (``man sysexits``) ou na página de ajuda ``Códigos de Erro do Sistema`` + no Windows. -Calling other Commands -====================== +Chamando Outros Comandos +======================== -You may need to call other commands from your command. You can use -``executeCommand`` to do that:: +Você pode precisar chamar outros comandos a partir do seu comando. Você pode usar +``executeCommand`` para fazer isso:: - // You can pass an array of CLI options and arguments. + // Você pode passar uma série de opções e argumentos da CLI. $this->executeCommand(OtherCommand::class, ['--verbose', 'deploy']); - // Can pass an instance of the command if it has constructor args + // Pode passar uma instância do comando se ele tiver argumentos de construtor $command = new OtherCommand($otherArgs); $this->executeCommand($command, ['--verbose', 'deploy']); .. note:: - When calling ``executeCommand()`` in a loop, it is recommended to pass in the - parent command's ``ConsoleIo`` instance as the optional 3rd argument to - avoid a potential "open files" limit that could occur in some environments. + Ao chamar ``executeCommand()`` em um loop, é recomendável passar a instância ``ConsoleIo`` + do comando pai como o terceiro argumento opcional para + evitar um potencial limite de "arquivos abertos" que pode ocorrer em alguns ambientes. -Setting Command Description -=========================== +Descrição do Comando de Configuração +==================================== -You may want to set a command description via:: +Você pode querer definir uma descrição de comando via:: class UserCommand extends Command { public static function getDescription(): string { - return 'My custom description'; + return 'Minha descrição personalizada'; } } -This will show your description in the Cake CLI: +Isso mostrará sua descrição no Cake CLI: .. code-block:: console @@ -272,33 +271,33 @@ This will show your description in the Cake CLI: App: - user - └─── My custom description + └─── Minha descrição personalizada -As well as in the help section of your command: +Bem como na seção de ajuda do seu comando: .. code-block:: console cake user --help - My custom description + Minha descrição personalizada Usage: cake user [-h] [-q] [-v] .. _console-integration-testing: -Testing Commands -================ +Comandos de Teste +================= -To make testing console applications easier, CakePHP comes with a -``ConsoleIntegrationTestTrait`` trait that can be used to test console applications -and assert against their results. +Para facilitar o teste de aplicações de console, o CakePHP vem com um trait +``ConsoleIntegrationTestTrait`` que pode ser usado para testar aplicações de console +e validar seus resultados. -To get started testing your console application, create a test case that uses the -``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait. This trait contains a method -``exec()`` that is used to execute your command. You can pass the same string -you would use in the CLI to this method. +Para começar a testar sua aplicação de console, crie um caso de teste que use o trait +``Cake\TestSuite\ConsoleIntegrationTestTrait``. Este trait contém um método +``exec()`` que é usado para executar seu comando. Você pode passar a mesma string +que usaria na CLI para este método. -Let's start with a very simple command, located in +Vamos começar com um comando bem simples, localizado em **src/Command/UpdateTableCommand.php**:: namespace App\Command; @@ -312,16 +311,16 @@ Let's start with a very simple command, located in { protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser { - $parser->setDescription('My cool console app'); + $parser->setDescription('Meu aplicativo legal de console'); return $parser; } } -To write an integration test for this command, we would create a test case in -**tests/TestCase/Command/UpdateTableTest.php** that uses the -``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait. This command doesn't do much at the -moment, but let's just test that our command's description is displayed in ``stdout``:: +Para escrever um teste de integração para este comando, criaríamos um caso de teste em +**tests/TestCase/Command/UpdateTableTest.php** que usa a trait +``Cake\TestSuite\ConsoleIntegrationTestTrait``. Este comando não faz muita coisa no +momento, mas vamos apenas testar se a descrição do nosso comando é exibida em ``stdout``:: namespace App\Test\TestCase\Command; @@ -335,13 +334,13 @@ moment, but let's just test that our command's description is displayed in ``std public function testDescriptionOutput() { $this->exec('update_table --help'); - $this->assertOutputContains('My cool console app'); + $this->assertOutputContains('Meu aplicativo legal de console'); } } -Our test passes! While this is very trivial example, it shows that creating an -integration test case for console applications can follow command line -conventions. Let's continue by adding more logic to our command:: +Nosso teste passou! Embora este seja um exemplo bastante trivial, ele mostra que a criação de um +caso de teste de integração para aplicativos de console pode seguir as convenções +da linha de comando. Vamos continuar adicionando mais lógica ao nosso comando:: namespace App\Command; @@ -356,9 +355,9 @@ conventions. Let's continue by adding more logic to our command:: protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser { $parser - ->setDescription('My cool console app') + ->setDescription('Meu aplicativo legal de console') ->addArgument('table', [ - 'help' => 'Table to update', + 'help' => 'Tabela para atualizar', 'required' => true ]); @@ -378,8 +377,8 @@ conventions. Let's continue by adding more logic to our command:: } } -This is a more complete command that has required options and relevant logic. -Modify your test case to the following snippet of code:: +Este é um comando mais completo que possui as opções necessárias e a lógica relevante. +Modifique seu caso de teste para o seguinte trecho de código:: namespace Cake\Test\TestCase\Command; @@ -393,14 +392,14 @@ Modify your test case to the following snippet of code:: use ConsoleIntegrationTestTrait; protected $fixtures = [ - // assumes you have a UsersFixture + // assume que você tem um UsersFixture 'app.Users', ]; public function testDescriptionOutput() { $this->exec('update_table --help'); - $this->assertOutputContains('My cool console app'); + $this->assertOutputContains('Meu aplicativo legal de console'); } public function testUpdateModified() @@ -420,25 +419,25 @@ Modify your test case to the following snippet of code:: } } -As you can see from the ``testUpdateModified`` method, we are testing that our -command updates the table that we are passing as the first argument. First, we -assert that the command exited with the proper status code, ``0``. Then we check -that our command did its work, that is, updated the table we provided and set -the ``modified`` column to the current time. +Como você pode ver no método ``testUpdateModified``, estamos testando se nosso +comando atualiza a tabela que estamos passando como primeiro argumento. Primeiro, +afirmamos que o comando saiu com o código de status correto, ``0``. Em seguida, verificamos +se nosso comando fez seu trabalho, ou seja, atualizou a tabela que fornecemos e definiu +a coluna ``modified`` para a hora atual. -Remember, ``exec()`` will take the same string you type into your CLI, so you -can include options and arguments in your command string. +Lembre-se de que ``exec()`` receberá a mesma string que você digitar na sua CLI, para que você +possa incluir opções e argumentos na sua string de comando. -Testing Interactive Commands ----------------------------- +Testando Comandos Interativos +----------------------------- -Consoles are often interactive. Testing interactive commands with the -``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait only requires passing the -inputs you expect as the second parameter of ``exec()``. They should be -included as an array in the order that you expect them. +Consoles costumam ser interativos. Testar comandos interativos com a característica +``Cake\TestSuite\ConsoleIntegrationTestTrait`` requer apenas a passagem das +entradas esperadas como o segundo parâmetro de ``exec()``. Elas devem ser +incluídas como um array na ordem em que você as espera. -Continuing with our example command, let's add an interactive confirmation. -Update the command class to the following:: +Continuando com nosso comando de exemplo, vamos adicionar uma confirmação interativa. +Atualize a classe de comando para o seguinte:: namespace App\Command; @@ -453,9 +452,9 @@ Update the command class to the following:: protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser { $parser - ->setDescription('My cool console app') + ->setDescription('Meu aplicativo legal de console') ->addArgument('table', [ - 'help' => 'Table to update', + 'help' => 'Tabela para atualizar', 'required' => true ]); @@ -465,8 +464,8 @@ Update the command class to the following:: 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.'); + if ($io->ask('Tem certeza?', 'n', ['y', 'n']) !== 'y') { + $io->error('Você precisa ter certeza.'); $this->abort(); } $this->fetchTable($table)->updateQuery() @@ -479,9 +478,9 @@ Update the command class to the following:: } } -Now that we have an interactive command, we can add a test case that tests -that we receive the proper response, and one that tests that we receive an -incorrect response. Remove the ``testUpdateModified`` method and, add the following methods to +Agora que temos um comando interativo, podemos adicionar um caso de teste que testa +se recebemos a resposta correta e outro que testa se recebemos uma +resposta incorreta. Remova o método ``testUpdateModified`` e adicione os seguintes métodos a **tests/TestCase/Command/UpdateTableCommandTest.php**:: @@ -514,61 +513,61 @@ incorrect response. Remove the ``testUpdateModified`` method and, add the follow $this->assertSame($original, $user->timestamp); } -In the first test case, we confirm the question, and records are updated. In the -second test we don't confirm and records are not updated, and we can check that -our error message was written to ``stderr``. +No primeiro caso de teste, confirmamos a pergunta e os registros são atualizados. No +segundo teste, não confirmamos e os registros não são atualizados, e podemos verificar se +nossa mensagem de erro foi escrita em ``stderr``. -Assertion methods ------------------ +Métodos de Asserção +------------------- -The ``Cake\TestSuite\ConsoleIntegrationTestTrait`` trait provides a number of -assertion methods that make help assert against console output:: +O atributo ``Cake\TestSuite\ConsoleIntegrationTestTrait`` fornece uma série de +métodos de asserção que ajudam a fazer a asserção na saída do console:: - // assert that the command exited as success + // afirmar que o comando saiu como sucesso $this->assertExitSuccess(); - // assert that the command exited as an error + // afirmar que o comando saiu como um erro $this->assertExitError(); - // assert that the command exited with the expected code + // afirmar que o comando saiu com o código esperado $this->assertExitCode($expected); - // assert that stdout contains a string + // afirmar que stdout contém uma string $this->assertOutputContains($expected); - // assert that stderr contains a string + // afirmar que stderr contém uma string $this->assertErrorContains($expected); - // assert that stdout matches a regular expression + // afirmar que stdout corresponde a uma expressão regular $this->assertOutputRegExp($expected); - // assert that stderr matches a regular expression + // afirmar que stderr corresponde a uma expressão regular $this->assertErrorRegExp($expected); Debug Helpers ------------- -You can use ``debugOutput()`` to output the exit code, stdout and stderr of the -last run command:: +Você pode usar ``debugOutput()`` para gerar o código de saída, stdout e stderr do +último comando executado:: $this->exec('update_table Users'); $this->assertExitCode(Command::CODE_SUCCESS); $this->debugOutput(); .. versionadded:: 4.2.0 - The ``debugOutput()`` method was added. + O método ``debugOutput()`` foi adicionado. -Lifecycle Callbacks -=================== +Retornos de Ciclo de Vida +========================= -Like Controllers, Commands offer lifecycle events that allow you to observe -the framework calling your application code. Commands have: +Assim como os Controllers, os Comandos oferecem eventos de ciclo de vida que permitem observar +o framework chamando o código da sua aplicação. Os Comandos possuem: -- ``Command.beforeExecute`` Is called before a command's ``execute()`` method - is. The event is passed the ``ConsoleArguments`` parameter as ``args``. This - event cannot be stopped or have its result replaced. -- ``Command.afterExecute`` Is called after a command's ``execute()`` method is - complete. The event contains ``ConsoleArguments`` as ``args`` and the command - result as ``result``. This event cannot be stopped or have its result - replaced. +- ``Command.beforeExecute`` É chamado antes do método ``execute()`` de um comando. +O evento recebe o parâmetro ``ConsoleArguments`` como ``args``. Este +evento não pode ser interrompido ou ter seu resultado substituído. +- ``Command.afterExecute`` É chamado após o método ``execute()`` de um comando ser +concluído. O evento contém ``ConsoleArguments`` como ``args`` e o resultado +do comando como ``result``. Este evento não pode ser interrompido ou ter seu resultado +substituído. From f17aa58e6857ec261235ba93a1d1d0f09a34edc4 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 06:51:17 -0300 Subject: [PATCH 33/46] [refact]: translated (pt/console-commands/commands.rst) --- pt/console-commands/commands.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pt/console-commands/commands.rst b/pt/console-commands/commands.rst index 95331da84c..527255089d 100644 --- a/pt/console-commands/commands.rst +++ b/pt/console-commands/commands.rst @@ -167,7 +167,7 @@ usando ``$this->fetchTable()``, já que o comando usa o ``LocatorAwareTrait``:: { $parser ->addArgument('name', [ - 'help' => 'What is your name' + 'help' => 'Qual o seu nome' ]); return $parser; From bf990db8abf294504eeaf59a5b5f006cbccbef3e Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 07:04:29 -0300 Subject: [PATCH 34/46] [refact]: translated (pt/console-commands/completion.rst) --- pt/console-commands/completion.rst | 110 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/pt/console-commands/completion.rst b/pt/console-commands/completion.rst index c197d5e4a2..e95b6e08a6 100644 --- a/pt/console-commands/completion.rst +++ b/pt/console-commands/completion.rst @@ -1,54 +1,54 @@ -Completion Tool -################ +Ferramenta de Conclusão +####################### -Working with the console gives the developer a lot of possibilities but having -to completely know and write those commands can be tedious. Especially when -developing new shells where the commands differ per minute iteration. The -Completion Shells aids in this matter by providing an API to write completion -scripts for shells like bash, zsh, fish etc. +Trabalhar com o console oferece muitas possibilidades ao desenvolvedor, mas ter +que conhecer e escrever completamente esses comandos pode ser tedioso. Especialmente ao +desenvolver novos shells, onde os comandos diferem a cada minuto de iteração. Os +Shells de Conclusão auxiliam nessa questão, fornecendo uma API para escrever scripts de conclusão +para shells como bash, zsh, fish etc. -Sub Commands +Sub Comandos ============ -The Completion Shell consists of a number of sub commands to assist the -developer creating its completion script. Each for a different step in the -autocompletion process. +O Shell de Conclusão consiste em vários subcomandos para auxiliar o +desenvolvedor na criação do seu script de conclusão. Cada um para uma etapa diferente no +processo de conclusão automática. -Commands +Comandos -------- -For the first step commands outputs the available Shell Commands, including -plugin name when applicable. (All returned possibilities, for this and the other -sub commands, are separated by a space.) For example:: +Para o primeiro passo, os comandos geram os Comandos Shell disponíveis, incluindo +o nome do plugin, quando aplicável. (Todas as possibilidades retornadas, para este e os outros +subcomandos, são separadas por um espaço.) Por exemplo:: bin/cake Completion commands Returns:: - acl api bake command_list completion console i18n schema server test testsuite upgrade + acl api bake command_list conclusão console i18n esquema servidor teste suíte de testes atualização -Your completion script can select the relevant commands from that list to -continue with. (For this and the following sub commands.) +Seu script de conclusão pode selecionar os comandos relevantes dessa lista para +continuar. (Para este e os subcomandos seguintes.) -subCommands +subComandos ----------- -Once the preferred command has been chosen subCommands comes in as the second -step and outputs the possible sub command for the given shell command. For -example:: +Uma vez escolhido o comando preferido, o comando subComandos entra como a segunda +etapa e gera o possível subcomando para o comando shell fornecido. Por +exemplo:: bin/cake Completion subcommands bake Returns:: - controller db_config fixture model plugin project test view + controller db_config modelo de fixação plugin projeto teste visualização -options -------- +opções +------ -As the third and final options outputs options for the given (sub) command as -set in getOptionParser. (Including the default options inherited from Shell.) -For example:: +Como a terceira e última opção, são geradas opções para o (sub)comando fornecido, conforme +definido em getOptionParser. (Incluindo as opções padrão herdadas do Shell.) +Por exemplo:: bin/cake Completion options bake @@ -56,41 +56,41 @@ Returns:: --help -h --verbose -v --quiet -q --everything --connection -c --force -f --plugin -p --prefix --theme -t -You can also pass an additional argument being the shell sub-command : it will -output the specific options of this sub-command. +Você também pode passar um argumento adicional, que é o subcomando do shell: ele irá +exibir as opções específicas deste subcomando. -How to enable Bash autocompletion for the CakePHP Console -========================================================= +Como Habilitar o Preenchimento Automático do Bash para o Console CakePHP +======================================================================== -First, make sure the **bash-completion** library is installed. If not, you do it -with the following command:: +Primeiro, certifique-se de que a biblioteca **bash-completion** esteja instalada. Caso contrário, faça isso +com o seguinte comando:: apt-get install bash-completion -Create a file named **cake** in **/etc/bash_completion.d/** and put the -:ref:`bash-completion-file-content` inside it. +Crie um arquivo chamado **cake** em **/etc/bash_completion.d/** e insira o +:ref:`bash-completion-file-content` dentro dele. -Save the file, then restart your console. +Salve o arquivo e reinicie o console. .. note:: - If you are using MacOS X, you can install the **bash-completion** library - using **homebrew** with the command ``brew install bash-completion``. - The target directory for the **cake** file will be + Se estiver usando o MacOS X, você pode instalar a biblioteca **bash-completion** + usando o **homebrew** com o comando ``brew install bash-completion``. + O diretório de destino para o arquivo **cake** será **/usr/local/etc/bash_completion.d/**. .. _bash-completion-file-content: -Bash Completion file content ----------------------------- +Conteúdo do arquivo Bash Conclusão +---------------------------------- -This is the code you need to put inside the **cake** file in the correct location -in order to get autocompletion when using the CakePHP console: +Este é o código que você precisa inserir dentro do arquivo **cake** no local correto +para obter o preenchimento automático ao usar o console do CakePHP: .. code-block:: bash # - # Bash completion file for CakePHP console + # Arquivo de conclusão Bash para console CakePHP # _cake() @@ -141,17 +141,17 @@ in order to get autocompletion when using the CakePHP console: complete -F _cake cake bin/cake -Using autocompletion +Usando autoconclusão ==================== -Once enabled, the autocompletion can be used the same way than for other -built-in commands, using the **TAB** key. -Three type of autocompletion are provided. The following output are from a fresh CakePHP install. +Uma vez habilitado, a autoconclusão pode ser usado da mesma forma que para outros +comandos integrados, usando a tecla **TAB**. +São fornecidos três tipos de autoconclusão. A saída a seguir é de uma instalação recente do CakePHP. -Commands +Comandos -------- -Sample output for commands autocompletion: +Exemplo de saída para comandos de autoconclusão: .. code-block:: console @@ -162,7 +162,7 @@ Sample output for commands autocompletion: Subcommands ----------- -Sample output for subcommands autocompletion: +Exemplo de saída para subcomandos de autoconclusão: .. code-block:: console @@ -174,10 +174,10 @@ Sample output for subcommands autocompletion: fixture model form plugin -Options -------- +Opções +------ -Sample output for subcommands options autocompletion: +Exemplo de saída para subcomandos de autoconclusão: .. code-block:: console From 1877641de677f0e3ffbc3c12307abf6eaf69a056 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 07:06:36 -0300 Subject: [PATCH 35/46] [refact]: translated (pt/console-commands/counter-cache.rst) --- pt/console-commands/counter-cache.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/pt/console-commands/counter-cache.rst b/pt/console-commands/counter-cache.rst index cc50932bcf..e2028ea509 100644 --- a/pt/console-commands/counter-cache.rst +++ b/pt/console-commands/counter-cache.rst @@ -1,24 +1,24 @@ -CounterCache Tool -################# +Ferramenta de CounterCache +########################## -The CounterCacheCommand provides a CLI tool for rebuilding the counter caches -in your application and plugin models. It can be used in maintenance and -recovery operations, or to populate new counter caches added to your -application. +O CounterCacheCommand fornece uma ferramenta CLI para reconstruir os caches de contadores +nos seus modelos de aplicativo e plugin. Ele pode ser usado em operações de manutenção e +recuperação, ou para preencher novos caches de contadores adicionados ao seu +aplicativo. .. code-block:: console bin/cake counter_cache --assoc Comments Articles -This would rebuild the ``Comments`` related counters on the ``Articles`` table. -For very large tables you may need to rebuild counters in batches. You can use -the ``--limit`` and ``--page`` options to incrementally rebuild counter state. +Isso reconstruiria os contadores relacionados a ``Comentários`` na tabela ``Artigos``. +Para tabelas muito grandes, pode ser necessário reconstruir os contadores em lotes. Você pode usar +as opções ``--limit`` e ``--page`` para reconstruir o estado dos contadores de forma incremental. .. code-block:: console bin/cake counter_cache --assoc Comments --limit 100 --page 2 Articles -When ``limit`` and ``page`` are used, records will be ordered by the table's -primary key. +Quando ``limit`` e ``page`` são usados, os registros serão ordenados pela +chave primária da tabela. .. versionadded:: 5.2.0 From 0b4f4770f9fb811e72a3b48402eaa43224e83972 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 07:09:19 -0300 Subject: [PATCH 36/46] [fix]: identing in (pt/console-commands/commands.rst) file --- pt/console-commands/commands.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pt/console-commands/commands.rst b/pt/console-commands/commands.rst index 527255089d..4f7321966b 100644 --- a/pt/console-commands/commands.rst +++ b/pt/console-commands/commands.rst @@ -565,9 +565,9 @@ Assim como os Controllers, os Comandos oferecem eventos de ciclo de vida que per o framework chamando o código da sua aplicação. Os Comandos possuem: - ``Command.beforeExecute`` É chamado antes do método ``execute()`` de um comando. -O evento recebe o parâmetro ``ConsoleArguments`` como ``args``. Este -evento não pode ser interrompido ou ter seu resultado substituído. + O evento recebe o parâmetro ``ConsoleArguments`` como ``args``. Este + evento não pode ser interrompido ou ter seu resultado substituído. - ``Command.afterExecute`` É chamado após o método ``execute()`` de um comando ser -concluído. O evento contém ``ConsoleArguments`` como ``args`` e o resultado -do comando como ``result``. Este evento não pode ser interrompido ou ter seu resultado -substituído. + concluído. O evento contém ``ConsoleArguments`` como ``args`` e o resultado + do comando como ``result``. Este evento não pode ser interrompido ou ter seu resultado + substituído. From 65a84064d377286cd21142caff92dc8ecd36a212 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 07:14:32 -0300 Subject: [PATCH 37/46] [refact]: translated (pt/console-commands/cron-jobs.rst) --- pt/console-commands/cron-jobs.rst | 48 +++++++++++++++---------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/pt/console-commands/cron-jobs.rst b/pt/console-commands/cron-jobs.rst index 6b29f9d01a..6dc838ad8f 100644 --- a/pt/console-commands/cron-jobs.rst +++ b/pt/console-commands/cron-jobs.rst @@ -1,43 +1,43 @@ -Running Shells as Cron Jobs -########################### +Executando Shells como Cronjobs +############################### -A common thing to do with a shell is making it run as a cronjob to -clean up the database once in a while or send newsletters. This is -trivial to setup, for example:: +Uma coisa comum a se fazer com um shell é executá-lo como um cronjob para +limpar o banco de dados de vez em quando ou enviar newsletters. Isso é +trivial de configurar, por exemplo:: */5 * * * * cd /full/path/to/root && bin/cake myshell myparam - # * * * * * command to execute + # * * * * * comando para executar # │ │ │ │ │ # │ │ │ │ │ - # │ │ │ │ \───── 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) + # │ │ │ │ \───── dia da semana (0 - 6) (0 a 6 são de domingo a sábado, + # | | | | ou usar nomes) + # │ │ │ \────────── mês (1 - 12) + # │ │ \─────────────── dia do mês (1 - 31) + # │ \──────────────────── hora (0 - 23) + # \───────────────────────── minuto (0 - 59) -You can see more info here: https://en.wikipedia.org/wiki/Cron +Você pode ver mais informações aqui: https://en.wikipedia.org/wiki/Cron .. tip:: - Use ``-q`` (or `--quiet`) to silence any output for cronjobs. + Use ``-q`` (ou `--quiet`) para silenciar qualquer saída de cronjobs. -Cron Jobs on Shared Hosting ---------------------------- +Tarefas Cron em Hospedagem Compartilhada +---------------------------------------- -On some shared hostings ``cd /full/path/to/root && bin/cake mycommand myparam`` -might not work. Instead you can use +Em algumas hospedagens compartilhadas, ``cd /full/path/to/root && bin/cake mycommand myparam`` +pode não funcionar. Em vez disso, você pode usar ``php /full/path/to/root/bin/cake.php mycommand 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 + register_argc_argv precisa ser ativado incluindo ``register_argc_argv + = 1`` no seu php.ini. Se você não puder alterar register_argc_argv globalmente, + você pode instruir o cron job a usar sua própria configuração + especificando-a com o parâmetro ``-d register_argc_argv=1``. Exemplo: ``php -d register_argc_argv=1 /full/path/to/root/bin/cake.php myshell myparam`` .. meta:: - :title lang=en: Running Shells as cronjobs - :keywords lang=en: cronjob,bash script,crontab + :title lang=pt: Executando Shells como cronjobs + :keywords lang=pt: cronjob,bash script,crontab From b17e53149faaa0090c1a9a585b53fb72398bac95 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Mon, 8 Sep 2025 07:23:29 -0300 Subject: [PATCH 38/46] [refact]: translated (pt/console-commands/i18n.rst) --- pt/console-commands/i18n.rst | 90 ++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/pt/console-commands/i18n.rst b/pt/console-commands/i18n.rst index 4cd828bfdb..478c5be184 100644 --- a/pt/console-commands/i18n.rst +++ b/pt/console-commands/i18n.rst @@ -1,92 +1,92 @@ -I18N Tool -######### +Ferramenta I18N +############### -The i18n features of CakePHP use `po files `_ -as their translation source. PO files integrate with commonly used translation tools -like `Poedit `_. +Os recursos i18n do CakePHP usam arquivos `po `_ +como fonte de tradução. Os arquivos PO se integram a ferramentas de tradução comumente usadas, +como `Poedit `_. -The i18n commands provides a quick way to generate po template files. -These templates files can then be given to translators so they can translate the -strings in your application. Once you have translations done, pot files can be -merged with existing translations to help update your translations. +Os comandos i18n fornecem uma maneira rápida de gerar arquivos de modelo PO. +Esses arquivos de modelo podem ser fornecidos aos tradutores para que eles possam traduzir as +strings em sua aplicação. Depois que as traduções forem concluídas, os arquivos POT podem ser +mesclados com as traduções existentes para ajudar a atualizar suas traduções. -Generating POT Files +Gerando arquivos 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: +Arquivos POT podem ser gerados para um aplicativo existente usando o comando ``extract``. +Este comando verificará todo o seu aplicativo em busca de chamadas de função no estilo ``__()`` +e extrairá a string da mensagem. Cada string exclusiva do seu +aplicativo será combinada em um único arquivo POT: .. code-block:: console bin/cake i18n extract -The above will run the extraction command. The result of this command will be the -file **resources/locales/default.pot**. You use the pot file as a template for creating -po files. If you are manually creating po files from the pot file, be sure to -correctly set the ``Plural-Forms`` header line. +O comando acima executará o comando de extração. O resultado deste comando será o +arquivo **resources/locales/default.pot**. Use o arquivo pot como modelo para criar +arquivos po. Se estiver criando arquivos po manualmente a partir do arquivo pot, certifique-se de +definir corretamente a linha de cabeçalho ``Plural-Forms``. -Generating POT Files for Plugins --------------------------------- +Gerando arquivos POT para Plugins +--------------------------------- -You can generate a POT file for a specific plugin using: +Você pode gerar um arquivo POT para um plugin específico usando: .. code-block:: console bin/cake i18n extract --plugin -This will generate the required POT files used in the plugins. +Isso gerará os arquivos POT necessários usados ​​nos plugins. -Extracting from multiple folders at once +Extraindo de várias pastas de uma só vez ---------------------------------------- -Sometimes, you might need to extract strings from more than one directory of -your application. For instance, if you are defining some strings in the -``config`` directory of your application, you probably want to extract strings -from this directory as well as from the ``src`` directory. You can do it by -using the ``--paths`` option. It takes a comma-separated list of absolute paths -to extract: +Às vezes, você pode precisar extrair strings de mais de um diretório +do seu aplicativo. Por exemplo, se você estiver definindo algumas strings no diretório +``config`` do seu aplicativo, provavelmente desejará extrair strings +desse diretório, bem como do diretório ``src``. Você pode fazer isso +usando a opção ``--paths``. É necessária uma lista de caminhos absolutos separados por vírgulas +para extrair: .. code-block:: console bin/cake i18n extract --paths /var/www/app/config,/var/www/app/src -Excluding Folders ------------------ +Excluindo Pastas +---------------- -You can pass a comma separated list of folders that you wish to be excluded. -Any path containing a path segment with the provided values will be ignored: +Você pode passar uma lista separada por vírgulas de pastas que deseja excluir. +Qualquer caminho que contenha um segmento de caminho com os valores fornecidos será ignorado: .. code-block:: console bin/cake i18n extract --exclude vendor,tests -Skipping Overwrite Warnings for Existing POT Files --------------------------------------------------- +Ignorando Avisos de Substituição para Arquivos POT Existentes +------------------------------------------------------------- -By adding ``--overwrite``, the shell script will no longer warn you if a POT -file already exists and will overwrite by default: +Ao adicionar ``--overwrite``, o script de shell não irá mais avisá-lo se um arquivo POT +já existir e irá sobrescrever por padrão: .. code-block:: console bin/cake i18n extract --overwrite -Extracting Messages from the CakePHP Core Libraries ---------------------------------------------------- +Extraindo Mensagens das Bibliotecas Principais do CakePHP +--------------------------------------------------------- -By default, the extract shell script will ask you if you like to extract -the messages used in the CakePHP core libraries. Set ``--extract-core`` to yes -or no to set the default behavior: +Por padrão, o script de shell de extração perguntará se você deseja extrair +as mensagens usadas nas bibliotecas principais do CakePHP. Defina ``--extract-core`` como sim +ou não para definir o comportamento padrão: .. code-block:: console bin/cake i18n extract --extract-core yes - // or + // ou bin/cake i18n extract --extract-core no .. meta:: - :title lang=en: I18N command - :keywords lang=en: pot files,locale default,translation tools,message string,app locale,php class,validation,i18n,translations,command,models + :title lang=pt: ferramenta I18N + :keywords lang=pt: arquivos pot, localidade padrão,ferramentas de tradução,mensagens,app locale,classe php,validação,i18n,translations,comando,modelos From 85bd4104cf13340cc6ba5c2b70af9cdd913ef0b6 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 18 Sep 2025 06:18:38 -0300 Subject: [PATCH 39/46] [refact]: translated (pt/controllers/middleware.rst) --- pt/controllers/middleware.rst | 233 +++++++--------------------------- 1 file changed, 43 insertions(+), 190 deletions(-) diff --git a/pt/controllers/middleware.rst b/pt/controllers/middleware.rst index 373154ac60..cc6d1d74c8 100644 --- a/pt/controllers/middleware.rst +++ b/pt/controllers/middleware.rst @@ -25,30 +25,36 @@ resposta PSR-7. O CakePHP também suporta o padrão PSR-15 para manipuladores de servidor, para que você possa usar qualquer middleware compatível com PSR-15 disponível em `The Packagist `_. -Middleware em CakePHP +Middleware no CakePHP ===================== O CakePHP fornece vários middlewares para lidar com tarefas comuns em aplicativos da web: -* ``Cake\Error\Middleware\ErrorHandlerMiddleware`` intercepta exceções do middleware - empacotado e renderiza uma página de erro usando o manipulador de - exceção :doc:`/development/errors`. -* ``Cake\Routing\AssetMiddleware`` verifica se a solicitação está se referindo a um tema ou - arquivo estático do plug-in, como CSS, JavaScript ou arquivo de imagem armazenado na pasta - raiz da web de um plug-in ou na pasta correspondente a um Tema. -* ``Cake\Routing\Middleware\RoutingMiddleware`` usa o ``Router`` para analisar a URL - recebida e atribuir parâmetros de roteamento à solicitação. -* ``Cake\I18n\Middleware\LocaleSelectorMiddleware`` habilita a troca automática de idioma no - cabeçalho ``Accept-Language`` enviado pelo navegador. -* ``Cake\Http\Middleware\SecurityHeadersMiddleware`` facilita adicionar cabeçalhos relacionados - à segurança como ``X-Frame-Options`` às respostas. -* ``Cake\Http\Middleware\EncryptedCookieMiddleware`` oferece a capacidade de manipular cookies - criptografados, caso você precise manipular cookies com dados ofuscados. -* ``Cake\Http\Middleware\CsrfProtectionMiddleware`` adiciona proteção CSRF ao seu aplicativo. -* ``Cake\Http\Middleware\BodyParserMiddleware`` permite decodificar JSON, XML e outros corpos - de solicitação codificados com base no cabeçalho ``Content-Type``. -* ``Cake\Http\Middleware\CspMiddleware`` simplifica a adição de cabeçalhos de política de - segurança de conteúdo ao seu aplicativo. +* ``Cake\Error\Middleware\ErrorHandlerMiddleware`` captura exceções do middleware encapsulado e renderiza + uma página de erro usando o manipulador de exceções :doc:`/development/errors`. +* ``Cake\Routing\AssetMiddleware`` verifica se a solicitação está se referindo a um + arquivo de recursos de tema ou plugin, como um arquivo CSS, JavaScript ou de imagem armazenado + na pasta webroot de um plugin ou na pasta correspondente de um tema. +* ``Cake\Routing\Middleware\RoutingMiddleware`` usa o ``Router`` para analisar a + URL de entrada e atribuir parâmetros de roteamento à solicitação. +* ``Cake\I18n\Middleware\LocaleSelectorMiddleware`` permite a troca automática de idioma + a partir do cabeçalho ``Accept-Language`` enviado pelo navegador. +* ``Cake\Http\Middleware\EncryptedCookieMiddleware`` permite que você + manipule cookies criptografados caso precise manipular cookies com + dados ofuscados. +* ``Cake\Http\Middleware\BodyParserMiddleware`` permite decodificar JSON, XML + e outros corpos de solicitação codificados com base no cabeçalho ``Content-Type``. +* :doc:`Cake\Http\Middleware\HttpsEnforcerMiddleware ` + requer o uso de HTTPS. +* :doc:`Cake\Http\Middleware\CsrfProtectionMiddleware ` adiciona + proteção CSRF baseada em cookie de envio duplo ao seu aplicativo. +* :doc:`Cake\Http\Middleware\SessionCsrfProtectionMiddleware ` + adiciona proteção CSRF baseada em sessão ao seu aplicativo. +* :doc:`Cake\Http\Middleware\CspMiddleware ` + simplifica a adição de cabeçalhos Content-Security-Policy ao seu aplicativo. +* :doc:`Cake\Http\Middleware\SecurityHeadersMiddleware ` + possibilita a adição de cabeçalhos relacionados à segurança, como ``X-Frame-Options``, a + respostas. .. _using-middleware: @@ -72,12 +78,18 @@ middleware:: class Application extends BaseApplication { - public function middleware(MiddlewareQueue $middlwareQueue): MiddlewareQueue + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { - // Vincule o manipulador de erros à fila do middleware. + // Vincule o handler de erros à fila do middleware. $middlewareQueue->add(new ErrorHandlerMiddleware(Configure::read('Error'), $this)); - return $middlwareQueue; + // Adicionar middleware por nome de classe. + // A partir da versão 4.5.0, o middleware de nome de classe é resolvido opcionalmente + // usando o contêiner DI. Se a classe não for encontrada no + // contêiner, uma instância será criada pela fila de middleware. + $middlewareQueue->add(UserRateLimiting::class); + + return $middlewareQueue; } } @@ -111,9 +123,9 @@ Além de adicionar ao final do ``MiddlewareQueue``, você pode executar várias $layer ); -Além de aplicar o middleware a todo o aplicativo, você pode aplicar o -middleware a conjuntos específicos de rotas usando -:ref:`Scope Middleware `. +Se o seu middleware for aplicável apenas a um subconjunto de rotas ou controllers +individuais, você pode usar :ref:`Middleware com escopo de rota `, +ou :ref:`Middleware do controller `. Adicionando Middleware a partir de Plugins ------------------------------------------ @@ -218,81 +230,16 @@ aplicativo:: Roteamento de Middleware ======================== -O middleware de roteamento é responsável por aplicar as rotas no seu aplicativo e -resolver o: plug-in, o controlador e a ação que uma solicitação está pedindo. -Ele pode armazenar em cache a coleção de rotas usada no seu aplicativo para aumentar o -tempo de inicialização. Para habilitar o cache de rotas em, forneça o -:ref:`cache configuration ` desejado como um parâmetro:: +O middleware de roteamento é responsável por aplicar as rotas da sua aplicação e +resolver o plugin, o controller e a ação para a qual uma solicitação será enviada:: - // Em Application.php - public function middleware(MiddlewareQueue $middlwareQueue): MiddlewareQueue + // In Application.php + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { // ... - $middlwareQueue->add(new RoutingMiddleware($this, 'routing')); + $middlewareQueue->add(new RoutingMiddleware($this)); } -O exemplo acima usaria o mecanismo de cache ``routing`` para armazenar a coleção -de rotas gerada. - -.. _security-header-middleware: - -Middleware de Cabeçalho de Segurança -==================================== - -A camada ``Security Headers Middleware`` facilita a aplicação de cabeçalhos -relacionados à segurança em seu aplicativo. Depois de configurado, o middleware -pode aplicar os seguintes cabeçalhos às respostas: - -* ``X-Content-Type-Options`` -* ``X-Download-Options`` -* ``X-Frame-Options`` -* ``X-Permitted-Cross-Domain-Policies`` -* ``Referrer-Policy`` - -Esse middleware é configurado usando uma interface simples antes de ser aplicado à -pilha de middleware do seu aplicativo:: - - use Cake\Http\Middleware\SecurityHeadersMiddleware; - - $securityHeaders = new SecurityHeadersMiddleware(); - $securityHeaders - ->setCrossDomainPolicy() - ->setReferrerPolicy() - ->setXFrameOptions() - ->setXssProtection() - ->noOpen() - ->noSniff(); - - $middlwareQueue->add($securityHeaders); - -Middleware do Cabeçalho da Política de Segurança de Conteúdo -============================================================ - -O ``CspMiddleware`` facilita a adição de cabeçalhos referente a política de segurança de -conteúdo em seu aplicativo. Antes de usá-lo, você deve instalar o ``paragonie/csp-builder``: - -.. code-block::bash - - composer require paragonie/csp-builder - -Você pode configurar o middleware usando uma matriz ou passando um -objeto ``CSPBuilder`` integrado:: - - 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); - .. _encrypted-cookie-middleware: Middleware de Cookie Criptografado @@ -321,100 +268,6 @@ OpenSSL usando AES:: Os algoritmos de criptografia e o estilo de preenchimento usados pelo middleware do cookie são compatíveis com o ``CookieComponent`` de versões anteriores do CakePHP. -.. _csrf-middleware: - -Falsificação de Solicitação entre Sites (CSRF) Middleware -========================================================= - -A proteção CSRF pode ser aplicada a todo o aplicativo ou a escopos de roteamento específicos. - -.. note:: - - Você não pode usar as duas abordagens a seguir juntas; deve escolher apenas uma. - Se você usar as duas abordagens juntas, ocorrerá um erro de incompatibilidade de - token CSRF em cada solicitação `PUT` e` POST` - -Ao aplicar o ``CsrfProtectionMiddleware`` à pilha de middleware do Aplicativo, -você protege todas as ações no aplicativo:: - - // Em src/Application.php - use Cake\Http\Middleware\CsrfProtectionMiddleware; - - public function middleware($middlwareQueue) { - $options = [ - // ... - ]; - $csrf = new CsrfProtectionMiddleware($options); - - $middlwareQueue->add($csrf); - - return $middlwareQueue; - } - -Ao aplicar o ``CsrfProtectionMiddleware`` aos escopos de roteamento, você pode -incluir ou excluir grupos de rotas específicos:: - - // Em src/Application.php - use Cake\Http\Middleware\CsrfProtectionMiddleware; - - public function routes($routes) { - $options = [ - // ... - ]; - $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options)); - parent::routes($routes); - } - - // Em config/routes.php - Router::scope('/', function (RouteBuilder $routes) { - $routes->applyMiddleware('csrf'); - }); - - -As opções podem ser passadas para o construtor do middleware. -As opções de configuração disponíveis são: - -- ``cookieName`` O nome do cookie a ser enviado. O padrão é `` csrfToken``. -- ``expiry`` Quanto tempo o token CSRF deve durar. O padrão é a sessão do navegador. -- ``secure`` Se o cookie será ou não definido com o sinalizador Secure. Isso é, -   o cookie será definido apenas em uma conexão HTTPS e qualquer tentativa no - HTTP normal falhará. O padrão é ``false``. -- ``httpOnly`` Se o cookie será ou não definido com o sinalizador HttpOnly. O padrão é ``false``. -- ``field`` O campo do formulário a ser verificado. O padrão é ``_csrfToken``. - Alterar isso também exigirá a configuração do FormHelper. - -Quando ativado, você pode acessar o token CSRF atual no objeto de solicitação:: - - $token = $this->request->getParam('_csrfToken'); - -.. note:: - - Você deve aplicar o middleware de proteção CSRF apenas para URLs que manipulam solicitações - com estado usando cookies/sessão. Solicitações sem estado, por ex. ao desenvolver uma API, - não são afetados pelo CSRF; portanto, o middleware não precisa ser aplicado a essas URLs. - -Integração com FormHelper -------------------------- - -O ``CsrfProtectionMiddleware`` se integra perfeitamente ao ``FormHelper``. Cada vez -que você cria um formulário com ``FormHelper``, ele insere um campo oculto que contém o token CSRF. - -.. note:: - - Ao usar a proteção CSRF, você sempre deve iniciar seus formulários com o ``FormHelper``. - Caso contrário, será necessário criar manualmente entradas ocultas em cada um dos seus formulários. - -Solicitações de Proteção CSRF e AJAX ------------------------------------- - -Além de solicitar parâmetros de dados, os tokens CSRF podem ser enviados por meio -de um cabeçalho especial ``X-CSRF-Token``. O uso de um cabeçalho geralmente facilita -a integração de um token CSRF com aplicativos pesados de JavaScript ou endpoints de API -baseados em XML/JSON. - -O token CSRF pode ser obtido através do cookie ``csrfToken``. - - .. _body-parser-middleware: Body Parser Middleware From 413e305278918cf240d6dc6c35e5cd984321bc30 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 18 Sep 2025 06:26:10 -0300 Subject: [PATCH 40/46] [refact]: translated (pt/security/content-security-policy.rst) --- pt/security/content-security-policy.rst | 53 +++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 pt/security/content-security-policy.rst diff --git a/pt/security/content-security-policy.rst b/pt/security/content-security-policy.rst new file mode 100644 index 0000000000..e7916665ea --- /dev/null +++ b/pt/security/content-security-policy.rst @@ -0,0 +1,53 @@ +Middleware de Política de Segurança de Conteúdo +############################################### + +O ``CspMiddleware`` simplifica a adição de cabeçalhos Content-Security-Policy +em seu aplicativo. Antes de usá-lo, você deve instalar ``paragonie/csp-builder``: + +.. code-block:: bash + + composer require paragonie/csp-builder + +Você pode então configurar o middleware usando um array ou passando um +objeto ``CSPBuilder`` construído:: + + 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); + +Se desejar usar uma configuração CSP mais restrita, você pode habilitar regras CSP baseadas +em nonce com as opções ``scriptNonce`` e ``styleNonce``. Quando habilitadas, +essas opções modificarão sua política CSP e definirão os atributos ``cspScriptNonce`` e +``cspStyleNonce`` na solicitação. Esses atributos são aplicados ao atributo ``nonce`` de todos +os elementos de script e link CSS criados pelo ``HtmlHelper``. Isso simplifica a adoção de políticas que usam +um `nonce-base64 +`__ +e ``strict-dynamic`` para maior segurança e manutenção mais fácil:: + + + $policy = [ + // Deve existir mesmo se estiver vazio para definir nonce para script-src + 'script-src' => [], + 'style-src' => [], + ]; + // Habilitar adição automática de nonce em scripts e tags de links CSS. + $csp = new CspMiddleware($policy, [ + 'scriptNonce' => true, + 'styleNonce' => true, + ]); + $middlewareQueue->add($csp); + +.. meta:: + :title lang=pt: Middleware de Política de Segurança de Conteúdo + :keywords lang=pt: segurança, Política de Segurança de Conteúdo, csp, middleware, cross-site scripting From e578c610d236603e859dd5e7b8c1b748eb40a241 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 18 Sep 2025 06:34:43 -0300 Subject: [PATCH 41/46] [refact]: translated (pt/security/https-enforcer.rst) --- pt/security/https-enforcer.rst | 65 ++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 pt/security/https-enforcer.rst diff --git a/pt/security/https-enforcer.rst b/pt/security/https-enforcer.rst new file mode 100644 index 0000000000..0342871d5a --- /dev/null +++ b/pt/security/https-enforcer.rst @@ -0,0 +1,65 @@ +.. _https-enforcer-middleware: + +Middleware do Executor HTTPS +############################ + +Se você deseja que seu aplicativo esteja disponível apenas por meio de conexões HTTPS, você pode +usar o ``HttpsEnforcerMiddleware``:: + + use Cake\Http\Middleware\HttpsEnforcerMiddleware; + + // Sempre gere uma exceção e nunca redirecione. + $https = new HttpsEnforcerMiddleware([ + 'redirect' => false, + ]); + + // Enviar um código de status 302 ao redirecionar + $https = new HttpsEnforcerMiddleware([ + 'redirect' => true, + 'statusCode' => 302, + ]); + + // Envie cabeçalhos adicionais na resposta de redirecionamento. + $https = new HttpsEnforcerMiddleware([ + 'headers' => ['X-Https-Upgrade' => 1], + ]); + + // Desabilite a aplicação de HTTPs quando ``debug`` estiver ativado. + $https = new HttpsEnforcerMiddleware([ + 'disableOnDebug' => true, + ]); + + // Confie somente nos cabeçalhos HTTP_X_ dos servidores listados. + $https = new HttpsEnforcerMiddleware([ + 'trustProxies' => ['192.168.1.1'], + ]); + +Se uma requisição não HTTP for recebida e não utilizar GET, uma exceção ``BadRequestException`` será gerada. + +NOTA: O cabeçalho Strict-Transport-Security é ignorado pelo navegador quando seu site é acessado apenas via HTTP. +Assim que seu site for acessado via HTTPS sem erros de certificado, o navegador saberá que seu site é compatível com +HTTPS e respeitará o cabeçalho Strict-Transport-Security. + +Adicionando Strict-Transport-Security +===================================== + +Quando seu aplicativo requer SSL, é uma boa ideia definir o cabeçalho +``Strict-Transport-Security``. Este valor de cabeçalho é armazenado em cache no +navegador e informa aos navegadores que eles devem sempre se conectar via HTTPS. +Você pode configurar este cabeçalho com a opção ``hsts``:: + + $https = new HttpsEnforcerMiddleware([ + 'hsts' => [ + // Por quanto tempo o valor do cabeçalho deve ser armazenado em cache. + 'maxAge' => 60 * 60 * 24 * 365, + // esta política deve ser aplicada a subdomínios? + 'includeSubDomains' => true, + // O valor do cabeçalho deve ser armazenável em cache no serviço de pré-carregamento HSTS do Google? + // Embora não faça parte da especificação, é amplamente implementado. + 'preload' => true, + ], + ]); + +.. meta:: + :title lang=pt: Middleware do Executor HTTPS + :keywords lang=pt: segurança, https, requisição https From c2a563ffa76abbc3ece119abb870858727f0d958 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 18 Sep 2025 06:37:22 -0300 Subject: [PATCH 42/46] [refact]: translated (pt/security/security-headers.rst) --- pt/security/security-headers.rst | 36 ++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 pt/security/security-headers.rst diff --git a/pt/security/security-headers.rst b/pt/security/security-headers.rst new file mode 100644 index 0000000000..c50fcb3ce6 --- /dev/null +++ b/pt/security/security-headers.rst @@ -0,0 +1,36 @@ +.. _security-header-middleware: + +Middleware de Cabeçalho de Segurança +#################################### + +A camada ``SecurityHeaderMiddleware`` permite aplicar cabeçalhos +relacionados à segurança à sua aplicação. Uma vez configurado, o middleware pode aplicar os seguintes +cabeçalhos às respostas: + +* ``X-Content-Type-Options`` +* ``X-Download-Options`` +* ``X-Frame-Options`` +* ``Referrer-Policy`` +* ``Permissions-Policy`` + +Este middleware é configurado usando uma interface fluente antes de ser aplicado à pilha de +middleware do seu aplicativo:: + + use Cake\Http\Middleware\SecurityHeadersMiddleware; + + $securityHeaders = new SecurityHeadersMiddleware(); + $securityHeaders + ->setReferrerPolicy() + ->setXFrameOptions() + ->noOpen() + ->noSniff(); + + $middlewareQueue->add($securityHeaders); + +Aqui está uma lista de `cabeçalhos HTTP comuns `__, +e as `configurações recomendadas `__ +da Mozilla para proteger aplicativos web. + +.. meta:: + :title lang=pt: Middleware de Cabeçalho de Segurança + :keywords lang=pt: x-frame-options, cross-domain, referrer-policy, download-options, middleware, content-type-options From 38ff2583f2616929ee16a789deb7ae674bf967b6 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Thu, 18 Sep 2025 06:49:11 -0300 Subject: [PATCH 43/46] [refact]: translated (pt/security/csrf.rst) --- pt/security/csrf.rst | 197 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) create mode 100644 pt/security/csrf.rst diff --git a/pt/security/csrf.rst b/pt/security/csrf.rst new file mode 100644 index 0000000000..c92ee245f8 --- /dev/null +++ b/pt/security/csrf.rst @@ -0,0 +1,197 @@ +Proteção CSRF +############# + +Falsificações de Solicitações Cruzadas (CSRF) são uma classe de exploração em que +comandos não autorizados são executados em nome de um usuário autenticado sem seu +conhecimento ou consentimento. + +O CakePHP oferece duas formas de proteção contra CSRF: + +* ``SessionCsrfProtectionMiddleware`` armazena tokens CSRF na sessão. Isso + requer que seu aplicativo abra a sessão em cada solicitação, com + efeitos colaterais. Os benefícios dos tokens CSRF baseados em sessão são que eles + têm como escopo um usuário específico e são válidos apenas enquanto a sessão estiver ativa. +* ``CsrfProtectionMiddleware`` armazena tokens CSRF em um cookie. O uso de um cookie + permite que as verificações de CSRF sejam feitas sem nenhum estado no servidor. Os valores dos cookies + são verificados quanto à autenticidade usando uma verificação HMAC. No entanto, devido à sua + natureza sem estado, os tokens CSRF são reutilizáveis ​​entre usuários e sessões. + +.. note:: + + Você não pode usar as duas abordagens a seguir juntas, você deve escolher + apenas uma. Se você usar as duas abordagens juntas, um erro de incompatibilidade de token CSRF + ocorrerá em cada solicitação `PUT` e `POST` + +.. _csrf-middleware: + +Middleware de falsificação de solicitação entre sites (CSRF) +============================================================ + +A proteção CSRF pode ser aplicada a toda a sua aplicação ou a escopos de roteamento +específicos. Ao aplicar um middleware CSRF à sua pilha de middleware +de aplicação, você protege todas as ações na aplicação:: + + // in src/Application.php + // Para tokens CSRF baseados em cookies. + use Cake\Http\Middleware\CsrfProtectionMiddleware; + + // Para tokens CSRF baseados em sessão. + use Cake\Http\Middleware\SessionCsrfProtectionMiddleware; + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $options = [ + // ... + ]; + $csrf = new CsrfProtectionMiddleware($options); + // ou + $csrf = new SessionCsrfProtectionMiddleware($options); + + $middlewareQueue->add($csrf); + + return $middlewareQueue; + } + +Ao aplicar a proteção CSRF aos escopos de roteamento, você pode aplicar CSRF +condicionalmente a grupos específicos de rotas:: + + // in src/Application.php + use Cake\Http\Middleware\CsrfProtectionMiddleware; + use Cake\Routing\RouteBuilder; + + public function routes(RouteBuilder $routes) : void + { + $options = [ + // ... + ]; + $routes->registerMiddleware('csrf', new CsrfProtectionMiddleware($options)); + parent::routes($routes); + } + + // in config/routes.php + $routes->scope('/', function (RouteBuilder $routes) { + $routes->applyMiddleware('csrf'); + }); + +Opções de middleware CSRF baseadas em cookies +--------------------------------------------- + +As opções de configuração disponíveis são: + +- ``cookieName`` O nome do cookie a ser enviado. O padrão é ``csrfToken``. +- ``expiry`` A duração do token CSRF. O padrão é a sessão do navegador. +- ``secure`` Se o cookie será ou não definido com o sinalizador Secure. Ou seja, + o cookie será definido apenas em uma conexão HTTPS e qualquer tentativa via HTTP normal + falhará. O padrão é ``false``. +- ``httponly`` Se o cookie será ou não definido com o sinalizador HttpOnly. + O padrão é ``false``. Antes da versão 4.1.0, use a opção ``httpOnly``. +- ``samesite`` Permite declarar se o seu cookie deve ser restrito a um + contexto de primeira parte ou mesmo site. Os valores possíveis são ``Lax``, ``Strict`` e + ``None``. O padrão é ``null``. +- ``field`` O campo do formulário a ser verificado. O padrão é ``_csrfToken``. Alterar isso + também exigirá a configuração do FormHelper. + +Opções de middleware CSRF baseadas em sessão +-------------------------------------------- + +As opções de configuração disponíveis são: + +- ``key`` A chave de sessão a ser usada. O padrão é `csrfToken` +- ``field`` O campo do formulário a ser verificado. Alterar isso também exigirá a configuração do + FormHelper. + +Quando habilitado, você pode acessar o token CSRF atual no objeto de solicitação:: + + $token = $this->request->getAttribute('csrfToken'); + +Caso você precise girar ou substituir o token CSRF da sessão, você pode fazer isso com:: + + $this->request = SessionCsrfProtectionMiddleware::replaceToken($this->request); + +.. versionadded:: 4.3.0 + O método ``replaceToken`` foi adicionado. + +Ignorando verificações de CSRF para ações específicas +----------------------------------------------------- + +Ambas as implementações de middleware CSRF permitem o recurso de retorno de chamada de verificação de salto +para um controle mais refinado sobre URLs para as quais a verificação de token CSRF +deve ser feita:: + + // in src/Application.php + use Cake\Http\Middleware\CsrfProtectionMiddleware; + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $csrf = new CsrfProtectionMiddleware(); + + // A verificação do token será ignorada quando o retorno de chamada retornar `true`. + $csrf->skipCheckCallback(function ($request) { + // Skip token check for API URLs. + if ($request->getParam('prefix') === 'Api') { + return true; + } + }); + + // Garanta que o middleware de roteamento seja adicionado à fila antes do middleware de proteção CSRF. + $middlewareQueue->add($csrf); + + return $middlewareQueue; + } + +.. note:: + + Você deve aplicar o middleware de proteção CSRF apenas para rotas que lidam + com solicitações com estado usando cookies/sessões. Por exemplo, ao desenvolver + uma API, solicitações sem estado que não usam cookies para autenticação não são + afetadas pelo CSRF, portanto, o middleware não precisa ser aplicado a essas rotas. + +Integração com FormHelper +------------------------- + +O ``CsrfProtectionMiddleware`` integra-se perfeitamente com o ``FormHelper``. Cada +vez que você criar um formulário com o ``FormHelper``, ele inserirá um campo oculto contendo +o token CSRF. + +.. note:: + + Ao usar a proteção CSRF, você deve sempre iniciar seus formulários com o + ``FormHelper``. Caso contrário, você precisará criar manualmente entradas ocultas em + cada um dos seus formulários. + +Proteção CSRF e solicitações AJAX +--------------------------------- + +Além dos parâmetros de dados da solicitação, os tokens CSRF podem ser enviados por meio de +um cabeçalho especial ``X-CSRF-Token``. O uso de um cabeçalho geralmente facilita a integração +de um token CSRF com aplicativos JavaScript pesados ​​ou endpoints de API baseados em XML/JSON. + +O Token CSRF pode ser obtido em JavaScript por meio do Cookie ``csrfToken`` ou em PHP +por meio do atributo do objeto de solicitação ``csrfToken``. Usar o cookie pode ser mais fácil +quando seu código JavaScript reside em arquivos separados dos templates de visualização do CakePHP +e quando você já possui a funcionalidade de analisar cookies via JavaScript. + +Se você possui arquivos JavaScript separados, mas não deseja lidar com o tratamento de cookies, +você pode, por exemplo, definir o token em uma variável JavaScript global em seu layout, +definindo um bloco de script como este:: + + echo $this->Html->scriptBlock(sprintf( + 'var csrfToken = %s;', + json_encode($this->request->getAttribute('csrfToken')) + )); + +Você pode então acessar o token como ``csrfToken`` ou ``window.csrfToken`` em qualquer arquivo de script +carregado após este bloco de script. + +Outra alternativa seria colocar o token em uma meta tag personalizada como esta:: + + echo $this->Html->meta('csrfToken', $this->request->getAttribute('csrfToken')); + +que pode ser acessado em seus scripts procurando pelo elemento ``meta`` com +o nome ``csrfToken``, que pode ser tão simples quanto isso ao usar jQuery:: + + var csrfToken = $('meta[name="csrfToken"]').attr('content'); + +.. meta:: + :title lang=pt: Proteção CSRF + :keywords lang=pt: segurança, csrf, cross site request forgery, middleware, sessão From e54ecb7fee94b3ee15646bfbd16ee4a3784d661b Mon Sep 17 00:00:00 2001 From: kromodoro Date: Fri, 19 Sep 2025 17:42:33 -0300 Subject: [PATCH 44/46] [refact]: translation (pt/controllers/components/form-protection.rst) --- pt/controllers/components/form-protection.rst | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 pt/controllers/components/form-protection.rst diff --git a/pt/controllers/components/form-protection.rst b/pt/controllers/components/form-protection.rst new file mode 100644 index 0000000000..cce5608cbb --- /dev/null +++ b/pt/controllers/components/form-protection.rst @@ -0,0 +1,167 @@ +Componente FormProtection +########################## + +.. php:class:: FormProtection(ComponentCollection $collection, array $config = []) + +O componente FormProtection fornece proteção contra adulteração de dados de formulário. + +Como todos os componentes, ele é configurado por meio de diversos parâmetros configuráveis. +Todas essas propriedades podem ser definidas diretamente ou por meio de métodos setter +de mesmo nome nos métodos ``initialize()`` ou ``beforeFilter()`` do seu controller. + +Se você estiver usando outros componentes que processam dados de formulário em seus retornos +de chamada ``startup()``, certifique-se de colocar o componente FormProtection antes desses +componentes no seu método ``initialize()``. + +.. note:: + + Ao usar o Componente FormProtection, você **deve** usar o FormHelper para criar + seus formulários. Além disso, você **não** deve substituir nenhum dos atributos "name" + dos campos. O Componente FormProtection procura por certos indicadores que são + criados e gerenciados pelo FormHelper (especialmente aqueles criados em + :php:meth:`~Cake\\View\\Helper\\FormHelper::create()` e + :php:meth:`~Cake\\View\\Helper\\FormHelper::end()`). Alterar dinamicamente + os campos enviados em uma solicitação POST, como desabilitar, excluir + ou criar novos campos via JavaScript, provavelmente causará falha na validação do token + do formulário. + +Prevenção de adulteração de formulários +======================================= + +Por padrão, o ``FormProtectionComponent`` impede que os usuários alterem +formulários de maneiras específicas. Isso impedirá o seguinte: + +* A ação do formulário (URL) não pode ser modificada. +* Campos desconhecidos não podem ser adicionados ao formulário. +* Campos não podem ser removidos do formulário. +* Valores em entradas ocultas não podem ser modificados. + +A prevenção desses tipos de adulteração é realizada trabalhando com o ``FormHelper`` +e rastreando quais campos estão em um formulário. Os valores dos campos ocultos também são +rastreados. Todos esses dados são combinados e transformados em um hash, e os campos de token ocultos +são inseridos automaticamente nos formulários. Quando um formulário é enviado, +o ``FormProtectionComponent`` usará os dados POST para construir a mesma estrutura +e comparar o hash. + +.. note:: + + O FormProtectionComponent **não** impedirá que opções selecionadas sejam + adicionadas/alteradas. Também não impedirá que opções de rádio sejam adicionadas/alteradas. + +Uso +=== + +A configuração do componente de proteção de formulário geralmente é feita nos +callbacks ``initialize()`` ou ``beforeFilter()`` do controller. + +As opções disponíveis são: + +validate + Defina como ``false`` para pular completamente a validação de solicitações POST + , essencialmente desativando a validação do formulário. + +unlockedFields + Defina uma lista de campos de formulário a serem excluídos da validação POST. Os campos podem ser + desbloqueados no Componente ou com + :php:meth:`FormHelper::unlockField()`. Campos que foram desbloqueados + não precisam fazer parte do POST e campos desbloqueados ocultos não têm + seus valores verificados. + +unlockedActions + Ações a serem excluídas das verificações de validação POST. + +validationFailureCallback + Callback para chamar em caso de falha de validação. Deve ser um Closure válido. + Não definido por padrão, caso em que uma exceção é lançada em caso de falha de validação. + +Desabilitando verificações de adulteração de formulários +======================================================== + +:: + + namespace App\Controller; + + use App\Controller\AppController; + use Cake\Event\EventInterface; + + class WidgetsController extends AppController + { + public function initialize(): void + { + parent::initialize(); + + $this->loadComponent('FormProtection'); + } + + public function beforeFilter(EventInterface $event): void + { + parent::beforeFilter($event); + + if ($this->request->getParam('prefix') === 'Admin') { + $this->FormProtection->setConfig('validate', false); + } + } + } + +O exemplo acima desabilitaria a prevenção de adulteração de formulários para rotas +prefixadas pelo administrador. + +Desabilitando a adulteração de formulários para ações específicas +================================================================= + +Pode haver casos em que você queira desabilitar a prevenção de adulteração de formulário para uma +ação (por exemplo, solicitações AJAX). Você pode "desbloquear" essas ações listando-as em +``$this->FormProtection->setConfig('unlockedActions', ['edit']);`` no seu ``beforeFilter()``:: + + namespace App\Controller; + + use App\Controller\AppController; + use Cake\Event\EventInterface; + + class WidgetController extends AppController + { + public function initialize(): void + { + parent::initialize(); + $this->loadComponent('FormProtection'); + } + + public function beforeFilter(EventInterface $event): void + { + parent::beforeFilter($event); + + $this->FormProtection->setConfig('unlockedActions', ['edit']); + } + } + +Este exemplo desabilitaria todas as verificações de segurança para a ação de edição. + +Lidando com falhas de validação por meio de retornos de chamada +=============================================================== + +Se a validação da proteção do formulário falhar, resultará em um erro 400 por padrão. +Você pode configurar esse comportamento definindo a opção de configuração ``validationFailureCallback`` +para uma função de retorno de chamada no controller. + +Ao configurar um método de retorno de chamada, você pode personalizar como o processo de tratamento de falhas +funciona:: + + use Cake\Controller\Exception\FormProtectionException; + + public function beforeFilter(EventInterface $event): void + { + parent::beforeFilter($event); + + $this->FormProtection->setConfig( + 'validationFailureCallback', + // Antes do uso do 5.2 Cake\Http\Exception\BadRequestException. + function (FormProtectionException $exception) { + // Você pode retornar uma instância de resposta ou lançar a exceção + // recebida como argumento. + } + ); + } + +.. meta:: + :title lang=pt: FormProtection + :keywords lang=pt: parâmetros configuráveis,form protection component,configuração de parâmetros,recursos de proteção,segurança mais apertada,php class,meth,array,submissão,security class,disable security,unlockActions From 71183861a01cdf6311a078ac20bd737b8acab251 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Sun, 5 Oct 2025 21:09:36 -0300 Subject: [PATCH 45/46] [feat]: package of translated files --- pt/404.rst | 8 +- pt/appendices.rst | 36 +- pt/appendices/cakephp-development-process.rst | 112 +- pt/appendices/glossary.rst | 4 +- pt/bake.rst | 4 +- pt/chronos.rst | 4 +- pt/console-commands.rst | 146 +- pt/console-commands/input-output.rst | 254 +- pt/console-commands/option-parsers.rst | 286 +- pt/console-commands/plugin.rst | 46 +- pt/console-commands/repl.rst | 26 +- pt/console-commands/routes.rst | 32 +- pt/console-commands/schema-cache.rst | 22 +- pt/console-commands/server.rst | 26 +- pt/contents.rst | 4 +- pt/contributing.rst | 6 +- pt/contributing/backwards-compatibility.rst | 207 +- .../cakephp-coding-conventions.rst | 534 ++-- pt/controllers.rst | 689 +++-- pt/controllers/components/flash.rst | 99 +- pt/controllers/pages-controller.rst | 22 +- pt/controllers/pagination.rst | 224 +- pt/controllers/request-response.rst | 1355 ++++++--- pt/core-libraries/app.rst | 115 +- pt/core-libraries/caching.rst | 100 +- pt/core-libraries/collections.rst | 718 ++--- pt/core-libraries/email.rst | 2 +- pt/core-libraries/events.rst | 220 +- pt/core-libraries/form.rst | 167 +- .../global-constants-and-functions.rst | 265 +- pt/core-libraries/hash.rst | 285 +- pt/core-libraries/httpclient.rst | 530 ++-- pt/core-libraries/inflector.rst | 137 +- .../internationalization-and-localization.rst | 699 +++-- pt/core-libraries/logging.rst | 547 +++- pt/core-libraries/number.rst | 277 +- pt/core-libraries/plugin.rst | 36 +- pt/core-libraries/registry-objects.rst | 59 +- pt/core-libraries/security.rst | 115 +- pt/core-libraries/text.rst | 387 ++- pt/core-libraries/time.rst | 447 ++- pt/core-libraries/validation.rst | 638 +++- pt/core-libraries/xml.rst | 189 +- pt/debug-kit.rst | 2 +- pt/deployment.rst | 182 +- pt/development/application.rst | 138 +- pt/development/configuration.rst | 815 +++-- pt/development/debugging.rst | 63 +- pt/development/dependency-injection.rst | 260 +- pt/development/errors.rst | 460 ++- pt/development/rest.rst | 206 +- pt/development/routing.rst | 1606 ++++++---- pt/development/sessions.rst | 331 +- pt/development/testing.rst | 1960 ++++++------ pt/elasticsearch.rst | 2 +- pt/epub-contents.rst | 12 +- pt/index.rst | 50 +- pt/installation.rst | 655 ++-- pt/intro.rst | 175 +- pt/intro/cakephp-folder-structure.rst | 98 +- pt/intro/conventions.rst | 295 +- pt/intro/where-to-get-help.rst | 101 +- pt/orm.rst | 68 +- pt/orm/behaviors/counter-cache.rst | 72 +- pt/orm/behaviors/timestamp.rst | 44 +- pt/orm/behaviors/translate.rst | 359 ++- pt/orm/behaviors/tree.rst | 49 +- pt/orm/database-basics.rst | 837 +++--- pt/orm/deleting-data.rst | 82 +- pt/orm/entities.rst | 409 +-- pt/orm/query-builder.rst | 1 - pt/orm/retrieving-data-and-resultsets.rst | 25 +- pt/orm/saving-data.rst | 121 +- pt/orm/table-objects.rst | 524 +++- pt/orm/validation.rst | 786 ++++- pt/pdf-contents.rst | 14 +- pt/plugins.rst | 10 +- pt/quickstart.rst | 6 +- pt/security.rst | 10 +- pt/standalone-packages.rst | 44 +- pt/views.rst | 4 +- pt/views/cells.rst | 107 + pt/views/helpers.rst | 390 ++- pt/views/helpers/breadcrumbs.rst | 193 +- pt/views/helpers/flash.rst | 72 +- pt/views/helpers/form.rst | 2649 ++++++++++++++++- pt/views/helpers/html.rst | 839 +++++- pt/views/helpers/number.rst | 23 +- pt/views/helpers/paginator.rst | 577 +++- pt/views/helpers/text.rst | 92 +- pt/views/helpers/time.rst | 49 +- pt/views/helpers/url.rst | 161 +- pt/views/json-and-xml-views.rst | 253 +- pt/views/themes.rst | 2 +- 94 files changed, 18685 insertions(+), 7677 deletions(-) diff --git a/pt/404.rst b/pt/404.rst index 3d3459dd4d..682e7d9dab 100644 --- a/pt/404.rst +++ b/pt/404.rst @@ -1,7 +1,7 @@ :orphan: True -Página não encontrada -##################### +Não Encontrado +############## -A página que você está procurando não foi encontrada. Tente usar o campo de -busca para localizar o que você procura. +A página que você está procurando não pode ser encontrada. Tente usar a barra de pesquisa para encontrar +o que você está procurando. diff --git a/pt/appendices.rst b/pt/appendices.rst index 804abaa20c..55710fe6a7 100644 --- a/pt/appendices.rst +++ b/pt/appendices.rst @@ -2,33 +2,33 @@ Apêndices ######### Os apêndices contêm informações sobre os novos recursos -introduzidos em cada versão e a forma de executar a migração entre versões. +introduzidos em cada versão e o caminho de migração entre versões. -Guia de Migração -================ +Guias de Migração +================= :doc:`appendices/migration-guides` -Retrocompatibilidade por Adaptação -================================== +Compatibilidade Retroativa com Shim +==================================== -Se você precisar/quiser corrigir o comportamento do 4.x ou migrar parcialmente em etapas, confira -o plugin `Shim `__ que pode ajudar a mitigar algumas alterações que quebram o BC. +Se você precisa/quer adicionar o comportamento do 4.x, ou migrar parcialmente em etapas, confira +o `Plugin Shim `__ que pode ajudar a mitigar algumas mudanças que quebram a compatibilidade retroativa. -Compatibilidade Futura -====================== +Compatibilidade Futura com Shim +================================ -A correção de compatibilidade com versões anteriores pode preparar seu aplicativo 4.x para o próximo grande -lançamento (5.x). +A compatibilidade futura com shim pode preparar sua aplicação 4.x para a próxima versão principal +(5.x). -Se você já deseja aplicar o comportamento do 5.x no 4.x, confira o `plugin Shim -`__. Este plugin visa mitigar -algumas falhas de compatibilidade com versões anteriores e ajudar a retroportar recursos do 5.x para -o 4.x. Quanto mais próximo o seu aplicativo 3.x estiver do 4.x, menor será a diferença -das mudanças e mais suave será a atualização final. +Se você já quer adicionar o comportamento do 5.x no 4.x, confira o `Plugin Shim +`__. Este plugin tem como objetivo mitigar +algumas quebras de compatibilidade retroativa e ajudar a fazer backport de recursos do 5.x para +4.x. Quanto mais próxima sua aplicação 3.x estiver do 4.x, menor será o diff de +mudanças, e mais suave será a atualização final. Informações Gerais -=================== +================== .. toctree:: :maxdepth: 1 @@ -38,4 +38,4 @@ Informações Gerais .. meta:: :title lang=pt: Apêndices - :keywords lang=pt: guia de migração,rota de migração,novas funcionalidades,glossário, + :keywords lang=pt: guia de migração,caminho de migração,novos recursos,glossário diff --git a/pt/appendices/cakephp-development-process.rst b/pt/appendices/cakephp-development-process.rst index 4dc57c48b9..a6cd6f66ac 100644 --- a/pt/appendices/cakephp-development-process.rst +++ b/pt/appendices/cakephp-development-process.rst @@ -1,53 +1,67 @@ -Processo de desenvolvimento no CakePHP +Processo de Desenvolvimento do CakePHP ###################################### -Aqui tentamos explicar o processo utilizado no desenvolvimento com o framework -CakePHP. Nós dependemos fortemente da interação por tickets e no canal do IRC. -O IRC é o melhor lugar para encontrar membros do -`time de desenvolvimento `_ e discutir -idéias, o ultimo código e fazer comentários gerais. Se algo mais formal tem que -ser proposto ou exite um problema com uma versão, o sistema de tickets é o -melhor lugar para compartilhar seus pensamentos. - -Nós atualmente mantemos 4 versões do CakePHP. - -- **versões tageadas** : Versões tageadas são destinadas para produção onde uma - estabilidade maior é mais importante do que funcionalidades. Questões sobre - versões tageadas serão resolvidas no branch relacionado e serão parte do - próximo release. -- **branch principal** : Esses branches são onde todas as correções são - fundidas. Versões estáveis são rotuladas apartir desses branches. ``master`` é - o principal branch para a versão atual. ``2.x`` é o branch de manutenção para - a versão 2.x. Se você está usando versões estáveis e precisa de correções que - não chegaram em uma versão tageada olhe aqui. -- **desenvolvimento** : O branch de desenvolvimento contém sempre as ultimas - correções e funcionalidades. Eles são nomeados pela versão a qual se destinam, - ex: *3.next*. Uma vez que estas braches estão estáveis elas são fundidas na - branch principal da versão. -- **branches de funcionalidades** : Branches de funcionalidade contém trabalhos - que estão sendo desenvolvidos ou possivelmente instáveis e são recomendadas - apenas para usuários avançados interessados e dispostos a contribuir com a - comunidade. Branches de funcionalidade são nomeadas pela seguinte convenção - *versão-funcionalidade*. Um exemplo seria *3.3-router* Que conteria novas - funcionalidades para o Router na 3.3 - -Esperamos que isso te ajudará a entender que versão é correta pra você. -Uma vez que escolhida a versão você pode se sentir compelido a reportar um erro -ou fazer comentários gerais no código. - -- Se você está usando uma versão estável ou de manutenção, por favor envie - tickets ou discuta conosco no IRC. -- Se você está usando uma branch de desenvolvimento ou funcionalidade, o - primeiro lugar para ir é o IRC. Se você tem um comentário e não consegue - entrar no IRC depois de um ou dois dias, envie um ticket. - -Se você encontrar um problema, a melhor resposta é escrever um teste. O melhor -conselho que podemos oferecer em escrever testes é olhar nos que estão no núcleo -do projeto. - -E sempre, se você tiver alguma questão ou cometários, nos visite no #cakephp no -irc.freenode.net +Os projetos CakePHP seguem amplamente o `semver `__. Isso significa que: + +- As versões são numeradas na forma de **A.B.C** +- Versões **A** são *versões principais*. Elas contêm mudanças incompatíveis e exigirão + quantidades não triviais de trabalho para atualizar de uma versão **A** inferior. +- Versões **A.B** são *versões de funcionalidades*. Cada versão será compatível com versões + anteriores, mas pode introduzir novas depreciações. Se uma mudança incompatível for + absolutamente necessária, será anotada no guia de migração para essa versão. +- Versões **A.B.C** são versões de *correção*. Elas devem ser compatíveis com a versão + de correção anterior. A exceção a esta regra é se um problema de segurança for + descoberto e a única solução for quebrar uma API existente. + +Veja :doc:`/contributing/backwards-compatibility` para o que consideramos ser +compatível com versões anteriores e mudanças incompatíveis. + +Versões Principais +================== + +Versões principais introduzem novos recursos e podem remover funcionalidades depreciadas em +uma versão anterior. Essas versões vivem em branches ``next`` que correspondem ao seu +número de versão, como ``5.next``. Uma vez lançadas, elas são promovidas para ``master`` +e então a branch ``5.next`` é usada para futuras versões de funcionalidades. + +Versões de Funcionalidades +=========================== + +Versões de funcionalidades são onde novos recursos ou extensões para recursos existentes são +entregues. Cada série de versão que recebe atualizações terá uma branch ``next``. Por +exemplo ``4.next``. Se você gostaria de contribuir com um novo recurso, por favor, direcione +para essas branches. + +Versões de Correção +==================== + +Versões de correção corrigem bugs em código/documentação existente e devem sempre ser +compatíveis com versões de correção anteriores da mesma versão de funcionalidade. Essas +versões são criadas a partir das branches estáveis. Branches estáveis são frequentemente nomeadas +após a série de versão, como ``3.x``. + +Cadência de Versões +=================== + +- *Versões Principais* são entregues aproximadamente a cada dois a três anos. Esse prazo + nos força a ser deliberados e cuidadosos com nossas mudanças incompatíveis e dá + tempo para a comunidade acompanhar sem sentir que está sendo deixada para trás. +- *Versões de Funcionalidades* são entregues a cada cinco a oito meses. +- *Versões de Correção* são inicialmente entregues a cada duas semanas. À medida que uma versão de funcionalidade + amadurece, essa cadência relaxa para um cronograma mensal. + +Política de Depreciação +======================== + +Antes que um recurso possa ser removido em uma versão principal, ele precisa ser depreciado. +Quando um comportamento é depreciado na versão **A.x**, ele continuará a funcionar para +o restante de todas as versões **A.x**. Depreciações são geralmente indicadas via +avisos do PHP. Você pode habilitar avisos de depreciação adicionando ``E_USER_DEPRECATED`` ao +valor ``Error.level`` da sua aplicação. + +Uma vez depreciado, o comportamento não é removido até a próxima versão principal. Por +exemplo, comportamento depreciado em ``4.1`` será removido em ``5.0``. .. meta:: - :title lang=pt: Processo de desenvolvimento no CakePHP - :keywords lang=pt: manutenção, interação com a comunidade, comunidade, funcionalidade, versão estável, ticket, funcionalidade avançada, usuários avançados, irc, desenvolvimento, tentativas + :title lang=pt: Processo de Desenvolvimento do CakePHP + :keywords lang=pt: branch de manutenção,interação com a comunidade,recurso da comunidade,recurso necessário,versão estável,sistema de tickets,recurso avançado,usuários avançados,conjunto de recursos,chat irc,ponta de lança,router,novos recursos,membros,tentativa,branches de desenvolvimento,desenvolvimento de branch diff --git a/pt/appendices/glossary.rst b/pt/appendices/glossary.rst index b150094f89..2fba249197 100644 --- a/pt/appendices/glossary.rst +++ b/pt/appendices/glossary.rst @@ -78,8 +78,8 @@ Glossário propriedades Usado ao referenciar colunas mapeadas em uma entidade ORM. - sintaxe plugin - Sintaxe plugin refere-se ao nome da classe separado por pontos, indicando que as classes + sintaxe de plugin + Sintaxe de plugin refere-se ao nome da classe separado por pontos, indicando que as classes fazem parte de um plugin:: // O plugin é "DebugKit" e o nome da classe é "Toolbar". diff --git a/pt/bake.rst b/pt/bake.rst index 662af4d4eb..34f4920853 100644 --- a/pt/bake.rst +++ b/pt/bake.rst @@ -1,4 +1,4 @@ -Bake Console +Console Bake ############ -Esta página foi `movida `__. +Esta página foi `movida `__. diff --git a/pt/chronos.rst b/pt/chronos.rst index 8a552bd6c6..4d3de5c3cc 100644 --- a/pt/chronos.rst +++ b/pt/chronos.rst @@ -1,4 +1,4 @@ Chronos -####### +======= -Esta página foi `movida `__. +Esta página foi `movida `__. diff --git a/pt/console-commands.rst b/pt/console-commands.rst index c40f966c9c..b48add1941 100644 --- a/pt/console-commands.rst +++ b/pt/console-commands.rst @@ -3,24 +3,24 @@ Console Commands .. php:namespace:: Cake\Console -In addition to a web framework, CakePHP also provides a console framework for -creating command line tools & applications. Console applications are ideal for -handling a variety of background & maintenance tasks that leverage your existing -application configuration, models, plugins and domain logic. +Além de um framework web, o CakePHP também fornece um framework de console para +criar ferramentas e aplicações de linha de comando. Aplicações de console são ideais para +lidar com uma variedade de tarefas de background e manutenção que aproveitam sua +configuração de aplicação, models, plugins e lógica de domínio existentes. -CakePHP provides several console tools for interacting with CakePHP features -like i18n and routing that enable you to introspect your application and -generate related files. +O CakePHP fornece várias ferramentas de console para interagir com recursos do CakePHP +como i18n e roteamento que permitem introspectar sua aplicação e +gerar arquivos relacionados. -The CakePHP Console -=================== +O Console do CakePHP +==================== -The CakePHP Console uses a dispatcher-type system to load commands, parse -their arguments and invoke the correct command. While the examples below use -bash the CakePHP console is compatible with any \*nix shell and windows. +O Console do CakePHP usa um sistema tipo dispatcher para carregar comandos, analisar +seus argumentos e invocar o comando correto. Embora os exemplos abaixo usem +bash, o console do CakePHP é compatível com qualquer shell \*nix e windows. -A CakePHP application contains **src/Command** directory that contain its commands. -It also comes with an executable in the **bin** directory: +Uma aplicação CakePHP contém o diretório **src/Command** que contém seus comandos. +Ela também vem com um executável no diretório **bin**: .. code-block:: console @@ -29,38 +29,38 @@ It also comes with an executable in the **bin** directory: .. note:: - For Windows, the command needs to be ``bin\cake`` (note the backslash). + Para Windows, o comando precisa ser ``bin\cake`` (observe a barra invertida). -Running the Console with no arguments will list out available commands. You -could then run the any of the listed commands by using its name: +Executar o Console sem argumentos listará os comandos disponíveis. Você +pode então executar qualquer um dos comandos listados usando seu nome: .. code-block:: console - # run server command + # executar comando server bin/cake server - # run migrations command + # executar comando migrations bin/cake migrations -h - # run bake (with plugin prefix) + # executar bake (com prefixo de plugin) bin/cake bake.bake -h -Plugin commands can be invoked without a plugin prefix if the commands's name -does not overlap with an application or framework command. In the case that two -plugins provide a command with the same name, the first loaded plugin will get -the short alias. You can always use the ``plugin.command`` format to -unambiguously reference a command. +Comandos de plugin podem ser invocados sem um prefixo de plugin se o nome do comando +não sobrepõe um comando da aplicação ou do framework. No caso de dois +plugins fornecerem um comando com o mesmo nome, o primeiro plugin carregado terá +o alias curto. Você sempre pode usar o formato ``plugin.command`` para +referenciar inequivocamente um comando. -Console Applications -==================== +Aplicações de Console +====================== -By default CakePHP will automatically discover all the commands in your -application and its plugins. You may want to reduce the number of exposed -commands, when building standalone console applications. You can use your -``Application``'s ``console()`` hook to limit which commands are exposed and -rename commands that are exposed:: +Por padrão, o CakePHP descobrirá automaticamente todos os comandos em sua +aplicação e seus plugins. Você pode querer reduzir o número de comandos expostos +ao construir aplicações de console standalone. Você pode usar o hook ``console()`` +da sua ``Application`` para limitar quais comandos são expostos e +renomear comandos que são expostos:: - // in src/Application.php + // em src/Application.php namespace App; use App\Command\UserCommand; @@ -72,62 +72,62 @@ rename commands that are exposed:: { public function console(CommandCollection $commands): CommandCollection { - // Add by classname + // Adicionar por nome de classe $commands->add('user', UserCommand::class); - // Add instance + // Adicionar instância $commands->add('version', new VersionCommand()); return $commands; } } -In the above example, the only commands available would be ``help``, ``version`` -and ``user``. See the :ref:`plugin-commands` section for how to add commands in -your plugins. +No exemplo acima, os únicos comandos disponíveis seriam ``help``, ``version`` +e ``user``. Veja a seção :ref:`plugin-commands` para saber como adicionar comandos em +seus plugins. .. note:: - When adding multiple commands that use the same Command class, the ``help`` - command will display the shortest option. + Ao adicionar múltiplos comandos que usam a mesma classe Command, o comando ``help`` + exibirá a opção mais curta. .. _renaming-commands: .. index:: nested commands, subcommands -Renaming Commands -================= +Renomeando Comandos +=================== -There are cases where you will want to rename commands, to create nested -commands or subcommands. While the default auto-discovery of commands will not -do this, you can register your commands to create any desired naming. +Há casos em que você desejará renomear comandos, para criar comandos aninhados +ou subcomandos. Embora a descoberta automática padrão de comandos não faça +isso, você pode registrar seus comandos para criar qualquer nomenclatura desejada. -You can customize the command names by defining each command in your plugin:: +Você pode personalizar os nomes dos comandos definindo cada comando em seu plugin:: public function console(CommandCollection $commands): CommandCollection { - // Add commands with nested naming + // Adicionar comandos com nomenclatura aninhada $commands->add('user dump', UserDumpCommand::class); $commands->add('user:show', UserShowCommand::class); - // Rename a command entirely + // Renomear um comando completamente $commands->add('lazer', UserDeleteCommand::class); return $commands; } -When overriding the ``console()`` hook in your application, remember to -call ``$commands->autoDiscover()`` to add commands from CakePHP, your -application, and plugins. +Ao sobrescrever o hook ``console()`` em sua aplicação, lembre-se de +chamar ``$commands->autoDiscover()`` para adicionar comandos do CakePHP, sua +aplicação e plugins. -If you need to rename/remove any attached commands, you can use the -``Console.buildCommands`` event on your application event manager to modify the -available commands. +Se você precisar renomear/remover quaisquer comandos anexados, você pode usar o +evento ``Console.buildCommands`` no gerenciador de eventos da sua aplicação para modificar os +comandos disponíveis. Commands ======== -See the :doc:`/console-commands/commands` chapter on how to create your first -command. Then learn more about commands: +Veja o capítulo :doc:`/console-commands/commands` sobre como criar seu primeiro +comando. Então aprenda mais sobre comandos: .. toctree:: :maxdepth: 1 @@ -137,8 +137,8 @@ command. Then learn more about commands: console-commands/option-parsers console-commands/cron-jobs -CakePHP Provided Commands -========================= +Comandos Fornecidos pelo CakePHP +================================= .. toctree:: :maxdepth: 1 @@ -153,31 +153,31 @@ CakePHP Provided Commands console-commands/server console-commands/repl -Routing in the Console Environment +Roteamento no Ambiente de Console ================================== -In command-line interface (CLI), specifically your console commands, -``env('HTTP_HOST')`` and other webbrowser specific environment variables are not -set. +Na interface de linha de comando (CLI), especificamente seus comandos de console, +``env('HTTP_HOST')`` e outras variáveis de ambiente específicas do navegador web não são +definidas. -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. +Se você gerar relatórios ou enviar e-mails que façam uso de ``Router::url()``, eles +conterão o host padrão ``http://localhost/`` e, portanto, resultarão em +URLs inválidas. Neste caso, você precisa especificar o domínio manualmente. +Você pode fazer isso usando o valor Configure ``App.fullBaseUrl`` do seu +bootstrap ou config, por exemplo. -For sending emails, you should provide Email class with the host you want to -send the email with:: +Para enviar e-mails, você deve fornecer à classe Email o host que deseja usar para +enviar o e-mail:: 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. +Isso garante que os IDs de mensagem gerados são válidos e se ajustam ao domínio +de onde os e-mails são enviados. .. meta:: - :title lang=en: Shells, Tasks & Console Tools - :keywords lang=en: 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 + :title lang=pt: Shells, Tasks & Console Tools + :keywords lang=pt: 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/pt/console-commands/input-output.rst b/pt/console-commands/input-output.rst index 3d03c3cd03..8350692178 100644 --- a/pt/console-commands/input-output.rst +++ b/pt/console-commands/input-output.rst @@ -1,18 +1,18 @@ -Command Input/Output -#################### +Entrada/Saída de Comandos +######################### .. php:namespace:: Cake\Console .. php:class:: ConsoleIo -CakePHP provides the ``ConsoleIo`` object to commands so that they can -interactively read user input and output information to the user. +O CakePHP fornece o objeto ``ConsoleIo`` para comandos, permitindo que eles +leiam a entrada do usuário de forma interativa e exibam informações para o usuário. .. _command-helpers: -Command Helpers -=============== +Helpers de Comandos +=================== -Command Helpers can be accessed and used from any command:: +Os Helpers de Comandos podem ser acessados e usados a partir de qualquer comando:: // Output some data as a table. $io->helper('Table')->output($data); @@ -20,20 +20,20 @@ Command Helpers can be accessed and used from any command:: // Get a helper from a plugin. $io->helper('Plugin.HelperName')->output($data); -You can also get instances of helpers and call any public methods on them:: +Você também pode obter instâncias de helpers e chamar quaisquer métodos públicos neles:: // Get and use the Progress Helper. $progress = $io->helper('Progress'); $progress->increment(10); $progress->draw(); -Creating Helpers -================ +Criando Helpers +=============== -While CakePHP comes with a few command helpers you can create more in your -application or plugins. As an example, we'll create a simple helper to generate -fancy headings. First create the **src/Command/Helper/HeadingHelper.php** and put -the following in it:: +Embora o CakePHP venha com alguns helpers de comando, você pode criar mais em sua +aplicação ou plugins. Como exemplo, vamos criar um helper simples para gerar +cabeçalhos elegantes. Primeiro crie o arquivo **src/Command/Helper/HeadingHelper.php** e coloque +o seguinte nele:: helper('Heading')->output(['It works!']); @@ -58,21 +58,21 @@ We can then use this new helper in one of our shell commands by calling it:: // With ~~~~ on either side $this->helper('Heading')->output(['It works!', '~', 4]); -Helpers generally implement the ``output()`` method which takes an array of -parameters. However, because Console Helpers are vanilla classes they can -implement additional methods that take any form of arguments. +Os Helpers geralmente implementam o método ``output()`` que recebe um array de +parâmetros. No entanto, como os Console Helpers são classes comuns, eles podem +implementar métodos adicionais que aceitam qualquer forma de argumentos. .. note:: - Helpers can also live in ``src/Shell/Helper`` for backwards compatibility. + Os Helpers também podem estar em ``src/Shell/Helper`` para compatibilidade com versões anteriores. -Built-In Helpers -================ +Helpers Integrados +================== -Table Helper +Helper Table ------------ -The TableHelper assists in making well formatted ASCII art tables. Using it is -pretty simple:: +O TableHelper ajuda a criar tabelas ASCII art bem formatadas. Usá-lo é +bastante simples:: $data = [ ['Header 1', 'Header', 'Long Header'], @@ -89,8 +89,8 @@ pretty simple:: | Longer thing | short | Longest Value | +--------------+---------------+---------------+ -You can use the ```` formatting tag in tables to right align -content:: +Você pode usar a tag de formatação ```` em tabelas para alinhar +o conteúdo à direita:: $data = [ ['Name', 'Total Price'], @@ -105,11 +105,11 @@ content:: | Cake Mix | 1.50 | +----------+-------------+ -Progress Helper +Helper Progress --------------- -The ProgressHelper can be used in two different ways. The simple mode lets you -provide a callback that is invoked until the progress is complete:: +O ProgressHelper pode ser usado de duas maneiras diferentes. O modo simples permite que você +forneça um callback que é invocado até que o progresso esteja completo:: $io->helper('Progress')->output(['callback' => function ($progress) { // Do work here. @@ -117,15 +117,15 @@ provide a callback that is invoked until the progress is complete:: $progress->draw(); }]); -You can control the progress bar more by providing additional options: +Você pode controlar a barra de progresso melhor fornecendo opções adicionais: -- ``total`` The total number of items in the progress bar. Defaults - to 100. -- ``width`` The width of the progress bar. Defaults to 80. -- ``callback`` The callback that will be called in a loop to advance the - progress bar. +- ``total`` O número total de itens na barra de progresso. Padrão + é 100. +- ``width`` A largura da barra de progresso. Padrão é 80. +- ``callback`` O callback que será chamado em um loop para avançar a + barra de progresso. -An example of all the options in use would be:: +Um exemplo de todas as opções em uso seria:: $io->helper('Progress')->output([ 'total' => 10, @@ -136,8 +136,8 @@ An example of all the options in use would be:: } ]); -The progress helper can also be used manually to increment and re-render the -progress bar as necessary:: +O helper de progresso também pode ser usado manualmente para incrementar e re-renderizar a +barra de progresso conforme necessário:: $progress = $io->helper('Progress'); $progress->init([ @@ -148,11 +148,11 @@ progress bar as necessary:: $progress->increment(4); $progress->draw(); -Banner Helper +Helper Banner ------------- -The ``BannerHelper`` can be used to format one or more lines of text into -a banner with a background and horizontal padding:: +O ``BannerHelper`` pode ser usado para formatar uma ou mais linhas de texto em +um banner com plano de fundo e preenchimento horizontal:: $io->helper('Banner') ->withPadding(5) @@ -160,15 +160,15 @@ a banner with a background and horizontal padding:: ->output(['Work complete']); .. versionadded:: 5.1.0 - The ``BannerHelper`` was added in 5.1 + O ``BannerHelper`` foi adicionado na versão 5.1 -Getting User Input -================== +Obtendo Entrada do Usuário +=========================== .. php:method:: ask($question, $choices = null, $default = null) -When building interactive console applications you'll need to get user input. -CakePHP provides a way to do this:: +Ao construir aplicações de console interativas, você precisará obter entrada do usuário. +O CakePHP fornece uma forma de fazer isso:: // Get arbitrary text from the user. $color = $io->ask('What color do you like?'); @@ -176,16 +176,16 @@ CakePHP provides a way to do this:: // Get a choice from the user. $selection = $io->askChoice('Red or Green?', ['R', 'G'], 'R'); -Selection validation is case-insensitive. +A validação de seleção não diferencia maiúsculas de minúsculas. -Creating Files -============== +Criando Arquivos +================ .. php:method:: createFile($path, $contents) -Creating files is often important part of many console commands that help -automate development and deployment. The ``createFile()`` method gives you -a simple interface for creating files with interactive confirmation:: +Criar arquivos é frequentemente uma parte importante de muitos comandos de console que ajudam +a automatizar o desenvolvimento e implantação. O método ``createFile()`` oferece uma +interface simples para criar arquivos com confirmação interativa:: // Create a file with confirmation on overwrite $io->createFile('bower.json', $stuff); @@ -193,13 +193,13 @@ a simple interface for creating files with interactive confirmation:: // Force overwriting without asking $io->createFile('bower.json', $stuff, true); -Creating Output -=============== +Criando Saída +============= .. php:method:out($message, $newlines, $level) .. php:method:err($message, $newlines) -Writing to ``stdout`` and ``stderr`` is another common operation in CakePHP:: +Escrever para ``stdout`` e ``stderr`` é outra operação comum no CakePHP:: // Write to stdout $io->out('Normal message'); @@ -207,8 +207,8 @@ Writing to ``stdout`` and ``stderr`` is another common operation in CakePHP:: // Write to stderr $io->err('Error message'); -In addition to vanilla output methods, CakePHP provides wrapper methods that -style output with appropriate ANSI colors:: +Além dos métodos de saída convencionais, o CakePHP fornece métodos wrapper que +estilizam a saída com cores ANSI apropriadas:: // Green text on stdout $io->success('Success message'); @@ -225,10 +225,10 @@ style output with appropriate ANSI colors:: // Yellow text on stderr $io->warning('Warning text'); -Color formatting will automatically be disabled if ``posix_isatty`` returns -true, or if the ``NO_COLOR`` environment variable is set. +A formatação de cores será automaticamente desabilitada se ``posix_isatty`` retornar +true, ou se a variável de ambiente ``NO_COLOR`` estiver definida. -``ConsoleIo`` provides two convenience methods regarding the output level:: +O ``ConsoleIo`` fornece dois métodos de conveniência relacionados ao nível de saída:: // Would only appear when verbose output is enabled (-v) $io->verbose('Verbose message'); @@ -236,7 +236,7 @@ true, or if the ``NO_COLOR`` environment variable is set. // Would appear at all levels. $io->quiet('Quiet message'); -You can also create blank lines or draw lines of dashes:: +Você também pode criar linhas em branco ou desenhar linhas de traços:: // Output 2 newlines $io->out($io->nl(2)); @@ -244,7 +244,7 @@ You can also create blank lines or draw lines of dashes:: // Draw a horizontal line $io->hr(); -Lastly, you can update the current line of text on the screen:: +Por fim, você pode atualizar a linha atual de texto na tela:: $io->out('Counting down'); $io->out('10', 0); @@ -254,27 +254,27 @@ Lastly, you can update the current line of text on the screen:: } .. note:: - It is important to remember, that you cannot overwrite text - once a new line has been output. + É importante lembrar que você não pode sobrescrever texto + uma vez que uma nova linha tenha sido exibida. .. _shell-output-level: -Output Levels -============= +Níveis de Saída +=============== -Console applications often need different levels of verbosity. For example, when -running as a cron job, most output is un-necessary. You can use output levels to -flag output appropriately. The user of the shell, can then decide what level of -detail they are interested in by setting the correct flag when calling the -command. There are 3 levels: +As aplicações de console frequentemente precisam de diferentes níveis de verbosidade. Por exemplo, quando +executado como um cron job, a maior parte da saída é desnecessária. Você pode usar níveis de saída para +marcar a saída adequadamente. O usuário do shell pode então decidir qual nível de +detalhe lhe interessa configurando a flag correta ao chamar o +comando. Existem 3 níveis: -* ``QUIET`` - Only absolutely important information should be marked for quiet - output. -* ``NORMAL`` - The default level, and normal usage. -* ``VERBOSE`` - Mark messages that may be too noisy for everyday use, but - helpful for debugging as ``VERBOSE``. +* ``QUIET`` - Apenas informações absolutamente importantes devem ser marcadas para saída + silenciosa. +* ``NORMAL`` - O nível padrão e uso normal. +* ``VERBOSE`` - Marque mensagens que podem ser muito ruidosas para uso diário, mas + úteis para depuração como ``VERBOSE``. -You can mark output as follows:: +Você pode marcar a saída da seguinte forma:: // Would appear at all levels. $io->out('Quiet message', 1, ConsoleIo::QUIET); @@ -289,45 +289,45 @@ You can mark output as follows:: $io->out('extra message', 1, ConsoleIo::VERBOSE); $io->verbose('Verbose output'); -You can control the output level of commands, by using the ``--quiet`` and -``--verbose`` options. These options are added by default, and allow you to -consistently control output levels inside your CakePHP comands. +Você pode controlar o nível de saída dos comandos usando as opções ``--quiet`` e +``--verbose``. Essas opções são adicionadas por padrão e permitem que você +controle consistentemente os níveis de saída dentro dos seus comandos CakePHP. -The ``--quiet`` and ``--verbose`` options also control how logging data is -output to stdout/stderr. Normally info and higher log messages are output to -stdout/stderr. When ``--verbose`` is used, debug logs will be output to stdout. -When ``--quiet`` is used, only warning and higher log messages will be output to +As opções ``--quiet`` e ``--verbose`` também controlam como os dados de log são +exibidos em stdout/stderr. Normalmente, mensagens de log de nível info e superior são exibidas em +stdout/stderr. Quando ``--verbose`` é usado, logs de depuração serão exibidos em stdout. +Quando ``--quiet`` é usado, apenas mensagens de log de warning e superior serão exibidas em stderr. -Styling Output -============== +Estilizando a Saída +=================== -Styling output is done by including tags - just like HTML - in your output. -These tags will be replaced with the correct ansi code sequence, or -stripped if you are on a console that doesn't support ansi codes. There -are several built-in styles, and you can create more. The built-in ones are +A estilização da saída é feita incluindo tags - assim como HTML - em sua saída. +Essas tags serão substituídas pela sequência de código ansi correta, ou +removidas se você estiver em um console que não suporta códigos ansi. Existem +vários estilos integrados, e você pode criar mais. Os integrados são -* ``success`` Success messages. Green text. -* ``error`` Error messages. Red text. -* ``warning`` Warning messages. Yellow text. -* ``info`` Informational messages. Cyan text. -* ``comment`` Additional text. Blue text. -* ``question`` Text that is a question, added automatically by shell. -* ``info.bg`` White background with cyan text. -* ``warning.bg`` Yellow background with black text. -* ``error.bg`` Red background with black text. -* ``success.bg`` Green background with black text. +* ``success`` Mensagens de sucesso. Texto verde. +* ``error`` Mensagens de erro. Texto vermelho. +* ``warning`` Mensagens de aviso. Texto amarelo. +* ``info`` Mensagens informativas. Texto ciano. +* ``comment`` Texto adicional. Texto azul. +* ``question`` Texto que é uma pergunta, adicionado automaticamente pelo shell. +* ``info.bg`` Fundo branco com texto ciano. +* ``warning.bg`` Fundo amarelo com texto preto. +* ``error.bg`` Fundo vermelho com texto preto. +* ``success.bg`` Fundo verde com texto preto. -You can create additional styles using ``$io->setStyle()``. To declare a -new output style you could do:: +Você pode criar estilos adicionais usando ``$io->setStyle()``. Para declarar um +novo estilo de saída, você pode fazer:: $io->setStyle('flashy', ['text' => 'magenta', 'blink' => true]); -This would then allow you to use a ```` tag in your shell output, and if -ansi colors 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 colors for the ``text`` and -``background`` attributes: +Isso permitiria que você usasse uma tag ```` em sua saída de shell, e se +as cores ansi estiverem habilitadas, o seguinte seria renderizado como texto magenta +piscando ``$this->out('Whoooa Something went wrong');``. Ao +definir estilos, você pode usar as seguintes cores para os atributos ``text`` e +``background``: * black * blue @@ -338,38 +338,38 @@ defining styles you can use the following colors for the ``text`` and * white * yellow -You can also use the following options as boolean switches, setting them to a -truthy value enables them. +Você também pode usar as seguintes opções como switches booleanos, definindo-os para um +valor verdadeiro os habilita. * blink * bold * reverse * underline -Adding a style makes it available on all instances of ConsoleOutput as well, -so you don't have to redeclare styles for both stdout and stderr objects. +Adicionar um estilo o torna disponível em todas as instâncias de ConsoleOutput também, +então você não precisa redeclarar estilos para os objetos stdout e stderr. .. versionchanged:: 5.1.0 - The ``info.bg``, ``warning.bg``, ``error.bg``, and ``success.bg`` were added. + Os estilos ``info.bg``, ``warning.bg``, ``error.bg`` e ``success.bg`` foram adicionados. -Turning Off Coloring -==================== +Desativando a Colorização +========================== -Although coloring is pretty, there may be times when you want to turn it off, -or force it on:: +Embora a colorização seja interessante, pode haver momentos em que você queira desativá-la +ou forçar sua ativação:: $io->outputAs(ConsoleOutput::RAW); -The above will put the output object into raw output mode. In raw output mode, -no styling is done at all. There are three modes you can use. +O exemplo acima colocará o objeto de saída no modo de saída bruta. No modo de saída bruta, +nenhuma estilização é feita. Existem três modos que você pode usar. -* ``ConsoleOutput::COLOR`` - Output with color escape codes in place. -* ``ConsoleOutput::PLAIN`` - Plain text output, known style tags will be - stripped from the output. -* ``ConsoleOutput::RAW`` - Raw output, no styling or formatting will be done. - This is a good mode to use if you are outputting XML or, want to debug why - your styling isn't working. +* ``ConsoleOutput::COLOR`` - Saída com códigos de escape de cor no lugar. +* ``ConsoleOutput::PLAIN`` - Saída de texto simples, tags de estilo conhecidas serão + removidas da saída. +* ``ConsoleOutput::RAW`` - Saída bruta, nenhuma estilização ou formatação será feita. + Este é um bom modo para usar se você estiver gerando XML ou quiser depurar por que + sua estilização não está funcionando. -By default on \*nix systems ConsoleOutput objects default to color output. -On Windows systems, plain output is the default unless the ``ANSICON`` -environment variable is present. +Por padrão, em sistemas \*nix, os objetos ConsoleOutput usam saída colorida por padrão. +Em sistemas Windows, a saída simples é o padrão, a menos que a variável de +ambiente ``ANSICON`` esteja presente. diff --git a/pt/console-commands/option-parsers.rst b/pt/console-commands/option-parsers.rst index 9d5152becc..740f037e04 100644 --- a/pt/console-commands/option-parsers.rst +++ b/pt/console-commands/option-parsers.rst @@ -1,17 +1,17 @@ -Option Parsers -############## +Analisadores de Opções +###################### .. php:namespace:: Cake\Console .. php:class:: ConsoleOptionParser -Console applications typically take options and arguments as the primary way to -get information from the terminal into your commands. +As aplicações de console normalmente recebem opções e argumentos como a principal forma de +obter informações do terminal para seus comandos. -Defining an OptionParser -======================== +Definindo um OptionParser +========================= -Commands and Shells provide a ``buildOptionParser($parser)`` hook method that -you can use to define the options and arguments for your commands:: +Os Comandos e Shells fornecem um método hook ``buildOptionParser($parser)`` que +você pode usar para definir as opções e argumentos para seus comandos:: protected function buildOptionParser(ConsoleOptionParser $parser): ConsoleOptionParser { @@ -21,8 +21,8 @@ you can use to define the options and arguments for your commands:: return $parser; } -Shell classes use the ``getOptionParser()`` hook method to define their option -parser:: +As classes Shell usam o método hook ``getOptionParser()`` para definir seu +analisador de opções:: public function getOptionParser() { @@ -36,62 +36,62 @@ parser:: } -Using Arguments -=============== +Usando Argumentos +================= .. php:method:: addArgument($name, $params = []) -Positional arguments are frequently used in command line tools, -and ``ConsoleOptionParser`` allows you to define positional -arguments as well as make them required. You can add arguments -one at a time with ``$parser->addArgument();`` or multiple at once -with ``$parser->addArguments();``:: +Os argumentos posicionais são frequentemente usados em ferramentas de linha de comando, +e o ``ConsoleOptionParser`` permite que você defina argumentos +posicionais e também os torne obrigatórios. Você pode adicionar argumentos +um de cada vez com ``$parser->addArgument();`` ou múltiplos de uma vez +com ``$parser->addArguments();``:: $parser->addArgument('model', ['help' => 'The model to bake']); -You can use the following options when creating an argument: +Você pode usar as seguintes opções ao criar um argumento: -* ``help`` The help text to display for this argument. -* ``required`` Whether this parameter is required. -* ``index`` The index for the arg, if left undefined the argument will be put - onto the end of the arguments. If you define the same index twice the - first option will be overwritten. -* ``choices`` An array of valid choices for this argument. If left empty all - values are valid. An exception will be raised when parse() encounters an - invalid value. -* ``separator`` A character sequence that separates arguments that should be - parsed into an array. +* ``help`` O texto de ajuda a ser exibido para este argumento. +* ``required`` Se este parâmetro é obrigatório. +* ``index`` O índice para o argumento, se deixado indefinido o argumento será colocado + no final dos argumentos. Se você definir o mesmo índice duas vezes, a + primeira opção será sobrescrita. +* ``choices`` Um array de escolhas válidas para este argumento. Se deixado vazio, todos + os valores são válidos. Uma exceção será lançada quando parse() encontrar um + valor inválido. +* ``separator`` Uma sequência de caracteres que separa argumentos que devem ser + analisados em um array. -Arguments that have been marked as required will throw an exception when -parsing the command if they have been omitted. So you don't have to -handle that in your shell. +Argumentos que foram marcados como obrigatórios lançarão uma exceção ao +analisar o comando se tiverem sido omitidos. Portanto, você não precisa +lidar com isso em seu shell. .. versionadded:: 5.2.0 - The ``separator`` option was added. + A opção ``separator`` foi adicionada. -Adding Multiple Arguments -------------------------- +Adicionando Múltiplos Argumentos +--------------------------------- .. php:method:: addArguments(array $args) -If you have an array with multiple arguments you can use -``$parser->addArguments()`` to add multiple arguments at once. :: +Se você tiver um array com múltiplos argumentos, pode usar +``$parser->addArguments()`` para adicionar múltiplos argumentos de uma vez. :: $parser->addArguments([ 'node' => ['help' => 'The node to create', 'required' => true], 'parent' => ['help' => 'The parent node', 'required' => true], ]); -As with all the builder methods on ConsoleOptionParser, addArguments -can be used as part of a fluent method chain. +Como todos os métodos de construção do ConsoleOptionParser, addArguments +pode ser usado como parte de uma cadeia de métodos fluente. -Validating Arguments +Validando Argumentos -------------------- -When creating positional arguments, you can use the ``required`` flag, to -indicate that an argument must be present when a shell is called. -Additionally you can use ``choices`` to force an argument to be from a list of -valid choices:: +Ao criar argumentos posicionais, você pode usar a flag ``required`` para +indicar que um argumento deve estar presente quando um shell é chamado. +Adicionalmente, você pode usar ``choices`` para forçar um argumento a ser de uma lista de +escolhas válidas:: $parser->addArgument('type', [ 'help' => 'The type of node to interact with.', @@ -99,19 +99,19 @@ valid choices:: 'choices' => ['aro', 'aco'], ]); -The above will create an argument that is required and has validation on the -input. If the argument is either missing, or has an incorrect value an exception -will be raised and the shell will be stopped. +O exemplo acima criará um argumento que é obrigatório e tem validação na +entrada. Se o argumento estiver ausente ou tiver um valor incorreto, uma exceção +será lançada e o shell será interrompido. -Using Options +Usando Opções ============= .. php:method:: addOption($name, array $options = []) -Options or flags are used in command line tools to provide unordered key/value -arguments for your commands. Options can define both verbose and short aliases. -They can accept a value (e.g ``--connection=default``) or be boolean options -(e.g ``--verbose``). Options are defined with the ``addOption()`` method:: +As opções ou flags são usadas em ferramentas de linha de comando para fornecer argumentos chave/valor +não ordenados para seus comandos. As opções podem definir aliases detalhados e curtos. +Elas podem aceitar um valor (por exemplo, ``--connection=default``) ou serem opções booleanas +(por exemplo, ``--verbose``). As opções são definidas com o método ``addOption()``:: $parser->addOption('connection', [ 'short' => 'c', @@ -119,98 +119,98 @@ They can accept a value (e.g ``--connection=default``) or be boolean options 'default' => 'default', ]); -The above would allow you to use either ``cake myshell --connection=other``, -``cake myshell --connection other``, or ``cake myshell -c other`` -when invoking the shell. +O exemplo acima permitiria que você usasse ``cake myshell --connection=other``, +``cake myshell --connection other``, ou ``cake myshell -c other`` +ao invocar o shell. -Boolean switches do not accept or consume values, and their presence just -enables them in the parsed parameters:: +As chaves booleanas não aceitam ou consomem valores, e sua presença apenas +as habilita nos parâmetros analisados:: $parser->addOption('no-commit', ['boolean' => true]); -This option when used like ``cake mycommand --no-commit something`` would have -a value of ``true``, and 'something' would be a treated as a positional -argument. - -When creating options you can use the following options to define the behavior -of the option: - -* ``short`` - The single letter variant for this option, leave undefined for - none. -* ``help`` - Help text for this option. Used when generating help for the - option. -* ``default`` - The default value for this option. If not defined the default - will be ``true``. -* ``boolean`` - The option uses no value, it's just a boolean switch. - Defaults to ``false``. -* ``multiple`` - The option can be provided multiple times. The parsed option - will be an array of values when this option is enabled. -* ``separator`` - A character sequence that the option value is split into an - array with. -* ``choices`` - An array of valid choices for this option. If left empty all - values are valid. An exception will be raised when parse() encounters an - invalid value. +Esta opção, quando usada como ``cake mycommand --no-commit something``, teria +um valor de ``true``, e 'something' seria tratado como um argumento +posicional. + +Ao criar opções, você pode usar as seguintes opções para definir o comportamento +da opção: + +* ``short`` - A variante de letra única para esta opção, deixe indefinido para + nenhuma. +* ``help`` - Texto de ajuda para esta opção. Usado ao gerar ajuda para a + opção. +* ``default`` - O valor padrão para esta opção. Se não definido, o padrão + será ``true``. +* ``boolean`` - A opção não usa valor, é apenas uma chave booleana. + Padrão é ``false``. +* ``multiple`` - A opção pode ser fornecida múltiplas vezes. A opção analisada + será um array de valores quando esta opção estiver habilitada. +* ``separator`` - Uma sequência de caracteres pela qual o valor da opção é dividido em um + array. +* ``choices`` - Um array de escolhas válidas para esta opção. Se deixado vazio, todos + os valores são válidos. Uma exceção será lançada quando parse() encontrar um + valor inválido. .. versionadded:: 5.2.0 - The ``separator`` option was added. + A opção ``separator`` foi adicionada. -Adding Multiple Options ------------------------ +Adicionando Múltiplas Opções +----------------------------- .. php:method:: addOptions(array $options) -If you have an array with multiple options you can use ``$parser->addOptions()`` -to add multiple options at once. :: +Se você tiver um array com múltiplas opções, pode usar ``$parser->addOptions()`` +para adicionar múltiplas opções de uma vez. :: $parser->addOptions([ 'node' => ['short' => 'n', 'help' => 'The node to create'], 'parent' => ['short' => 'p', 'help' => 'The parent node'], ]); -As with all the builder methods on ConsoleOptionParser, addOptions can be used -as part of a fluent method chain. +Como todos os métodos de construção do ConsoleOptionParser, addOptions pode ser usado +como parte de uma cadeia de métodos fluente. -Validating Options ------------------- +Validando Opções +---------------- -Options can be provided with a set of choices much like positional arguments -can be. When an option has defined choices, those are the only valid choices -for an option. All other values will raise an ``InvalidArgumentException``:: +As opções podem ser fornecidas com um conjunto de escolhas, assim como os argumentos posicionais +podem ser. Quando uma opção tem escolhas definidas, essas são as únicas escolhas válidas +para uma opção. Todos os outros valores lançarão uma ``InvalidArgumentException``:: $parser->addOption('accept', [ 'help' => 'What version to accept.', 'choices' => ['working', 'theirs', 'mine'], ]); -Using Boolean Options ---------------------- +Usando Opções Booleanas +----------------------- -Options can be defined as boolean options, which are useful when you need to -create some flag options. Like options with defaults, boolean options always -include themselves into the parsed parameters. When the flags are present they -are set to ``true``, when they are absent they are set to ``false``:: +As opções podem ser definidas como opções booleanas, que são úteis quando você precisa +criar algumas opções de flag. Como opções com padrões, as opções booleanas sempre +se incluem nos parâmetros analisados. Quando as flags estão presentes, elas +são definidas como ``true``, quando estão ausentes são definidas como ``false``:: $parser->addOption('verbose', [ 'help' => 'Enable verbose output.', 'boolean' => true ]); -The following option would always have a value in the parsed parameter. When not -included its default value would be ``false``, and when defined it will be +A opção seguinte sempre terá um valor no parâmetro analisado. Quando não +incluída, seu valor padrão seria ``false``, e quando definida será ``true``. -Building a ConsoleOptionParser from an Array --------------------------------------------- +Construindo um ConsoleOptionParser a partir de um Array +-------------------------------------------------------- .. php:method:: buildFromArray($spec) -Option parsers can also be defined as arrays. Within the array, you can define -keys for ``arguments``, ``options``, ``description`` and ``epilog``. The values -for arguments, and options, should follow the format that -:php:func:`Cake\\Console\\ConsoleOptionParser::addArguments()` and -:php:func:`Cake\\Console\\ConsoleOptionParser::addOptions()` use. You can also -use ``buildFromArray`` on its own, to build an option parser:: +Os analisadores de opções também podem ser definidos como arrays. Dentro do array, você pode definir +chaves para ``arguments``, ``options``, ``description`` e ``epilog``. Os valores +para argumentos e opções devem seguir o formato que +:php:func:`Cake\\Console\\ConsoleOptionParser::addArguments()` e +:php:func:`Cake\\Console\\ConsoleOptionParser::addOptions()` usam. Você também pode +usar ``buildFromArray`` por conta própria para construir um analisador de opções:: public function getOptionParser() { @@ -228,59 +228,59 @@ use ``buildFromArray`` on its own, to build an option parser:: ]); } -Merging Option Parsers ----------------------- +Mesclando Analisadores de Opções +--------------------------------- .. php:method:: merge($spec) -When building a group command, you maybe want to combine several parsers for -this:: +Ao construir um comando de grupo, você pode querer combinar vários analisadores para +isso:: $parser->merge($anotherParser); -Note that the order of arguments for each parser must be the same, and that -options must also be compatible for it work. So do not use keys for different -things. +Note que a ordem dos argumentos para cada analisador deve ser a mesma, e que +as opções também devem ser compatíveis para funcionar. Portanto, não use chaves para coisas +diferentes. -Getting Help from Shells -======================== +Obtendo Ajuda dos Shells +========================= -By defining your options and arguments with the option parser CakePHP can -automatically generate rudimentary help information and add a ``--help`` and -``-h`` to each of your commands. Using one of these options will allow you to -see the generated help content: +Ao definir suas opções e argumentos com o analisador de opções, o CakePHP pode +gerar automaticamente informações de ajuda rudimentares e adicionar ``--help`` e +``-h`` a cada um dos seus comandos. Usar uma dessas opções permitirá que você +veja o conteúdo de ajuda gerado: .. code-block:: console bin/cake bake --help bin/cake bake -h -Would both generate the help for bake. You can also get help for nested -commands: +Ambos gerariam a ajuda para bake. Você também pode obter ajuda para comandos +aninhados: .. code-block:: console bin/cake bake model --help bin/cake bake model -h -The above would get you the help specific to bake's model command. +O exemplo acima obteria a ajuda específica para o comando model do bake. -Getting Help as XML -------------------- +Obtendo Ajuda como XML +---------------------- -When building automated tools or development tools that need to interact with -CakePHP shell commands, it's nice to have help available in a machine parse-able format. -By providing the ``xml`` option when requesting help you can have help content -returned as XML: +Ao construir ferramentas automatizadas ou ferramentas de desenvolvimento que precisam interagir com +comandos shell do CakePHP, é bom ter ajuda disponível em um formato analisável por máquina. +Ao fornecer a opção ``xml`` ao solicitar ajuda, você pode ter o conteúdo de ajuda +retornado como XML: .. code-block:: console cake bake --help xml cake bake -h xml -The above would return an XML document with the generated help, options, and -arguments for the selected shell. A sample XML document would -look like: +O exemplo acima retornaria um documento XML com a ajuda gerada, opções e +argumentos para o shell selecionado. Um documento XML de exemplo +seria parecido com: .. code-block:: xml @@ -331,19 +331,19 @@ look like: -Customizing Help Output -======================= +Personalizando a Saída de Ajuda +================================ -You can further enrich the generated help content by adding a description, and -epilog. +Você pode enriquecer ainda mais o conteúdo de ajuda gerado adicionando uma descrição e +epílogo. -Set the Description +Definir a Descrição ------------------- .. php:method:: setDescription($text) -The description displays above the argument and option information. By passing -in either an array or a string, you can set the value of the description:: +A descrição é exibida acima das informações de argumento e opção. Ao passar +um array ou uma string, você pode definir o valor da descrição:: // Set multiple lines at once $parser->setDescription(['line one', 'line two']); @@ -351,14 +351,14 @@ in either an array or a string, you can set the value of the description:: // Read the current value $parser->getDescription(); -Set the Epilog --------------- +Definir o Epílogo +----------------- .. php:method:: setEpilog($text) -Gets or sets the epilog for the option parser. The epilog is displayed after the -argument and option information. By passing in either an array or a string, you -can set the value of the epilog:: +Obtém ou define o epílogo para o analisador de opções. O epílogo é exibido após as +informações de argumento e opção. Ao passar um array ou uma string, você +pode definir o valor do epílogo:: // Set multiple lines at once $parser->setEpilog(['line one', 'line two']); diff --git a/pt/console-commands/plugin.rst b/pt/console-commands/plugin.rst index 4a9fe5a5b5..163b3eb661 100644 --- a/pt/console-commands/plugin.rst +++ b/pt/console-commands/plugin.rst @@ -1,60 +1,60 @@ .. _plugin-shell: -Plugin Tool -########### +Ferramenta de Plugin +#################### -The plugin tool allows you to load and unload plugins via the command prompt. -If you need help, run: +A ferramenta de plugin permite carregar e descarregar plugins via prompt de comando. +Se você precisar de ajuda, execute: .. code-block:: console bin/cake plugin --help -Loading Plugins ---------------- +Carregando Plugins +------------------ -Via the ``Load`` task you are able to load plugins in your -**config/bootstrap.php**. You can do this by running: +Através da tarefa ``Load`` você pode carregar plugins em seu +**config/bootstrap.php**. Você pode fazer isso executando: .. code-block:: console bin/cake plugin load MyPlugin -This will add the following to your **src/Application.php**:: +Isso adicionará o seguinte ao seu **src/Application.php**:: // In the bootstrap method add: $this->addPlugin('MyPlugin'); -Unloading Plugins ------------------ +Descarregando Plugins +--------------------- -You can unload a plugin by specifying its name: +Você pode descarregar um plugin especificando seu nome: .. code-block:: console bin/cake plugin unload MyPlugin -This will remove the line ``$this->addPlugin('MyPlugin',...)`` from +Isso removerá a linha ``$this->addPlugin('MyPlugin',...)`` de **src/Application.php**. -Plugin Assets -------------- +Assets de Plugin +---------------- -CakePHP by default serves plugins assets using the ``AssetMiddleware`` middleware. -While this is a good convenience, it is recommended to symlink / copy -the plugin assets under app's webroot so that they can be directly served by the -web server without invoking PHP. You can do this by running: +O CakePHP por padrão serve assets de plugins usando o middleware ``AssetMiddleware``. +Embora isso seja conveniente, é recomendado criar um link simbólico / copiar +os assets do plugin para o webroot da aplicação para que eles possam ser servidos diretamente pelo +servidor web sem invocar o PHP. Você pode fazer isso executando: .. code-block:: console bin/cake plugin assets symlink -Running the above command will symlink all plugins assets under app's webroot. -On Windows, which doesn't support symlinks, the assets will be copied in -respective folders instead of being symlinked. +Executar o comando acima criará links simbólicos de todos os assets de plugins no webroot da aplicação. +No Windows, que não suporta links simbólicos, os assets serão copiados nas +respectivas pastas em vez de serem vinculados simbolicamente. -You can symlink assets of one particular plugin by specifying its name: +Você pode criar links simbólicos de assets de um plugin específico especificando seu nome: .. code-block:: console diff --git a/pt/console-commands/repl.rst b/pt/console-commands/repl.rst index 48465ba3bd..d1a560e9ca 100644 --- a/pt/console-commands/repl.rst +++ b/pt/console-commands/repl.rst @@ -1,23 +1,23 @@ -Interactive Console (REPL) -########################## +Console Interativo (REPL) +######################### -CakePHP offers -`REPL(Read Eval Print Loop) plugin `__ to let -you explore some CakePHP and your application in an interactive console. +O CakePHP oferece o +`plugin REPL(Read Eval Print Loop) `__ para permitir +que você explore o CakePHP e sua aplicação em um console interativo. .. note:: - The plugin was shipped with the CakePHP app skeleton before 4.3. + O plugin era incluído no esqueleto da aplicação CakePHP antes da versão 4.3. -You can start the interactive console using: +Você pode iniciar o console interativo usando: .. code-block:: console bin/cake console -This will bootstrap your application and start an interactive console. At this -point you can interact with your application code and execute queries using your -application's models: +Isso inicializará sua aplicação e iniciará um console interativo. Neste +ponto você pode interagir com o código da sua aplicação e executar consultas usando os +models da sua aplicação: .. code-block:: console @@ -29,7 +29,7 @@ application's models: // ) >>> $articles->find()->all(); -Since your application has been bootstrapped you can also test routing using the +Como sua aplicação foi inicializada, você também pode testar o roteamento usando o REPL:: >>> Cake\Routing\Router::parse('/articles/view/1'); @@ -42,9 +42,9 @@ REPL:: // 'plugin' => NULL // ] -You can also test generating URLs:: +Você também pode testar a geração de URLs:: >>> Cake\Routing\Router::url(['controller' => 'Articles', 'action' => 'edit', 99]); // '/articles/edit/99' -To quit the REPL you can use ``CTRL-C`` or by typing ``exit``. +Para sair do REPL você pode usar ``CTRL-C`` ou digitando ``exit``. diff --git a/pt/console-commands/routes.rst b/pt/console-commands/routes.rst index d3c0986984..ec7e00b6e7 100644 --- a/pt/console-commands/routes.rst +++ b/pt/console-commands/routes.rst @@ -1,38 +1,38 @@ -Routes Tool -########### +Ferramenta de Rotas +################### -The routes tool provides a simple to use CLI interface for testing and debugging -routes. You can use it to test how routes are parsed, and what URLs routing -parameters will generate. +A ferramenta de rotas fornece uma interface CLI simples de usar para testar e depurar +rotas. Você pode usá-la para testar como as rotas são analisadas e quais URLs os parâmetros de +roteamento irão gerar. -Getting a List of all Routes ----------------------------- +Obtendo uma Lista de todas as Rotas +------------------------------------ .. code-block:: console bin/cake routes -Testing URL parsing -------------------- +Testando a Análise de URL +------------------------- -You can quickly see how a URL will be parsed using the ``check`` method: +Você pode ver rapidamente como uma URL será analisada usando o método ``check``: .. code-block:: console bin/cake routes check /articles/edit/1 -If your route contains any query string parameters remember to surround the URL -in quotes: +Se sua rota contiver parâmetros de query string, lembre-se de colocar a URL +entre aspas: .. code-block:: console bin/cake routes check "/articles/?page=1&sort=title&direction=desc" -Testing URL Generation ----------------------- +Testando a Geração de URL +------------------------- -You can see the URL a :term:`routing array` will generate using the -``generate`` method: +Você pode ver a URL que um :term:`routing array` irá gerar usando o +método ``generate``: .. code-block:: console diff --git a/pt/console-commands/schema-cache.rst b/pt/console-commands/schema-cache.rst index df891ab6a5..8dcd65219f 100644 --- a/pt/console-commands/schema-cache.rst +++ b/pt/console-commands/schema-cache.rst @@ -1,25 +1,25 @@ -Schema Cache Tool -################# +Ferramenta de Cache de Schema +############################## -The SchemaCacheCommand provides a simple CLI tool for managing your application's -metadata caches. In deployment situations it is helpful to rebuild the metadata -cache in-place without clearing the existing cache data. You can do this by -running: +O SchemaCacheCommand fornece uma ferramenta CLI simples para gerenciar os +caches de metadados da sua aplicação. Em situações de implantação, é útil reconstruir o cache de metadados +no local sem limpar os dados de cache existentes. Você pode fazer isso +executando: .. code-block:: console bin/cake schema_cache build --connection default -This will rebuild the metadata cache for all tables on the ``default`` -connection. If you only need to rebuild a single table you can do that by -providing its name: +Isso reconstruirá o cache de metadados para todas as tabelas na conexão ``default``. +Se você precisar reconstruir apenas uma única tabela, pode fazer isso +fornecendo seu nome: .. code-block:: console bin/cake schema_cache build --connection default articles -In addition to building cached data, you can use the SchemaCacheShell to remove -cached metadata as well: +Além de construir dados em cache, você pode usar o SchemaCacheShell para remover +metadados em cache também: .. code-block:: console diff --git a/pt/console-commands/server.rst b/pt/console-commands/server.rst index 7c3eb62f6e..e381a33bba 100644 --- a/pt/console-commands/server.rst +++ b/pt/console-commands/server.rst @@ -1,28 +1,28 @@ -Server Tool -########### +Ferramenta de Servidor +###################### -The ``ServerCommand`` lets you stand up a simple webserver using the built in PHP -webserver. While this server is *not* intended for production use it can -be handy in development when you want to quickly try an idea out and don't want -to spend time configuring Apache or Nginx. You can start the server command with: +O ``ServerCommand`` permite que você inicie um servidor web simples usando o servidor web +integrado do PHP. Embora este servidor *não* seja destinado ao uso em produção, ele pode +ser útil no desenvolvimento quando você quiser testar rapidamente uma ideia e não quiser +gastar tempo configurando o Apache ou Nginx. Você pode iniciar o comando do servidor com: .. code-block:: console bin/cake server -You should see the server boot up and attach to port 8765. You can visit the -CLI server by visiting ``http://localhost:8765`` -in your web-browser. You can close the server by pressing ``CTRL-C`` in your +Você deve ver o servidor inicializar e se conectar à porta 8765. Você pode visitar o +servidor CLI visitando ``http://localhost:8765`` +em seu navegador web. Você pode fechar o servidor pressionando ``CTRL-C`` em seu terminal. .. note:: - Try ``bin/cake server -H 0.0.0.0`` if the server is unreachable from other hosts. + Tente ``bin/cake server -H 0.0.0.0`` se o servidor estiver inacessível de outros hosts. -Changing the Port and Document Root -=================================== +Alterando a Porta e o Document Root +==================================== -You can customize the port and document root using options: +Você pode personalizar a porta e o document root usando opções: .. code-block:: console diff --git a/pt/contents.rst b/pt/contents.rst index c87f94badd..bb083ce654 100644 --- a/pt/contents.rst +++ b/pt/contents.rst @@ -31,7 +31,7 @@ Conteúdo orm .. toctree:: - :caption: Using CakePHP + :caption: Usando o CakePHP core-libraries/caching console-commands @@ -81,7 +81,7 @@ Conteúdo Queue .. toctree:: - :caption: Diversos + :caption: Outros core-libraries/global-constants-and-functions appendices diff --git a/pt/contributing.rst b/pt/contributing.rst index 8518ed5340..53c053a283 100644 --- a/pt/contributing.rst +++ b/pt/contributing.rst @@ -1,8 +1,8 @@ Contribuindo ############ -Existem várias maneiras de contribuir com o CakePHP. -As seções abaixo irão abordar essas formas de contribuição: +Existem várias maneiras de você contribuir para o CakePHP. As seguintes seções +cobrem as várias maneiras de você contribuir para o CakePHP: .. toctree:: :maxdepth: 1 @@ -15,4 +15,4 @@ As seções abaixo irão abordar essas formas de contribuição: .. meta:: :title lang=pt: Contribuindo - :keywords lang=pt: contribuindo,open source,ajudando,documentação,doc,docs + :keywords lang=pt: convenções de codificação,documentação,maxdepth diff --git a/pt/contributing/backwards-compatibility.rst b/pt/contributing/backwards-compatibility.rst index dfe9b248ab..d3880e18f1 100644 --- a/pt/contributing/backwards-compatibility.rst +++ b/pt/contributing/backwards-compatibility.rst @@ -1,123 +1,120 @@ -Guia de retrocompatibilidade -############################ - -Garantir que você possa atualizar suas aplicações facilmente é importante para -nós. Por esse motivo, apenas quebramos compatibilidade nos *major releases*. -Você deve estar familiarizado com -`versionamento semântico `_, orientação usada em -todos os projetos do CakePHP. Resumindo, significa que apenas *major releases* -(tais como 2.0, 3.0, 4.0) podem quebrar retrocompatibilidades. *Minor releases* -(tais como 2.1, 3.1, 4.1) podem introduzir novos recursos, mas não podem quebrar -retrocompatibilidades. *Releases* de correção de *bugs* (tais como 2.1.2, 3.0.1) -não incluem novos recursos, são destinados apenas à correção de erros e melhora -de desempenho. +Guia de Compatibilidade com Versões Anteriores +############################################## + +Garantir que você possa atualizar suas aplicações de forma fácil e suave é importante +para nós. É por isso que só quebramos a compatibilidade em marcos de lançamentos principais. +Você pode estar familiarizado com `versionamento semântico `_, que é +a diretriz geral que usamos em todos os projetos CakePHP. Resumindo, versionamento +semântico significa que apenas lançamentos principais (como 2.0, 3.0, 4.0) podem quebrar +a compatibilidade com versões anteriores. Lançamentos menores (como 2.1, 3.1, 3.2) podem introduzir novos +recursos, mas não têm permissão para quebrar a compatibilidade. Lançamentos de correção de bugs (como 2.1.2, +3.0.1) não adicionam novos recursos, mas corrigem bugs ou melhoram apenas o desempenho. .. note:: - O CakePHP começou a seguir o versionamento semântico na versão 2.0.0. Essas - regras não se aplicam às versões 1.x. + Depreciações são removidas com a próxima versão principal do framework. + É aconselhável que você se adapte às depreciações conforme são introduzidas para + garantir que futuras atualizações sejam mais fáceis. -Para esclarecer que mudanças você pode esperar em cada ciclo de *release*, nós -temos mais informações detalhadas para desenvolvedores usando o CakePHP, e para -desenvolvedores trabalhando Não CakePHP que ajudam a definir espectativas do que -pode ser feito em *minor releases*. *Major releases* podem ter tantas quebras -quanto forem necessárias. +Para esclarecer quais mudanças você pode esperar em cada nível de lançamento, temos +informações mais detalhadas para desenvolvedores usando CakePHP e para desenvolvedores trabalhando no +CakePHP que ajudam a definir expectativas do que pode ser feito em lançamentos menores. Lançamentos principais +podem ter tantas mudanças quebradas quanto necessário. -Guia de migração -================ +Guias de Migração +================= -Para cada *major* ou *minor releases*, a equipe do CakePHP vai disponibilizar -um guia de migração. Esses guias explicam os novos recursos e qualquer quebra -de compatibilidade. Eles podem ser encontrados na seção :doc:`/appendices` do -manual. +Para cada lançamento principal e menor, a equipe CakePHP fornecerá um guia de migração. +Esses guias explicam os novos recursos e quaisquer mudanças quebradas que estão +em cada lançamento. Eles podem ser encontrados na seção :doc:`/appendices` do +cookbook. -Usando o CakePHP -================ +Usando CakePHP +============== -Se você está construindo sua aplicação com o CakePHP, as orientações a seguir -vão demonstrar a estabilidade que você pode esperar. +Se você está construindo sua aplicação com CakePHP, as seguintes diretrizes +explicam a estabilidade que você pode esperar. Interfaces ---------- -Com exceção dos *major releases*, interfaces oferecidas pelo CakePHP **não** -irão ter alterações em qualquer método. Novos métodos podem ser incluídos, mas -nenhum método existente será alterado. +Fora de lançamentos principais, interfaces fornecidas pelo CakePHP **não** terão nenhum +método existente alterado. Novos métodos podem ser adicionados, mas nenhum método existente será +alterado. Classes ------- -Classes oferecidas pelo CakePHP podem ser construidas e ter seus métodos -públicos e propriedades usados Não código da aplicação e com exceção de -*major releases* a retrocompatibilidade é garantida. +Classes fornecidas pelo CakePHP podem ser construídas e ter seus métodos públicos e +propriedades usadas por código de aplicação e fora de lançamentos principais a +compatibilidade com versões anteriores é garantida. .. note:: - Algumas classes Não CakePHP são marcadas com a *tag* da documentação da API - ``@internal``. Essas classes **não** são estáveis e não tem garantias de - retrocompatibilidade. + Algumas classes no CakePHP são marcadas com a tag de documentação API ``@internal``. Essas + classes **não** são estáveis e não têm nenhuma promessa de compatibilidade com versões anteriores. -Em *minor releases*, novos métodos podem ser adicionados a classes, e métodos -existentes podem passar a receber novos argumentos. Qualquer novo argumento vai -ter valores padrões, mas se você sobrescrever métodos com uma assinatura -diferente, é possível que você receba erros fatais. Métodos que recebem novos -argumentos serão documentados Não guia de migração correspondente ao *release*. +Em lançamentos menores, novos métodos podem ser adicionados a classes, e métodos existentes podem +ter novos argumentos adicionados. Quaisquer novos argumentos terão valores padrão, mas se +você substituiu métodos com uma assinatura diferente, pode ver erros fatais. +Métodos que tiverem novos argumentos adicionados serão documentados no guia de migração +para aquele lançamento. -A tabela a seguir descreve quais casos de uso e que tipo de compatibilidade -você pode esperar do CakePHP. +A tabela a seguir descreve vários casos de uso e qual compatibilidade você pode +esperar do CakePHP: +-------------------------------+--------------------------+ -| Se você... | Retrocompatibilidade? | +| Se você... | Compatibilidade? | +===============================+==========================+ -| Typehint referente à classe | Sim | +| Typehint contra a classe | Sim | +-------------------------------+--------------------------+ -| Criar uma nova instância | Sim | +| Cria uma nova instância | Sim | +-------------------------------+--------------------------+ -| Estender a classe | Sim | +| Estende a classe | Sim | +-------------------------------+--------------------------+ -| Acessar uma propriedade | Sim | -| pública | | +| Acessa uma propriedade pública| Sim | +-------------------------------+--------------------------+ -| Chamar um método público | Sim | +| Chama um método público | Sim | +-------------------------------+--------------------------+ -| **Estender uma classe e...** | +| **Estende uma classe e...** | +-------------------------------+--------------------------+ -| Sobrescrever uma | Sim | -| propriedade pública | | +| Substitui uma propriedade | Sim | +| pública | | +-------------------------------+--------------------------+ -| Acessar uma propriedade | Não [1]_ | +| Acessa uma propriedade | Não [1]_ | | protegida | | +-------------------------------+--------------------------+ -| Sobrescrever uma | Não [1]_ | -| propriedade protegida | | +| Substitui uma propriedade | Não [1]_ | +| protegida | | +-------------------------------+--------------------------+ -| Sobrescrever um método | Não [1]_ | +| Substitui um método protegido | Não [1]_ | +-------------------------------+--------------------------+ -| Chamar um método protegido | Não [1]_ | +| Chama um método protegido | Não [1]_ | +-------------------------------+--------------------------+ -| Adicionar uma propriedade | Não | +| Adiciona uma propriedade | Não | | pública | | +-------------------------------+--------------------------+ -| Adicionar um método público | Não | +| Adiciona um método público | Não | +-------------------------------+--------------------------+ -| Adicionar um argumento | Não [1]_ | -| a um método sobrescrito | | +| Adiciona um argumento a um | Não [1]_ | +| método substituído | | +-------------------------------+--------------------------+ -| Adicinar um valor padrão | Sim | -| a um argumento de método | | -| existente | | +| Adiciona um valor de argumento| Sim | +| padrão a um argumento de | | +| método existente | | +-------------------------------+--------------------------+ Trabalhando no CakePHP ====================== -Se você está ajudando a fazer o CakePHP ainda melhor, por favor, siga as -orientações a seguir quando estiver adicionando/alterando funcionalidades: +Se você está ajudando a tornar o CakePHP ainda melhor, por favor, mantenha as seguintes diretrizes +em mente ao adicionar/alterar funcionalidades: -Em um *minor release* você pode: +Em um lançamento menor você pode: +-------------------------------+--------------------------+ -| Em um *minor release* você pode... | +| Em um lançamento menor você | +| pode... | | +===============================+==========================+ | **Classes** | +-------------------------------+--------------------------+ @@ -125,20 +122,21 @@ Em um *minor release* você pode: +-------------------------------+--------------------------+ | Remover uma interface | Não | +-------------------------------+--------------------------+ -| Remover um trait | Não | +| Remover uma trait | Não | +-------------------------------+--------------------------+ | Tornar final | Não | +-------------------------------+--------------------------+ -| Tornar abstract | Não | +| Tornar abstrata | Não | +-------------------------------+--------------------------+ -| Trocar o nome | Sim [2]_ | +| Mudar nome | Sim [2]_ | +-------------------------------+--------------------------+ -| **Properties** | +| **Propriedades** | +-------------------------------+--------------------------+ | Adicionar uma propriedade | Sim | | pública | | +-------------------------------+--------------------------+ -| Remove a public property | Não | +| Remover uma propriedade | Não | +| pública | | +-------------------------------+--------------------------+ | Adicionar uma propriedade | Sim | | protegida | | @@ -152,9 +150,9 @@ Em um *minor release* você pode: +-------------------------------+--------------------------+ | Remover um método público | Não | +-------------------------------+--------------------------+ -| Adicionar um método público | Sim | +| Adicionar um método protegido | Sim | +-------------------------------+--------------------------+ -| Mover para uma classe parente | Sim | +| Mover para classe pai | Sim | +-------------------------------+--------------------------+ | Remover um método protegido | Sim [3]_ | +-------------------------------+--------------------------+ @@ -166,17 +164,52 @@ Em um *minor release* você pode: | com valor padrão | | +-------------------------------+--------------------------+ | Adicionar um novo argumento | Não | -| a um método existente. | | +| obrigatório a um método | | +| existente | | +-------------------------------+--------------------------+ | Remover um valor padrão de | Não | | um argumento existente | | +-------------------------------+--------------------------+ +| Mudar tipo de método para void| Sim | ++-------------------------------+--------------------------+ + +.. [1] Seu código *pode* ser quebrado por lançamentos menores. Verifique o guia de migração + para detalhes. +.. [2] Você pode mudar um nome de classe/método desde que o nome antigo permaneça + disponível. Isso geralmente é evitado a menos que a renomeação tenha benefício + significativo. +.. [3] Evite sempre que possível. Quaisquer remoções precisam ser documentadas no + guia de migração. + +Depreciações +============ + +Em cada lançamento menor, recursos podem ser depreciados. Se recursos forem depreciados, +documentação da API e avisos em tempo de execução serão adicionados. Erros em tempo de execução ajudam você +a localizar código que precisa ser atualizado antes de quebrar. Se você deseja desabilitar +avisos em tempo de execução, pode fazê-lo usando o valor de configuração ``Error.errorLevel``:: + + // em config/app.php + // ... + 'Error' => [ + 'errorLevel' => E_ALL ^ E_USER_DEPRECATED, + ] + // ... + +Isso desabilitará avisos de depreciação em tempo de execução. + +.. _experimental-features: + +Recursos Experimentais +====================== + +Recursos experimentais **não estão incluídos** nas promessas de compatibilidade com versões anteriores +acima. Recursos experimentais podem ter mudanças quebradas feitas em lançamentos menores +enquanto permanecerem experimentais. Recursos experimentais podem ser identificados pelo +aviso no livro e pelo uso de ``@experimental`` na documentação +da API. -.. [1] Seu código *pode* ser quebrado por *minor releases*. Verifique o guia de - migração para mais detalhes. -.. [2] Você pode mudar o nome de uma classe/método desde que o nome antigo - permaneça disponível. Isso normalmente é evitado, a não ser que a - renomeação traga algum benefício significante. -.. [3] Evite sempre que possível. Qualquer remoção precisa ser documentada - no guia de migração. +Recursos experimentais são destinados a ajudar a coletar feedback sobre como um recurso +funciona antes de se tornar estável. Uma vez que as interfaces e comportamento tenham sido verificados +com a comunidade, as flags experimentais serão removidas. diff --git a/pt/contributing/cakephp-coding-conventions.rst b/pt/contributing/cakephp-coding-conventions.rst index eab94b75c0..9cb363433d 100644 --- a/pt/contributing/cakephp-coding-conventions.rst +++ b/pt/contributing/cakephp-coding-conventions.rst @@ -1,30 +1,42 @@ -Padrões de codificação +Padrões de Codificação ###################### -Desenvolvedores do CakePHP deverão usar o `guia de codificação -PSR-12 `_ em adição às regras apresentadas -a seguir e definidas como padrão. +Os desenvolvedores do CakePHP utilizarão o `guia de estilo de codificação PSR-12 +`_ além das seguintes regras como +padrões de codificação. -É recomendado que outros desenvolvedores que optem pelo CakePHP sigam os mesmos +É recomendado que outros desenvolvedores de CakeIngredients sigam os mesmos padrões. Você pode usar o `CakePHP Code Sniffer -`_ para verificar se o seu -código segue os padrões estabelecidos. +`_ para verificar se seu código +segue os padrões exigidos. -Adicionando novos recursos -========================== +Adicionando Novos Recursos +=========================== + +Nenhum novo recurso deve ser adicionado sem ter seus próprios testes – que +devem ser aprovados antes de serem enviados ao repositório. + +Configuração da IDE +=================== + +Por favor, certifique-se de que sua IDE está configurada para "aparar à direita" os espaços em branco. +Não deve haver espaços em branco no final de cada linha. + +A maioria das IDEs modernas também suporta um arquivo ``.editorconfig``. O esqueleto +da aplicação CakePHP vem com ele por padrão. Ele já contém os padrões de boas práticas. -Nenhum novo recurso deve ser adicionado sem que tenha seus próprios testes -definidos, que por sua vez, devem estar passando antes que o novo recurso seja -enviado para o repositório. +Recomendamos usar o plugin `IdeHelper `_ se você +deseja maximizar a compatibilidade da IDE. Ele ajudará a manter as anotações atualizadas, o que fará +a IDE entender completamente como todas as classes funcionam juntas e fornece melhor dicas de tipo e auto-completar. Indentação ========== Quatro espaços serão usados para indentação. -Então, teremos uma estrutura similar a:: +Portanto, a indentação deve parecer assim:: // nível base // nível 1 @@ -35,30 +47,29 @@ Então, teremos uma estrutura similar a:: Ou:: $booleanVariable = true; - $stringVariable = 'jacaré'; + $stringVariable = 'moose'; if ($booleanVariable) { - echo 'Valor booleano é true'; - if ($stringVariable === 'jacaré') { - echo 'Nós encontramos um jacaré'; + echo 'Boolean value is true'; + if ($stringVariable === 'moose') { + echo 'We have encountered a moose'; } } -Em situações onde você estiver usando uma função em mais de uma linha, siga -as seguintes orientações: +Nos casos em que você está usando uma chamada de função de várias linhas, use as seguintes +diretrizes: -* O parêntese de abertura de uma função multi-linha deve ser o último conteúdo - da linha. -* Apenas um argumento é permitido por linha em uma função multi-linha. -* O parêntese de fechamento de uma função multi-linha deve ter uma linha - reservada para sí. +* O parêntese de abertura de uma chamada de função de várias linhas deve ser o último conteúdo na + linha. +* Apenas um argumento é permitido por linha em uma chamada de função de várias linhas. +* O parêntese de fechamento de uma chamada de função de várias linhas deve estar em uma linha separada. -Um exemplo, ao invés de usar a seguinte formatação:: +Como exemplo, em vez de usar a seguinte formatação:: $matches = array_intersect_key($this->_listeners, array_flip(preg_grep($matchPattern, array_keys($this->_listeners), 0))); -Use esta:: +Use isto em vez disso:: $matches = array_intersect_key( $this->_listeners, @@ -67,118 +78,114 @@ Use esta:: ) ); -Comprimento da linha +Comprimento da Linha ==================== -É recomendado manter as linhas próximas de 100 caracteres no comprimento para -melhor leitura do código. As linhas não devem ser mais longas que 120 -caracteres. +É recomendado manter as linhas com aproximadamente 100 caracteres para melhor +legibilidade do código. Um limite de 80 ou 120 caracteres torna necessário +distribuir lógica ou expressões complexas por função, bem como dar funções +e objetos nomes mais curtos e expressivos. As linhas não devem ter +mais de 120 caracteres. -Resumindo: +Em resumo: -* 100 caracteres é o limite recomendado. -* 120 caracteres é o limite máximo. +* 100 caracteres é o limite suave. +* 120 caracteres é o limite rígido. -Estruturas de controle -====================== +Estruturas de Controle +======================= -Estruturas de controle são por exemplo, "``if``", "``for``", "``foreach``", -"``while``", "``switch``", etc. A baixo, um exemplo com "``if``":: +Estruturas de controle são, por exemplo, "``if``", "``for``", "``foreach``", +"``while``", "``switch``" etc. Abaixo, um exemplo com "``if``":: if ((expr_1) || (expr_2)) { - // ação_1; + // action_1; } elseif (!(expr_3) && (expr_4)) { - // ação_2; + // action_2; } else { - // ação_padrão; + // default_action; } -* Nas estruturas de controle deve existir 1 (um) espaço antes do primeiro - parêntese e 1 (um) espaço entre o último parêntese e a chave de abertura. -* Sempre use chaves nas estruturas de controle, mesmo que não sejam - necessárias. Elas melhorar a leitura do código e tendem a causar menos erros - lógicos. -* A abertura da chave deve ser posicionada na mesma linha que a estrutura de - controle. A chave de fechamento deve ser colocada em uma nova linha e ter o - mesmo nível de indentação que a estrutura de controle. O conteúdo de dentro - das chaves deve começar em uma nova linha e receber um novo nível de - indentação. -* Atribuições em linha não devem ser usadas dentro de estruturas de controle. +* Nas estruturas de controle deve haver 1 (um) espaço antes do primeiro + parêntese e 1 (um) espaço entre o último parêntese e o colchete de abertura. +* Sempre use chaves nas estruturas de controle, mesmo que não sejam necessárias. + Elas aumentam a legibilidade do código e dão menos erros lógicos. +* As chaves de abertura devem ser colocadas na mesma linha que a estrutura de + controle. As chaves de fechamento devem ser colocadas em novas linhas e devem + ter o mesmo nível de indentação que a estrutura de controle. A declaração + incluída nas chaves deve começar em uma nova linha, e o código contido + nela deve ganhar um novo nível de indentação. +* Atribuições inline não devem ser usadas dentro das estruturas de controle. :: // errado = sem chaves, declaração mal posicionada - if (expr) declaração; + if (expr) statement; // errado = sem chaves if (expr) - declaração; + statement; - // certo + // bom if (expr) { - declaração; + statement; } - // errado = atribuição em linha + // errado = atribuição inline if ($variable = Class::function()) { - declaração; + statement; } - // certo + // bom $variable = Class::function(); if ($variable) { - declaração; + statement; } -Operadores ternários --------------------- +Operador Ternário +----------------- -Operadores ternários são admissíveis quando toda a operação ternária se encaixa -em uma única linha. Já operações mais longas devem ser divididas em -declarações ``if else``. Operadores ternários nunca devem ser aninhados. -Opcionalmente parênteses podem ser usados ao redor da verificação de condição -ternária para esclarecer a operação:: +Operadores ternários são permitidos quando toda a operação ternária cabe em uma +linha. Ternários mais longos devem ser divididos em instruções ``if else``. Operadores +ternários nunca devem ser aninhados. Opcionalmente, parênteses podem ser usados ao redor +da verificação de condição do ternário para maior clareza:: // Bom, simples e legível $variable = isset($options['variable']) ? $options['variable'] : true; - // Aninhamento é ruim + // Ternários aninhados são ruins $variable = isset($options['variable']) ? isset($options['othervar']) ? true : false : false; -Arquivos de template +Arquivos de Template -------------------- -Em arquivos de *template* (arquivos .php) os desenvolvedores devem usar -estruturas de controle por palavra-chave. A legibilidade em arquivos de -*template* complexos é muito melhor dessa forma. As estruturas de controle -podem tanto estar contidas em grandes blocos de código PHP, ou ainda em *tags* -PHP separadas:: +Em arquivos de template, os desenvolvedores devem usar estruturas de controle com palavras-chave. +Estruturas de controle com palavras-chave são mais fáceis de ler em arquivos de template complexos. Estruturas de +controle podem estar contidas em um bloco PHP maior ou em tags PHP separadas:: Você é o usuário administrador.

'; + echo '

You are the admin user.

'; endif; ?> -

A seguinte estrutura também é aceitável:

+

The following is also acceptable:

-

Você é o usuário administrador.

+

You are the admin user.

Comparação ========== -Sempre tente ser o mais rigoroso possível. Se uma comparação deliberadamente não -é estrita, pode ser inteligente comentar sobre isso para evitar confusões -geradas por falta de informação. +Sempre tente ser o mais estrito possível. Se um teste não estrito for deliberado, +pode ser prudente comentá-lo como tal para evitar confundi-lo com um erro. -Para testar se uma variável é nula, é recomendado usar uma verificação -estrita:: +Para testar se uma variável é null, é recomendado usar uma verificação estrita:: if ($value === null) { // ... } -O valor a ser verificado deve ser posto do lado direito:: +O valor a ser verificado deve ser colocado no lado direito:: // não recomendado if (null === $this->foo()) { @@ -190,35 +197,33 @@ O valor a ser verificado deve ser posto do lado direito:: // ... } -Chamadas de função +Chamadas de Função ================== -Funções devem ser chamadas sem espaço entre o nome da função e o parêntese -de abertura. Deve haver um espaço entre cada parâmetro de uma chamada de -função:: +Funções devem ser chamadas sem espaço entre o nome da função e o +parêntese de abertura. Deve haver um espaço entre cada parâmetro de uma chamada de função:: $var = foo($bar, $bar2, $bar3); -Como você pode ver a cima, deve haver um espaço em ambos os lados do sinal de -igual (=). +Como você pode ver acima, deve haver um espaço em ambos os lados do sinal de igual (=). -Definição de método -=================== +Definição de Método +==================== Exemplo de uma definição de método:: public function someFunction($arg1, $arg2 = '') { if (expr) { - declaração; + statement; } return $var; } -Parâmetros com um valor padrão, devem ser posicionados por último na definição -de uma função. Tente fazer suas funções retornarem algo, pelo menos ``true`` ou -``false``, assim pode-se determinar se a chamada de função foi bem-sucedida:: +Parâmetros com um valor padrão devem ser colocados por último na definição da função. +Tente fazer suas funções retornarem algo, pelo menos ``true`` ou ``false``, para que +se possa determinar se a chamada da função foi bem-sucedida:: public function connection($dns, $persistent = false) { @@ -235,80 +240,103 @@ de uma função. Tente fazer suas funções retornarem algo, pelo menos ``true`` return true; } -Existem espaços em ambos os lados dos sinais de igual. +Há espaços em ambos os lados do sinal de igual. -Declaração de tipo ------------------- +Retorno Antecipado +================== + +Tente evitar aninhamento desnecessário retornando antecipadamente:: + + public function run(array $data) + { + ... + if (!$success) { + return false; + } + + ... + } + + public function check(array $data) + { + ... + if (!$success) { + throw new RuntimeException(/* ... */); + } + + ... + } + +Isso ajuda a manter a lógica sequencial, o que melhora a legibilidade. + +Tipagem +------- -Argumentos que esperam objetos, *arrays* ou *callbacks* (válidos) podem ser -declarados por tipo. -Nós apenas declaramos métodos públicos, porém, o uso da declaração por tipo não -é livre de custos:: +Argumentos que esperam objetos, arrays ou callbacks (callable) podem ter tipo definido. +No entanto, apenas tipamos métodos públicos, pois a tipagem não é sem custo:: /** - * Descrição do método. + * Some method description. * - * @param \Cake\ORM\Table $table A classe Table a ser usada. - * @param array $array Algum valor em formato array. - * @param callable $callback Algum callback. - * @param bool $boolean Algum valor booleano. + * @param \Cake\ORM\Table $table The table class to use. + * @param array $array Some array value. + * @param callable $callback Some callback. + * @param bool $boolean Some boolean value. */ public function foo(Table $table, array $array, callable $callback, $boolean) { } -Aqui ``$table`` deve ser uma instância de ``\Cake\ORM\Table``, ``$array`` deve -ser um ``array`` e ``$callback`` deve ser do tipo ``callable`` (um *callback* -válido). +Aqui ``$table`` deve ser uma instância de ``\Cake\ORM\Table``, ``$array`` deve ser +um ``array`` e ``$callback`` deve ser do tipo ``callable`` (um callback válido). -Perceba que se você quiser permitir ``$array`` ser também uma instância de -``\ArrayObject`` você não deve declará-lo, pois ``array`` aceita apenas o tipo +Note que se você quiser permitir que ``$array`` também seja uma instância de +``\ArrayObject``, você não deve definir o tipo como ``array``, pois aceita apenas o tipo primitivo:: /** - * Descrição do método. + * Some method description. * - * @param array|\ArrayObject $array Algum valor em formato array. + * @param array|\ArrayObject $array Some array value. */ public function foo($array) { } -Funções anônimas (Closures) ---------------------------- +Funções Anônimas (Closures) +---------------------------- -Para se definir funções anônimas, segue-se o estilo de codificação `PSR-12 -`_, onde elas são declaradas com um espaço -depois da palavra-chave `function`, e um espaço antes e depois da palavra-chave -`use`:: +Definir funções anônimas segue o `guia de estilo de codificação PSR-12 +`_, onde são +declaradas com um espaço após a palavra-chave `function` e um espaço antes e depois +da palavra-chave `use`:: $closure = function ($arg1, $arg2) use ($var1, $var2) { - // código + // code }; -Encadeamento de métodos -======================= +Encadeamento de Métodos +======================== -Encadeamento de métodos deve ter múltiplos métodos distribuidos em linhas -separadas e indentados com quatro espaços:: +O encadeamento de métodos deve ter vários métodos distribuídos em linhas separadas e +indentados com quatro espaços:: - $email->from('foo@exemplo.com') - ->to('bar@exemplo.com') - ->subject('Uma mensagem legal') + $email->from('foo@example.com') + ->to('bar@example.com') + ->subject('A great message') ->send(); -Comentando código +Comentando Código ================= -Todos os comentários devem ser escritos em inglês, e devem de forma clara -descrever o bloco de código comentado. +Todos os comentários devem ser escritos em inglês e devem descrever de forma clara +o bloco de código comentado. -Comentários podem incluir as seguintes *tags* do -`phpDocumentor `_: +Os comentários podem incluir as seguintes tags do `phpDocumentor `_: * `@deprecated `_ - Usando o formato ``@version ``, onde ``version`` e - ``description`` são obrigatórios. + Usando o formato ``@version ``, onde ``version`` + e ``description`` são obrigatórios. Version refere-se àquela em que foi descontinuado. * `@example `_ * `@ignore `_ * `@internal `_ @@ -317,22 +345,22 @@ Comentários podem incluir as seguintes *tags* do * `@since `_ * `@version `_ -*Tags* PhpDoc são muito semelhantes a *tags* JavaDoc no Java. *Tags* são apenas -processadas se forem a primeira coisa numa linha de DocBlock, por exemplo:: +As tags PhpDoc são muito parecidas com as tags JavaDoc em Java. As tags só são processadas se +forem a primeira coisa em uma linha DocBlock, por exemplo:: /** - * Exemplo de tag. + * Tag example. * - * @author essa tag é analisada, mas essa versão é ignorada - * @version 1.0 essa tag também é analisada + * @author this tag is parsed, but this @version is ignored + * @version 1.0 this tag is also parsed */ :: /** - * Exemplo de tags phpDoc em linha. + * Example of inline phpDoc tags. * - * Essa função cria planos com foo() para conquistar o mundo. + * This function works hard with foo() to rule the world. * * @return void */ @@ -341,7 +369,7 @@ processadas se forem a primeira coisa numa linha de DocBlock, por exemplo:: } /** - * Função foo. + * Foo function. * * @return void */ @@ -349,50 +377,50 @@ processadas se forem a primeira coisa numa linha de DocBlock, por exemplo:: { } -Blocos de comentários, com a exceção do primeiro bloco em um arquivo, devem -sempre ser precedidos por uma nova linha. +Blocos de comentários, com exceção do primeiro bloco em um arquivo, devem sempre +ser precedidos por uma nova linha. -Tipos de variáveis +Tipos de Variáveis ------------------ -Tipos de variáveis para serem usadas em DocBlocks: +Tipos de variáveis para uso em DocBlocks: -Tipo - Descrição +Type + Description mixed - Uma variável com múltiplos tipos ou tipo indefinido. + Uma variável com tipo indefinido (ou múltiplo). int - Variável de tipo *int* (número inteiro). + Variável do tipo inteiro (número inteiro). float - Variável de tipo *float* (número decimal). + Tipo float (número com ponto decimal). bool - Variável de tipo *bool* (lógico, verdadeiro ou falso). + Tipo lógico (true ou false). string - Variável de tipo *string* (qualquer valor dentro de " " ou ' '). + Tipo string (qualquer valor entre " " ou ' '). null - Variável de tipo *null*. Normalmente usada em conjunto com outro tipo. + Tipo null. Geralmente usado em conjunto com outro tipo. array - Variável de tipo *array*. + Tipo array. object - Variável de tipo *object*. Um nome específico de classe deve ser usado, se - possível. + Tipo objeto. Um nome de classe específico deve ser usado se possível. resource - Variável de tipo *resource* (retornado de mysql\_connect() por exemplo). - Lembre-se que quando você especificar o tipo como *mixed*, você deve indicar - se o mesmo é desconhecido, ou quais os tipos possíveis. + Tipo resource (retornado por exemplo por mysql\_connect()). + Lembre-se de que quando você especifica o tipo como mixed, deve indicar + se é desconhecido ou quais são os tipos possíveis. callable - Variável de tipo função. + Função callable. -Você também pode combinar tipos usando o caractere de barra vertical:: +Você também pode combinar tipos usando o caractere pipe:: int|bool -Para mais de dois tipos é melhor usar ``mixed``. +Para mais de dois tipos, geralmente é melhor usar apenas ``mixed``. -Ao retornar o próprio objeto, e.g. para encadeamento, use ``$this`` ao invés:: +Ao retornar o próprio objeto (por exemplo, para encadeamento), deve-se usar ``$this`` +em vez disso:: /** - * Função Foo. + * Foo function. * * @return $this */ @@ -401,56 +429,52 @@ Ao retornar o próprio objeto, e.g. para encadeamento, use ``$this`` ao invés:: return $this; } -Incluindo arquivos +Incluindo Arquivos ================== -``include``, ``require``, ``include_once`` e ``require_once`` não tem +``include``, ``require``, ``include_once`` e ``require_once`` não têm parênteses:: - // errado = com parênteses + // errado = parênteses require_once('ClassFileName.php'); require_once ($class); - // certo = sem parênteses + // bom = sem parênteses require_once 'ClassFileName.php'; require_once $class; -Ao incluir arquivos com classes ou bibliotecas, use sempre e apenas a função -`require\_once `_. +Ao incluir arquivos com classes ou bibliotecas, use apenas e sempre a +função `require\_once `_. -Tags do PHP -=========== +Tags PHP +======== -Use sempre *tags* longas (````) ao invés de *tags* curtas (````). -O *short echo* deve ser usado em arquivos de template (**.php**) quando -apropriado. +Sempre use tags longas (````) em vez de tags curtas (````). O echo +curto deve ser usado em arquivos de template quando apropriado. -Short Echo +Echo Curto ---------- -O *short echo* deve ser usado em arquivos de template no lugar de -`` - // certo = sem ponto-e-virgula, com espaços + // bom = espaços, sem ponto e vírgula -A partir do PHP 5.4 a *tag short echo* (```_ * FTP: `ftp://ftp.example.com `_ -O nome de domínio "example.com" foi reservado para isso (see :rfc:`2606`), sendo -recomendado o seu uso em documentações como exemplos. +O nome de domínio "example.com" foi reservado para isso (veja :rfc:`2606`) e +é recomendado para uso em documentação ou como exemplos. Arquivos -------- -Nomes de arquivos que não contém classes devem ser em caixa baixa e sublinhados, +Nomes de arquivos que não contêm classes devem estar em letras minúsculas e sublinhados, por exemplo:: long_file_name.php -Moldagem de tipos +Conversão de Tipo ----------------- -Para moldagem usamos: +Para conversão de tipo usamos: -Tipo - Descrição +Type + Description (bool) - Converte para *boolean*. + Converter para boolean. (int) - Converte para *integer*. + Converter para integer. (float) - Converte para *float*. + Converter para float. (string) - Converte para *string*. + Converter para string. (array) - Converte para *array*. + Converter para array. (object) - Converte para *object*. + Converter para object. -Por favor use ``(int)$var`` ao invés de ``intval($var)`` e ``(float)$var`` ao -invés de ``floatval($var)`` quando aplicável. +Por favor, use ``(int)$var`` em vez de ``intval($var)`` e ``(float)$var`` em vez +de ``floatval($var)`` quando aplicável. -Constante ---------- +Constantes +---------- -Constantes devem ser definidas em caixa alta:: +Constantes devem ser definidas em letras maiúsculas:: define('CONSTANT', 1); -Se o nome de uma constante consiste de múltiplas palavras, eles devem ser -separados por um *underscore*, por exemplo:: +Se um nome de constante consiste em várias palavras, elas devem ser separadas por um +caractere de sublinhado, por exemplo:: define('LONG_NAMED_CONSTANT', 2); -Cuidados usando empty()/isset() -=============================== +Enums +----- + +Casos de Enum são definidos no estilo ``CamelCase``:: + + enum ArticleStatus: string + { + case Published = 'Y'; + case NotPublishedYet = 'N'; + } + +Cuidado ao usar empty()/isset() +================================ -Apesar de ``empty()`` ser uma função simples de ser usada, pode mascarar erros e -causar efeitos não intencionais quando ``'0'`` e ``0`` são retornados. Quando -variáveis ou propriedades já estão definidas, o uso de ``empty()`` não é -recomendado. Ao trabalhar com variáveis, é melhor confiar em coerção de tipo -com booleanos ao invés de ``empty()``:: +Embora ``empty()`` frequentemente pareça correto de usar, pode mascarar erros +e causar efeitos não intencionais quando ``'0'`` e ``0`` são fornecidos. Quando variáveis ou +propriedades já estão definidas, o uso de ``empty()`` não é recomendado. +Ao trabalhar com variáveis, é melhor confiar na coerção de tipo para boolean +em vez de ``empty()``:: function manipulate($var) { @@ -575,7 +591,7 @@ com booleanos ao invés de ``empty()``:: // ... } - // Recomendado, use coerção de tipo booleano + // Use coerção de tipo boolean if (!$var) { // ... } @@ -584,16 +600,16 @@ com booleanos ao invés de ``empty()``:: } } -Ao lidar com propriedades definidas, você deve favorecer verificações por -``null`` sobre verificações por ``empty()``/``isset()``:: +Ao lidar com propriedades definidas, você deve favorecer verificações de ``null`` em vez de +verificações ``empty()``/``isset()``:: class Thing { - private $property; // Definida + private $property; // Defined public function readProperty() { - // Não recomendado já que a propriedade está definida na classe + // Não recomendado pois a propriedade está definida na classe if (!isset($this->property)) { // ... } @@ -604,13 +620,13 @@ Ao lidar com propriedades definidas, você deve favorecer verificações por } } -Ao trabalhar com *arrays*, é melhor mesclar valores padronizados ao usar -verificações por ``empty()``. Assim, você se assegura que as chaves necessárias -estão definidas:: +Ao trabalhar com arrays, é melhor mesclar em padrões do que usar +verificações ``empty()``. Ao mesclar em padrões, você pode garantir que as chaves necessárias +estejam definidas:: function doWork(array $array) { - // Mescla valores para remover a necessidade de verificações via empty. + // Mesclar padrões para remover a necessidade de verificações empty. $array += [ 'key' => null, ]; @@ -627,5 +643,5 @@ estão definidas:: } .. meta:: - :title lang=pt: Padrões de codificação - :keywords lang=pt: indentação,comprimento,linha,funções,classes,métodos,variáveis,propriedades,arquivos,tipos,visibilidade,inclusão,operadores ternários,template,estruturas de controle + :title lang=pt: Padrões de Codificação + :keywords lang=pt: chaves,nível de indentação,erros lógicos,estruturas de controle,estrutura de controle,expr,padrões de codificação,parêntese,foreach,legibilidade,moose,novos recursos,repositório,desenvolvedores diff --git a/pt/controllers.rst b/pt/controllers.rst index 7f3d361694..a43e2bfe55 100644 --- a/pt/controllers.rst +++ b/pt/controllers.rst @@ -1,49 +1,47 @@ -Controllers (Controladores) -########################### +Controllers +########### .. php:namespace:: Cake\Controller .. php:class:: Controller -Os controllers (controladores) correspondem ao 'C' no padrão MVC. Após o roteamento ter sido -aplicado e o controller correto encontrado, a ação do controller é chamada. Seu -controller deve lidar com a interpretação dos dados de uma requisição, -certificando-se que os models corretos são chamados e a resposta ou view -esperada seja exibida. Os controllers podem ser vistos como intermediários entre -a camada Model e View. Você vai querer manter seus controllers magros e seus -Models gordos. Isso lhe ajudará a reutilizar seu código e testá-los mais -facilmente. - -Mais comumente, controllers são usados para gerenciar a lógica de um único -model. Por exemplo, se você está construindo um site para uma padaria online, -você pode ter um ``RecipesController`` e um ``IngredientsController`` -gerenciando suas receitas e seus ingredientes. No CakePHP, controllers são -nomeados de acordo com o model que manipulam. É também absolutamente possível -ter controllers que usam mais de um model. - -Os controllers da sua aplicação são classes que estendem a classe -``AppController``, a qual por sua vez estende a classe do core -:php:class:`Controller`. A classe ``AppController`` pode ser definida em -**src/Controller/AppController.php** e deve conter métodos que são -compartilhados entre todos os controllers de sua aplicação. - -Os controllers fornecem uma série de métodos que lidam com requisições. Estas -são chamados de *actions*. Por padrão, todos os métodos públicos em -um controller são uma action e acessíveis por uma URL. Uma action é responsável -por interpretar a requisição e criar a resposta. Normalmente as respostas são -na forma de uma view renderizada, mas também existem outras formas de criar -respostas. +Controllers são o 'C' no MVC. Após o roteamento ser aplicado e o controller +correto ser encontrado, a action do seu controller é chamada. Seu controller +deve manipular a interpretação dos dados da requisição, garantindo que os models +corretos sejam chamados e a resposta ou view correta seja renderizada. Controllers podem ser +pensados como camada intermediária entre o Model e a View. Você deve manter seus +controllers magros e seus models gordos. Isso ajudará você a reutilizar +seu código e tornará seu código mais fácil de testar. + +Comumente, um controller é usado para gerenciar a lógica em torno de um único model. Por +exemplo, se você estivesse construindo um site para uma padaria online, você pode ter um +RecipesController gerenciando suas receitas e um IngredientsController gerenciando seus +ingredientes. No entanto, também é possível ter controllers trabalhando com mais de +um model. No CakePHP, um controller é nomeado após o model primário que ele +manipula. + +Os controllers da sua aplicação estendem a classe ``AppController``, que por sua vez +estende a classe :php:class:`Controller` do núcleo. A classe ``AppController`` +pode ser definida em **src/Controller/AppController.php** e deve +conter métodos que são compartilhados entre todos os controllers da sua aplicação. + +Controllers fornecem vários métodos que manipulam requisições. Estes são chamados +*actions*. Por padrão, cada método público em +um controller é uma action e está acessível a partir de uma URL. Uma action é responsável +por interpretar a requisição e criar a resposta. Normalmente as respostas estão +na forma de uma view renderizada, mas existem outras formas de criar respostas +também. .. _app-controller: O App Controller ================ -Como mencionado anteriormente, a classe ``AppController`` é a mãe de todos os -outros controllers da sua aplicação. A própria ``AppController`` é estendida da +Como mencionado na introdução, a classe ``AppController`` é a classe pai +de todos os controllers da sua aplicação. ``AppController`` por si só estende a classe :php:class:`Cake\\Controller\\Controller` incluída no CakePHP. -Assim sendo, ``AppController`` é definida em -**src/Controller/AppController.php** como a seguir:: +``AppController`` é definida em **src/Controller/AppController.php** como +segue:: namespace App\Controller; @@ -53,15 +51,14 @@ Assim sendo, ``AppController`` é definida em { } -Os atributos e métodos criados em seu ``AppController`` vão estar disponíveis -para todos os controllers que a extendam. Components (sobre os quais você irá -aprender mais tarde) são a melhor alternativa para códigos usados por -muitos (mas não necessariamente em todos) controllers. +Atributos e métodos de controller criados no seu ``AppController`` estarão +disponíveis em todos os controllers que a estendem. Components (que você +aprenderá mais tarde) são melhor utilizados para código que é usado em muitos (mas não +necessariamente todos) controllers. -Você pode usar seu ``AppController`` para carregar components que serão usados -em cada controller de sua aplicação. O CakePHP oferece um método -``initialize()`` que é invocado ao final do construtor do controller para esse -tipo de uso:: +Você pode usar seu ``AppController`` para carregar components que serão usados em todos os +controllers da sua aplicação. CakePHP fornece um método ``initialize()`` que é +invocado no final do construtor de um Controller para este tipo de uso:: namespace App\Controller; @@ -69,146 +66,152 @@ tipo de uso:: class AppController extends Controller { - - public function initialize() + public function initialize(): void { - // Sempre habilite o CSRF component. - $this->loadComponent('Csrf'); + // Sempre habilita o component FormProtection. + $this->loadComponent('FormProtection'); } - } -Em adição ao método ``initialize()``, a antiga propriedade ``$components`` -também vai permitir você declarar quais components devem ser carregados. -Enquanto heranças objeto-orientadas normais são enquadradas, os components e -helpers usados por um controller são especialmente tratados. Nestes casos, os -valores de propriedade do ``AppController`` são mesclados com arrays de classes -controller filhas. Os valores na classe filha irão sempre sobre-escrever aqueles -na ``AppController``. - -Fluxo de requisições -==================== - -Quando uma requisição é feita para uma aplicação CakePHP, a classe -:php:class:`Cake\\Routing\\Router` e a classe -:php:class:`Cake\\Routing\\Dispatcher` usam :ref:`routes-configuration` para -encontrar e criar a instância correta do controller. Os dados da requisição são -encapsulados em um objeto de requisição. O CakePHP coloca todas as informações -importantes de uma requisição na propriedade ``$this->request``. Veja a seção -:ref:`cake-request` para mais informações sobre o objeto de requisição do -CakePHP. - -Métodos (actions) de controllers -================================ - -Actions de controllers são responsáveis por converter os parâmetros de -requisição em uma resposta para o navegador/usuário que fez a requisição. O -CakePHP usa convenções para automatizar este processo e remove alguns códigos -clichês que você teria que escrever de qualquer forma. - -Por convenção, o CakePHP renderiza uma view com uma versão flexionada do nome -da action. Retornando ao nosso exemplo da padaria online, nosso -``RecipesController`` poderia abrigar as actions ``view()``, ``share()`` e -``search()``. O controller seria encontrado em -**src/Controller/RecipesController.php** contendo:: +Fluxo de Requisição +=================== + +Quando uma requisição é feita para uma aplicação CakePHP, as classes +:php:class:`Cake\\Routing\\Router` e :php:class:`Cake\\Routing\\Dispatcher` +do CakePHP usam :ref:`routes-configuration` para encontrar e criar a +instância correta do controller. Os dados da requisição são encapsulados em um objeto de requisição. +CakePHP coloca todas as informações importantes da requisição na propriedade ``$this->request``. +Veja a seção sobre :ref:`cake-request` para mais informações sobre o +objeto de requisição do CakePHP. + +Controller Actions +================== + +Actions de controller são responsáveis por converter os parâmetros da requisição em uma +resposta para o navegador/usuário fazendo a requisição. CakePHP usa convenções para +automatizar este processo e remover algum código boilerplate que você de outra forma precisaria +escrever. + +Por convenção, CakePHP renderiza uma view com uma versão inflectada do nome da action. +Retornando ao nosso exemplo de padaria online, nosso RecipesController pode conter as +actions ``view()``, ``share()`` e ``search()``. O controller seria encontrado +em **src/Controller/RecipesController.php** e conteria:: // src/Controller/RecipesController.php class RecipesController extends AppController { - function view($id) + public function view($id) { - // A lógica da action vai aqui. + // Lógica da action vai aqui. } - function share($customerId, $recipeId) + public function share($customerId, $recipeId) { - // A lógica da action vai aqui. + // Lógica da action vai aqui. } - function search($query) + public function search($query) { - // A lógica da action vai aqui. + // Lógica da action vai aqui. } } -Os arquivos de template para estas actions seriam -**templates/Recipes/view.php**, **templates/Recipes/share.php** e -**templates/Recipes/search.php**. A nomenclatura convencional para arquivos -view é a versão lowercased (minúscula) e underscored (sem sublinhado) do nome -da action. +Os arquivos de template para essas actions seriam **templates/Recipes/view.php**, +**templates/Recipes/share.php** e **templates/Recipes/search.php**. O +nome convencional do arquivo de view é a versão em minúsculas e sublinhadas do +nome da action. -Actions dos controllers geralmente usam ``Controller::set()`` para criar um -contexto que a ``View`` usa para renderizar a camada view. Devido às convenções -que o CakePHP usa, você não precisa criar e renderizar as views manualmente. Ao -invés, uma vez que uma action de controller é completada, o CakePHP irá -manipular a renderização e devolver a view. +Actions de controller geralmente usam +``Controller::set()`` para criar um contexto que +``View`` usa para renderizar a camada de view. Por causa das convenções que o +CakePHP usa, você não precisa criar e renderizar a view manualmente. Em vez disso, +uma vez que uma action de controller tenha sido concluída, CakePHP manipulará a renderização e +entrega da View. -Se por alguma razão você quiser pular o comportamento padrão, você pode retornar -um objeto :php:class:`Cake\\Network\\Response` a partir da action com a resposta -definida. +Se por alguma razão você gostaria de pular o comportamento padrão, pode retornar um +objeto :php:class:`Cake\\Http\\Response` da action com a resposta totalmente +criada. -Para que você possa utilizar um controller de forma eficiente em sua própria -aplicação, nós iremos cobrir alguns dos atributos e métodos oferecidos pelo -controller do core do CakePHP. +Para que você use um controller efetivamente na sua própria aplicação, cobriremos +alguns dos atributos e métodos principais fornecidos pelos controllers do CakePHP. -Interagindo com views ---------------------- +Interagindo com Views +===================== -Os controllers interagem com as views de diversas maneiras. Primeiro eles -são capazes de passar dados para as views usando ``Controller::set()``. Você -também pode decidir no seu controller qual arquivo view deve ser renderizado -através do controller. +Controllers interagem com views de várias maneiras. Primeiro, eles +são capazes de passar dados para as views, usando ``Controller::set()``. Você também pode +decidir qual classe de view usar e qual arquivo de view deve ser +renderizado a partir do controller. .. _setting-view_variables: -Definindo variáveis para a view -------------------------------- +Definindo Variáveis de View +--------------------------- .. php:method:: set(string $var, mixed $value) O método ``Controller::set()`` é a principal maneira de enviar dados do seu -controller para a sua view. Após ter usado o método ``Controller::set()``, a -variável pode ser acessada em sua view:: +controller para sua view. Uma vez que você tenha usado ``Controller::set()``, a variável +pode ser acessada na sua view:: - // Primeiro você passa os dados do controller: + // Primeiro você passa dados do controller: $this->set('color', 'pink'); // Então, na view, você pode utilizar os dados: ?> - Você selecionou a cobertura para o bolo. + You have selected icing for the cake. -O método ``Controller::set()`` também aceita um array associativo como primeiro -parâmetro. Isto pode oferecer uma forma rápida para atribuir uma série de -informações para a view:: +O método ``Controller::set()`` também aceita um +array associativo como seu primeiro parâmetro. Isso pode frequentemente ser uma maneira rápida de +atribuir um conjunto de informações para a view:: $data = [ 'color' => 'pink', 'type' => 'sugar', - 'base_price' => 23.95 + 'base_price' => 23.95, ]; - // Faça $color, $type, e $base_price - // disponíveis na view: + // Torna $color, $type e $base_price + // disponíveis para a view: $this->set($data); -Renderizando uma view +Tenha em mente que variáveis de view são compartilhadas entre todas as partes renderizadas pela sua view. +Elas estarão disponíveis em todas as partes da view: o template, o layout e +todos os elementos dentro dos dois primeiros. + +Definindo Opções de View +------------------------ + +Se você quiser customizar a classe de view, caminhos de layout/template, helpers ou o +tema que será usado ao renderizar a view, você pode usar o +método ``viewBuilder()`` para obter um builder. Este builder pode ser usado para definir +propriedades da view antes de ser criada:: + + $this->viewBuilder() + ->addHelper('MyCustom') + ->setTheme('Modern') + ->setClassName('Modern.Admin'); + +O exemplo acima mostra como você pode carregar helpers customizados, definir o tema e usar uma +classe de view customizada. + +Renderizando uma View --------------------- .. php:method:: render(string $view, string $layout) -O método ``Controller::render()`` é chamado automaticamente no fim de cada ação -requisitada de um controller. Este método executa toda a lógica da view -(usando os dados que você passou usando o método ``Controller::set()``), coloca -a view em ``View::$layout``, e serve de volta para o usuário final. +O método ``Controller::render()`` é automaticamente chamado no final de cada action de +controller requisitada. Este método executa toda a lógica de view (usando os dados +que você submeteu usando o método ``Controller::set()``), coloca a view dentro do seu +``View::$layout`` e a serve de volta para o usuário final. -O arquivo view usado pelo método ``Controller::render()`` é determinado por -convenção. Se a action ``search()`` do controller ``RecipesController`` é -requisitada, o arquivo view encontrado em **templates/Recipes/search.php** -será renderizado:: +O arquivo de view padrão usado pelo render é determinado por convenção. +Se a action ``search()`` do RecipesController for requisitada, +o arquivo de view em **templates/Recipes/search.php** será renderizado:: namespace App\Controller; @@ -217,35 +220,33 @@ será renderizado:: // ... public function search() { - // Render the view in templates/Recipes/search.php - $this->render(); + // Renderiza a view em templates/Recipes/search.php + return $this->render(); } // ... } -Embora o CakePHP irá chamar o método ``Controller::render()`` automaticamente -(ao menos que você altere o atributo ``$this->autoRender`` para ``false``) após -cada action, você pode usá-lo para especificar um arquivo view alternativo -especificando o nome do arquivo view como primeiro parâmetro do método -``Controller::render()``. +Embora o CakePHP vá chamá-lo automaticamente após a lógica de cada action +(a menos que você tenha chamado ``$this->disableAutoRender()``), você pode usá-lo para especificar +um arquivo de view alternativo especificando um nome de arquivo de view como primeiro argumento do +método ``Controller::render()``. -Se o parâmetro ``$view`` começar com '/', é assumido ser um arquivo view -ou elemento relativo ao diretório ``/src/Template``. Isto -permite a renderização direta de elementos, muito útil em chamadas AJAX:: +Se ``$view`` começa com '/', assume-se que é um arquivo de view ou +elemento relativo à pasta **templates**. Isso permite +renderização direta de elementos, muito útil em chamadas AJAX:: // Renderiza o elemento em templates/element/ajaxreturn.php $this->render('/element/ajaxreturn'); -O segundo parâmetro ``$layout`` do ``Controller::render()`` permite que você -especifique o layout pelo qual a view é renderizada. +O segundo parâmetro ``$layout`` de ``Controller::render()`` permite que você especifique o layout +com o qual a view é renderizada. -Renderizando uma view específica -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Renderizando um Template Específico +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Em seu controller você pode querer renderizar uma view diferente do que a -convencional. Você pode fazer isso chamando o método -``Controller::render()`` diretamente. Uma vez chamado o método -``Controller::render()``, o CakePHP não tentará renderizar novamente a view:: +No seu controller, você pode querer renderizar uma view diferente da +convencional. Você pode fazer isso chamando ``Controller::render()`` diretamente. Uma vez que você +tenha chamado ``Controller::render()``, CakePHP não tentará re-renderizar a view:: namespace App\Controller; @@ -257,10 +258,10 @@ convencional. Você pode fazer isso chamando o método } } -Isto renderizaria o arquivo **templates/Posts/custom_file.php** ao invés de -**templates/Posts/my_action.php** +Isso renderizaria **templates/Posts/custom_file.php** em vez de +**templates/Posts/my_action.php**. -Você também pode renderizar views de plugins utilizando a seguinte sintaxe: +Você também pode renderizar views dentro de plugins usando a seguinte sintaxe: ``$this->render('PluginName.PluginController/custom_file')``. Por exemplo:: @@ -268,68 +269,142 @@ Por exemplo:: class PostsController extends AppController { - public function my_action() + public function myAction() { $this->render('Users.UserDetails/custom_file'); } } -Isto renderizaria **plugins/Users/templates/UserDetails/custom_file.php** +Isso renderizaria **plugins/Users/templates/UserDetails/custom_file.php** -Redirecionando para outras páginas -================================== +.. _controller-viewclasses: -.. php:method:: redirect(string|array $url, integer $status) +Negociação de Tipo de Conteúdo +============================== + +.. php:method:: addViewClasses() -O método de controle de fluxo que você vai usar na majoritariamente é -``Controller::redirect()``. Este método recebe seu primeiro parâmetro na forma -de uma URL relativa do CakePHP. Quando um usuário executar um pedido com êxito, -você pode querer redirecioná-lo para uma tela de recepção. :: +Controllers podem definir uma lista de classes de view que eles suportam. Após a +action do controller estar completa, CakePHP usará a lista de view para executar +negociação de tipo de conteúdo com :ref:`file-extensions` ou cabeçalhos ``Accept``. +Isso permite que sua aplicação reutilize a mesma action de controller para +renderizar uma view HTML ou renderizar uma resposta JSON ou XML. Para definir a lista de +classes de view suportadas por um controller, use o método ``addViewClasses()``:: - public function place_order() + namespace App\Controller; + + use Cake\View\JsonView; + use Cake\View\XmlView; + + class PostsController extends AppController { - // Logic for finalizing order goes here - if ($success) { - return $this->redirect( - ['controller' => 'Orders', 'action' => 'thanks'] - ); + public function initialize(): void + { + parent::initialize(); + + $this->addViewClasses([JsonView::class, XmlView::class]); } + } + +A classe ``View`` da aplicação é automaticamente usada como fallback quando nenhuma +outra view pode ser selecionada com base no cabeçalho ``Accept`` da requisição ou extensão de roteamento. +Se sua aplicação suporta apenas tipos de conteúdo para actions específicas, +você pode chamar ``addClasses()`` dentro da sua action também:: + + public function export(): void + { + // Use uma view CSV customizada para exportação de dados. + $this->addViewClasses([CsvView::class]); - return $this->redirect( - ['controller' => 'Orders', 'action' => 'confirm'] - ); + // Resto do código da action } -Este método irá retornar a instância da resposta com cabeçalhos apropriados -definidos. Você deve retornar a instância da resposta da sua action para -prevenir renderização de view e deixar o dispatcher controlar o redirecionamento -corrente. +Se dentro das actions do seu controller você precisar processar a requisição ou carregar dados +de forma diferente com base no tipo de conteúdo, você pode usar +:ref:`check-the-request`:: -Você também pode usar uma URL relativa ou absoluta como o parâmetro $url:: + // Em uma action de controller - return $this->redirect('/orders/thanks'); + // Carrega dados adicionais ao preparar respostas JSON + if ($this->request->is('json')) { + $query->contain('Authors'); + } - return $this->redirect('http://www.example.com'); +Caso sua aplicação precise de lógica mais complexa para decidir quais classes de view usar, +então você pode sobrescrever o método ``Controller::viewClasses()`` e retornar +um array de classes de view conforme necessário. + +.. note:: + Classes de view devem implementar o método hook estático ``contentType()`` para + participar da negociação de tipo de conteúdo. + +Fallbacks de Negociação de Tipo de Conteúdo +=========================================== + +Se nenhuma View puder ser correspondida com as preferências de tipo de conteúdo da requisição, CakePHP +usará a classe ``View`` base. Se você quiser exigir negociação de tipo de conteúdo, +pode usar a ``NegotiationRequiredView`` que define um código de status ``406``:: + + public function initialize(): void + { + parent::initialize(); -Você também pode passar dados para a action:: + // Exige negociação de cabeçalho Accept ou retorna uma resposta 406. + $this->addViewClasses([JsonView::class, NegotiationRequiredView::class]); + } - return $this->redirect(['action' => 'edit', $id]); +Você pode usar o valor de tipo de conteúdo ``TYPE_MATCH_ALL`` para construir sua própria lógica de fallback +de view:: -O segundo parâmetro passado no ``Controller::redirect()`` permite a você definir -um código de status HTTP para acompanhar o redirecionamento. Você pode querer -usar o código 301 (movido permanentemente) ou 303 (veja outro), dependendo -da natureza do redirecionamento. + namespace App\View; -Se você precisa redirecionar o usuário de volta para a página que fez a -requisição, você pode usar:: + use Cake\View\View; - $this->redirect($this->referer()); + class CustomFallbackView extends View + { + public static function contentType(): string + { + return static::TYPE_MATCH_ALL; + } -Um exemplo usando seqüências de consulta e hash pareceria com:: + } + +É importante lembrar que views match-all são aplicadas apenas *após* +a negociação de tipo de conteúdo ser tentada. + +Usando AjaxView +=============== + +Em aplicações que usam clientes hypermedia ou AJAX, você frequentemente precisa renderizar +conteúdos de view sem o layout envolvente. Você pode usar a ``AjaxView`` que é +fornecida com o esqueleto da aplicação:: + + // Em uma action de controller, ou em beforeRender. + if ($this->request->is('ajax')) { + $this->viewBuilder()->setClassName('Ajax'); + } + +``AjaxView`` responderá como ``text/html`` e usará o layout ``ajax``. +Geralmente este layout é mínimo ou contém marcação específica do cliente. Isso +substitui o uso de ``RequestHandlerComponent`` automaticamente usando a +``AjaxView`` no 4.x. + +Redirecionando para Outras Páginas +================================== + +.. php:method:: redirect(string|array $url, integer $status) + +O método ``redirect()`` adiciona um cabeçalho ``Location`` e define o código de status de +uma resposta e a retorna. Você deve retornar a resposta criada por +``redirect()`` para que o CakePHP envie o redirecionamento em vez de completar a +action do controller e renderizar uma view. + +Você pode redirecionar usando valores de :term:`routing array`:: return $this->redirect([ 'controller' => 'Orders', 'action' => 'confirm', + $order->id, '?' => [ 'product' => 'pizza', 'quantity' => 5 @@ -337,168 +412,210 @@ Um exemplo usando seqüências de consulta e hash pareceria com:: '#' => 'top' ]); -A URL gerada seria:: +Ou usando uma URL relativa ou absoluta:: - http://www.example.com/orders/confirm?product=pizza&quantity=5#top + return $this->redirect('/orders/confirm'); -Redirecionando para outra action no mesmo Controller ----------------------------------------------------- + return $this->redirect('http://www.example.com'); -.. php:method:: setAction($action, $args...) +Ou para a página referenciadora:: -Se você precisar redirecionar a atual action para uma diferente no *mesmo* -controller, você pode usar ``Controller::setAction()`` para atualizar o objeto -da requisição, modificar o template da view que será renderizado e redirecionar -a execução para a action especificada:: + return $this->redirect($this->referer()); - // De uma action delete, você pode renderizar uma página - // de índice atualizada. - $this->setAction('index'); +Usando o segundo parâmetro você pode definir um código de status para seu redirecionamento:: -Carregando models adicionais -============================ + // Faz um 301 (movido permanentemente) + return $this->redirect('/order/confirm', 301); -.. php:method:: loadModel(string $modelClass, string $type) + // Faz um 303 (veja outro) + return $this->redirect('/order/confirm', 303); -O método ``loadModel`` vem a calhar quando você precisa usar um model que -não é padrão do controller ou o seu model não está associado com -este.:: +Veja a seção :ref:`redirect-component-events` para como redirecionar a partir de +um manipulador de ciclo de vida. - // Em um método do controller. - $this->loadModel('Articles'); - $recentArticles = $this->Articles->find('all', [ - 'limit' => 5, - 'order' => 'Articles.created DESC' - ]); +Carregando Tables/Models Adicionais +=================================== -Se você está usando um provedor de tabelas que não os da ORM nativa você pode -ligar este sistema de tabelas aos controllers do CakePHP conectando seus -métodos de factory:: +.. php:method:: fetchTable(string $alias, array $config = []) - // Em um método do controller. - $this->modelFactory( - 'ElasticIndex', - ['ElasticIndexes', 'factory'] - ); +O método ``fetchTable()`` é útil quando você precisa usar uma table ORM que não é +a padrão do controller:: -Depois de registrar uma tabela factory, você pode usar o ``loadModel`` para -carregar instâncias:: + // Em um método de controller. + $recentArticles = $this->fetchTable('Articles')->find('all', + limit: 5, + order: 'Articles.created DESC' + ) + ->all(); - // Em um método do controller - $this->loadModel('Locations', 'ElasticIndex'); +.. php:method:: fetchModel(string|null $modelClass = null, string|null $modelType = null) -.. note:: +O método ``fetchModel()`` é útil para carregar models não-ORM ou tables ORM que +não são as padrões do controller:: - O TableRegistry da ORM nativa é conectado por padrão como o provedor de - 'Tabelas'. + // ModelAwareTrait precisa ser explicitamente adicionado ao seu controller primeiro para fetchModel() funcionar. + use ModelAwareTrait; -Paginando um model + // Obtém um model ElasticSearch + $articles = $this->fetchModel('Articles', 'Elastic'); + + // Obtém um model webservices + $github = $this->fetchModel('GitHub', 'Webservice'); + + // Se você pular o 2º argumento, por padrão tentará carregar uma table ORM. + $authors = $this->fetchModel('Authors'); + +.. versionadded:: 4.5.0 + +Paginando um Model ================== .. php:method:: paginate() -Este método é usado para fazer a paginação dos resultados retornados por -seus models. Você pode especificar o tamanho da página (quantos resultados -serão retornados), as condições de busca e outros parâmetros. Veja a seção -:doc:`pagination ` para mais detalhes -sobre como usar o método ``paginate()`` +Este método é usado para paginar resultados obtidos pelos seus models. +Você pode especificar tamanhos de página, condições de busca do model e mais. Veja a +seção de :doc:`pagination ` para mais detalhes sobre +como usar ``paginate()``. -O atributo paginate lhe oferece uma forma fácil de customizar como -``paginate()`` se comporta:: +O atributo ``$paginate`` oferece uma maneira de customizar como ``paginate()`` +se comporta:: class ArticlesController extends AppController { - public $paginate = [ + protected array $paginate = [ 'Articles' => [ - 'conditions' => ['published' => 1] - ] + 'conditions' => ['published' => 1], + ], ]; } -Configurando components para carregar +Configurando Components para Carregar ===================================== .. php:method:: loadComponent($name, $config = []) -Em seu método ``initialize()`` do controller você pode definir qualquer -component que quiser carregado, e qualquer configuração de dados para eles:: +No método ``initialize()`` do seu Controller você pode definir quaisquer components que +deseja carregar e quaisquer dados de configuração para eles:: - public function intialize() + public function initialize(): void { parent::initialize(); - $this->loadComponent('Csrf'); + $this->loadComponent('Flash'); $this->loadComponent('Comments', Configure::read('Comments')); } -.. php:attr:: components +.. _controller-life-cycle: -A propriedade ``$components`` em seus controllers permitem a você configurar -components. Components configurados e suas dependências serão criados pelo -CakePHP para você. Leia a seção :ref:`configuring-components` para mais -informações. Como mencionado anteriormente, a propriedade ``$components`` será -mesclada com a propriedade definida em cada classe parente do seu controller. +Callbacks do Ciclo de Vida da Requisição +======================================== -Configurando helpers para carregar -================================== +Controllers do CakePHP disparam vários eventos/callbacks que você pode usar para inserir +lógica em torno do ciclo de vida da requisição: -.. php:attr:: helpers +Lista de Eventos +---------------- -Vamos observar como dizer ao controller do CakePHP que você planeja usar -classes MVC adicionais:: +* ``Controller.initialize`` +* ``Controller.startup`` +* ``Controller.beforeRedirect`` +* ``Controller.beforeRender`` +* ``Controller.shutdown`` - class RecipesController extends AppController - { - public $helpers = ['Form']; - } +Métodos Callback de Controller +------------------------------ -Cada uma dessas variáveis são mescladas com seus valores herdados, -portanto não é necessário (por exemplo) redeclarar o ``FormHelper``, -ou qualquer coisa declarada em seu ``AppController``. +Por padrão, os seguintes métodos callback são conectados aos eventos relacionados se os +métodos forem implementados pelos seus controllers -.. _controller-life-cycle: +.. php:method:: beforeFilter(EventInterface $event) + + Chamado durante o evento ``Controller.initialize`` que ocorre antes de toda + action no controller. É um lugar útil para verificar uma sessão ativa + ou inspecionar permissões de usuário. -Ciclo de vida de callbacks em uma requisição -============================================ + .. note:: -Os controllers do CakePHP vêm equipados com callbacks que você pode usar para -inserir lógicas em torno do ciclo de vida de uma requisição: + O método beforeFilter() será chamado para actions inexistentes. -.. php:method:: beforeFilter(Event $event) + Retornar uma resposta de um método ``beforeFilter`` não impedirá outros + ouvintes do mesmo evento de serem chamados. Você deve explicitamente + :ref:`parar o evento `. - Este método é executado antes de cada ação dos controllers. - É um ótimo lugar para verificar se há uma sessão ativa ou inspecionar as - permissões de um usuário. +.. php:method:: beforeRender(EventInterface $event) - .. note:: + Chamado durante o evento ``Controller.beforeRender`` que ocorre após + a lógica da action do controller, mas antes da view ser renderizada. Este callback não é + usado frequentemente, mas pode ser necessário se você estiver chamando + :php:meth:`Cake\\Controller\\Controller::render()` manualmente antes do fim + de uma action específica. + +.. php:method:: afterFilter(EventInterface $event) - O método beforeFilter() será chamado para ações ausêntes. + Chamado durante o evento ``Controller.shutdown`` que é disparado após + toda action de controller e após a renderização estar completa. Este é o último + método de controller a ser executado. -.. php:method:: beforeRender(Event $event) +Além dos callbacks de ciclo de vida de controller, :doc:`/controllers/components` +também fornecem um conjunto similar de callbacks. - Chamada após a lógica da action de um controller, mas antes da view ser - renderizada. Esse callback não é usado frequentemente, mas pode ser - necessário se você estiver chamando - :php:meth:`~Cake\\Controller\\Controller::render()` manualmente antes do - final de uma determinada action. +Lembre-se de chamar os callbacks do ``AppController`` dentro dos callbacks de controllers filhos +para melhores resultados:: -.. php:method:: afterFilter() + //use Cake\Event\EventInterface; + public function beforeFilter(EventInterface $event): void + { + parent::beforeFilter($event); + } - Chamada após cada ação dos controllers, e após a completa renderização da - view. Este é o último método executado do controller. +.. _controller-middleware: -Em adição ao ciclo de vida dos callbacks do controller, -:doc:`/controllers/components` também oferece um conjunto de callbacks -similares. +Usando Redirecionamentos em Eventos de Controller +================================================= -Lembre de chamar os callbacks do ``AppController`` em conjunto com os callbacks -dos controllers para melhores resultados:: +Para redirecionar a partir de um método callback de controller você pode usar o seguinte:: - public function beforeFilter(Event $event) + public function beforeFilter(EventInterface $event): void { - parent::beforeFilter($event); + if (...) { + $event->setResult($this->redirect('/')); + + return; + } + + ... + } + +Ao definir um redirecionamento como resultado de evento, você permite que o CakePHP saiba que você não quer que nenhum outro +callback de component seja executado e que o controller não deve manipular a action +mais adiante. + +A partir da versão 4.1.0 você também pode lançar uma ``RedirectException`` para sinalizar um redirecionamento. + +Controller Middleware +===================== + +.. php:method:: middleware($middleware, array $options = []) + +:doc:`Middleware ` pode ser definido globalmente, em +um escopo de roteamento ou dentro de um controller. Para definir middleware para um controller específico, +use o método ``middleware()`` do método ``initialize()`` +do seu controller:: + + public function initialize(): void + { + parent::initialize(); + + $this->middleware(function ($request, $handler) { + // Lógica do middleware. + + // Certifique-se de retornar uma resposta ou chamar handle() + return $handler->handle($request); + }); } -Mais sobre controllers +Middleware definido por um controller será chamado **antes** dos métodos ``beforeFilter()`` e de action serem chamados. + +Mais sobre Controllers ====================== .. toctree:: diff --git a/pt/controllers/components/flash.rst b/pt/controllers/components/flash.rst index e231b0552f..fe0bee6bf4 100644 --- a/pt/controllers/components/flash.rst +++ b/pt/controllers/components/flash.rst @@ -5,21 +5,21 @@ Flash .. php:class:: FlashComponent(ComponentCollection $collection, array $config = []) -O FlashComponent fornece uma maneira de definir as mensagens de notificação únicas -a serem exibidas após o processamento de um formulário ou o reconhecimento de dados. -CakePHP refere-se a essas mensagens como "mensagens flash". O FlashComponent grava -mensagens flash em ``$_SESSION``, para serem renderizadas em uma View usando +FlashComponent fornece uma maneira de definir mensagens de notificação únicas a serem +exibidas após processar um formulário ou reconhecer dados. O CakePHP se refere a essas +mensagens como "mensagens flash". FlashComponent grava mensagens flash em +``$_SESSION``, para serem renderizadas em uma View usando :doc:`FlashHelper `. -Configurando Mensagens em Flash -=============================== +Definindo Mensagens Flash +========================== -O FlashComponent fornece duas maneiras de definir mensagens em flash: seu método -mágico ``__call()`` e seu método ``set()``. Para fornecer detalhes ao seu aplicativo, -o método mágico ``__call()`` do FlashComponent permite que você use um nome de método -que mapeie para um elemento localizado no diretório **templates/element/flash**. Por -convenção, os métodos camelcased serão mapeados para o nome do elemento em minúsculas -e sublinhado:: +FlashComponent fornece duas maneiras de definir mensagens flash: seu método mágico ``__call()`` +e seu método ``set()``. Para fornecer verbosidade à sua aplicação, +o método mágico ``__call()`` do FlashComponent permite que você use um nome de método que +mapeia para um elemento localizado no diretório **templates/element/flash**. +Por convenção, métodos camelcased serão mapeados para o nome do elemento +em letras minúsculas e com underscores:: // Usa templates/element/flash/success.php $this->Flash->success('This was successful'); @@ -27,75 +27,76 @@ e sublinhado:: // Usa templates/element/flash/great_success.php $this->Flash->greatSuccess('This was greatly successful'); -Como alternativa, para definir uma mensagem de texto sem processar um elemento, -você pode usar o método ``set()``:: +Alternativamente, para definir uma mensagem de texto simples sem renderizar um elemento, você pode +usar o método ``set()``:: $this->Flash->set('This is a message'); -Mensagens em Flash são anexadas a uma matriz internamente. Chamadas sucessivas -para ``set()`` ou ``__call()`` com a mesma chave anexarão as mensagens em -``$_SESSION``. Se você deseja sobrescrever as mensagens existentes ao definir -uma mensagem flash, defina a opção ``clear`` como ``true`` ao configurar o -componente. +Mensagens flash são anexadas a um array internamente. Chamadas sucessivas a +``set()`` ou ``__call()`` com a mesma chave irão anexar as mensagens em +``$_SESSION``. Se você quiser sobrescrever mensagens existentes ao definir uma mensagem +flash, defina a opção ``clear`` como ``true`` ao configurar o Component. -Os métodos ``__call()`` e ``set()`` do FlashComponent recebem opcionalmente um -segundo parâmetro, uma matriz de opções: +Os métodos ``__call()`` e ``set()`` do FlashComponent opcionalmente aceitam um segundo +parâmetro, um array de opções: -* ``key`` O padrão é 'flash'. A chave da matriz encontrada sob a chave ``Flash`` na sessão. -* ``element`` O padrão é ``null``, mas será automaticamente definido ao usar o método mágico - ``__call()``. O nome do elemento a ser usado para renderização. -* ``params`` Uma matriz opcional de chaves/valores para disponibilizar como variáveis dentro de um elemento. -* ``clear`` espera um ``bool`` e permite excluir todas as mensagens da pilha atual e iniciar uma nova. +* ``key`` Padrão é 'flash'. A chave do array encontrada sob a chave ``Flash`` na + sessão. +* ``element`` Padrão é ``null``, mas será automaticamente definido ao usar o + método mágico ``__call()``. O nome do elemento a ser usado para renderização. +* ``params`` Um array opcional de chaves/valores para disponibilizar como variáveis + dentro de um elemento. +* ``clear`` espera um ``bool`` e permite que você exclua todas as mensagens na + pilha atual e inicie uma nova. Um exemplo de uso dessas opções:: - // Em seu Controller + // In your Controller $this->Flash->success('The user has been saved', [ 'key' => 'positive', 'clear' => true, 'params' => [ 'name' => $user->name, - 'email' => $user->email - ] + 'email' => $user->email, + ], ]); - // Em sua View + // In your View Flash->render('positive') ?> - +
: , .
-Observe que o parâmetro ``element`` sempre será substituído ao usar +Note que o parâmetro ``element`` sempre será sobrescrito ao usar +``__call()``. Para recuperar um elemento específico de um plugin, você deve +definir o parâmetro ``plugin``. Por exemplo:: -Observe que o parâmetro ``element`` sempre será substituído ao usar ``__call()``. -Para recuperar um elemento específico de um plugin, você deve definir o parâmetro -``plugin``. Por exemplo:: - - // Em seu Controller + // In your Controller $this->Flash->warning('My message', ['plugin' => 'PluginName']); -O código acima usará o elemento **warning.php** em -**plugins/PluginName/templates/element/flash** para renderizar a mensagem flash. +O código acima usará o elemento **warning.php** sob +**plugins/PluginName/templates/element/flash** para renderizar a mensagem +flash. .. note:: - Por padrão, o CakePHP escapa o conteúdo das mensagens em flash para evitar - scripts entre sites. Os dados do usuário em suas mensagens flash serão codificados - em HTML e seguros para serem impressos. Se você deseja incluir HTML em suas mensagens - em flash, é necessário passar a opção ``escape`` e ajustar seus modelos de mensagens - em flash para permitir desativar a fuga quando a opção de escape é aprovada. + Por padrão, o CakePHP escapa o conteúdo em mensagens flash para prevenir cross + site scripting. Dados do usuário em suas mensagens flash serão codificados em HTML e + seguros para serem impressos. Se você quiser incluir HTML em suas mensagens flash, você + precisa passar a opção ``escape`` e ajustar seus templates de mensagens flash + para permitir desabilitar o escape quando a opção escape for passada. -HTML Em Mensagens Flash -======================= +HTML em Mensagens Flash +======================== É possível gerar HTML em mensagens flash usando a chave de opção ``'escape'``:: $this->Flash->info(sprintf('%s %s', h($highlight), h($message)), ['escape' => false]); -Certifique-se de escapar da entrada manualmente, então. No exemplo acima, ``$highlights`` e -``$message`` são entradas não HTML e, portanto, escapam. +Certifique-se de escapar a entrada manualmente. No exemplo acima, +``$highlight`` e ``$message`` são entradas não-HTML e, portanto, escapadas. -Para obter mais informações sobre como renderizar suas mensagens em flash, consulte a +Para mais informações sobre renderização de suas mensagens flash, consulte a seção :doc:`FlashHelper `. diff --git a/pt/controllers/pages-controller.rst b/pt/controllers/pages-controller.rst index 3c52c960dd..717310a9d3 100644 --- a/pt/controllers/pages-controller.rst +++ b/pt/controllers/pages-controller.rst @@ -1,17 +1,17 @@ O Pages Controller ################## -CakePHP é distribuído com o controller **PagesController.php**. Esse controller -é simples, seu uso é opcional e normalmente direcionado a prover páginas -estáticas. A homepage que você vê logo depois de instalar o CakePHP utiliza esse -controller e o arquivo da view fica em **templates/Pages/home.php**. Se você -criar o arquivo **templates/Pages/about.php**, você poderá acessá-lo em -**http://example.com/pages/about**. Fique a vontade para alterar esse controller -para atender suas necessacidades ou mesmo excluí-lo. +A aplicação skeleton oficial do CakePHP vem com um controller padrão **PagesController.php**. +Este é um controller simples e opcional para servir conteúdo estático. A página inicial +que você vê após a instalação é gerada usando este controller e o arquivo de view +**templates/Pages/home.php**. Se você criar o arquivo de view +**templates/Pages/about_us.php**, você pode acessá-lo usando a URL +**http://example.com/pages/about_us**. Você é livre para modificar o Pages +Controller para atender às suas necessidades. -Quando você cria sua aplicação pelo Composer, o ``PagesController`` vai ser -criado na pasta **src/Controller/**. +Quando você "bake" uma aplicação usando o Composer, o Pages Controller é criado na sua +pasta **src/Controller/**. .. meta:: - :title lang=pt: O Controlador Pages - :keywords lang=pt: pages controller,default controller,cakephp,ships,php,home page,página estática + :title lang=en: The Pages Controller + :keywords lang=en: pages controller,default controller,cakephp,ships,php,file folder,home page diff --git a/pt/controllers/pagination.rst b/pt/controllers/pagination.rst index 474674a046..eac526b647 100644 --- a/pt/controllers/pagination.rst +++ b/pt/controllers/pagination.rst @@ -1,44 +1,44 @@ -Pagination +Paginação ########## -One of the main obstacles of creating flexible and user-friendly web -applications is designing an intuitive user interface. Many applications tend to -grow in size and complexity quickly, and designers and programmers alike find -they are unable to cope with displaying hundreds or thousands of records. -Refactoring takes time, and performance and user satisfaction can suffer. +Um dos principais obstáculos na criação de aplicações web flexíveis e amigáveis +é o design de uma interface de usuário intuitiva. Muitas aplicações tendem a +crescer rapidamente em tamanho e complexidade, e designers e programadores acabam +descobrindo que não conseguem lidar com a exibição de centenas ou milhares de registros. +A refatoração leva tempo, e o desempenho e a satisfação do usuário podem sofrer. -Displaying a reasonable number of records per page has always been a critical -part of every application and used to cause many headaches for developers. -CakePHP eases the burden on the developer by providing a terse way to -paginate data. +Exibir um número razoável de registros por página sempre foi uma parte crítica +de todas as aplicações e costumava causar muitas dores de cabeça para os desenvolvedores. +O CakePHP alivia o fardo do desenvolvedor fornecendo uma maneira concisa de +paginar dados. -Pagination in CakePHP controllers is done through the ``paginate()`` method. You -then use :php:class:`~Cake\\View\\Helper\\PaginatorHelper` in your view templates -to generate pagination controls. +A paginação nos controllers do CakePHP é feita através do método ``paginate()``. Você +então usa :php:class:`~Cake\\View\\Helper\\PaginatorHelper` nos seus view templates +para gerar controles de paginação. -Basic Usage +Uso Básico =========== -You can call ``paginate()`` using an ORM table instance or ``Query`` object:: +Você pode chamar ``paginate()`` usando uma instância de tabela ORM ou objeto ``Query``:: public function index() { - // Paginate the ORM table. + // Paginar a tabela ORM. $this->set('articles', $this->paginate($this->Articles)); - // Paginate a select query + // Paginar uma query select $query = $this->Articles->find('published')->contain('Comments'); $this->set('articles', $this->paginate($query)); } -Advanced Usage +Uso Avançado ============== -More complex use cases are supported by configuring the ``$paginate`` -controller property or as the ``$settings`` argument to ``paginate()``. These -conditions serve as the basis for you pagination queries. They are augmented -by the ``sort``, ``direction``, ``limit``, and ``page`` parameters passed in -from the URL:: +Casos de uso mais complexos são suportados através da configuração da propriedade +``$paginate`` do controller ou como argumento ``$settings`` para ``paginate()``. Essas +condições servem como base para suas queries de paginação. Elas são aumentadas +pelos parâmetros ``sort``, ``direction``, ``limit`` e ``page`` passados +da URL:: class ArticlesController extends AppController { @@ -51,9 +51,9 @@ from the URL:: } .. tip:: - Default ``order`` options must be defined as an array. + As opções padrão de ``order`` devem ser definidas como um array. -You can also use :ref:`custom-find-methods` in pagination by using the ``finder`` option:: +Você também pode usar :ref:`custom-find-methods` na paginação usando a opção ``finder``:: class ArticlesController extends AppController { @@ -62,14 +62,14 @@ You can also use :ref:`custom-find-methods` in pagination by using the ``finder` ]; } -Note: This only works with Table as string input in ``$this->paginate('MyTable')``. Once you use ``$this->MyTable->find()`` as input for ``paginate()``, you must directly use that Query object instead. +Nota: Isso funciona apenas com Table como entrada de string em ``$this->paginate('MyTable')``. Depois de usar ``$this->MyTable->find()`` como entrada para ``paginate()``, você deve usar diretamente esse objeto Query. -If your finder method requires additional options you can pass those -as values for the finder:: +Se o seu método finder requer opções adicionais, você pode passá-las +como valores para o finder:: class ArticlesController extends AppController { - // find articles by tag + // encontrar artigos por tag public function tags() { $tags = $this->request->getParam('pass'); @@ -77,12 +77,12 @@ as values for the finder:: $customFinderOptions = [ 'tags' => $tags ]; - // We're using the $settings argument to paginate() here. - // But the same structure could be used in $this->paginate + // Estamos usando o argumento $settings para paginate() aqui. + // Mas a mesma estrutura poderia ser usada em $this->paginate // - // Our custom finder is called findTagged inside ArticlesTable.php - // which is why we're using `tagged` as the key. - // Our finder should look like: + // Nosso finder customizado é chamado findTagged dentro de ArticlesTable.php + // que é por isso que estamos usando `tagged` como chave. + // Nosso finder deve ser assim: // public function findTagged(Query $query, array $tagged = []) $settings = [ 'finder' => [ @@ -94,9 +94,9 @@ as values for the finder:: } } -In addition to defining general pagination values, you can define more than one -set of pagination defaults in the controller. The name of each model can be used -as a key in the ``$paginate`` property:: +Além de definir valores gerais de paginação, você pode definir mais de um +conjunto de padrões de paginação no controller. O nome de cada model pode ser usado +como uma chave na propriedade ``$paginate``:: class ArticlesController extends AppController { @@ -106,69 +106,69 @@ as a key in the ``$paginate`` property:: ]; } -The values of the ``Articles`` and ``Authors`` keys could contain all the keys -that a basic ``$paginate`` array would. +Os valores das chaves ``Articles`` e ``Authors`` podem conter todas as chaves +que um array ``$paginate`` básico conteria. -``Controller::paginate()`` returns an instance of ``Cake\Datasource\Paging\PaginatedResultSet`` -which implements the ``Cake\Datasource\Paging\PaginatedInterface``. +``Controller::paginate()`` retorna uma instância de ``Cake\Datasource\Paging\PaginatedResultSet`` +que implementa a ``Cake\Datasource\Paging\PaginatedInterface``. -This object contains the paginated records and the paging params. +Este objeto contém os registros paginados e os parâmetros de paginação. -Simple Pagination +Paginação Simples ================= -By default ``Controller::paginate()`` uses the ``Cake\Datasource\Paging\NumericPaginator`` -class which does a ``COUNT()`` query to calculate the size of the result set so -that page number links can be rendered. On very large datasets this count query -can be very expensive. In situations where you only want to show 'Next' and 'Previous' -links you can use the 'simple' paginator which does not do a count query:: +Por padrão, ``Controller::paginate()`` usa a classe ``Cake\Datasource\Paging\NumericPaginator`` +que faz uma query ``COUNT()`` para calcular o tamanho do conjunto de resultados para +que os links de número de página possam ser renderizados. Em conjuntos de dados muito grandes, essa query de contagem +pode ser muito cara. Em situações onde você quer mostrar apenas os links 'Próximo' e 'Anterior', +você pode usar o paginador 'simple' que não faz uma query de contagem:: class ArticlesController extends AppController { protected array $paginate = [ - 'className' => 'Simple', // Or use Cake\Datasource\Paging\SimplePaginator::class FQCN + 'className' => 'Simple', // Ou use Cake\Datasource\Paging\SimplePaginator::class FQCN ]; } -When using the ``SimplePaginator`` you will not be able to generate page -numbers, counter data, links to the last page, or total record count controls. +Ao usar o ``SimplePaginator``, você não será capaz de gerar números de +página, dados de contador, links para a última página ou controles de contagem total de registros. .. _paginating-multiple-queries: -Paginating Multiple Queries +Paginando Múltiplas Queries =========================== -You can paginate multiple models in a single controller action, using the -``scope`` option both in the controller's ``$paginate`` property and in the -call to the ``paginate()`` method:: +Você pode paginar múltiplos models em uma única ação de controller, usando a +opção ``scope`` tanto na propriedade ``$paginate`` do controller quanto na +chamada ao método ``paginate()``:: - // Paginate property + // Propriedade Paginate protected array $paginate = [ 'Articles' => ['scope' => 'article'], 'Tags' => ['scope' => 'tag'] ]; - // In a controller action + // Em uma ação do controller $articles = $this->paginate($this->Articles, ['scope' => 'article']); $tags = $this->paginate($this->Tags, ['scope' => 'tag']); $this->set(compact('articles', 'tags')); -The ``scope`` option will result in the paginator looking in -scoped query string parameters. For example, the following URL could be used to -paginate both tags and articles at the same time:: +A opção ``scope`` fará com que o paginador procure por +parâmetros de query string com escopo. Por exemplo, a seguinte URL poderia ser usada para +paginar tags e artigos ao mesmo tempo:: /dashboard?article[page]=1&tag[page]=3 -See the :ref:`paginator-helper-multiple` section for how to generate scoped HTML -elements and URLs for pagination. +Veja a seção :ref:`paginator-helper-multiple` para saber como gerar elementos HTML +e URLs com escopo para paginação. -Paginating the Same Model multiple Times ----------------------------------------- +Paginando o Mesmo Model várias vezes +------------------------------------ -To paginate the same model multiple times within a single controller action you -need to define an alias for the model.:: +Para paginar o mesmo model várias vezes dentro de uma única ação do controller você +precisa definir um alias para o model.:: - // In a controller action + // Em uma ação do controller $this->paginate = [ 'Articles' => [ 'scope' => 'published_articles', @@ -191,7 +191,7 @@ need to define an alias for the model.:: ->where(['published' => true]) ); - // Load an additional table object to allow differentiating in the paginator + // Carregar um objeto de tabela adicional para permitir a diferenciação no paginador $unpublishedArticlesTable = $this->fetchTable('UnpublishedArticles', [ 'className' => 'App\Model\Table\ArticlesTable', 'table' => 'articles', @@ -205,15 +205,15 @@ need to define an alias for the model.:: .. _control-which-fields-used-for-ordering: -Control which Fields Used for Ordering -====================================== +Controlar quais Campos são Usados para Ordenação +================================================ -By default sorting can be done on any non-virtual column a table has. This is -sometimes undesirable as it allows users to sort on un-indexed columns that can -be expensive to order by. You can set the allowed list of fields that can be sorted -using the ``sortableFields`` option. This option is required when you want to -sort on any associated data, or computed fields that may be part of your -pagination query:: +Por padrão, a ordenação pode ser feita em qualquer coluna não virtual que uma tabela tenha. Isso às vezes +é indesejável, pois permite que os usuários ordenem por colunas não indexadas que podem +ser caras para ordenar. Você pode definir a lista de campos permitidos que podem ser ordenados +usando a opção ``sortableFields``. Esta opção é necessária quando você deseja +ordenar por quaisquer dados associados ou campos computados que possam fazer parte da sua +query de paginação:: protected array $paginate = [ 'sortableFields' => [ @@ -221,37 +221,37 @@ pagination query:: ], ]; -Any requests that attempt to sort on fields not in the allowed list will be -ignored. +Quaisquer requisições que tentem ordenar por campos que não estão na lista permitida serão +ignoradas. -Limit the Maximum Number of Rows per Page -========================================= +Limitar o Número Máximo de Linhas por Página +============================================ -The number of results that are fetched per page is exposed to the user as the -``limit`` parameter. It is generally undesirable to allow users to fetch all -rows in a paginated set. The ``maxLimit`` option asserts that no one can set -this limit too high from the outside. By default CakePHP limits the maximum -number of rows that can be fetched to 100. If this default is not appropriate -for your application, you can adjust it as part of the pagination options, for -example reducing it to ``10``:: +O número de resultados que são buscados por página é exposto ao usuário como o +parâmetro ``limit``. Geralmente é indesejável permitir que os usuários busquem todas +as linhas em um conjunto paginado. A opção ``maxLimit`` garante que ninguém possa definir +esse limite muito alto de fora. Por padrão, o CakePHP limita o número máximo +de linhas que podem ser buscadas para 100. Se esse padrão não for apropriado +para sua aplicação, você pode ajustá-lo como parte das opções de paginação, por +exemplo, reduzindo-o para ``10``:: protected array $paginate = [ - // Other keys here. + // Outras chaves aqui. 'maxLimit' => 10 ]; -If the request's limit param is greater than this value, it will be reduced to -the ``maxLimit`` value. +Se o parâmetro limit da requisição for maior que este valor, ele será reduzido para +o valor ``maxLimit``. -Out of Range Page Requests -========================== +Requisições de Página Fora do Intervalo +======================================= -``Controller::paginate()`` will throw a ``NotFoundException`` when trying to -access a non-existent page, i.e. page number requested is greater than total -page count. +``Controller::paginate()`` lançará uma ``NotFoundException`` ao tentar +acessar uma página inexistente, ou seja, o número da página solicitado é maior que a +contagem total de páginas. -So you could either let the normal error page be rendered or use a try catch -block and take appropriate action when a ``NotFoundException`` is caught:: +Então você pode deixar a página de erro normal ser renderizada ou usar um bloco try catch +e tomar a ação apropriada quando uma ``NotFoundException`` for capturada:: use Cake\Http\Exception\NotFoundException; @@ -260,37 +260,37 @@ block and take appropriate action when a ``NotFoundException`` is caught:: try { $this->paginate(); } catch (NotFoundException $e) { - // Do something here like redirecting to first or last page. - // $e->getPrevious()->getAttributes('pagingParams') will give you required info. + // Faça algo aqui como redirecionar para a primeira ou última página. + // $e->getPrevious()->getAttributes('pagingParams') fornecerá as informações necessárias. } } -Using a paginator class directly -================================ +Usando uma classe de paginador diretamente +========================================== -You can also use a paginator directly.:: +Você também pode usar um paginador diretamente.:: - // Create a paginator + // Criar um paginador $paginator = new \Cake\Datasource\Paginator\NumericPaginator(); - // Paginate the model + // Paginar o model $results = $paginator->paginate( - // Query or table instance which you need to paginate + // Query ou instância de tabela que você precisa paginar $this->fetchTable('Articles'), - // Request params + // Parâmetros da requisição $this->request->getQueryParams(), - // Config array having the same structure as options as Controller::$paginate + // Array de configuração com a mesma estrutura das opções do Controller::$paginate [ 'finder' => 'latest', ] ); -Pagination in the View +Paginação na View ====================== -Check the :php:class:`~Cake\\View\\Helper\\PaginatorHelper` documentation for -how to create links for pagination navigation. +Verifique a documentação do :php:class:`~Cake\\View\\Helper\\PaginatorHelper` para +saber como criar links para navegação de paginação. .. meta:: - :title lang=en: Pagination - :keywords lang=en: paginate,pagination,paging + :title lang=pt: Paginação + :keywords lang=pt: paginate,pagination,paging diff --git a/pt/controllers/request-response.rst b/pt/controllers/request-response.rst index 4e2d2df319..06c8f18804 100644 --- a/pt/controllers/request-response.rst +++ b/pt/controllers/request-response.rst @@ -1,43 +1,53 @@ -Objetos de Requisição e Resposta -################################ +Objetos Request & Response +########################## .. php:namespace:: Cake\Http -Os objetos de solicitação e resposta fornecem uma abstração em torno de solicitações e -respostas HTTP. O objeto de solicitação no CakePHP permite que você examine uma solicitação -de entrada, enquanto o objeto de resposta permite criar respostas HTTP sem esforço do seus -controladores. +Os objetos de solicitação e resposta fornecem uma abstração em torno de solicitações +e respostas HTTP. O objeto de solicitação no CakePHP permite que você introspecte uma +solicitação recebida, enquanto o objeto de resposta permite que você crie respostas +HTTP facilmente a partir dos seus controllers. .. index:: $this->request .. _cake-request: -Requisição -========== +Request +======= .. php:class:: ServerRequest -``ServerRequest`` é o objeto de solicitação padrão usado no CakePHP. Ele centraliza -vários recursos para interrogar e interagir com os dados da solicitação. Em cada -solicitação, uma requisição é criada e depois passada por referência às várias camadas -de um aplicativo que usam dados da solicitação. Por padrão, a solicitação é atribuída -a ``$this->request`` e está disponível em Controllers, Cells, Views e Helpers. Você -também pode acessá-lo em Components usando a referência do controlador. Algumas das -tarefas que o ``ServerRequest`` executa incluem: +``ServerRequest`` é o objeto de requisição padrão usado no CakePHP. Ele centraliza +uma série de recursos para interrogar e interagir com os dados da requisição. +Em cada requisição, uma Requisição é criada e então passada por referência às +diversas camadas de uma aplicação que utilizam os dados da requisição. Por padrão, a requisição +é atribuída a ``$this->request`` e está disponível em Controllers, Células, Views +e Helpers. Você também pode acessá-la em Componentes usando a referência +ao controller. -* Processar as matrizes GET, POST e FILES nas estruturas de dados que você conhece. -* Fornecer introspecção do ambiente referente à solicitação. Informações como os - cabeçalhos enviados, o endereço IP do cliente e os nomes de subdomínio/domínio - no servidor em que seu aplicativo está sendo executado. -* Fornecendo acesso a parâmetros de solicitação, como índices de matriz e propriedades de objetos. +.. versionchanged:: 4.4.0 + O ``ServerRequest`` está disponível via DI. + Então você pode obtê-lo do contêiner ou usá-lo como uma dependência para o seu serviço. + +Algumas das tarefas que ``ServerRequest`` executa incluem: + +* Processando os arrays GET, POST e FILES nas estruturas de dados + com as quais você está familiarizado. +* Fornecendo introspecção do ambiente referente à solicitação. Informações + como os cabeçalhos enviados, o endereço IP do cliente e os nomes de subdomínio/domínio + do servidor em que sua aplicação está sendo executada. +* Fornecendo acesso aos parâmetros da solicitação, tanto como índices do array quanto como propriedades + do objeto. O objeto de solicitação do CakePHP implementa a `PSR-7 -ServerRequestInterface `_ -facilitando o uso de bibliotecas de fora do CakePHP. +ServerRequestInterface `_ facilitando o +uso de bibliotecas de fora do CakePHP. -Parâmetros de Requsição ------------------------ +.. _request-parameters: -A solicitação expõe parâmetros de roteamento através do método ``getParam()``:: +Request Parameters +------------------ + +A solicitação expõe parâmetros de roteamento por meio do método ``getParam()``:: $controllerName = $this->request->getParam('controller'); @@ -45,35 +55,39 @@ Para obter todos os parâmetros de roteamento como uma matriz, use ``getAttribut $parameters = $this->request->getAttribute('params'); -Todos :ref:`route-elements` são acessados através desta interface. +Todos os :ref:`route-elements` são acessados ​​por meio desta interface. -Além de :ref:`route-elements`, você também precisa frequentemente acessar :ref:`passed-arguments`. -Ambos estão disponíveis no objeto de solicitação também:: +Além de :ref:`route-elements`, você também precisa acessar frequentemente +:ref:`passed-arguments`. Ambos estão disponíveis no objeto de solicitação, +bem como:: // Argumentos passados $passedArgs = $this->request->getParam('pass'); -Todos fornecerão acesso aos argumentos passados. Existem vários parâmetros importantes/úteis -que o CakePHP usa internamente, todos eles também são encontrados nos parâmetros de roteamento: +Todos fornecerão acesso aos argumentos passados. Existem +vários parâmetros importantes/úteis que o CakePHP usa internamente, +todos eles também são encontrados nos parâmetros de roteamento: -* ``plugin`` O plug-in que manipula a solicitação. Será nulo quando não houver plug-in. -* ``controller`` O controlador que manipula a solicitação atual. -* ``action`` A ação que manipula a solicitação atual. -* ``prefix`` O prefixo da ação atual. Veja :ref:`prefix-routing` para mais informações. +* ``plugin`` O plugin que manipula a requisição. Será nulo quando não houver + plugin. +* ``controller`` O controller que manipula a requisição atual. +* ``action`` A ação que manipula a requisição atual. +* ``prefix`` O prefixo da ação atual. Veja :ref:`prefix-routing` para + mais informações. -Parâmetros em URL ------------------ +Parâmetros Query String +----------------------- .. php:method:: getQuery($name, $default = null) -Os parâmetros em URL podem ser lidos usando o método ``getQuery()``:: +Os parâmetros da string de consulta podem ser lidos usando o método ``getQuery()``:: // A URL é /posts/index?page=1&sort=title $page = $this->request->getQuery('page'); -Você pode acessar diretamente a propriedade query, ou pode usar o método ``getQuery()`` -para ler a matriz de consultas de URL de maneira livre de erros. Quaisquer chaves que -não existirem retornarão ``null``:: +Você pode acessar diretamente a propriedade de consulta ou usar o método +``getQuery()`` para ler o array de consultas de URL sem erros. +Quaisquer chaves que não existam retornarão ``null``:: $foo = $this->request->getQuery('value_that_does_not_exist'); // $foo === null @@ -81,158 +95,310 @@ não existirem retornarão ``null``:: // Você também pode fornecer valores padrão $foo = $this->request->getQuery('does_not_exist', 'default val'); -Se você deseja acessar todos os parâmetros da consulta, pode usar +Se você quiser acessar todos os parâmetros de consulta, você pode usar ``getQueryParams()``:: $query = $this->request->getQueryParams(); -Dados do Corpo da Requisição ----------------------------- +Você pode usar as funções de utilitário de conversão para fornecer acesso seguro a dados +de solicitação e outras entradas:: + + use function Cake\Core\toBool; + use function Cake\Core\toInt; + use function Cake\Core\toString; + use function Cake\I18n\toDate; + use function Cake\I18n\toDateTime; + + // $active é bool|null. + $active = toBool($this->request->getQuery('active')); + + // $page é int|null. + $page = toInt($this->request->getQuery('page')); + + // $query é string|null. + $query = toString($this->request->getQuery('query')); + + // Analisar uma data com base no formato ou nulo + $date = toDate($this->request->getQuery('date'), 'Y-m-d'); + + // Analisar uma data e hora com base em um formato ou nulo + $date = toDateTime($this->request->getQuery('datetime'), 'Y-m-d H:i:s'); + +.. versionadded:: 5.1.0 + Funções de transmissão foram adicionadas. + +Dados do corpo da solicitação +----------------------------- .. php:method:: getData($name, $default = null) -Todos os dados do POST podem ser acessados usando :php:meth:`Cake\\Http\\ServerRequest::getData()`. -Qualquer dado de formulário que contenha um prefixo ``data`` terá esse prefixo de dados removido. -Por exemplo:: +All POST data normally available through PHP's ``$_POST`` global variable can be +accessed using :php:meth:`Cake\\Http\\ServerRequest::getData()`. For example:: + + // An input with a name attribute equal to 'title' is accessible at + $title = $this->request->getData('title'); + +You can use a dot separated names to access nested data. For example:: - // Uma entrada com um atributo de nome igual a 'MyModel [title]' está acessível em - $title = $this->request->getData('MyModel.title'); + $value = $this->request->getData('address.street_name'); -Quaisquer chaves que não existem retornarão ``null``:: +For non-existent names the ``$default`` value will be returned:: - $foo = $this->request->getData('Value.that.does.not.exist'); + $foo = $this->request->getData('value.that.does.not.exist'); // $foo == null -Dados PUT, PATCH ou DELETE --------------------------- +You can also use :ref:`body-parser-middleware` to parse request body of different +content types into an array, so that it's accessible through ``ServerRequest::getData()``. -.. php:method:: input($callback, [$options]) +If you want to access all the data parameters you can use +``getParsedBody()``:: -Ao criar serviços REST, você geralmente aceita dados de solicitação em -solicitações ``PUT`` e ``DELETE``. Qualquer dado do corpo da solicitação -``application/x-www-form-urlencoded`` será automaticamente analisado e -definido como ``$this->data`` para as solicitações ``PUT`` e ``DELETE``. -Se você estiver aceitando dados JSON ou XML, veja abaixo como acessar -esses corpos de solicitação. + $data = $this->request->getParsedBody(); -Ao acessar os dados de entrada, você pode decodificá-los com uma função opcional. -Isso é útil ao interagir com o conteúdo do corpo da solicitação XML ou JSON. -Parâmetros adicionais para a função de decodificação podem ser passados como -argumentos para ``input()``:: +.. _request-file-uploads: - $jsonData = $this->request->input('json_decode'); +File Uploads +------------ + +Uploaded files can be accessed through the request body data, using the :php:meth:`Cake\\Http\\ServerRequest::getData()` +method described above. For example, a file from an input element with a name attribute of ``attachment``, can +be accessed like this:: + + $attachment = $this->request->getData('attachment'); + +By default file uploads are represented in the request data as objects that implement +`\\Psr\\Http\\Message\\UploadedFileInterface `__. In the current +implementation, the ``$attachment`` variable in the above example would by default hold an instance of +``\Laminas\Diactoros\UploadedFile``. + +Accessing the uploaded file details is fairly simple, here's how you can obtain the same data as provided by the old +style file upload array:: + + $name = $attachment->getClientFilename(); + $type = $attachment->getClientMediaType(); + $size = $attachment->getSize(); + $tmpName = $attachment->getStream()->getMetadata('uri'); + $error = $attachment->getError(); + +Moving the uploaded file from its temporary location to the desired target +location, doesn't require manually accessing the temporary file, instead it can +be easily done by using the objects ``moveTo()`` method:: + + $attachment->moveTo($targetPath); + +In an HTTP environment, the ``moveTo()`` method will automatically validate +whether the file is an actual uploaded file, and throw an exception in case +necessary. In an CLI environment, where the concept of uploading files doesn't +exist, it will allow to move the file that you've referenced irrespective of its +origins, which makes testing file uploads possible. + +.. php:method:: getUploadedFile($path) + +Returns the uploaded file at a specific path. The path uses the same dot syntax as the +:php:meth:`Cake\\Http\\ServerRequest::getData()` method:: + + $attachment = $this->request->getUploadedFile('attachment'); + +Unlike :php:meth:`Cake\\Http\\ServerRequest::getData()`, :php:meth:`Cake\\Http\\ServerRequest::getUploadedFile()` would +only return data when an actual file upload exists for the given path, if there is regular, non-file request body data +present at the given path, then this method will return ``null``, just like it would for any non-existent path. + +.. php:method:: getUploadedFiles() + +Returns all uploaded files in a normalized array structure. For the above example with the file input name of +``attachment``, the structure would look like:: + + [ + 'attachment' => object(Laminas\Diactoros\UploadedFile) { + // ... + } + ] + +.. php:method:: withUploadedFiles(array $files) + +This method sets the uploaded files of the request object, it accepts an array of objects that implement +`\\Psr\\Http\\Message\\UploadedFileInterface `__. It will +replace all possibly existing uploaded files:: + + $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:: + + Uploaded files that have been added to the request via this method, will *not* be available in the request body + data, ie you cannot retrieve them via :php:meth:`Cake\\Http\\ServerRequest::getData()`! If you need them in the + request data (too), then you have to set them via :php:meth:`Cake\\Http\\ServerRequest::withData()` or + :php:meth:`Cake\\Http\\ServerRequest::withParsedBody()`. + +PUT, PATCH or DELETE Data +------------------------- + +.. php:method:: getBody() -Variáveis de Ambiente ($_SERVER e $_ENV) ----------------------------------------- +When building REST services, you often accept request data on ``PUT`` and +``DELETE`` requests. Any ``application/x-www-form-urlencoded`` request body data +will automatically be parsed and available via ``$request->getData()`` for ``PUT`` and +``DELETE`` requests. If you are accepting JSON or XML data, you can +access the raw data with ``getBody()``:: -.. php:method:: env($key, $value = null) + // Get the stream wrapper on the request body + $body = $request->getBody(); -``ServerRequest::env()`` é um wrapper para a função global ``env()`` e -atua como um getter/setter para variáveis de ambiente sem precisar modificar -as globais ``$_SERVER`` e ``$_ENV``:: + // Get the request body as a string + $bodyString = (string)$request->getBody(); - // Obter o host - $host = $this->request->env('HTTP_HOST'); +If your requests contain XML or JSON request content, you should consider using +:ref:`body-parser-middleware` to have CakePHP automatically parse those content +types making the parsed data available in ``$request->getData()`` and +``$request->getParsedBody()``. - // Defina um valor, geralmente útil nos testes. - $this->request->env('REQUEST_METHOD', 'POST'); +Environment Variables (from $_SERVER and $_ENV) +----------------------------------------------- -Para acessar todas as variáveis de ambiente em uma solicitação, use ``getServerParams()``:: +.. php:method:: getEnv($key, $default = null) + +``ServerRequest::getEnv()`` is a wrapper for ``getenv()`` global function and acts as +a getter for environment variables without possible undefined keys:: + + $host = $this->request->getEnv('HTTP_HOST'); + +To access all the environment variables in a request use ``getServerParams()``:: $env = $this->request->getServerParams(); -Dados XML ou JSON ------------------ +.. php:method:: withEnv($key, $value) + +``ServerRequest::withEnv()`` is a wrapper for ``putenv()`` global function and acts as +a setter for environment variables without having to modify globals +``$_SERVER`` and ``$_ENV``:: -Os aplicativos que empregam :doc:`/development/rest` geralmente trocam dados em -corpos de postagem não codificados em URL. Você pode ler dados de entrada em -qualquer formato usando :php:meth:`~Cake\\Http\\ServerRequest::input()`. Ao -fornecer uma função de decodificação, você pode receber o conteúdo em um -formato desserializado:: + // Set a value, generally helpful in testing. + $this->request->withEnv('REQUEST_METHOD', 'POST'); - // Obter dados codificados em JSON enviados para uma ação PUT/POST +XML or JSON Data +---------------- + +Applications employing :doc:`/development/rest` often exchange data in +non-URL-encoded post bodies. You can read input data in any format using +:php:meth:`~Cake\\Http\\ServerRequest::input()`. By providing a decoding function, +you can receive the content in a deserialized format:: + + // Get JSON encoded data submitted to a PUT/POST action $jsonData = $this->request->input('json_decode'); -Alguns métodos de desserialização requerem parâmetros adicionais quando chamados, -como o parâmetro 'as array' em ``json_decode``. Se você desejar que o XML seja -convertido em um objeto DOMDocument, :php:meth:`~Cake\\Http\\ServerRequest::input()` -também suporta a passagem de parâmetros adicionais:: +Some deserializing methods require additional parameters when called, such as +the 'as array' parameter on ``json_decode``. If you want XML converted into a +DOMDocument object, :php:meth:`~Cake\\Http\\ServerRequest::input()` supports +passing in additional parameters as well:: - // Obter dados codificados em XML enviados para uma ação PUT/POST + // Get XML encoded data submitted to a PUT/POST action $data = $this->request->input('Cake\Utility\Xml::build', ['return' => 'domdocument']); -Informações de Caminho ----------------------- +Path Information +---------------- -O objeto de solicitação também fornece informações úteis sobre os caminhos -em seu aplicativo. Os atributos ``base`` e ``webroot`` são úteis para -gerar URLs e determinar se seu aplicativo está ou não em um subdiretório. -Os atributos que você pode usar são:: +The request object also provides useful information about the paths in your +application. The ``base`` and ``webroot`` attributes are useful for +generating URLs, and determining whether or not your application is in a +subdirectory. The attributes you can use are:: - // Suponha que o URL da solicitação atual seja /subdir/articles/edit/1?page=1 + // Assume the current request URL is /subdir/articles/edit/1?page=1 - // Possui /subdir/articles/edit/1?page=1 + // Holds /subdir/articles/edit/1?page=1 $here = $request->getRequestTarget(); - // Possui /subdir + // Holds /subdir $base = $request->getAttribute('base'); - // Possui /subdir/ + // Holds /subdir/ $base = $request->getAttribute('webroot'); .. _check-the-request: -Verificando as Condições da Solicitação ---------------------------------------- +Checking Request Conditions +--------------------------- .. php:method:: is($type, $args...) -O objeto de solicitação fornece uma maneira fácil de inspecionar determinadas -condições em uma determinada solicitação. Usando o método ``is()``, você -pode verificar várias condições comuns, bem como inspecionar outros critérios -de solicitação específicos do aplicativo:: +The request object provides a way to inspect certain conditions in a given +request. By using the ``is()`` method you can check a number of common +conditions, as well as inspect other application specific request criteria:: $isPost = $this->request->is('post'); -Você também pode estender os detectores de solicitação disponíveis, usando -:php:meth:`Cake\\Http\\ServerRequest::addDetector()` para criar -novos tipos de detectores. Existem quatro tipos diferentes de detectores -que você pode criar: - -* Comparação de valores do ambiente - Compara um valor obtido de :php:func:`env()` - para igualdade com o valor fornecido. -* Comparação de valores padrão - A comparação de valores padrão permite comparar - um valor obtido de :php:func:`env()` com uma expressão regular. -* Comparação baseada em opção - Comparações baseadas em opção usam uma lista de - opções para criar uma expressão regular. As chamadas subseqüentes para adicionar - um detector de opções já definido mesclarão as opções. -* Detectores de retorno de chamada - Os detectores de retorno de chamada permitem - que você forneça um tipo de 'callback' para lidar com a verificação. - O retorno de chamada receberá o objeto de solicitação como seu único parâmetro. +You can also extend the request detectors that are available, by using +:php:meth:`Cake\\Http\\ServerRequest::addDetector()` to create new kinds of +detectors. There are different types of detectors that you can create: + +* Environment value comparison - Compares a value fetched from :php:func:`env()` + for equality with the provided value. +* Header value comparison - If the specified header exists with the specified + value, or if the callable returns true. +* Pattern value comparison - Pattern value comparison allows you to compare a + value fetched from :php:func:`env()` to a regular expression. +* Option based comparison - Option based comparisons use a list of options to + create a regular expression. Subsequent calls to add an already defined + options detector will merge the options. +* Callback detectors - Callback detectors allow you to provide a 'callback' type + to handle the check. The callback will receive the request object as its only + parameter. .. php:method:: addDetector($name, $options) -Alguns exemplos seriam:: +Some examples would be:: - // Adicione um detector de ambiente. + // Add an environment detector. $this->request->addDetector( 'post', ['env' => 'REQUEST_METHOD', 'value' => 'POST'] ); - // Adicione um detector de valor padrão. + // Add a pattern value detector. $this->request->addDetector( 'iphone', ['env' => 'HTTP_USER_AGENT', 'pattern' => '/iPhone/i'] ); - // Adicione um detector de opção + // Add an option detector $this->request->addDetector('internalIp', [ 'env' => 'CLIENT_IP', 'options' => ['192.168.0.101', '192.168.0.100'] ]); - // Adicione um detector de callback. Deve ser uma chamada válida. + + // Add a header detector with value comparison + $this->request->addDetector('fancy', [ + 'env' => 'CLIENT_IP', + 'header' => ['X-Fancy' => 1] + ]); + + // Add a header detector with callable comparison + $this->request->addDetector('fancy', [ + 'env' => 'CLIENT_IP', + 'header' => ['X-Fancy' => function ($value, $header) { + return in_array($value, ['1', '0', 'yes', 'no'], true); + }] + ]); + + // Add a callback detector. Must be a valid callable. $this->request->addDetector( 'awesome', function ($request) { @@ -240,236 +406,237 @@ Alguns exemplos seriam:: } ); - // Adicione um detector que use argumentos adicionais. + // Add a detector that uses additional arguments. $this->request->addDetector( - 'controller', - function ($request, $name) { - return $request->getParam('controller') === $name; - } + 'csv', + [ + 'accept' => ['text/csv'], + 'param' => '_ext', + 'value' => 'csv', + ] ); -``Request`` também inclui métodos como -:php:meth:`Cake\\Http\\ServerRequest::domain()`, -:php:meth:`Cake\\Http\\ServerRequest::subdomains()` e -:php:meth:`Cake\\Http\\ServerRequest::host()` para ajudar aplicativos com subdomínios, -tenha uma vida um pouco mais fácil. - -Existem vários detectores embutidos que você pode usar: - -* ``is('get')`` Verifique se a solicitação atual é um GET. -* ``is('put')`` Verifique se a solicitação atual é um PUT. -* ``is('patch')`` Verifique se a solicitação atual é um PATCH. -* ``is('post')`` Verifique se a solicitação atual é um POST. -* ``is('delete')`` Verifique se a solicitação atual é um DELETE. -* ``is('head')`` Verifique se a solicitação atual é HEAD. -* ``is('options')`` Verifique se a solicitação atual é OPTIONS. -* ``is('ajax')`` Verifique se a solicitação atual veio com +There are several built-in detectors that you can use: + +* ``is('get')`` Check to see whether the current request is a GET. +* ``is('put')`` Check to see whether the current request is a PUT. +* ``is('patch')`` Check to see whether the current request is a PATCH. +* ``is('post')`` Check to see whether the current request is a POST. +* ``is('delete')`` Check to see whether the current request is a DELETE. +* ``is('head')`` Check to see whether the current request is HEAD. +* ``is('options')`` Check to see whether the current request is OPTIONS. +* ``is('ajax')`` Check to see whether the current request came with X-Requested-With = XMLHttpRequest. -* ``is('ssl')`` Verifique se a solicitação é via SSL. -* ``is('flash')`` Verifique se a solicitação possui um User-Agent de Flash. -* ``is('requested')`` Verifique se a solicitação possui um parâmetro de consulta - 'solicitado' com o valor 1. -* ``is('json')`` Verifique se a solicitação possui extensão 'json' e aceite - mimetype 'application/json'. -* ``is('xml')`` Verifique se a solicitação possui extensão 'xml' e aceite - mimetype 'application/xml' ou 'text/xml'. - -Dados da Sessão ---------------- +* ``is('ssl')`` Check to see whether the request is via SSL. +* ``is('flash')`` Check to see whether the request has a User-Agent of Flash. +* ``is('json')`` Check to see whether the request URL has 'json' extension or the + `Accept` header is set to 'application/json'. +* ``is('xml')`` Check to see whether the request URL has 'xml' extension or the `Accept` header is set to + 'application/xml' or 'text/xml'. + +``ServerRequest`` also includes methods like +:php:meth:`Cake\\Http\\ServerRequest::domain()`, +:php:meth:`Cake\\Http\\ServerRequest::subdomains()` and +:php:meth:`Cake\\Http\\ServerRequest::host()` to make applications that use +subdomains simpler. -Para acessar a sessão para uma determinada solicitação, use o método ``getSession()`` ou use o atributo ``session``:: +Session Data +------------ + +To access the session for a given request use the ``getSession()`` method or use the ``session`` attribute:: $session = $this->request->getSession(); $session = $this->request->getAttribute('session'); - $userName = $session->read('Auth.User.name'); + $data = $session->read('sessionKey'); -Para obter mais informações, consulte a documentação :doc:`/development/sessions` -para saber como usar o objeto de sessão. +For more information, see the :doc:`/development/sessions` documentation for how +to use the session object. -Host e Nome de Domínio ----------------------- +Host and Domain Name +-------------------- .. php:method:: domain($tldLength = 1) -Retorna o nome de domínio em que seu aplicativo está sendo executado:: +Returns the domain name your application is running on:: // Prints 'example.org' echo $request->domain(); .. php:method:: subdomains($tldLength = 1) -Retorna os subdomínios em que seu aplicativo está sendo executado como uma matriz:: +Returns the subdomains your application is running on as an array:: - // Retorna ['my', 'dev'] para 'my.dev.example.org' + // Returns ['my', 'dev'] for 'my.dev.example.org' $subdomains = $request->subdomains(); .. php:method:: host() -Retorna o host em que seu aplicativo está:: +Returns the host your application is on:: - // Exibe 'my.dev.example.org' + // Prints 'my.dev.example.org' echo $request->host(); -Lendo o método HTTP -------------------- +Reading the HTTP Method +----------------------- .. php:method:: getMethod() -Retorna o método HTTP com o qual a solicitação foi feita:: +Returns the HTTP method the request was made with:: - // Saída POST + // Output POST echo $request->getMethod(); -Restringindo Qual Método HTTP Uma Ação Aceita ---------------------------------------------- +Restricting Which HTTP method an Action Accepts +----------------------------------------------- .. php:method:: allowMethod($methods) -Defina métodos HTTP permitidos. Se não corresponder, lançará ``MethodNotAllowedException``. -A resposta 405 incluirá o cabeçalho ``Allow`` necessário com os métodos passados:: +Set allowed HTTP methods. If not matched, will throw +``MethodNotAllowedException``. The 405 response will include the required +``Allow`` header with the passed methods:: public function delete() { - // Aceite apenas solicitações POST e DELETE + // Only accept POST and DELETE requests $this->request->allowMethod(['post', 'delete']); ... } -Lendo Cabeçalhos HTTP ---------------------- +Reading HTTP Headers +-------------------- -Permite acessar qualquer um dos cabeçalhos ``HTTP_*`` -que foram usados para a solicitação. Por exemplo:: +Allows you to access any of the ``HTTP_*`` headers that were used +for the request. For example:: - // Obter o cabeçalho como uma string + // Get the header as a string $userAgent = $this->request->getHeaderLine('User-Agent'); - // Obtenha uma matriz de todos os valores. + // Get an array of all values. $acceptHeader = $this->request->getHeader('Accept'); - // Verifique se existe um cabeçalho + // Check if a header exists $hasAcceptHeader = $this->request->hasHeader('Accept'); -Enquanto algumas instalações do apache não tornam o cabeçalho ``Authorization`` -acessível, o CakePHP o torna disponível através de métodos específicos do apache, -conforme necessário. +While some apache installs don't make the ``Authorization`` header accessible, +CakePHP will make it available through apache specific methods as required. .. php:method:: referer($local = true) -Retorna o endereço de referência para a solicitação. +Returns the referring address for the request. .. php:method:: clientIp() -Retorna o endereço IP do visitante atual. +Returns the current visitor's IP address. -Confiando em Cabeçalhos de Proxy --------------------------------- +Trusting Proxy Headers +---------------------- -Se o seu aplicativo estiver atrás de um balanceador de carga ou em execução em -um serviço de nuvem, geralmente você receberá o host, a porta e o esquema do -balanceador de carga em suas solicitações. Freqüentemente, os balanceadores de -carga também enviam cabeçalhos ``HTTP-X-Forwarded-*`` com os valores originais. -Os cabeçalhos encaminhados não serão usados pelo CakePHP imediatamente. Para -que o objeto de solicitação use esses cabeçalhos, defina a propriedade ``trustProxy`` -como ``true``:: +If your application is behind a load balancer or running on a cloud service, you +will often get the load balancer host, port and scheme in your requests. Often +load balancers will also send ``HTTP-X-Forwarded-*`` headers with the original +values. The forwarded headers will not be used by CakePHP out of the box. To +have the request object use these headers set the ``trustProxy`` property to +``true``:: $this->request->trustProxy = true; - // Esses métodos agora usarão os cabeçalhos com proxy. + // These methods will now use the proxied headers. $port = $this->request->port(); $host = $this->request->host(); $scheme = $this->request->scheme(); $clientIp = $this->request->clientIp(); -Uma vez que os proxies são confiáveis, o método ``clientIp()`` usará o *último* -endereço IP no cabeçalho ``X-Forwarded-For``. Se o seu aplicativo estiver protegido -por vários proxies, você poderá usar ``setTrustedProxies()`` para definir os -endereços IP dos proxies em seu controle:: +Once proxies are trusted the ``clientIp()`` method will use the *last* IP +address in the ``X-Forwarded-For`` header. If your application is behind +multiple proxies, you can use ``setTrustedProxies()`` to define the IP addresses +of proxies in your control:: $request->setTrustedProxies(['127.1.1.1', '127.8.1.3']); -Depois que os proxies forem confiáveis, o ``clientIp()`` usará o primeiro endereço -IP no cabeçalho ``X-Forwarded-For``, desde que seja o único valor que não seja de um -proxy confiável. +After proxies are trusted ``clientIp()`` will use the first IP address in the +``X-Forwarded-For`` header providing it is the only value that isn't from a trusted +proxy. -Verificando Aceitar Cabeçalhos ------------------------------- +Checking Accept Headers +----------------------- .. php:method:: accepts($type = null) -Descubra quais tipos de conteúdo o cliente aceita ou verifique se -ele aceita um tipo específico de conteúdo. +Find out which content types the client accepts, or check whether it accepts a +particular type of content. -Obter todos os tipos:: +Get all types:: $accepts = $this->request->accepts(); -Verifique se há um único tipo:: +Check for a single type:: $acceptsJson = $this->request->accepts('application/json'); .. php:method:: acceptLanguage($language = null) -Obtenha todos os idiomas aceitos pelo cliente, -ou verifique se um idioma específico é aceito. +Get all the languages accepted by the client, +or check whether a specific language is accepted. -Obter a lista de idiomas aceitos:: +Get the list of accepted languages:: $acceptsLanguages = $this->request->acceptLanguage(); -Verifique se um idioma específico é aceito:: +Check whether a specific language is accepted:: $acceptsSpanish = $this->request->acceptLanguage('es-es'); .. _request-cookies: -Lendo Cookies -------------- +Reading Cookies +--------------- -Os cookies de solicitação podem ser lidos através de vários métodos:: +Request cookies can be read through a number of methods:: - // Obtem o valor de um cookie, ou nulo se o cookie não existir. + // Get the cookie value, or null if the cookie is missing. $rememberMe = $this->request->getCookie('remember_me'); - // Leia o valor ou obtenha o padrão 0 + // Read the value, or get the default of 0 $rememberMe = $this->request->getCookie('remember_me', 0); - // Obter todos os cookies como um hash + // Get all cookies as an hash $cookies = $this->request->getCookieParams(); - // Obter uma instância CookieCollection + // Get a CookieCollection instance $cookies = $this->request->getCookieCollection() -Consulte a documentação :php:class:`Cake\\Http\\Cookie\\CookieCollection` -para saber como trabalhar com a coleção de cookies. +See the :php:class:`Cake\\Http\\Cookie\\CookieCollection` documentation for how +to work with cookie collection. -Arquivos Enviados ------------------ -Solicitações expõem os dados do arquivo carregado em ``getData()`` -como matrizes e como objetos ``UploadedFileInterface`` por ``getUploadedFiles()``:: +Uploaded Files +-------------- - // Obter uma lista de objetos UploadedFile +Requests expose the uploaded file data in ``getData()`` or +``getUploadedFiles()`` as ``UploadedFileInterface`` objects:: + + // Get a list of UploadedFile objects $files = $request->getUploadedFiles(); - // Leia os dados do arquivo. + // Read the file data. $files[0]->getStream(); $files[0]->getSize(); $files[0]->getClientFileName(); - // Move o arquivo. + // Move the file. $files[0]->moveTo($targetPath); -Manipulando URIs ----------------- +Manipulating URIs +----------------- -Requisições contêm um objeto URI, que tem métodos para interagir com o URI solicitado:: +Requests contain a URI object, which contains methods for interacting with the +requested URI:: - // Obtem o URI + // Get the URI $uri = $request->getUri(); - // Leia dados fora do URI. + // Read data out of the URI. $path = $uri->getPath(); $query = $uri->getQuery(); $host = $uri->getHost(); @@ -477,186 +644,176 @@ Requisições contêm um objeto URI, que tem métodos para interagir com o URI s .. index:: $this->response -Resposta +Response ======== .. php:class:: Response -:php:class:`Cake\\Http\\Response` é a classe de resposta padrão no CakePHP. -Ele encapsula vários recursos e funcionalidades para gerar respostas HTTP em -seu aplicativo. Também auxilia nos testes, pois pode ser simulado/esboçado, -permitindo que você inspecione os cabeçalhos que serão enviados. Como -:php:class:`Cake\\Http\\ServerRequest`, :php:class:`Cake\\Http\\Response` -consolida uma série de métodos encontrados anteriormente em :php:class:`Controller`, -:php:class:`RequestHandlerComponent` e :php:class:`Dispatcher`. Os métodos -antigos são preteridos no uso de :php:class:`Cake\\Http\\Response`. +:php:class:`Cake\\Http\\Response` is the default response class in CakePHP. +It encapsulates a number of features and functionality for generating HTTP +responses in your application. It also assists in testing, as it can be +mocked/stubbed allowing you to inspect headers that will be sent. -``Response`` fornece uma interface para agrupar tarefas comuns -relacionadas à resposta, como: +``Response`` provides an interface to wrap the common response-related +tasks such as: -* Enviar cabeçalhos para redirecionamentos. -* Enviar cabeçalhos de tipo de conteúdo. -* Enviar qualquer cabeçalho. -* Enviar o corpo da resposta. +* Sending headers for redirects. +* Sending content type headers. +* Sending any header. +* Sending the response body. -Lidando com Tipos de Conteúdo ------------------------------ +Dealing with Content Types +-------------------------- .. php:method:: withType($contentType = null) -Você pode controlar o tipo de conteúdo das respostas do seu aplicativo com -:php:meth:`Cake\\Http\\Response::withType()`. Se seu aplicativo precisar -lidar com tipos de conteúdo que não estão embutidos no Response, você pode -mapeá-los com ``type()`` também:: +You can control the Content-Type of your application's responses with +:php:meth:`Cake\\Http\\Response::withType()`. If your application needs to deal +with content types that are not built into Response, you can map them with +``setTypeMap()`` as well:: - // Adiciona um tipo de vCard - $this->response->type(['vcf' => 'text/v-card']); + // Add a vCard type + $this->response->setTypeMap('vcf', ['text/v-card']); - // Defina a resposta Content-Type como vcard + // Set the response Content-Type to vcard. $this->response = $this->response->withType('vcf'); -Normalmente, você deseja mapear tipos de conteúdo adicionais no retorno de -chamada do seu controlador :php:meth:`~Controller::beforeFilter()`, -para poder aproveitar os recursos de troca automática de exibição de -:php:class:`RequestHandlerComponent` se você está usando. +Usually, you'll want to map additional content types in your controller's +:php:meth:`~Controller::beforeFilter()` callback, so you can benefit from +automatic view switching provided by :ref:`controller-viewclasses`. .. _cake-response-file: -Enviando Arquivos ------------------ +Sending Files +------------- -.. php:method:: withFile($path, $options = []) +.. php:method:: withFile(string $path, array $options = []) -Há momentos em que você deseja enviar arquivos como respostas para suas -solicitações. Você pode fazer isso usando :php:meth:`Cake\\Http\\Response::withFile()`:: +There are times when you want to send files as responses for your requests. +You can accomplish that by using :php:meth:`Cake\\Http\\Response::withFile()`:: public function sendFile($id) { $file = $this->Attachments->getFile($id); $response = $this->response->withFile($file['path']); - // Retorna a resposta para impedir que o controlador tente renderizar - // uma view. + // Return the response to prevent controller from trying to render + // a view. return $response; } -Como mostrado no exemplo acima, você deve passar o caminho do arquivo para o -método. O CakePHP enviará um cabeçalho de tipo de conteúdo adequado se for um -tipo de arquivo conhecido listado em `Cake\\Http\\Response::$_mimeTypes`. -Você pode adicionar novos tipos antes de chamar :php:meth:`Cake\\Http\\Response::withFile()` -usando o método :php:meth:`Cake\\Http\\Response::withType()`. +As shown in the above example, you must pass the file path to the method. +CakePHP will send a proper content type header if it's a known file type listed +in `Cake\\Http\\Response::$_mimeTypes`. You can add new types prior to calling +:php:meth:`Cake\\Http\\Response::withFile()` by using the +:php:meth:`Cake\\Http\\Response::withType()` method. -Se desejar, você também pode forçar o download de um arquivo em vez de ser -exibido no navegador, especificando as opções:: +If you want, you can also force a file to be downloaded instead of displayed in +the browser by specifying the options:: $response = $this->response->withFile( $file['path'], ['download' => true, 'name' => 'foo'] ); -As opções suportadas são: +The supported options are: name - O nome permite especificar um nome de arquivo alternativo a ser enviado - ao usuário. + The name allows you to specify an alternate file name to be sent to + the user. download - Um valor booleano indicando se os cabeçalhos devem ser definidos para forçar o + A boolean value indicating whether headers should be set to force download. -Enviando uma String como Arquivo --------------------------------- +Sending a String as File +------------------------ -Você pode responder com um arquivo que não existe no disco, como um pdf ou um ics -gerado on-line a partir de uma string:: +You can respond with a file that does not exist on the disk, such as a pdf or an +ics generated on the fly from a string:: public function sendIcs() { $icsString = $this->Calendars->generateIcs(); $response = $this->response; - // Injetar conteúdo da string no corpo da resposta + // Inject string content into response body $response = $response->withStringBody($icsString); $response = $response->withType('ics'); - // Opcionalmente, obriga o download do arquivo + // Optionally force file download $response = $response->withDownload('filename_for_download.ics'); - // Retorne o objeto de resposta para impedir que o controlador tente renderizar - // uma view. + // Return response object to prevent controller from trying to render + // a view. return $response; } -Os retornos de chamada também podem retornar o corpo como uma sequência:: - - $path = '/some/file.png'; - $this->response->body(function () use ($path) { - return file_get_contents($path); - }); - -Definindo Cabeçalhos --------------------- +Setting Headers +--------------- .. php:method:: withHeader($header, $value) -A configuração dos cabeçalhos é feita com o método :php:meth:`Cake\\Http\\Response::withHeader()`. -Como todos os métodos de interface PSR-7, esse método retorna uma instância *new* com o novo cabeçalho:: +Setting headers is done with the :php:meth:`Cake\\Http\\Response::withHeader()` +method. Like all of the PSR-7 interface methods, this method returns a *new* +instance with the new header:: - // Adicionar/substituir um cabeçalho + // Add/replace a header $response = $response->withHeader('X-Extra', 'My header'); - // Define vários cabeçalhos + // Set multiple headers $response = $response->withHeader('X-Extra', 'My header') ->withHeader('Location', 'http://example.com'); - // Anexa um valor a um cabeçalho existente + // Append a value to an existing header $response = $response->withAddedHeader('Set-Cookie', 'remember_me=1'); -Os cabeçalhos não são enviados quando definidos. Em vez disso, eles são mantidos -até que a resposta seja emitida por ``Cake\Http\Server``. +Headers are not sent when set. Instead, they are held until the response is +emitted by ``Cake\Http\Server``. -Agora você pode usar o método conveniente :php:meth:`Cake\\Http\\Response::withLocation()` -para definir diretamente ou obter o cabeçalho do local de redirecionamento. +You can now use the convenience method +:php:meth:`Cake\\Http\\Response::withLocation()` to directly set or get the +redirect location header. -Definindo o Corpo ------------------ +Setting the Body +---------------- .. php:method:: withStringBody($string) -Para definir uma sequência como o corpo da resposta, faça o seguinte:: +To set a string as the response body, do the following:: - // Define uma string no corpo da resposta + // Set a string into the body $response = $response->withStringBody('My Body'); - // Se você deseja enviar uma resposta em JSON + // If you want a json response $response = $response->withType('application/json') ->withStringBody(json_encode(['Foo' => 'bar'])); .. php:method:: withBody($body) -Para definir o corpo da resposta, use o método ``withBody()``, fornecido pelo -:php:class:`Zend\\Diactoros\\MessageTrait`:: +To set the response body, use the ``withBody()`` method, which is provided by the +:php:class:`Laminas\\Diactoros\\MessageTrait`:: $response = $response->withBody($stream); -Certifique-se de que ``$stream`` seja um objeto :php:class:`Psr\\Http\\Message\\StreamInterface`. -Veja abaixo como criar um novo fluxo. +Be sure that ``$stream`` is a :php:class:`Psr\\Http\\Message\\StreamInterface` object. +See below on how to create a new stream. -Você também pode transmitir respostas de arquivos usando :php:class:`Zend\\Diactoros\\Stream` streams:: +You can also stream responses from files using :php:class:`Laminas\\Diactoros\\Stream` streams:: - // Para transmitir a partir de um arquivo - use Zend\Diactoros\Stream; + // To stream from a file + use Laminas\Diactoros\Stream; $stream = new Stream('/path/to/file', 'rb'); $response = $response->withBody($stream); -Você também pode transmitir respostas de um retorno de chamada usando o -``CallbackStream``. Isso é útil quando você possui recursos como imagens, -arquivos CSV ou PDFs que precisam ser transmitidos para o cliente:: +You can also stream responses from a callback using the ``CallbackStream``. This +is useful when you have resources like images, CSV files or PDFs you need to +stream to the client:: - // Streaming a partir de um retorno de chamada + // Streaming from a callback use Cake\Http\CallbackStream; - // Cria uma imagem + // Create an image. $img = imagecreate(100, 100); // ... @@ -665,173 +822,183 @@ arquivos CSV ou PDFs que precisam ser transmitidos para o cliente:: }); $response = $response->withBody($stream); -Definindo o Conjunto de Caracteres ----------------------------------- +Setting the Character Set +------------------------- .. php:method:: withCharset($charset) -Define o conjunto de caracteres que será usado na resposta:: +Sets the charset that will be used in the response:: $this->response = $this->response->withCharset('UTF-8'); -Interagindo com o Cache do Navegador ------------------------------------- +Interacting with Browser Caching +-------------------------------- .. php:method:: withDisabledCache() -Às vezes, você precisa forçar os navegadores a não armazenar em cache os resultados -de uma ação do controlador. :php:meth:`Cake\\Http\\Response::withDisabledCache()` -é destinado apenas para isso:: +You sometimes need to force browsers not to cache the results of a controller +action. :php:meth:`Cake\\Http\\Response::withDisabledCache()` is intended for just +that:: public function index() { - // Desabilita o caching + // Disable caching $this->response = $this->response->withDisabledCache(); } .. warning:: - Desativando o armazenamento em cache de domínios SSL - ao tentar enviar arquivos no Internet Explorer podem resultar em erros. + Disabling caching from SSL domains while trying to send + files to Internet Explorer can result in errors. .. php:method:: withCache($since, $time = '+1 day') -Você também pode dizer aos clientes que deseja que eles armazenem respostas em cache. -Usando :php:meth:`Cake\\Http\\Response::withCache()`:: +You can also tell clients that you want them to cache responses. By using +:php:meth:`Cake\\Http\\Response::withCache()`:: public function index() { - // Habilita o caching + // Enable caching $this->response = $this->response->withCache('-1 minute', '+5 days'); } -O exposto acima informava aos clientes para armazenar em cache a resposta -resultante por 5 dias, acelerando a experiência dos visitantes. O método ``withCache()`` -define o valor ``Last-Modified`` para o primeiro argumento. O cabeçalho ``Expires`` e -a diretiva ``max-age`` são configurados com base no segundo parâmetro. A diretiva -``public`` do Cache-Control também é definida. +The above would tell clients to cache the resulting response for 5 days, +hopefully speeding up your visitors' experience. +The ``withCache()`` method sets the ``Last-Modified`` value to the first +argument. ``Expires`` header and the ``max-age`` directive are set based on the +second parameter. Cache-Control's ``public`` directive is set as well. .. _cake-response-caching: -Ajuste Fino de Cache HTTP -------------------------- +Fine Tuning HTTP Cache +---------------------- -Uma das melhores e mais fáceis maneiras de acelerar seu aplicativo é usar o cache HTTP. -Sob esse modelo de armazenamento em cache, você só precisa ajudar os clientes a decidir -se devem usar uma cópia em cache da resposta, definindo alguns cabeçalhos, como tempo -modificado e tag da entidade de resposta. +One of the best and easiest ways of speeding up your application is to use HTTP +cache. Under this caching model, you are only required to help clients decide if +they should use a cached copy of the response by setting a few headers such as +modified time and response entity tag. -Em vez de forçar você a codificar a lógica para armazenar em cache e invalidá-la -(atualizando) depois que os dados forem alterados, o HTTP usa dois modelos, expiração -e validação, que geralmente são muito mais simples de usar. +Rather than forcing you to code the logic for caching and for invalidating +(refreshing) it once the data has changed, HTTP uses two models, expiration and +validation, which usually are much simpler to use. -Além de usar :php:meth:`Cake\\Http\\Response::withCache()`, você também pode usar -muitos outros métodos para ajustar os cabeçalhos de cache HTTP para tirar proveito -do cache do navegador ou do proxy reverso. +Apart from using :php:meth:`Cake\\Http\\Response::withCache()`, you can also use +many other methods to fine-tune HTTP cache headers to take advantage of browser +or reverse proxy caching. -O cabeçalho para Controle de Cache -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The Cache Control Header +~~~~~~~~~~~~~~~~~~~~~~~~ .. php:method:: withSharable($public, $time = null) -Usado como modelo de expiração, esse cabeçalho contém vários indicadores que podem -alterar a maneira como navegadores ou proxies usam o conteúdo em cache. Um -cabeçalho ``Cache-Control`` pode ser assim:: +Used under the expiration model, this header contains multiple indicators that +can change the way browsers or proxies use the cached content. A +``Cache-Control`` header can look like this:: Cache-Control: private, max-age=3600, must-revalidate -A classe ``Response`` ajuda a definir esse cabeçalho com alguns métodos utilitários -que produzirão um cabeçalho final ``Cache-Control`` válido. O primeiro é o método -``withSharable()``, que indica se uma resposta deve ser considerada compartilhável -entre diferentes usuários ou clientes. Este método realmente controla a parte ``public`` -ou ``private`` deste cabeçalho. Definir uma resposta como privada indica que a totalidade -ou parte dela é destinada a um único usuário. Para tirar proveito dos caches compartilhados, -a diretiva de controle deve ser definida como pública. +``Response`` class helps you set this header with some utility methods that will +produce a final valid ``Cache-Control`` header. The first is the +``withSharable()`` method, which indicates whether a response is to be +considered sharable across different users or clients. This method actually +controls the ``public`` or ``private`` part of this header. Setting a response +as private indicates that all or part of it is intended for a single user. To +take advantage of shared caches, the control directive must be set as public. -O segundo parâmetro desse método é usado para especificar uma ``idade máxima`` para o cache, -que é o número de segundos após os quais a resposta não é mais considerada nova:: +The second parameter of this method is used to specify a ``max-age`` for the +cache, which is the number of seconds after which the response is no longer +considered fresh:: public function view() { // ... - // Define o controle de cache como público por 3600 segundos + // Set the Cache-Control as public for 3600 seconds $this->response = $this->response->withSharable(true, 3600); } public function my_data() { // ... - // Define o Cache-Control como privado por 3600 segundos + // Set the Cache-Control as private for 3600 seconds $this->response = $this->response->withSharable(false, 3600); } -``Response`` expõe métodos separados para definir cada uma das diretivas no -cabeçalho ``Cache-Control``. +``Response`` exposes separate methods for setting each of the directives in +the ``Cache-Control`` header. -O Cabeçalho de Expiração -~~~~~~~~~~~~~~~~~~~~~~~~ +The Expiration Header +~~~~~~~~~~~~~~~~~~~~~ .. php:method:: withExpires($time) -Você pode definir o cabeçalho ``Expires`` para uma data e hora após a qual a -resposta não é mais considerada nova. Esse cabeçalho pode ser definido usando -o método ``withExpires()``:: +You can set the ``Expires`` header to a date and time after which the response +is no longer considered fresh. This header can be set using the +``withExpires()`` method:: public function view() { $this->response = $this->response->withExpires('+5 days'); } -Este método também aceita uma instância :php:class:`DateTime` ou qualquer string -que possa ser analisada pela classe :php:class:`DateTime`. +This method also accepts a :php:class:`DateTime` instance or any string that can +be parsed by the :php:class:`DateTime` class. -O Cabeçalho Etag -~~~~~~~~~~~~~~~~ +The Etag Header +~~~~~~~~~~~~~~~ .. php:method:: withEtag($tag, $weak = false) -A validação de cache no HTTP é frequentemente usada quando o conteúdo está em -constante mudança e solicita ao aplicativo que gere apenas o conteúdo da resposta -se o cache não estiver mais atualizado. Sob esse modelo, o cliente continua a armazenar -páginas no cache, mas pergunta sempre ao aplicativo se o recurso foi alterado, em vez de -usá-lo diretamente. Isso é comumente usado com recursos estáticos, como imagens e outros assets. +Cache validation in HTTP is often used when content is constantly changing, and +asks the application to only generate the response contents if the cache is no +longer fresh. Under this model, the client continues to store pages in the +cache, but it asks the application every time +whether the resource has changed, instead of using it directly. +This is commonly used with static resources such as images and other assets. -O método ``withEtag()`` (chamado tag de entidade) é uma string que identifica exclusivamente -o recurso solicitado, como a soma de verificação de um arquivo, para determinar se ele -corresponde a um recurso em cache. +The ``withEtag()`` method (called entity tag) is a string +that uniquely identifies the requested resource, as a checksum does for a file, +in order to determine whether it matches a cached resource. -Para tirar proveito desse cabeçalho, você deve chamar o método ``isNotModified()`` -manualmente ou incluir o seguinte :doc:`/controllers/components/request-handling` no seu controlador:: +To take advantage of this header, you must either call the +``isNotModified()`` method manually or include the +:doc:`/controllers/components/check-http-cache` in your controller:: public function index() { - $articles = $this->Articles->find('all'); - $response = $this->response->withEtag($this->Articles->generateHash($articles)); + $articles = $this->Articles->find('all')->all(); + + // Simple checksum of the article contents. + // You should use a more efficient implementation + // in a real world application. + $checksum = md5(json_encode($articles)); + + $response = $this->response->withEtag($checksum); if ($response->isNotModified($this->request)) { return $response; } + $this->response = $response; // ... } .. note:: - A maioria dos usuários proxy provavelmente deve considerar o uso do Último - Cabeçalho Modificado em vez de Etags por motivos de desempenho e compatibilidade. + Most proxy users should probably consider using the Last Modified Header + instead of Etags for performance and compatibility reasons. -O Último Cabeçalho Modificado -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +The Last Modified Header +~~~~~~~~~~~~~~~~~~~~~~~~ .. php:method:: withModified($time) -Além disso, no modelo de validação de cache HTTP, você pode definir o cabeçalho -``Last-Modified`` para indicar a data e a hora em que o recurso foi modificado -pela última vez. Definir este cabeçalho ajuda o CakePHP a informar aos clientes -de armazenamento em cache se a resposta foi modificada ou não com base em seu cache. +Also, under the HTTP cache validation model, you can set the ``Last-Modified`` +header to indicate the date and time at which the resource was modified for the +last time. Setting this header helps CakePHP tell caching clients whether the +response was modified or not based on their cache. -Para tirar proveito desse cabeçalho, você deve chamar o método ``isNotModified()`` -manualmente ou incluir o seguinte :doc:`/controllers/components/request-handling` -no seu controlador:: +To take advantage of this header, you must either call the +``isNotModified()`` method manually or include the +:doc:`/controllers/components/check-http-cache` in your controller:: public function view() { @@ -844,68 +1011,75 @@ no seu controlador:: // ... } -O Cabeçalho Vary -~~~~~~~~~~~~~~~~ +The Vary Header +~~~~~~~~~~~~~~~ .. php:method:: withVary($header) -Em alguns casos, convém veicular conteúdo diferente usando o mesmo URL. Geralmente, -esse é o caso se você tiver uma página multilíngue ou responder com HTML diferente, -dependendo do navegador. Nessas circunstâncias, você pode usar o cabeçalho ``Vary``:: +In some cases, you might want to serve different content using the same URL. +This is often the case if you have a multilingual page or respond with different +HTML depending on the browser. Under such circumstances you can use the ``Vary`` +header:: $response = $this->response->withVary('User-Agent'); $response = $this->response->withVary('Accept-Encoding', 'User-Agent'); $response = $this->response->withVary('Accept-Language'); -Enviando Respostas Não Modificadas -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Sending Not-Modified Responses +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. php:method:: isNotModified(Request $request) -Compara os cabeçalhos de cache do objeto de solicitação com o cabeçalho de cache -da resposta e determina se ele ainda pode ser considerado novo. Nesse caso, exclui -o conteúdo da resposta e envia o cabeçalho `304 Not Modified`:: +Compares the cache headers for the request object with the cache header from the +response and determines whether it can still be considered fresh. If so, deletes +the response content, and sends the `304 Not Modified` header:: - // Em um método do controlador. + // In a controller action. if ($this->response->isNotModified($this->request)) { return $this->response; } .. _response-cookies: -Configurando Cookies --------------------- +Setting Cookies +--------------- -Os cookies podem ser adicionados à resposta usando um array ou um objeto :php:class:`Cake\\Http\\Cookie\\Cookie`:: +Cookies can be added to response using either an array or a :php:class:`Cake\\Http\\Cookie\\Cookie` +object:: use Cake\Http\Cookie\Cookie; use DateTime; - // Adiciona um cookie - $this->response = $this->response->withCookie(new Cookie( + // Add a cookie + $this->response = $this->response->withCookie(Cookie::create( 'remember_me', 'yes', - new DateTime('+1 year'), // expiration time - '/', // path - '', // domain - false, // secure - true // httponly - ]); - -Veja a seção `created-cookies` para saber como usar o objeto cookie. -Você pode usar ``withExpiredCookie()`` para enviar um cookie expirado na -resposta. Isso fará com que o navegador remova seu cookie local:: - - $this->response = $this->response->withExpiredCookie('remember_me'); + // All keys are optional + [ + 'expires' => new DateTime('+1 year'), + 'path' => '', + 'domain' => '', + 'secure' => false, + 'httponly' => false, + 'samesite' => null // Or one of CookieInterface::SAMESITE_* constants + ] + )); + +See the :ref:`creating-cookies` section for how to use the cookie object. You +can use ``withExpiredCookie()`` to send an expired cookie in the response. This +will make the browser remove its local cookie:: + + $this->response = $this->response->withExpiredCookie(new Cookie('remember_me')); .. _cors-headers: -Definindo Cabeçalho de Solicitação de Origem Cruzada (CORS) -=========================================================== +Setting Cross Origin Request Headers (CORS) +------------------------------------------- -O método ``cors()`` é usado para definir o `HTTP Access Control -`__, -são cabeçalhos relacionados com uma interface fluente:: +The ``cors()`` method returns a ``CorsBuilder`` instance which provides a fluent +interface for defining `HTTP Access Control +`__ +related headers:: $this->response = $this->response->cors($this->request) ->allowOrigin(['*.cakephp.org']) @@ -916,28 +1090,212 @@ são cabeçalhos relacionados com uma interface fluente:: ->maxAge(300) ->build(); -Os cabeçalhos relacionados ao CORS somente serão aplicados à resposta se os seguintes -critérios forem atendidos: +CORS related headers will only be applied to the response if the following +criteria are met: + +#. The request has an ``Origin`` header. +#. The request's ``Origin`` value matches one of the allowed Origin values. + +CorsBuilder Methods +~~~~~~~~~~~~~~~~~~~ + +.. php:class:: CorsBuilder + +The ``CorsBuilder`` provides the following methods for configuring CORS: + +.. php:method:: allowOrigin(array|string $domains) + + Set the list of allowed domains. You can use wildcards ``*.example.com`` to + accept subdomains, or ``*`` to allow all domains:: + + // Allow a specific domain + ->allowOrigin('https://example.com') + + // Allow multiple domains + ->allowOrigin(['https://example.com', 'https://app.example.com']) + + // Allow all subdomains + ->allowOrigin(['*.example.com']) + + // Allow all origins (use with caution!) + ->allowOrigin('*') + +.. php:method:: allowMethods(array $methods) + + Set the list of allowed HTTP methods:: + + ->allowMethods(['GET', 'POST', 'PUT', 'DELETE']) + +.. php:method:: allowHeaders(array $headers) + + Define which headers can be sent in CORS requests:: + + ->allowHeaders(['X-CSRF-Token', 'Content-Type', 'Authorization']) + +.. php:method:: allowCredentials() + + Enable cookies to be sent in CORS requests. This sets the + ``Access-Control-Allow-Credentials`` header to ``true``:: + + ->allowCredentials() + +.. php:method:: exposeHeaders(array $headers) + + Define which headers the client library/browser can expose to scripting:: + + ->exposeHeaders(['X-Total-Count', 'Link']) + +.. php:method:: maxAge(string|int $age) + + Define how long preflight OPTIONS requests are valid for (in seconds):: -#. A solicitação possui um cabeçalho ``Origin``. -#. O valor ``Origem`` da solicitação corresponde a um dos valores de Origin permitidos. + ->maxAge(3600) // Cache preflight for 1 hour -Erros Comuns com Respostas Imutáveis -==================================== +.. php:method:: build() -Os objetos de resposta oferecem vários métodos que tratam as respostas como objetos -imutáveis. Objetos imutáveis ajudam a evitar efeitos colaterais acidentais difíceis -de controlar e reduzem os erros causados por chamadas de método causadas pela refatoração -dessa ordem de alteração. Embora ofereçam vários benefícios, objetos imutáveis podem levar -algum tempo para se acostumar. Qualquer método que comece com ``with`` opera a resposta de -maneira imutável e **sempre** retorna uma **nova** instância. Esquecer de manter a instância -modificada é o erro mais frequente que as pessoas cometem ao trabalhar com objetos imutáveis:: + Apply the configured headers to the response and return it. This must be + called to actually apply the CORS headers:: + + $response = $corsBuilder->build(); + +Practical CORS Examples +~~~~~~~~~~~~~~~~~~~~~~~ + +Here are some common CORS configurations: + +**API accepting requests from a SPA frontend**:: + + // In your controller + public function beforeFilter(EventInterface $event) + { + parent::beforeFilter($event); + + if ($this->request->is('options')) { + // Handle preflight requests + $this->response = $this->response->cors($this->request) + ->allowOrigin(['https://app.example.com']) + ->allowMethods(['GET', 'POST', 'PUT', 'DELETE']) + ->allowHeaders(['Content-Type', 'Authorization']) + ->allowCredentials() + ->maxAge(86400) + ->build(); + + return $this->response; + } + } + + public function index() + { + // Apply CORS to regular requests + $this->response = $this->response->cors($this->request) + ->allowOrigin(['https://app.example.com']) + ->allowCredentials() + ->build(); + + // Your regular controller logic... + } + +**Public API with relaxed CORS**:: + + $this->response = $this->response->cors($this->request) + ->allowOrigin('*') + ->allowMethods(['GET']) + ->exposeHeaders(['X-Total-Count', 'X-Page']) + ->maxAge(3600) + ->build(); + +Creating CORS Middleware +~~~~~~~~~~~~~~~~~~~~~~~~ + +For consistent CORS handling across your application, create a middleware:: + + // src/Middleware/CorsMiddleware.php + namespace App\Middleware; + + use Psr\Http\Message\ResponseInterface; + use Psr\Http\Message\ServerRequestInterface; + use Psr\Http\Server\MiddlewareInterface; + use Psr\Http\Server\RequestHandlerInterface; + + class CorsMiddleware implements MiddlewareInterface + { + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface { + // Handle preflight requests + if ($request->getMethod() === 'OPTIONS') { + $response = new \Cake\Http\Response(); + $response = $response->cors($request) + ->allowOrigin(['*.myapp.com']) + ->allowMethods(['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']) + ->allowHeaders(['Content-Type', 'Authorization']) + ->allowCredentials() + ->maxAge(3600) + ->build(); + + return $response; + } + + $response = $handler->handle($request); + + // Add CORS headers to regular requests + return $response->cors($request) + ->allowOrigin(['*.myapp.com']) + ->allowCredentials() + ->build(); + } + } + +Then add it to your application middleware stack in ``src/Application.php``:: + + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + $middlewareQueue + // Add CORS middleware early in the stack + ->add(new \App\Middleware\CorsMiddleware()) + // ... other middleware + ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) + ->add(new AssetMiddleware([ + 'cacheTime' => Configure::read('Asset.cacheTime'), + ])) + ->add(new RoutingMiddleware($this)); + + return $middlewareQueue; + } + +Running logic after the Response has been sent +---------------------------------------------- + +In fastcgi based environments you can listen to the ``Server.terminate`` event +to run logic **after** the response has been sent to the client. The +``terminate`` event will be passed a ``request`` and ``response``. The +``request`` is fetched from the applications' DI container, or from +``Router::getRequest()`` if the DI container does not have a request registered. + +.. warning:: + In non fastcgi environments the ``Server.terminate`` event is fired before + the response is sent. + +.. versionadded:: 5.1.0 + +Common Mistakes with Immutable Responses +======================================== + +Response objects offer a number of methods that treat +responses as immutable objects. Immutable objects help prevent difficult to +track accidental side-effects, and reduce mistakes caused by method calls caused +by refactoring that change ordering. While they offer a number of benefits, +immutable objects can take some getting used to. Any method that starts with +``with`` operates on the response in an immutable fashion, and will **always** +return a **new** instance. Forgetting to retain the modified instance is the most +frequent mistake people make when working with immutable objects:: $this->response->withHeader('X-CakePHP', 'yes!'); -No código acima, a resposta não terá o cabeçalho ``X-CakePHP``, pois o valor de -retorno do método ``withHeader()`` não foi mantido. Para corrigir o código acima, -você escreveria:: +In the above code, the response will be lacking the ``X-CakePHP`` header, as the +return value of the ``withHeader()`` method was not retained. To correct the +above code you would write:: $this->response = $this->response->withHeader('X-CakePHP', 'yes!'); @@ -948,34 +1306,34 @@ Cookie Collections .. php:class:: CookieCollection -Os objetos ``CookieCollection`` são acessíveis a partir dos objetos de solicitação -e resposta. Eles permitem que você interaja com grupos de cookies usando padrões -imutáveis, que permitem preservar a imutabilidade da solicitação e resposta. +``CookieCollection`` objects are accessible from the request and response objects. +They let you interact with groups of cookies using immutable patterns, which +allow the immutability of the request and response to be preserved. .. _creating-cookies: -Criando Cookies ---------------- +Creating Cookies +---------------- .. php:class:: Cookie -Os objetos ``Cookie`` podem ser definidos por meio de objetos construtores ou usando -a interface fluente que segue padrões imutáveis:: +``Cookie`` objects can be defined through constructor objects, or by using the +fluent interface that follows immutable patterns:: use Cake\Http\Cookie\Cookie; - // Todos os argumentos no construtor + // All arguments in the constructor $cookie = new Cookie( - 'remember_me', // nome + 'remember_me', // name 1, // value - new DateTime('+1 year'), // prazo de validade, se aplicável - '/', // caminho, se aplicável - 'example.com', // doomínio, se aplicável - false, // somente seguro? - true // somente HTTP? + new DateTime('+1 year'), // expiration time, if applicable + '/', // path, if applicable + 'example.com', // domain, if applicable + false, // secure only? + true // http only ? ); - // Usando os métodos do construtor + // Using the builder methods $cookie = (new Cookie('remember_me')) ->withValue('1') ->withExpiry(new DateTime('+1 year')) @@ -984,63 +1342,70 @@ a interface fluente que segue padrões imutáveis:: ->withSecure(false) ->withHttpOnly(true); -Depois de criar um cookie, você pode adicioná-lo a um ``CookieCollection`` -novo ou existente:: +Once you have created a cookie, you can add it to a new or existing +``CookieCollection``:: use Cake\Http\Cookie\CookieCollection; - // Crie uma nova coleção + // Create a new collection $cookies = new CookieCollection([$cookie]); - // Adicionar a uma coleção existente + // Add to an existing collection $cookies = $cookies->add($cookie); - // Remover um cookie pelo nome + // Remove a cookie by name $cookies = $cookies->remove('remember_me'); .. note:: - Lembre-se de que as coleções são imutáveis e a adição de cookies ou a remoção - de cookies de uma coleção cria um *novo* objeto de coleção. + Remember that collections are immutable and adding cookies into, or removing + cookies from a collection, creates a *new* collection object. -Objetos de cookie podem ser adicionados às respostas:: +Cookie objects can be added to responses:: - // Adiciona um cookie + // Add one cookie $response = $this->response->withCookie($cookie); - // Substitui inteiramente uma coleção de cookie + // Replace the entire cookie collection $response = $this->response->withCookieCollection($cookies); -Os cookies definidos como respostas podem ser criptografados usando o +Cookies set to responses can be encrypted using the :ref:`encrypted-cookie-middleware`. -Lendo Cookies -------------- +Reading Cookies +--------------- -Depois de ter uma instância ``CookieCollection``, você pode acessar os cookies que ela contém:: +Once you have a ``CookieCollection`` instance, you can access the cookies it +contains:: - // Verifica se o cookie existe + // Check if a cookie exists $cookies->has('remember_me'); - // Obter o número de cookies na coleção + // Get the number of cookies in the collection count($cookies); - // Obter uma instância de cookie + // Get a cookie instance. Will throw an error if the cookie is not found $cookie = $cookies->get('remember_me'); -Depois de ter um objeto ``Cookie``, você pode interagir com seu estado e modificá-lo. -Lembre-se de que os cookies são imutáveis, portanto, você precisará atualizar a coleção -se modificar um cookie:: + // Get a cookie or null + $cookie = $cookies->remember_me; + + // Check if a cookie exists + $exists = isset($cookies->remember_me) + +Once you have a ``Cookie`` object you can interact with it's state and modify +it. Keep in mind that cookies are immutable, so you'll need to update the +collection if you modify a cookie:: - // Obtenha o valor + // Get the value $value = $cookie->getValue() - // Acessar dados dentro de um valor JSON + // Access data inside a JSON value $id = $cookie->read('User.id'); - // Verifica o estado + // Check state $cookie->isHttpOnly(); $cookie->isSecure(); .. meta:: - :title lang=pt: Objectos de requisição e resposta - :keywords lang=pt: controlador de solicitação, parâmetros de solicitação, índices de arrays, índice de finalidade, objetos de resposta, informações de domínio, objeto de solicitação, dados de solicitação, interrogação, parâmetros, versões anteriores, introspecção, dispatcher, rota, estruturas de dados, matrizes, endereço IP, migração, índices, cakephp, PSR-7, imutável + :title lang=pt: Objetos Request e Response + :keywords lang=pt: 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/pt/core-libraries/app.rst b/pt/core-libraries/app.rst index 668f6967ac..6b787644c6 100644 --- a/pt/core-libraries/app.rst +++ b/pt/core-libraries/app.rst @@ -5,86 +5,85 @@ Classe App .. php:class:: App -A classe App é responsável pela localização dos recursos e pelo gerenciamento de caminhos. +A classe App é responsável pela localização de recursos e gerenciamento de caminhos. Encontrando Classes =================== -.. php:staticmethod:: classname($name, $type = '', $suffix = '') +.. php:staticmethod:: className($name, $type = '', $suffix = '') -Este método é usado para resolver nomes de classes no CakePHP. Resolve os -nomes abreviados do CakePHP e retorna o nome da classe totalmente resolvido:: +Este método é usado para resolver nomes de classe em todo o CakePHP. Ele resolve +os nomes abreviados que o CakePHP usa e retorna o nome de classe totalmente resolvido:: - // Resolva um nome de classe curto com o namespace + sufixo. - App::classname('Auth', 'Controller/Component', 'Component'); - // Retorna Cake\Controller\Component\AuthComponent + // Resolve a short class name with the namespace + suffix. + App::className('Flash', 'Controller/Component', 'Component'); + // Returns Cake\Controller\Component\FlashComponent - // Resolve o nome do plugin - App::classname('DebugKit.Toolbar', 'Controller/Component', 'Component'); - // Retorna DebugKit\Controller\Component\ToolbarComponent + // Resolve a plugin name. + App::className('DebugKit.Toolbar', 'Controller/Component', 'Component'); + // Returns DebugKit\Controller\Component\ToolbarComponent - // Os nomes com \ serão retornados inalterados. - App::classname('App\Cache\ComboCache'); - // Retorna App\Cache\ComboCache + // Names with \ in them will be returned unaltered. + App::className('App\Cache\ComboCache'); + // Returns App\Cache\ComboCache -Ao resolver as classes, o namespace ``App`` será tentado e, se -a classe não existir, o espaço para nome ``Cake`` será tentado. Se ambos +Ao resolver classes, o namespace ``App`` será tentado primeiro e, se a +classe não existir, o namespace ``Cake`` será tentado. Se ambos os nomes de classe não existirem, ``false`` será retornado. -Localizando Caminhos para Namespaces -==================================== +Encontrando Caminhos para Recursos +=================================== -.. php:staticmethod:: path(string $package, string $plugin = null) +.. php:staticmethod:: path(string $package, ?string $plugin = null) -Usado para obter locais para caminhos com base em convenções:: +O método retorna caminhos definidos usando a configuração da aplicação ``App.paths``:: - // Obtenha o caminho para o Controller / no seu aplicativo - App::path('Controller'); + // Get the templates path set using ``App.paths.templates`` app config. + App::path('templates'); -Isso pode ser feito para todos os namespaces que fazem parte do seu aplicativo. -Você também pode buscar caminhos para um plug-in:: +Da mesma forma, você pode recuperar caminhos para ``locales`` e ``plugins``. - // Retorna os caminhos do componente no DebugKit - App::path('Component', 'DebugKit'); +Encontrando Caminhos para Namespaces +===================================== -``App::path()`` retornará apenas o caminho padrão e não poderá fornecer -informações sobre caminhos adicionais para os quais o carregador automático -está configurado. +.. php:staticmethod:: classPath(string $package, ?string $plugin = null) -.. php:staticmethod:: core(string $package) +Usado para obter localizações de caminhos baseados em convenções:: -Usado para encontrar o caminho para um pacote dentro do CakePHP:: + // Get the path to Controller/ in your application + App::classPath('Controller'); - // Obtenha o caminho para os mecanismos de cache. - App::core('Cache/Engine'); +Isso pode ser feito para todos os namespaces que fazem parte da sua aplicação. -Localizando Plugins -=================== +``App::classPath()`` retornará apenas o caminho padrão e não será capaz de +fornecer informações sobre caminhos adicionais configurados no autoloader. -.. php:staticmethod:: Plugin::path(string $plugin) +.. php:staticmethod:: core(string $package) -Os plug-ins podem ser localizados com o Plugin. Usar ``Plugin::path('DebugKit');`` -por exemplo, fornecerá o caminho completo para o plug-in DebugKit:: +Usado para encontrar o caminho para um pacote dentro do CakePHP:: - $path = Plugin::path('DebugKit'); + // Get the path to Cache engines. + App::core('Cache/Engine'); -Localizando Temas -================= +Localizando Themes +================== -Como os temas são plugins, você pode usar os métodos acima para obter o caminho para um tema. +Como os themes são plugins, você pode usar os métodos acima para obter o caminho para +um theme. -Carregando Arquivos do Fornecedor -================================= +Carregando Arquivos de Vendor +============================== -O ideal é que os arquivos do fornecedor sejam carregados automaticamente com -o ``Composer``, se você tiver arquivos que não possam ser carregados ou instalados -automaticamente com o Composer, será necessário usar o ``require`` para carregá-los. +Idealmente, arquivos de vendor devem ser carregados automaticamente com o ``Composer``. Se você tem arquivos +de vendor que não podem ser carregados automaticamente ou instalados com Composer, você precisará usar +``require`` para carregá-los. -Se você não conseguir instalar uma biblioteca com o Composer, é melhor instalar cada -biblioteca em um diretório, seguindo a convenção do Composer de ``vendor/$author/$ package``. -Se você tiver uma biblioteca chamada AcmeLib, poderá instalá-la em ``vendor/Acme/AcmeLib``. -Supondo que ele não usasse nomes de classe compatíveis com PSR-0, você poderia carregar -automaticamente as classes dentro dele usando ``classmap`` no ``composer.json`` do seu aplicativo:: +Se você não puder instalar uma biblioteca com Composer, é melhor instalar cada biblioteca em +um diretório seguindo a convenção do Composer de ``vendor/$author/$package``. +Se você tivesse uma biblioteca chamada AcmeLib, você poderia instalá-la em +``vendor/Acme/AcmeLib``. Supondo que ela não use nomes de classe compatíveis com PSR-0, +você poderia carregar automaticamente as classes dentro dela usando ``classmap`` no +``composer.json`` da sua aplicação:: "autoload": { "psr-4": { @@ -96,9 +95,9 @@ automaticamente as classes dentro dele usando ``classmap`` no ``composer.json`` ] } -Se a sua biblioteca de fornecedores não usa classes e, em vez disso, fornece funções, -você pode configurar o Composer para carregar esses arquivos no início de cada solicitação -usando a estratégia de carregamento automático ``files``:: +Se sua biblioteca de vendor não usa classes e, em vez disso, fornece funções, você +pode configurar o Composer para carregar esses arquivos no início de cada requisição +usando a estratégia de autoloading ``files``:: "autoload": { "psr-4": { @@ -110,13 +109,13 @@ usando a estratégia de carregamento automático ``files``:: ] } -Depois de configurar as bibliotecas do fornecedor, você precisará regenerar -o carregador automático do seu aplicativo usando:: +Após configurar as bibliotecas de vendor, você precisará regenerar o +autoloader da sua aplicação usando:: $ php composer.phar dump-autoload -Se você não estiver usando o Composer em seu aplicativo, precisará carregar -manualmente todas as bibliotecas de fornecedores. +Se você não estiver usando Composer na sua aplicação, você precisará +carregar manualmente todas as bibliotecas de vendor. .. meta:: :title lang=pt: Classe App diff --git a/pt/core-libraries/caching.rst b/pt/core-libraries/caching.rst index 9f315b8185..7deb4519b7 100644 --- a/pt/core-libraries/caching.rst +++ b/pt/core-libraries/caching.rst @@ -29,12 +29,9 @@ seu próprio back-end. Os mecanismos de armazenamento em cache integrados são: * ``Apcu`` O cache do APCu usa a extensão PHP `APCu `_. Essa extensão usa memória compartilhada no servidor da web para armazenar objetos. Isso o torna muito rápido e capaz de fornecer recursos atômicos de leitura/gravação. -* ``Wincache`` O Wincache usa a extensão `Wincache `_. - O Wincache é semelhante ao APC em recursos e desempenho, mas otimizado para - Windows e IIS. -* ``Array`` Armazena todos os dados em uma matriz. Esse mecanismo não fornece - armazenamento persistente e deve ser usado em conjuntos de testes de aplicativos. -* ``Null`` O mecanismo nulo não armazena nada e falha em todas as operações de leitura. +* ``Array`` Armazena todos os dados em um array. Esse mecanismo não fornece + armazenamento persistente e é destinado ao uso em conjuntos de testes de aplicativos. +* ``Null`` O mecanismo nulo não armazena nada realmente e falha em todas as operações de leitura. Independentemente do CacheEngine que você escolher, seu aplicativo interage com :php:class:`Cake\\Cache\\Cache`. @@ -155,6 +152,8 @@ O ``FileEngine`` usa as seguintes opções específicas do mecanismo: * ``path`` Caminho para onde os arquivos de cache devem ser salvos. O padrão é o diretório temporário do sistema. +.. _caching-redisengine: + Opções RedisEngine ------------------ @@ -166,7 +165,14 @@ O RedisEngine usa as seguintes opções específicas do mecanismo: * ``password`` Senha do servidor Redis. * ``persistent`` Uma conexão persistente deve ser feita com Redis. * ``timeout`` Tempo limite de conexão para Redis. -* ``unix_socket`` Caminho para um soquete unix para Redist. +* ``unix_socket`` Caminho para um soquete unix para Redis. +* ``tls`` Conectar ao Redis por TLS. +* ``ssl_key`` A chave privada ssl usada para conexões TLS. +* ``ssl_ca`` O arquivo de autoridade de certificação ssl para conexões TLS. +* ``ssl_cert`` O certificado ssl usado para conexões TLS. + +.. versionadded:: 5.1.0 + Conexões TLS foram adicionadas na versão 5.1 Opções do MemcacheEngine ------------------------ @@ -180,6 +186,8 @@ Opções do MemcacheEngine igbinary e json. Ao lado do php, a extensão memcached deve ser compilada com o suporte serializador apropriado. - ``servers`` Cadeia ou matriz de servidores com cache de memória. Se for um array, o MemcacheEngine os usará como um pool. +- ``duration`` Esteja ciente de que qualquer duração maior que 30 dias será tratada como um + valor de tempo Unix real, em vez de um deslocamento do tempo atual. - ``options`` Opções adicionais para o cliente memcached. Deve ser uma matriz de opção => valor. Use as constantes ``\Memcached::OPT_*`` como chaves. @@ -247,7 +255,8 @@ será usado. ``Cache::write()`` pode armazenar qualquer tipo de objeto e é ideal para armazenar resultados de descobertas de modelos:: - if (($posts = Cache::read('posts')) === false) { + $posts = Cache::read('posts'); + if ($posts === null) { $posts = $someService->getAllPosts(); Cache::write('posts', $posts); } @@ -279,8 +288,31 @@ usando ``writeMany()`` salve várias conexões de rede ao usar o Memcached:: // $result poderá conter ['article-first-post' => true, 'article-first-post-comments' => true] +Gravações Atômicas +------------------ + +.. php:staticmethod:: add($key, $value $config = 'default') + +Usar ``Cache::add()`` permitirá que você defina atomicamente uma chave para um valor, +se a chave ainda não existir no cache. Se a chave já existir no backend do cache +ou a gravação falhar, ``add()`` retornará ``false``:: + + // Definir uma chave para atuar como um bloqueio + $result = Cache::add($lockKey, true); + if (!$result) { + return; + } + // Executar uma ação onde pode haver apenas um processo ativo por vez. + + // Remover a chave de bloqueio. + Cache::delete($lockKey); + +.. warning:: + + O cache baseado em arquivo não suporta gravações atômicas. + Armazenamento em Cache de Leitura ---------------------------------- +---------------------------------- .. php:staticmethod:: remember($key, $callable, $config = 'default') @@ -309,18 +341,17 @@ Lendo de um Cache ``Cache::read()`` é usado para ler o valor em cache armazenado em ``$key`` do ``$config``. Se ``$config`` for nulo, a configuração padrão será usada. ``Cache::read()`` retornará o valor em cache se for um cache válido ou -``false`` se o cache expirou ou não existe. O conteúdo do cache pode ser -avaliado como falso, portanto, use os operadores de comparação estritos: -``===`` ou ``!==``. +``null`` se o cache expirou ou não existe. Use os operadores de comparação +estritos ``===`` ou ``!==`` para verificar o sucesso da operação ``Cache::read()``. Por exemplo:: $cloud = Cache::read('cloud'); - if ($cloud !== false) { + if ($cloud !== null) { return $cloud; } - // Gere dados na nuvem + // Gerar dados na nuvem // ... // Armazenar dados no cache @@ -333,17 +364,14 @@ poderá especificá-la nas chamadas ``Cache::read()`` e ``Cache::write()``, conforme abaixo:: // Leia a chave "cloud", mas a partir da configuração curta em vez do padrão - $cloud = Cache::read('cloud', 'short'); - if ($cloud !== false) { - return $cloud; - } - - // Gere dados na nuvem - // ... + if ($cloud === null) { + // Gerar dados na nuvem + // ... - // Armazene dados no cache, usando a configuração de cache "short" em vez do padrão - Cache::write('cloud', $cloud, 'short'); + // Armazene dados no cache, usando a configuração de cache "short" em vez do padrão + Cache::write('cloud', $cloud, 'short'); + } return $cloud; @@ -374,6 +402,11 @@ Exclusão de um Cache // Remove uma chave Cache::delete('my_key'); +A partir da versão 4.4.0, o ``RedisEngine`` também fornece um método ``deleteAsync()`` +que usa a operação ``UNLINK`` para remover chaves de cache:: + + Cache::pool('redis')->deleteAsync('my_key'); + Exclusão de Várias Chaves de uma só Vez --------------------------------------- @@ -395,22 +428,24 @@ de rede ao usar o Memcached:: Limpando Dados em Cache ======================= -.. php:staticmethod:: clear($check, $config = 'default') +.. php:staticmethod:: clear($config = 'default') Destrua todos os valores em cache para uma configuração de cache. Em mecanismos -como: Apcu, Memcached e Wincache, o prefixo da configuração do cache é usado +como: Apcu e Memcached, o prefixo da configuração do cache é usado para remover as entradas do cache. Verifique se diferentes configurações de cache têm prefixos diferentes:: - // Limpa apenas as chaves expiradas. - Cache::clear(true); - // Limpará todas as chaves. - Cache::clear(false); + Cache::clear(); + +A partir da versão 4.4.0, o ``RedisEngine`` também fornece um método ``clearBlocking()`` +que usa a operação ``UNLINK`` para remover chaves de cache:: + + Cache::pool('redis')->clearBlocking(); .. note:: - Como o APCu e o Wincache usam caches isolados para servidor da web e CLI, + Como o APCu usa caches isolados para servidor da web e CLI, eles devem ser limpos separadamente (a CLI não pode limpar o servidor da web e vice-versa). Usando Cache para Armazenar Contadores @@ -441,7 +476,7 @@ Depois de definir um valor inteiro, você pode manipulá-lo usando ``increment() .. note:: Incrementar e decrementar não funcionam com o ``FileEngine``. - Você deve usar APCu, Wincache, Redis ou Memcached. + Você deve usar APCu, Redis ou Memcached. Usando o Cache para Armazenar Resultados Comuns de Consulta =========================================================== @@ -452,6 +487,9 @@ perfeito disso são os resultados de :php:meth:`Cake\\ORM\\Table::find()`. O obj Query permite armazenar resultados em cache usando o método ``cache()``. Veja a seção :ref:`caching-query-results` para mais informações. + +.. _cache-groups: + Usando Grupos ============= diff --git a/pt/core-libraries/collections.rst b/pt/core-libraries/collections.rst index 090dee81d6..478550e5ce 100644 --- a/pt/core-libraries/collections.rst +++ b/pt/core-libraries/collections.rst @@ -5,44 +5,47 @@ Coleções .. php:class:: Collection -As classes de coleção fornecem um conjunto de ferramentas para manipular matrizes -ou objetos ``Traversable``. Se você já usou underscore.js, tem uma idéia do que -pode esperar das classes de coleção. +As classes de coleção fornecem um conjunto de ferramentas para manipular arrays ou +objetos ``Traversable``. Se você já usou o underscore.js, +você tem uma ideia do que pode esperar das classes de coleção. -Instâncias de coleção são imutáveis; modificar uma coleção irá gerar uma nova coleção. -Isso torna o trabalho com objetos de coleção mais previsível, pois as operações são -livres de efeitos colaterais. +Instâncias de coleção são imutáveis; modificar uma coleção irá gerar +uma nova coleção. Isso torna o trabalho com objetos de coleção mais previsível, pois +as operações são livres de efeitos colaterais. Exemplo Rápido ============== -Coleções podem ser criadas usando uma matriz ou um objeto ``Traversable``. Você -também interagirá com as coleções sempre que interagir com o ORM no CakePHP. Um -simples uso de uma coleção seria:: +Coleções podem ser criadas usando um array ou objeto ``Traversable``. Você também +interagirá com coleções toda vez que interagir com o ORM no CakePHP. +Um uso simples de uma Collection seria:: use Cake\Collection\Collection; $items = ['a' => 1, 'b' => 2, 'c' => 3]; $collection = new Collection($items); - // Crie uma nova coleção contendo elementos + // Cria uma nova coleção contendo elementos // com um valor maior que um. $overOne = $collection->filter(function ($value, $key, $iterator) { return $value > 1; }); -Você também pode usar a função auxiliar ``collection()`` em vez de ``new Collection()``:: +Você também pode usar a função auxiliar ``collection()`` em vez de ``new +Collection()``:: $items = ['a' => 1, 'b' => 2, 'c' => 3]; - // Ambos formam uma instância de coleção. + // Ambos criam uma instância de Collection. $collectionA = new Collection($items); $collectionB = collection($items); -O benefício do método auxiliar é que é mais fácil encadear do que ``(new Collection($items))``. +O benefício do método auxiliar é que é mais fácil encadear do que +``(new Collection($items))``. -O :php:trait:`~Cake\\Collection\\CollectionTrait` permite integrar recursos semelhantes a -coleções em qualquer objeto ``Traversable`` que você possui no seu aplicativo. +O :php:trait:`~Cake\\Collection\\CollectionTrait` permite integrar +recursos semelhantes a coleções em qualquer objeto ``Traversable`` que você tenha em sua +aplicação também. Lista de Métodos ================ @@ -66,28 +69,27 @@ Lista de Métodos :php:meth:`take`, :php:meth:`through`, :php:meth:`transpose` :php:meth:`unfold`, :php:meth:`zip` -Iterando +Iteração ======== .. php:method:: each($callback) -As coleções podem ser iteradas e/ou transformadas em novas coleções com os -métodos ``each()`` e ``map()``. O método ``each()`` não criará uma -nova coleção, mas permitirá que você modifique quaisquer objetos dentro da -coleção:: +Coleções podem ser iteradas e/ou transformadas em novas coleções com os +métodos ``each()`` e ``map()``. O método ``each()`` não criará uma nova +coleção, mas permitirá que você modifique quaisquer objetos dentro da coleção:: $collection = new Collection($items); $collection = $collection->each(function ($value, $key) { - echo "Element $key: $value"; + echo "Elemento $key: $value"; }); -O retorno de ``each()`` será um objeto collection. Cada um iterará a coleção -imediatamente aplicando o retorno de chamada a cada valor na coleção. +O retorno de ``each()`` será o objeto de coleção. Each irá iterar a +coleção imediatamente aplicando o callback a cada valor na coleção. .. php:method:: map($callback) -O método ``map()`` criará uma nova coleção com base no retorno -de chamada que está sendo aplicada a cada objeto na coleção original:: +O método ``map()`` criará uma nova coleção com base na saída do +callback sendo aplicado a cada objeto na coleção original:: $items = ['a' => 1, 'b' => 2, 'c' => 3]; $collection = new Collection($items); @@ -102,14 +104,14 @@ de chamada que está sendo aplicada a cada objeto na coleção original:: // $result contém ['a' => 2, 'b' => 4, 'c' => 6]; $result = $new->toArray(); -O método ``map()`` criará um novo iterador que cria preguiçosamente os -itens resultantes quando iterado. +O método ``map()`` criará um novo iterador que cria preguiçosamente +os itens resultantes quando iterado. .. php:method:: extract($path) -Um dos usos mais comuns de uma função ``map()`` é extrair uma única coluna -de uma coleção. Se você deseja criar uma lista de elementos contendo os valores -de uma propriedade específica, pode usar o método ``extract()``:: +Um dos usos mais comuns para uma função ``map()`` é extrair uma única +coluna de uma coleção. Se você está procurando construir uma lista de elementos +contendo os valores de uma propriedade específica, você pode usar o método ``extract()``:: $collection = new Collection($people); $names = $collection->extract('name'); @@ -117,9 +119,9 @@ de uma propriedade específica, pode usar o método ``extract()``:: // $result contém ['mark', 'jose', 'barbara']; $result = $names->toList(); -Como em muitas outras funções da classe de coleção, você pode especificar um caminho -separado por pontos para extrair colunas. Este exemplo retornará uma coleção que -contém os nomes dos autores de uma lista de artigos:: +Como em muitas outras funções na classe de coleção, você pode especificar +um caminho separado por pontos para extrair colunas. Este exemplo retornará +uma coleção contendo os nomes dos autores de uma lista de artigos:: $collection = new Collection($articles); $names = $collection->extract('author.name'); @@ -127,18 +129,18 @@ contém os nomes dos autores de uma lista de artigos:: // $result contém ['Maria', 'Stacy', 'Larry']; $result = $names->toList(); -Por fim, se a propriedade que você está procurando não pode ser expressa como um caminho, -você pode usar uma função de retorno de chamada para retorná-la:: +Finalmente, se a propriedade que você está procurando não puder ser expressa como um caminho, +você pode usar uma função de callback para retorná-la:: $collection = new Collection($articles); $names = $collection->extract(function ($article) { return $article->author->name . ', ' . $article->author->last_name; }); -Freqüentemente, existem propriedades necessárias para extrair uma chave comum -presente em várias matrizes ou objetos profundamente aninhados em outras estruturas. -Para esses casos, você pode usar o combinador ``{*}`` na chave do caminho. -Esse correspondente geralmente é útil ao combinar dados da associação HasMany e BelongsToMany:: +Muitas vezes, as propriedades que você precisa extrair são uma chave comum presente em vários +arrays ou objetos que estão profundamente aninhados dentro de outras estruturas. Para esses +casos, você pode usar o matcher ``{*}`` na chave do caminho. Este matcher é frequentemente +útil ao combinar dados de associação HasMany e BelongsToMany:: $data = [ [ @@ -147,35 +149,33 @@ Esse correspondente geralmente é útil ao combinar dados da associação HasMan ['number' => 'number-1'], ['number' => 'number-2'], ['number' => 'number-3'], - ] + ], ], [ 'name' => 'James', 'phone_numbers' => [ ['number' => 'number-4'], ['number' => 'number-5'], - ] - ] + ], + ], ]; $numbers = (new Collection($data))->extract('phone_numbers.{*}.number'); - $numbers->toList(); - // Retorna ['number-1', 'number-2', 'number-3', 'number-4', 'number-5'] + $result = $numbers->toList(); + // $result contém ['number-1', 'number-2', 'number-3', 'number-4', 'number-5'] -Este último exemplo usa ``toList()`` diferente de outros exemplos, o que é -importante quando estamos obtendo resultados com chaves possivelmente duplicadas. -Ao usar ``toList()``, garantimos a obtenção de todos os valores, mesmo que haja -chaves duplicadas. +Este último exemplo usa ``toList()`` ao contrário de outros exemplos, o que é importante +quando estamos obtendo resultados com chaves possivelmente duplicadas. Ao usar ``toList()`` +teremos a garantia de obter todos os valores mesmo se houver chaves duplicadas. -Ao contrário de :php:meth:`Cake\\Utility\\Hash::extract()` este método suporta -apenas o curinga ``{*}``. Todos os outros correspondentes de curinga e atributos -não são suportados. +Ao contrário de :php:meth:`Cake\\Utility\\Hash::extract()` este método suporta apenas o +wildcard ``{*}``. Todos os outros matchers de wildcard e atributos não são suportados. .. php:method:: combine($keyPath, $valuePath, $groupPath = null) Coleções permitem que você crie uma nova coleção feita de chaves e valores em -uma coleção existente. Os caminhos de chave e valor podem ser especificados com -notação de caminhos com ponto:: +uma coleção existente. Tanto a chave quanto os caminhos de valor podem ser especificados com +caminhos de notação de ponto:: $items = [ ['id' => 1, 'name' => 'foo', 'parent' => 'a'], @@ -183,35 +183,38 @@ notação de caminhos com ponto:: ['id' => 3, 'name' => 'baz', 'parent' => 'a'], ]; $combined = (new Collection($items))->combine('id', 'name'); + $result = $combined->toArray(); - // O resultado ficará assim quando convertido em array + // $result contém [ 1 => 'foo', 2 => 'bar', 3 => 'baz', ]; -Opcionalmente, você também pode usar um ``groupPath`` para agrupar resultados com base em um caminho:: +Você também pode, opcionalmente, usar um ``groupPath`` para agrupar resultados com base em um caminho:: $combined = (new Collection($items))->combine('id', 'name', 'parent'); + $result = $combined->toArray(); - // O resultado ficará assim quando convertido em array + // $result contém [ 'a' => [1 => 'foo', 3 => 'baz'], 'b' => [2 => 'bar'] ]; -E por fim, você pode usar *closures* para criar caminhos de chaves/valores/grupos dinamicamente, -por exemplo, ao trabalhar com entidades e datas (convertidas em instâncias ``Cake/Time`` pelo ORM), -você pode querer agrupar os resultados por data:: +Finalmente, você pode usar *closures* para construir dinamicamente caminhos de chaves/valores/grupos, +por exemplo, ao trabalhar com entidades e datas (convertidas em instâncias ``I18n\DateTime`` +pelo ORM), você pode querer agrupar resultados por data:: $combined = (new Collection($entities))->combine( 'id', function ($entity) { return $entity; }, function ($entity) { return $entity->date->toDateString(); } ); + $result = $combined->toArray(); - // O resultado ficará assim quando convertido em array + // $result contém [ 'date string like 2015-05-01' => ['entity1->id' => entity1, 'entity2->id' => entity2, ..., 'entityN->id' => entityN] 'date string like 2015-06-01' => ['entity1->id' => entity1, 'entity2->id' => entity2, ..., 'entityN->id' => entityN] @@ -219,15 +222,15 @@ você pode querer agrupar os resultados por data:: .. php:method:: stopWhen(callable $c) -Você pode parar a iteração a qualquer momento usando o método ``stopWhen()``. -A chamada em uma coleção criará uma nova e irá interromper a execução de novos resultados -se a chamada passada retornar verdadeira para um dos elementos:: +Você pode parar a iteração em qualquer ponto usando o método ``stopWhen()``. Chamá-lo +em uma coleção criará uma nova que parará de produzir resultados se o +callable passado retornar true para um dos elementos:: $items = [10, 20, 50, 1, 2]; $collection = new Collection($items); $new = $collection->stopWhen(function ($value, $key) { - // Pare no primeiro valor maior que 30 + // Para no primeiro valor maior que 30 return $value > 30; }); @@ -236,10 +239,10 @@ se a chamada passada retornar verdadeira para um dos elementos:: .. php:method:: unfold(callable $callback) -Às vezes, os itens internos de uma coleção contêm matrizes ou iteradores com mais -itens. Se você deseja nivelar a estrutura interna para iterar uma vez todos os -elementos, pode usar o método ``unfold()``. Ele criará uma nova coleção que -produzirá todos os elementos aninhados na coleção:: +Às vezes, os itens internos de uma coleção conterão arrays ou iteradores +com mais itens. Se você deseja achatar a estrutura interna para iterar uma vez +sobre todos os elementos, você pode usar o método ``unfold()``. Ele criará uma nova +coleção que produzirá cada elemento único aninhado na coleção:: $items = [[1, 2, 3], [4, 5]]; $collection = new Collection($items); @@ -248,21 +251,21 @@ produzirá todos os elementos aninhados na coleção:: // $result contém [1, 2, 3, 4, 5]; $result = $new->toList(); -Ao passar uma chamada para ``unfold()``, você pode controlar quais elementos -serão desdobrados de cada item da coleção original. Isso é útil para retornar +Ao passar um callable para ``unfold()`` você pode controlar quais elementos serão +desdobrados de cada item na coleção original. Isso é útil para retornar dados de serviços paginados:: $pages = [1, 2, 3, 4]; $collection = new Collection($pages); $items = $collection->unfold(function ($page, $key) { - // Um serviço da web imaginário que retorna uma página de resultados + // Um serviço web imaginário que retorna uma página de resultados return MyService::fetchPage($page)->toList(); }); $allPagesItems = $items->toList(); -Se você estiver usando o PHP 5.5+, você pode usar a palavra-chave ``yield`` dentro de -``unfold()`` para retornar quantos elementos de cada item da coleção você precisará:: +Se você estiver usando PHP 5.5+, você pode usar a palavra-chave ``yield`` dentro de ``unfold()`` +para retornar quantos elementos você precisar para cada item na coleção:: $oddNumbers = [1, 3, 5, 7]; $collection = new Collection($oddNumbers); @@ -277,20 +280,20 @@ Se você estiver usando o PHP 5.5+, você pode usar a palavra-chave ``yield`` de .. php:method:: chunk($chunkSize) Ao lidar com grandes quantidades de itens em uma coleção, pode fazer sentido -processar os elementos em lotes, em vez de um por um. Para dividir uma coleção -em várias matrizes de um determinado tamanho, você pode usar a função ``chunk()``:: +processar os elementos em lotes em vez de um por um. Para dividir +uma coleção em vários arrays de um determinado tamanho, você pode usar a função ``chunk()``:: $items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]; $collection = new Collection($items); $chunked = $collection->chunk(2); $chunked->toList(); // [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10], [11]] -A função ``chunk`` é particularmente útil ao realizar o processamento em lote, por -exemplo, com um resultado no banco de dados:: +A função ``chunk`` é particularmente útil ao fazer processamento em lote, por +exemplo com um resultado de banco de dados:: $collection = new Collection($articles); $collection->map(function ($article) { - // Alterar uma propriedade no artigo + // Altera uma propriedade no artigo $article->property = 'changed'; }) ->chunk(20) @@ -300,8 +303,9 @@ exemplo, com um resultado no banco de dados:: .. php:method:: chunkWithKeys($chunkSize) -Bem como :php:meth:`chunk()`, ``chunkWithKeys()`` permite dividir uma coleção -em lotes menores, mas com as chaves preservadas. Isso é útil ao agrupar matrizes associativas:: +Assim como :php:meth:`chunk()`, ``chunkWithKeys()`` permite que você divida +uma coleção em lotes menores, mas com as chaves preservadas. Isso é útil ao +fragmentar arrays associativos:: $collection = new Collection([ 'a' => 1, @@ -309,8 +313,10 @@ em lotes menores, mas com as chaves preservadas. Isso é útil ao agrupar matriz 'c' => 3, 'd' => [4, 5] ]); - $chunked = $collection->chunkWithKeys(2)->toList(); - // Cria + $chunked = $collection->chunkWithKeys(2); + $result = $chunked->toList(); + + // $result contém [ ['a' => 1, 'b' => 2], ['c' => 3, 'd' => [4, 5]] @@ -321,9 +327,9 @@ Filtragem .. php:method:: filter($callback) -As coleções facilitam a filtragem e a criação de novas coleções com base no resultado -das funções de retorno de chamada. Você pode usar ``filter()`` para criar uma nova -coleção de elementos que correspondem a um retorno de chamada de critério:: +Coleções permitem que você filtre e crie novas coleções com base no +resultado de funções de callback. Você pode usar ``filter()`` para criar uma nova +coleção de elementos correspondentes a um callback de critério:: $collection = new Collection($people); $ladies = $collection->filter(function ($person, $key) { @@ -335,7 +341,7 @@ coleção de elementos que correspondem a um retorno de chamada de critério:: .. php:method:: reject(callable $c) -O inverso de ``filter()`` é ``reject()``. Este método cria um filtro negativo, +O inverso de ``filter()`` é ``reject()``. Este método faz uma filtragem negativa, removendo elementos que correspondem à função de filtro:: $collection = new Collection($people); @@ -345,8 +351,8 @@ removendo elementos que correspondem à função de filtro:: .. php:method:: every($callback) -Você pode fazer testes de verificação com funções de filtro. Para ver se todos -os elementos de uma coleção correspondem a um teste, você pode usar ``every()``:: +Você pode fazer testes de verdade com funções de filtro. Para ver se todos os elementos em +uma coleção correspondem a um teste, você pode usar ``every()``:: $collection = new Collection($people); $allYoungPeople = $collection->every(function ($person) { @@ -355,8 +361,8 @@ os elementos de uma coleção correspondem a um teste, você pode usar ``every() .. php:method:: some($callback) -Você pode ver se a coleção contém pelo menos um elemento que corresponde a -uma função de filtro usando o método ``some()``:: +Você pode ver se a coleção contém pelo menos um elemento que corresponda a uma função de filtro +usando o método ``some()``:: $collection = new Collection($people); $hasYoungPeople = $collection->some(function ($person) { @@ -365,18 +371,17 @@ uma função de filtro usando o método ``some()``:: .. php:method:: match($conditions) -Se você precisar extrair uma nova coleção contendo apenas os elementos que -contêm um determinado conjunto de propriedades, use o método ``match()``:: +Se você precisa extrair uma nova coleção contendo apenas os elementos que +contêm um determinado conjunto de propriedades, você deve usar o método ``match()``:: $collection = new Collection($comments); $commentsFromMark = $collection->match(['user.name' => 'Mark']); .. php:method:: firstMatch($conditions) -O nome da propriedade pode ser um caminho separado por pontos. Você pode -percorrer entidades aninhadas e verificar valores que elas contêm. -Quando você só precisa do primeiro elemento correspondente de uma coleção, -pode usar ``firstMatch()``:: +O nome da propriedade pode ser um caminho separado por pontos. Você pode atravessar +entidades aninhadas e corresponder aos valores que elas contêm. Quando você precisa apenas do primeiro +elemento correspondente de uma coleção, você pode usar ``firstMatch()``:: $collection = new Collection($comments); $comment = $collection->firstMatch([ @@ -384,45 +389,45 @@ pode usar ``firstMatch()``:: 'active' => true ]); -Como você pode ver acima, ``match()`` e ``firstMatch()`` permitem fornecer -várias condições para a correspondência. Além disso, as condições -podem ser para caminhos diferentes, permitindo expressar condições complexas -para comparação. +Como você pode ver acima, tanto ``match()`` quanto ``firstMatch()`` permitem que você +forneça várias condições para corresponder. Além disso, as condições podem ser +para caminhos diferentes, permitindo que você expresse condições complexas para corresponder. Agregação ========= -.. php:method:: reduce($callback) +.. php:method:: reduce($callback, $initial) -A contraparte de uma operação ``map()`` geralmente é uma ``reduce``. -Esta função ajudará você a criar um único resultado de todos os elementos -em uma coleção:: +A contraparte de uma operação ``map()`` geralmente é um ``reduce``. Esta +função o ajudará a construir um único resultado a partir de todos os elementos em uma +coleção:: $totalPrice = $collection->reduce(function ($accumulated, $orderLine) { return $accumulated + $orderLine->price; }, 0); -No exemplo acima, ``$totalPrice`` será a soma de todos os preços únicos contidos na -coleção. Observe que o segundo argumento para da função ``reduce()`` assume o valor -inicial da operação de redução que você está executando:: +No exemplo acima, ``$totalPrice`` será a soma de todos os preços únicos +contidos na coleção. Note o segundo argumento para a função ``reduce()`` +que recebe o valor inicial para a operação de redução que você está +executando:: $allTags = $collection->reduce(function ($accumulated, $article) { return array_merge($accumulated, $article->tags); }, []); -.. php:method:: min(string|$callback, $type = SORT_NUMERIC) +.. php:method:: min(string|callable $callback, $type = SORT_NUMERIC) -Para extrair o valor mínimo de uma coleção com base em uma propriedade, -basta usar a função ``min()``. Isso retornará o elemento completo da coleção -e não apenas o menor valor encontrado:: +Para extrair o valor mínimo de uma coleção com base em uma propriedade, use apenas a +função ``min()``. Isso retornará o elemento completo da coleção e +não apenas o menor valor encontrado:: $collection = new Collection($people); $youngest = $collection->min('age'); echo $youngest->name; -Você também pode expressar a propriedade para comparar, fornecendo um caminho ou uma -função de retorno de chamada:: +Você também pode expressar a propriedade a comparar fornecendo um caminho ou uma +função de callback:: $collection = new Collection($people); $personYoungestChild = $collection->min(function ($person) { @@ -431,10 +436,10 @@ função de retorno de chamada:: $personWithYoungestDad = $collection->min('dad.age'); -.. php:method:: max($callback, $type = SORT_NUMERIC) +.. php:method:: max(string|callable $callback, $type = SORT_NUMERIC) -O mesmo pode ser aplicado à função ``max()``, que retornará um único elemento -da coleção com o maior valor de propriedade:: +O mesmo pode ser aplicado à função ``max()``, que retornará um único +elemento da coleção tendo o maior valor de propriedade:: $collection = new Collection($people); $oldest = $collection->max('age'); @@ -447,7 +452,8 @@ da coleção com o maior valor de propriedade:: .. php:method:: sumOf($path = null) -Finalmente, o método ``sumOf()`` retornará a soma de uma propriedade de todos os elementos:: +Finalmente, o método ``sumOf()`` retornará a soma de uma propriedade de todos +os elementos:: $collection = new Collection($people); $sumOfAges = $collection->sumOf('age'); @@ -460,21 +466,22 @@ Finalmente, o método ``sumOf()`` retornará a soma de uma propriedade de todos .. php:method:: avg($path = null) -Calcule o valor médio dos elementos na coleção. Opcionalmente, forneça um -caminho correspondente ou função para extrair valores e gerar a média:: +Calcula o valor médio dos elementos na coleção. Opcionalmente, +forneça um caminho de correspondência ou função para extrair valores para gerar a média +para:: $items = [ ['invoice' => ['total' => 100]], ['invoice' => ['total' => 200]], ]; - // Média: 150 + // $average contém 150 $average = (new Collection($items))->avg('invoice.total'); .. php:method:: median($path = null) -Calcule o valor mediano de um conjunto de elementos. Opcionalmente, poderá fornecer -um caminho correspondente ou função para extrair valores para gerar a mediana:: +Calcula o valor mediano de um conjunto de elementos. Opcionalmente, forneça um caminho de correspondência +ou função para extrair valores para gerar a mediana para:: $items = [ ['invoice' => ['total' => 400]], @@ -484,16 +491,17 @@ um caminho correspondente ou função para extrair valores para gerar a mediana: ['invoice' => ['total' => 200]], ]; - // Média: 333 + // $median contém 333 $median = (new Collection($items))->median('invoice.total'); + Agrupamento e Contagem ----------------------- +----------------------- .. php:method:: groupBy($callback) -Os valores da coleção podem ser agrupados por chaves diferentes em uma nova -coleção quando eles compartilham o mesmo valor para uma propriedade:: +Os valores da coleção podem ser agrupados por chaves diferentes em uma nova coleção quando +compartilham o mesmo valor para uma propriedade:: $students = [ ['name' => 'Mark', 'grade' => 9], @@ -503,8 +511,9 @@ coleção quando eles compartilham o mesmo valor para uma propriedade:: ]; $collection = new Collection($students); $studentsByGrade = $collection->groupBy('grade'); + $result = $studentsByGrade->toArray(); - // O resultado ficará assim quando convertido em array: + // $result contém [ 10 => [ ['name' => 'Andrew', 'grade' => 10], @@ -516,8 +525,8 @@ coleção quando eles compartilham o mesmo valor para uma propriedade:: ] ] -Como de costume, é possível fornecer um caminho separado por pontos para propriedades -aninhadas ou sua própria função de retorno de chamada para gerar os grupos dinamicamente:: +Como de costume, é possível fornecer um caminho separado por pontos para propriedades aninhadas +ou sua própria função de callback para gerar os grupos dinamicamente:: $commentsByUserId = $comments->groupBy('user.id'); @@ -527,33 +536,34 @@ aninhadas ou sua própria função de retorno de chamada para gerar os grupos di .. php:method:: countBy($callback) -Se você deseja apenas saber o número de ocorrências por grupo, pode fazê-lo usando o -método ``countBy()``. Ele usa os mesmos argumentos de ``groupBy``, portanto já +Se você deseja apenas saber o número de ocorrências por grupo, você pode fazê-lo +usando o método ``countBy()``. Ele recebe os mesmos argumentos que ``groupBy``, então deve ser familiar para você:: $classResults = $students->countBy(function ($student) { return $student->grade > 6 ? 'approved' : 'denied'; }); - // O resultado ficará assim quando convertido em array: + // O resultado pode parecer com isso quando convertido para array: ['approved' => 70, 'denied' => 20] .. php:method:: indexBy($callback) -Em certos casos, você sabe que um elemento é exclusivo para a propriedade que -você deseja agrupar. Se você deseja um único resultado por grupo, pode usar a +Haverá certos casos em que você sabe que um elemento é único para a propriedade +pela qual deseja agrupar. Se você deseja um único resultado por grupo, pode usar a função ``indexBy()``:: $usersById = $users->indexBy('id'); - // Quando convertido em resultado da matriz, pode parecer + // Quando convertido para array, o resultado pode parecer [ 1 => 'markstory', 3 => 'jose_zap', 4 => 'jrbasso' ] -Assim como na função ``groupBy()``, você também pode usar um caminho de propriedade ou um retorno de chamada:: +Assim como com a função ``groupBy()`` você também pode usar um caminho de propriedade ou +um callback:: $articlesByAuthorId = $articles->indexBy('author.id'); @@ -563,22 +573,24 @@ Assim como na função ``groupBy()``, você também pode usar um caminho de prop .. php:method:: zip($items) -Os elementos de diferentes coleções podem ser agrupados usando o método ``zip()``. -Ele retornará uma nova coleção contendo uma matriz que agrupa os elementos de cada -coleção que são colocados na mesma posição:: +Os elementos de diferentes coleções podem ser agrupados usando o +método ``zip()``. Ele retornará uma nova coleção contendo um array agrupando +os elementos de cada coleção que estão colocados na mesma posição:: $odds = new Collection([1, 3, 5]); $pairs = new Collection([2, 4, 6]); $combined = $odds->zip($pairs)->toList(); // [[1, 2], [3, 4], [5, 6]] -Você também pode compactar várias coleções de uma só vez:: +Você também pode compactar várias coleções de uma vez:: $years = new Collection([2013, 2014, 2015, 2016]); $salaries = [1000, 1500, 2000, 2300]; $increments = [0, 500, 500, 300]; - $rows = $years->zip($salaries, $increments)->toList(); - // Retorna: + $rows = $years->zip($salaries, $increments); + $result = $rows->toList(); + + // $result contém [ [2013, 1000, 0], [2014, 1500, 500], @@ -587,22 +599,22 @@ Você também pode compactar várias coleções de uma só vez:: ] Como você já pode ver, o método ``zip()`` é muito útil para transpor -matrizes multidimensionais:: +arrays multidimensionais:: $data = [ 2014 => ['jan' => 100, 'feb' => 200], 2015 => ['jan' => 300, 'feb' => 500], 2016 => ['jan' => 400, 'feb' => 600], - ] + ]; - // Reunindo dados de janeiro e fevereiro + // Obtendo dados de janeiro e fevereiro juntos $firstYear = new Collection(array_shift($data)); - $firstYear->zip($data[0], $data[1])->toList(); + $result = $firstYear->zip($data[0], $data[1])->toList(); // Ou $firstYear->zip(...$data) em PHP >= 5.6 - // Retorna + // $result contém [ [100, 300, 400], [200, 500, 600] @@ -613,47 +625,47 @@ Ordenação .. php:method:: sortBy($callback, $order = SORT_DESC, $sort = SORT_NUMERIC) -Os valores da coleção podem ser classificados em ordem crescente ou decrescente -com base em uma coluna ou função personalizada. Para criar uma nova coleção -classificada com os valores de outra, você pode usar ``sortBy``:: +Os valores da coleção podem ser ordenados em ordem crescente ou decrescente com base em +uma coluna ou função personalizada. Para criar uma nova coleção ordenada a partir dos valores +de outra, você pode usar ``sortBy``:: $collection = new Collection($people); $sorted = $collection->sortBy('age'); -Como visto acima, você pode classificar passando o nome de uma coluna ou -propriedade presente nos valores da coleção. Você também pode especificar -um caminho de propriedade usando a notação de ponto. O próximo exemplo -classificará os artigos pelo nome do autor:: +Como visto acima, você pode ordenar passando o nome de uma coluna ou propriedade que +está presente nos valores da coleção. Você também pode especificar um caminho de propriedade +em vez disso usando a notação de ponto. O próximo exemplo ordenará artigos por +nome do autor:: $collection = new Collection($articles); $sorted = $collection->sortBy('author.name'); -O método ``sortBy()`` é flexível o suficiente para permitir que você -especifique uma função extratora que permitirá selecionar dinamicamente o -valor a ser usado para comparar dois valores diferentes na coleção:: +O método ``sortBy()`` é flexível o suficiente para permitir que você especifique uma função extratora +que permitirá selecionar dinamicamente o valor a ser usado para comparar dois +valores diferentes na coleção:: $collection = new Collection($articles); $sorted = $collection->sortBy(function ($article) { return $article->author->name . '-' . $article->title; }); -Para especificar em qual direção a coleção deve ser classificada, é -necessário fornecer ``SORT_ASC`` ou ``SORT_DESC`` como o segundo -parâmetro para classificar na direção ascendente ou descendente, -respectivamente. Por padrão, as coleções são classificadas em direção descendente:: +Para especificar em qual direção a coleção deve ser ordenada, você precisa +fornecer ``SORT_ASC`` ou ``SORT_DESC`` como o segundo parâmetro para +ordenação em direção crescente ou decrescente, respectivamente. Por padrão, +as coleções são ordenadas em direção decrescente:: $collection = new Collection($people); $sorted = $collection->sortBy('age', SORT_ASC); -Às vezes, você precisará especificar que tipo de dados você está tentando -comparar para obter resultados consistentes. Para esse fim, você deve fornecer -um terceiro argumento na função ``sortBy()`` com uma das seguintes constantes: +Às vezes, você precisará especificar qual tipo de dados está tentando comparar +para obter resultados consistentes. Para esse propósito, você deve fornecer um terceiro +argumento na função ``sortBy()`` com uma das seguintes constantes: - **SORT_NUMERIC**: Para comparar números -- **SORT_STRING**: Para comparar valores de sequência -- **SORT_NATURAL**: Para classificar sequência contendo números e se você desejar que esses números - sejam ordenados de maneira natural. Por exemplo: mostrando "10" depois de "2". -- **SORT_LOCALE_STRING**: Para comparar seqüências de caracteres com base na localidade atual. +- **SORT_STRING**: Para comparar valores de string +- **SORT_NATURAL**: Para ordenar strings contendo números e você gostaria que esses + números fossem ordenados de forma natural. Por exemplo: mostrando "10" após "2". +- **SORT_LOCALE_STRING**: Para comparar strings com base na localidade atual. Por padrão, ``SORT_NUMERIC`` é usado:: @@ -662,22 +674,23 @@ Por padrão, ``SORT_NUMERIC`` é usado:: .. warning:: - Muitas vezes, é caro iterar coleções classificadas mais de uma vez. Se você planeja fazer isso, - considere converter a coleção em uma matriz ou simplesmente use o método ``compile()`` nela. + Muitas vezes é caro iterar coleções ordenadas mais de uma vez. Se você + planeja fazer isso, considere converter a coleção em um array ou simplesmente use + o método ``compile()`` nela. -Trabalhando com dados em Árvore -=============================== +Trabalhando com Dados de Árvore +================================ -.. php:method:: nest($idPath, $parentPath) +.. php:method:: nest($idPath, $parentPath, $nestingKey = 'children') -Nem todos os dados devem ser representados de maneira linear. As coleções facilitam -a construção e o nivelamento de estruturas hierárquicas ou aninhadas. Criar uma estrutura -aninhada em que os filhos são agrupados por uma propriedade de identificador pai é fácil -com o método ``nest()``. +Nem todos os dados devem ser representados de forma linear. Coleções facilitam +a construção e achatamento de estruturas hierárquicas ou aninhadas. Criar +uma estrutura aninhada onde os filhos são agrupados por uma propriedade de identificador pai +pode ser feito com o método ``nest()``. -Dois parâmetros são necessários para esta função. O primeiro é a propriedade que representa o -identificador do item. O segundo parâmetro é o nome da propriedade que representa o identificador -para o item pai:: +Dois parâmetros são necessários para esta função. O primeiro é a propriedade +representando o identificador do item. O segundo parâmetro é o nome da +propriedade representando o identificador do item pai:: $collection = new Collection([ ['id' => 1, 'parent_id' => null, 'name' => 'Birds'], @@ -687,9 +700,10 @@ para o item pai:: ['id' => 5, 'parent_id' => 6, 'name' => 'Clown Fish'], ['id' => 6, 'parent_id' => null, 'name' => 'Fish'], ]); + $nested = $collection->nest('id', 'parent_id'); + $result = $nested->toList(); - $collection->nest('id', 'parent_id')->toList(); - // Retorna + // $result contém [ [ 'id' => 1, @@ -699,7 +713,7 @@ para o item pai:: ['id' => 2, 'parent_id' => 1, 'name' => 'Land Birds', 'children' => []], ['id' => 3, 'parent_id' => 1, 'name' => 'Eagle', 'children' => []], ['id' => 4, 'parent_id' => 1, 'name' => 'Seagull', 'children' => []], - ] + ], ], [ 'id' => 6, @@ -707,26 +721,28 @@ para o item pai:: 'name' => 'Fish', 'children' => [ ['id' => 5, 'parent_id' => 6, 'name' => 'Clown Fish', 'children' => []], - ] - ] + ], + ], ]; -Os elementos filhos são aninhados dentro da propriedade ``children`` dentro de -cada um dos itens da coleção. Esse tipo de representação de dados é útil para -renderizar menus ou percorrer elementos até um determinado nível na árvore. +Os elementos filhos são aninhados dentro da propriedade ``children`` dentro de cada um dos +itens na coleção. Este tipo de representação de dados é útil para +renderizar menus ou atravessar elementos até um certo nível na árvore. .. php:method:: listNested($order = 'desc', $nestingKey = 'children') -O inverso de ``nest()`` é ``listNested()``. Este método permite nivelar -uma estrutura de árvore novamente em uma estrutura linear. São necessários dois -parâmetros; o primeiro é o modo de deslocamento (asc, desc ou leaves) e o segundo -é o nome da propriedade que contém os filhos de cada elemento da coleção. +O inverso de ``nest()`` é ``listNested()``. Este método permite achatar +uma estrutura de árvore de volta em uma estrutura linear. Ele recebe dois parâmetros; o +primeiro é o modo de travessia (asc, desc ou leaves), e o segundo é +o nome da propriedade contendo os filhos para cada elemento na +coleção. -Tomando a entrada da coleção aninhada criada no exemplo anterior, podemos deixar nivelado:: +Tomando a entrada da coleção aninhada construída no exemplo anterior, podemos +achatá-la:: - $nested->listNested()->toList(); + $result = $nested->listNested()->toList(); - // Retorna + // $result contém [ ['id' => 1, 'parent_id' => null, 'name' => 'Birds', 'children' => [...]], ['id' => 2, 'parent_id' => 1, 'name' => 'Land Birds'], @@ -736,32 +752,37 @@ Tomando a entrada da coleção aninhada criada no exemplo anterior, podemos deix ['id' => 5, 'parent_id' => 6, 'name' => 'Clown Fish'] ] -Por padrão, a árvore é atravessada da raiz para as extremidades. Você também pode -instruí-lo a retornar apenas os elementos filhos da árvore:: +Por padrão, a árvore é percorrida da raiz às folhas. Você também pode +instruí-la a retornar apenas os elementos folha na árvore:: - $nested->listNested()->toList(); + $result = $nested->listNested('leaves')->toList(); - // Retorna + // $result contém [ - ['id' => 3, 'parent_id' => 1, 'name' => 'Eagle'], - ['id' => 4, 'parent_id' => 1, 'name' => 'Seagull'], - ['id' => 5, 'parent_id' => 6, 'name' => 'Clown Fish'] + ['id' => 2, 'parent_id' => 1, 'name' => 'Land Birds', 'children' => [], ], + ['id' => 3, 'parent_id' => 1, 'name' => 'Eagle', 'children' => [], ], + ['id' => 4, 'parent_id' => 1, 'name' => 'Seagull', 'children' => [], ], + ['id' => 5, 'parent_id' => 6, 'name' => 'Clown Fish', 'children' => [], ], ] -Depois de converter uma árvore em uma lista aninhada, você pode usar o método -``printer()`` para configurar como a saída da lista deve ser formatada:: - $nested->listNested()->printer('name', 'id', '--')->toArray(); +Depois de converter uma árvore em uma lista aninhada, você pode usar o método ``printer()`` +para configurar como a saída da lista deve ser formatada:: + + $result = $nested->listNested()->printer('name', 'id', '--')->toArray(); - // Retorna + // $result contém [ - 3 => 'Eagle', - 4 => 'Seagull', - 5 -> '--Clown Fish', + 1 => 'Birds', + 2 => '--Land Birds', + 3 => '--Eagle', + 4 => '--Seagull', + 6 => 'Fish', + 5 => '--Clown Fish', ] -O método ``printer()`` também permite usar um retorno de chamada para gerar as -chaves e/ou valores:: +O método ``printer()`` também permite que você use um callback para gerar as chaves e +ou valores:: $nested->listNested()->printer( function ($el) { @@ -789,169 +810,171 @@ Permite que você veja se uma coleção contém algum elemento:: .. php:method:: contains($value) -As coleções permitem que você verifique rapidamente se elas contêm um -valor específico usando o método ``contains()``:: +Coleções permitem que você verifique rapidamente se elas contêm um valor específico +usando o método ``contains()``:: $items = ['a' => 1, 'b' => 2, 'c' => 3]; $collection = new Collection($items); $hasThree = $collection->contains(3); -As comparações são realizadas usando o operador ``===``. Se você -deseja fazer tipos de comparação mais flexíveis, pode usar o método ``some()``. +As comparações são realizadas usando o operador ``===``. Se você deseja fazer tipos de comparação +mais frouxos, você pode usar o método ``some()``. .. php:method:: shuffle() -Às vezes, você pode querer mostrar uma coleção de valores em uma ordem aleatória. -Para criar uma nova coleção que retornará cada valor em uma posição diferente, -use o ``shuffle``:: +Às vezes, você pode desejar mostrar uma coleção de valores em ordem aleatória. Em +ordem para criar uma nova coleção que retornará cada valor em uma posição +aleatória, use o ``shuffle``:: $collection = new Collection(['a' => 1, 'b' => 2, 'c' => 3]); - // Isso poderia retornar [2, 3, 1] + // Isso pode retornar [2, 3, 1] $collection->shuffle()->toList(); .. php:method:: transpose() -Ao transpor uma coleção, você obtém uma nova coleção contendo uma linha feita de -cada uma das colunas originais:: +Quando você transpõe uma coleção, você obtém uma nova coleção contendo uma linha feita +de cada uma das colunas originais:: - $items = [ + $items = [ ['Products', '2012', '2013', '2014'], ['Product A', '200', '100', '50'], ['Product B', '300', '200', '100'], ['Product C', '400', '300', '200'], - ] - $transpose = (new Collection($items))->transpose()->toList(); + ]; + $transpose = (new Collection($items))->transpose(); + $result = $transpose->toList(); - // Retorna - [ - ['Products', 'Product A', 'Product B', 'Product C'], - ['2012', '200', '300', '400'], - ['2013', '100', '200', '300'], - ['2014', '50', '100', '200'], - ] + // $result contém + [ + ['Products', 'Product A', 'Product B', 'Product C'], + ['2012', '200', '300', '400'], + ['2013', '100', '200', '300'], + ['2014', '50', '100', '200'], + ] Retirando Elementos ------------------- .. php:method:: sample($length = 10) -Baralhar uma coleção geralmente é útil ao fazer análises estatísticas rápidas. -Outra operação comum ao executar esse tipo de tarefa é retirar alguns valores -aleatórios de uma coleção, para que mais testes possam ser realizados. Por exemplo, -se você quiser selecionar 5 usuários aleatórios aos quais deseja aplicar alguns -testes A/B, poderá usar a função ``sample()``:: +Embaralhar uma coleção geralmente é útil ao fazer análises estatísticas rápidas. +Outra operação comum ao fazer esse tipo de tarefa é retirar alguns +valores aleatórios de uma coleção para que mais testes possam ser realizados neles. +Por exemplo, se você quisesse selecionar 5 usuários aleatórios aos quais gostaria de aplicar +alguns testes A/B, você pode usar a função ``sample()``:: $collection = new Collection($people); - // Retire no máximo 20 usuários aleatórios desta coleção + // Retira no máximo 20 usuários aleatórios desta coleção $testSubjects = $collection->sample(20); -``sample()`` terá no máximo o número de valores que você especificar no primeiro -argumento. Se não houver elementos suficientes na coleção para satisfazer a amostra, -a coleção completa em uma ordem aleatória será retornada. +``sample()`` pegará no máximo o número de valores que você especificar no primeiro +argumento. Se não houver elementos suficientes na coleção para satisfazer a +amostra, a coleção completa em ordem aleatória é retornada. .. php:method:: take($length, $offset) -Sempre que você desejar obter uma fatia de uma coleção, use a função ``take()``, -ela criará uma nova coleção com, no máximo, o número de valores que você especificar -no primeiro argumento, iniciando na posição passada no segundo argumento:: +Sempre que você quiser pegar uma fatia de uma coleção, use a função ``take()``, +ela criará uma nova coleção com no máximo o número de valores que você especificar no +primeiro argumento, começando da posição passada no segundo argumento:: $topFive = $collection->sortBy('age')->take(5); - // Leve 5 pessoas da coleção a partir da posição 4 + // Pega 5 pessoas da coleção começando da posição 4 $nextTopFive = $collection->sortBy('age')->take(5, 4); -As posições são baseadas em zero, portanto, o número da primeira posição é ``0``. +As posições são baseadas em zero, portanto o primeiro número de posição é ``0``. .. php:method:: skip($length) -Embora o segundo argumento de ``take()`` possa ajudá-lo a pular alguns -elementos antes de obtê-los da coleção, você também pode usar ``skip()`` -para o mesmo objetivo que uma maneira de tirar o resto dos elementos depois -de uma certa posição:: +Embora o segundo argumento de ``take()`` possa ajudá-lo a pular alguns elementos antes +de obtê-los da coleção, você também pode usar ``skip()`` para o mesmo +propósito como uma forma de pegar o restante dos elementos após uma certa posição:: $collection = new Collection([1, 2, 3, 4]); $allExceptFirstTwo = $collection->skip(2)->toList(); // [3, 4] .. php:method:: first() -Um dos usos mais comuns de ``take()`` é obter o primeiro elemento da +Um dos usos mais comuns de ``take()`` é obter o primeiro elemento na coleção. Um método de atalho para alcançar o mesmo objetivo é usar o método ``first()``:: $collection = new Collection([5, 4, 3, 2]); - $collection->first(); // Returns 5 + $collection->first(); // Retorna 5 .. php:method:: last() -Da mesma forma, você pode obter o último elemento de uma coleção usando o -método ``last()``:: +Da mesma forma, você pode obter o último elemento de uma coleção usando o método ``last()``:: $collection = new Collection([5, 4, 3, 2]); $collection->last(); // Retorna 2 -Expansão de Coleções +Expandindo Coleções -------------------- .. php:method:: append(array|Traversable $items) -Você pode compor várias coleções em uma única. Isso permite coletar dados de -várias fontes, concatená-los e aplicar outras funções de coleta de maneira -muito suave. O método ``append()`` retornará uma nova coleção contendo os -valores das duas fontes:: +Você pode compor várias coleções em uma única. Isso permite que você +reúna dados de várias fontes, concatene-os e aplique outras funções de coleção +a eles de forma muito suave. O método ``append()`` retornará uma nova +coleção contendo os valores de ambas as fontes:: $cakephpTweets = new Collection($tweets); $myTimeline = $cakephpTweets->append($phpTweets); - // Tweets contendo cakefest de ambas as fontes + // Tweets contendo `cakefest` de ambas as fontes $myTimeline->filter(function ($tweet) { return strpos($tweet, 'cakefest'); }); .. php:method:: appendItem($value, $key) -Permite anexar um item com uma chave opcional à coleção. Se você especificar -uma chave que já existe na coleção, o valor não será substituído:: +Permite adicionar um item com uma chave opcional à coleção. Se você +especificar uma chave que já existe na coleção, o valor não será +sobrescrito:: $cakephpTweets = new Collection($tweets); $myTimeline = $cakephpTweets->appendItem($newTweet, 99); .. php:method:: prepend($items) -O método ``prepend()`` retornará uma nova coleção contendo os valores das duas fontes:: +O método ``prepend()`` retornará uma nova coleção contendo os valores de +ambas as fontes:: $cakephpTweets = new Collection($tweets); $myTimeline = $cakephpTweets->prepend($phpTweets); .. php:method:: prependItem($value, $key) -Permite anexar um item com uma chave opcional à coleção. Se você especificar uma -chave que já existe na coleção, o valor não será substituído:: +Permite adicionar um item ao início com uma chave opcional à coleção. Se você +especificar uma chave que já existe na coleção, o valor não será +sobrescrito:: $cakephpTweets = new Collection($tweets); $myTimeline = $cakephpTweets->prependItem($newTweet, 99); .. warning:: - Ao anexar de fontes diferentes, você pode esperar que algumas chaves de - ambas as coleções sejam iguais. Por exemplo, ao anexar duas matrizes simples. - Isso pode apresentar um problema ao converter uma coleção em uma matriz usando - ``toArray()``. Se você não deseja que os valores de uma coleção substituam os - outros na coleção anterior com base em sua chave, certifique-se de chamar - ``toList()`` para soltar as chaves e preservar todos os valores. + Ao anexar de diferentes fontes, você pode esperar algumas chaves de ambas + as coleções serem as mesmas. Por exemplo, ao anexar dois arrays simples. + Isso pode apresentar um problema ao converter uma coleção em um array usando + ``toArray()``. Se você não quiser que valores de uma coleção substituam + outros na anterior com base em sua chave, certifique-se de chamar + ``toList()`` para descartar as chaves e preservar todos os valores. -Elementos de Modificação ------------------------- +Modificando Elementos +---------------------- .. php:method:: insert($path, $items) -Às vezes, você pode ter dois conjuntos de dados separados que gostaria de -inserir os elementos de um conjunto em cada um dos elementos do outro conjunto. -Este é um caso muito comum quando você busca dados de uma fonte que não -oferece suporte à mesclagem de dados ou se une nativamente. +Às vezes, você pode ter dois conjuntos separados de dados que gostaria de inserir +os elementos de um conjunto em cada um dos elementos do outro conjunto. Este é +um caso muito comum quando você busca dados de uma fonte de dados que não suporta +mesclagem de dados ou junções nativamente. -As coleções oferecem um método ``insert()`` que permitirá inserir cada um dos +Coleções oferecem um método ``insert()`` que permitirá inserir cada um dos elementos em uma coleção em uma propriedade dentro de cada um dos elementos de outra coleção:: @@ -968,26 +991,25 @@ outra coleção:: ]; $merged = (new Collection($users))->insert('skills', $languages); + $result = $merged->toArray(); -Quando convertida em uma matriz, a coleção ``$merged`` ficará assim:: - + // $result contém [ ['username' => 'mark', 'skills' => ['PHP', 'Python', 'Ruby']], ['username' => 'juan', 'skills' => ['Bash', 'PHP', 'Javascript']], ['username' => 'jose', 'skills' => ['Javascript', 'Prolog']] ]; -O primeiro parâmetro para o método ``insert()`` é um caminho de propriedades -separado por pontos a seguir para que os elementos possam ser inseridos nessa -posição. O segundo argumento é qualquer coisa que possa ser convertida em um -objeto de coleção. +O primeiro parâmetro para o método ``insert()`` é um caminho separado por pontos de +propriedades a seguir para que os elementos possam ser inseridos nessa posição. O +segundo argumento é qualquer coisa que possa ser convertida em um objeto de coleção. -Observe que os elementos são inseridos pela posição em que foram encontrados, -portanto, o primeiro elemento da segunda coleção é mesclado no primeiro elemento -da primeira coleção. +Por favor, observe que os elementos são inseridos pela posição em que são encontrados, portanto, +o primeiro elemento da segunda coleção é mesclado no primeiro +elemento da primeira coleção. -Se não houver elementos suficientes na segunda coleção para inserir na primeira, -a propriedade target será preenchida com valores ``null``:: +Se não houver elementos suficientes na segunda coleção para inserir na +primeira, então a propriedade de destino não estará presente:: $languages = [ ['PHP', 'Python', 'Ruby'], @@ -995,28 +1017,30 @@ a propriedade target será preenchida com valores ``null``:: ]; $merged = (new Collection($users))->insert('skills', $languages); + $result = $merged->toArray(); - // Irá fornecer + // $result contém [ ['username' => 'mark', 'skills' => ['PHP', 'Python', 'Ruby']], ['username' => 'juan', 'skills' => ['Bash', 'PHP', 'Javascript']], - ['username' => 'jose', 'skills' => null] + ['username' => 'jose'] ]; -O método ``insert()`` pode operar elementos ou objetos da matriz -implementando a interface ``ArrayAccess``. +O método ``insert()`` pode operar elementos de array ou objetos que implementam a +interface ``ArrayAccess``. -Tornando Reutilizáveis os Métodos de Coleta -------------------------------------------- +Tornando os Métodos de Coleção Reutilizáveis +--------------------------------------------- -Usar closures para métodos de coleta é ótimo quando o trabalho a ser feito é -pequeno e focado, mas pode ficar confuso muito rapidamente. Isso se torna mais -óbvio quando muitos métodos diferentes precisam ser chamados ou quando o comprimento -dos métodos de closures é superior a apenas algumas linhas. +Usar closures para métodos de coleção é ótimo quando o trabalho a ser feito é pequeno +e focado, mas pode ficar confuso muito rapidamente. Isso se torna mais óbvio quando +muitos métodos diferentes precisam ser chamados ou quando o comprimento do closure +dos métodos é mais do que apenas algumas linhas. -Também existem casos em que a lógica usada para os métodos de coleta pode ser reutilizada -em várias partes do seu aplicativo. É recomendável considerar a extração de lógica de -coleção complexa para separar classes. Por exemplo, imagine uma closure longa como esta:: +Também há casos em que a lógica usada para os métodos de coleção pode ser +reutilizada em várias partes de sua aplicação. É recomendável que você +considere extrair lógica de coleção complexa para classes separadas. Por exemplo, +imagine um closure longo como este:: $collection ->map(function ($row, $key) { @@ -1053,16 +1077,15 @@ Isso pode ser refatorado criando outra classe:: } } - // Usa a lógica em sua chamada de map() + // Use a lógica em sua chamada map() $collection->map(new TotalOrderCalculator) .. php:method:: through($callback) -Às vezes, uma cadeia de chamadas de método de coleção pode se tornar reutilizável -em outras partes do seu aplicativo, mas apenas se elas forem chamadas nessa ordem -específica. Nesses casos, você pode usar ``through()`` em combinação com uma -classe implementando ``__invoke`` para distribuir suas chamadas úteis de -processamento de dados:: +Às vezes, uma cadeia de chamadas de métodos de coleção pode se tornar reutilizável em outras partes +de sua aplicação, mas apenas se elas forem chamadas nessa ordem específica. Em +esses casos, você pode usar ``through()`` em combinação com uma classe implementando +``__invoke`` para distribuir suas chamadas úteis de processamento de dados:: $collection ->map(new ShippingCostCalculator) @@ -1071,8 +1094,8 @@ processamento de dados:: ->buffered() ... -As chamadas de método acima podem ser extraídas para uma nova classe, para que -não precisem ser repetidas sempre:: +As chamadas de método acima podem ser extraídas para uma nova classe para que não precisem +ser repetidas toda vez:: class FinalCheckOutRowProcessor { @@ -1087,24 +1110,24 @@ não precisem ser repetidas sempre:: } } - // Agora você pode usar o método through() para chamar todos os métodos de uma só vez + // Agora você pode usar o método through() para chamar todos os métodos de uma vez $collection->through(new FinalCheckOutRowProcessor); Otimizando Coleções -------------------- +-------------------- .. php:method:: buffered() -As coleções geralmente executam a maioria das operações que você cria usando -suas funções de maneira lenta. Isso significa que, embora você possa chamar uma -função, isso não significa que ela seja executada imediatamente. Isso é verdade -para muitas funções nesta classe. A avaliação lenta permite economizar recursos -em situações em que você não usa todos os valores em uma coleção. Você não pode -usar todos os valores quando a iteração parar mais cedo ou quando um caso de exceção/falha -for atingido mais cedo. +Coleções geralmente executam a maioria das operações que você cria usando suas funções de +forma preguiçosa. Isso significa que, mesmo que você possa chamar uma função, isso não +significa que ela seja executada imediatamente. Isso é verdade para muitas funções nesta +classe. A avaliação preguiçosa permite que você economize recursos em situações +onde você não usa todos os valores em uma coleção. Você pode não usar todos os +valores quando a iteração para cedo, ou quando uma exceção/caso de falha é alcançado +cedo. -Além disso, a avaliação lenta ajuda a acelerar algumas operações. Considere -o seguinte exemplo:: +Além disso, a avaliação preguiçosa ajuda a acelerar algumas operações. Considere o +seguinte exemplo:: $collection = new Collection($oneMillionItems); $collection = $collection->map(function ($item) { @@ -1112,15 +1135,15 @@ o seguinte exemplo:: }); $itemsToShow = $collection->take(30); -Se as coleções não tivessem sido preguiçosas, teríamos executado um milhão -de operações, embora desejássemos mostrar apenas 30 elementos. Em vez disso, -nossa operação de mapa foi aplicada apenas aos 30 elementos que usamos. Também -podemos obter benefícios dessa avaliação preguiçosa para coleções menores quando -fazemos mais de uma operação nelas. Por exemplo: chamando ``map()`` duas vezes -e depois ``filter()``. +Se as coleções não fossem preguiçosas, teríamos executado um milhão de operações, +mesmo que quiséssemos mostrar apenas 30 elementos. Em vez disso, nossa operação de map +foi aplicada apenas aos 30 elementos que usamos. Também podemos +derivar benefícios dessa avaliação preguiçosa para coleções menores quando fazemos +mais de uma operação nelas. Por exemplo: chamar ``map()`` duas vezes e +depois ``filter()``. -A avaliação preguiçosa também traz sua desvantagem. Você pode estar executando as -mesmas operações mais de uma vez se otimizar uma coleção prematuramente. Considere +A avaliação preguiçosa também tem suas desvantagens. Você pode estar fazendo as mesmas +operações mais de uma vez se otimizar uma coleção prematuramente. Considere este exemplo:: $ages = $collection->extract('age'); @@ -1133,27 +1156,29 @@ este exemplo:: return $item > 30; }); -Se iterarmos ``youngerThan30`` e ``olderThan30``, infelizmente a coleção -executaria a operação ``extract()`` duas vezes. Isso ocorre porque as coleções -são imutáveis e a operação de extração lenta é feita para os dois filtros. +Se iterarmos tanto ``youngerThan30`` quanto ``olderThan30``, a coleção +infelizmente executaria a operação ``extract()`` duas vezes. Isso ocorre porque +as coleções são imutáveis e a operação de extração preguiçosa seria feita para +ambos os filtros. -Felizmente, podemos superar esse problema com uma única função. Se você planeja -reutilizar os valores de determinadas operações mais de uma vez, é possível -compilar os resultados em outra coleção usando a função ``buffered()``:: +Felizmente, podemos superar esse problema com uma única função. Se você planeja reutilizar +os valores de certas operações mais de uma vez, você pode compilar os resultados +em outra coleção usando a função ``buffered()``:: $ages = $collection->extract('age')->buffered(); $youngerThan30 = ... $olderThan30 = ... -Agora, quando as duas coleções forem iteradas, elas chamarão a operação de extração apenas uma vez. +Agora, quando ambas as coleções forem iteradas, elas chamarão apenas a +operação de extração uma vez. -Tornando as Coleções Rebobináveis ---------------------------------- +Tornando Coleções Rebobináveis +------------------------------- -O método ``buffered()`` também é útil para converter iteradores não-rebobináveis em -coleções que podem ser iteradas mais de uma vez:: +O método ``buffered()`` também é útil para converter iteradores não rebobináveis +em coleções que podem ser iteradas mais de uma vez:: - // Em PHP 5.5+ + // No PHP 5.5+ public function results() { ... @@ -1163,14 +1188,15 @@ coleções que podem ser iteradas mais de uma vez:: } $rewindable = (new Collection(results()))->buffered(); -Coleções de Clonagem --------------------- +Clonando Coleções +------------------ .. php:method:: compile($preserveKeys = true) -Às vezes, você precisa obter um clone dos elementos de outra coleção. Isso é -útil quando você precisa repetir o mesmo conjunto de locais diferentes ao mesmo -tempo. Para clonar uma coleção de outra, use o método ``compile()``:: +Às vezes você precisa obter um clone dos elementos de outra +coleção. Isso é útil quando você precisa iterar o mesmo conjunto de diferentes +lugares ao mesmo tempo. Para clonar uma coleção de outra, use o +método ``compile()``:: $ages = $collection->extract('age')->compile(); @@ -1182,4 +1208,4 @@ tempo. Para clonar uma coleção de outra, use o método ``compile()``:: .. meta:: :title lang=pt: Coleções - :keywords lang=pt: collections, cakephp, append, sort, compile, contains, countBy, each, every, extract, filter, first, firstMatch, groupBy, indexBy, jsonSerialize, map, match, max, min, reduce, reject, sample, shuffle, some, random, sortBy, take, toArray, insert, sumOf, stopWhen, unfold, through + :keywords lang=pt: coleções, cakephp, append, sort, compile, contains, countBy, each, every, extract, filter, first, firstMatch, groupBy, indexBy, jsonSerialize, map, match, max, min, reduce, reject, sample, shuffle, some, random, sortBy, take, toArray, insert, sumOf, stopWhen, unfold, through diff --git a/pt/core-libraries/email.rst b/pt/core-libraries/email.rst index 2dce0dcc7f..2bcbf89986 100644 --- a/pt/core-libraries/email.rst +++ b/pt/core-libraries/email.rst @@ -213,7 +213,7 @@ Ao configurar ajudantes, certifique-se de incluir 'Html' ou ele será removido d ajudantes carregados em seu modelo de email. Se você deseja enviar e-mail usando templates em um plugin, você pode usar a familiar -:term:`sintaxe plugin` para fazer isso:: +:term:`sintaxe de plugin` para fazer isso:: $mailer = new Mailer(); $mailer->viewBuilder()->setTemplate('Blog.new_comment'); diff --git a/pt/core-libraries/events.rst b/pt/core-libraries/events.rst index b965fa5dd3..1b9f878f84 100644 --- a/pt/core-libraries/events.rst +++ b/pt/core-libraries/events.rst @@ -4,9 +4,9 @@ Sistema de Eventos Criar aplicações com facilidade de manutenção é uma ciência e uma arte ao mesmo tempo. É de conhecimento geral que a chave para ter um código de qualidade é fazer objetos desacoplados e coesos ao mesmo tempo. Coesão significa que todos os -metodos e propriedades de uma classe são fortemente relacionados entre classes em sí +métodos e propriedades de uma classe são fortemente relacionados entre classes em si e não estão tentando fazer o trabalho que deveria ser feito por outros objetos, -equanto o desacoplamento é a medida de quão "estranha" uma classe é para objetos +enquanto o desacoplamento é a medida de quão "estranha" uma classe é para objetos externos e o quanto essa classe depende desses objetos. Existem alguns casos onde você precisa se comunicar com outras partes da @@ -32,13 +32,13 @@ View e Helper. Se você já usou um deles, você já está de alguma forma famil eventos no CakePHP. Exemplo de Uso dos Eventos -========================== +=========================== Vamos assumir que você está construindo um plugin de carrinho de compras e gostaria de focar somente na lógica de lidar com o pedido. Você não quer incluir nenhuma lógica de envios, notificação dos usuários ou incrementar/remover um item do estoque. Mas, essas são tarefas importantes para pessoas que vão usar o seu plugin. Se você não -estivesse usando eventos, você poderia tentar implementar isso incluindo Bahaviors no +estivesse usando eventos, você poderia tentar implementar isso incluindo Behaviors no seu Model, ou adicionando Components no seu Controller. Fazer isso é um desvio na maioria das vezes, já que você teria que adicionar código para carregar externamente esses Behaviors, ou adicionar hooks ao Controller do seu plugin. @@ -61,10 +61,10 @@ o Model Orders limpo você poderia usar eventos:: { if ($this->save($order)) { $this->Cart->remove($order); - $event = new Event('Model.Order.afterPlace', $this, [ + $event = new Event('Order.afterPlace', $this, [ 'order' => $order ]); - $this->eventManager()->dispatch($event); + $this->getEventManager()->dispatch($event); return true; } @@ -73,16 +73,13 @@ o Model Orders limpo você poderia usar eventos:: } } -.. deprecated:: 3.5.0 - Use ``getEventManager()``. - O exemplo acima permite você notificar outras partes da aplicação em que um pedido foi feito e você pode então, enviar emails, notificações, atualizar o estoque, fazer o log das estatísticas relevantes e outras tarefas em um objeto separado que foca nessas responsabilidades. -Acessando os Gerenciadores de Evento (Event Menagers) -===================================================== +Acessando os Gerenciadores de Evento (Event Managers) +====================================================== No CakePHP os eventos são disparados para os gerenciadores de evento (event managers). Gerenciadores de evento disponíveis estão em todas as Table, View e @@ -90,37 +87,37 @@ Controller, utilizando ``getEventManager()``:: $events = $this->getEventManager(); -Cada Model tem o seu próprio gerenciador de evento, enquando View e Controller -compartilham o mesmo, Isso permite que os eventos dos Models sejam isolados, e +Cada Model tem o seu próprio gerenciador de evento, enquanto View e Controller +compartilham o mesmo. Isso permite que os eventos dos Models sejam isolados, e permitem os Components ou Controller reagirem a eventos criados na View, caso necessário. Gerenciador de Eventos Global ------------------------------ +------------------------------ Adicionado aos gerenciadores de evento no nível da instância, o CakePHP provê um gerenciador de evento global, que permite ouvir a qualquer evento disparado pela -aplicação. isso é útil quando anexar Ouvintes a uma instancia pode ser incômodo ou +aplicação. Isso é útil quando anexar Ouvintes a uma instância pode ser incômodo ou difícil. O gerenciador de eventos global é um singleton de :php:class:`Cake\\Event\\EventManager`. Ouvintes anexados ao gerenciador de eventos global são executados antes dos Ouvintes de instâncias com a mesma prioridade. -você pode acessar o gerenciador de eventos glocal utilizando o metodo estático:: +Você pode acessar o gerenciador de eventos global utilizando o método estático:: // Em qualquer arquivo de configuração ou arquivo que seja executado *antes* do evento use Cake\Event\EventManager; EventManager::instance()->on( - 'Model.Order.afterPlace', + 'Order.afterPlace', $aCallback ); Uma coisa que deve ser levada em conta é que existem eventos com o mesmo nome, mas com assuntos divergentes, então verificar se o evento é requerido em qualquer função -que é anexada globalmente, desse modo, evitando bugs, lembre-se que com a +que é anexada globalmente, desse modo, evitando bugs. Lembre-se que com a flexibilidade de um gerenciador de evento global, uma certa complexidade é adicionada. -O metodo :php:meth:`Cake\\Event\\EventManager::dispatch()` aceita o objeto do evento -como um argumento, e notifica a todos os Ouvintes e Callbacks parando esse objeto +O método :php:meth:`Cake\\Event\\EventManager::dispatch()` aceita o objeto do evento +como um argumento, e notifica a todos os Ouvintes e Callbacks passando esse objeto adiante. Os Ouvintes vão lidar com toda a lógica extra ligada ao evento ``afterPlace``, você pode, enviar emails, atualizar estatísticas do usuário em objetos separados, ou também delegar isso para tarefas offline que você @@ -156,6 +153,43 @@ ouvir. Cada camada do CakePHP emite um evento que você pode utilizar na sua apl * :ref:`Controller events ` * :ref:`View events ` +``Server.terminate`` +-------------------- + +O evento ``Server.terminate`` é disparado após a resposta ter sido enviada ao +cliente. Esse evento é útil para executar tarefas que devem ser feitas após a +resposta ter sido enviada, como envio de emails ou logging. + +Você pode ouvir esse evento usando uma instância do gerenciador de eventos:: + + use Cake\Event\EventManager; + + EventManager::instance()->on('Server.terminate', function ($event) { + // Executar tarefas que devem ser feitas após a resposta ter sido + // enviada ao cliente. + }); + +Ou usando o hook ``events`` na sua classe Application/Plugin:: + + use Cake\Event\EventManagerInterface; + + public function events(EventManagerInterface $eventManager): EventManagerInterface + { + $eventManager->on('Server.terminate', function ($event) { + // Executar tarefas que devem ser feitas após a resposta ter sido + // enviada ao cliente. + }); + + return $eventManager; + } + +.. tip:: + Isso é chamado mesmo se uma exceção for lançada durante a requisição, por exemplo em páginas 404. + +.. note:: + O evento ``Server.terminate`` apenas funciona para implementações PHP-FPM que + suportam a função ``fastcgi_finish_request``. + .. _registering-event-listeners: Registrando Listeners @@ -164,54 +198,81 @@ Registrando Listeners Listeners são o meio preferido para registrar callbacks de qualquer evento. Isso é feito implementando a interface :php:class:`Cake\\Event\\EventListenerInterface` em qualquer classe que você deseje registrar um callback. Classes implementando a -interface devem ter o metodo ``implementedEvents()``. Esse método deve retornar um +interface devem ter o método ``implementedEvents()``. Esse método deve retornar um array associativo com o nome de todos os eventos que a classe vai gerenciar. -Para continuar o exemplo anterior, vamos imaginas que temos uma classe UserStatistic +Para continuar o exemplo anterior, vamos imaginar que temos uma classe UserStatistic responsável por calcular o histórico de compras do usuário, e compilar nas estatísticas globais do site. Esse é um ótimo exemplo de onde usar uma classe Listener. Fazendo isso permite você se concentrar nas lógica das estatísticas em um -local e responder ao eventos como necessários. Nosso listener ``UserStatistics`` pode +local e responder aos eventos como necessários. Nosso listener ``UserStatistics`` pode começar como abaixo:: + namespace App\Event; + use Cake\Event\EventListenerInterface; class UserStatistic implements EventListenerInterface { - public function implementedEvents() + public function implementedEvents(): array { return [ - 'Model.Order.afterPlace' => 'updateBuyStatistic', + // Nomes de eventos personalizados permitem projetar seus eventos de aplicação + // conforme necessário. + 'Order.afterPlace' => 'updateBuyStatistic', ]; } - public function updateBuyStatistic($event, $order) + public function updateBuyStatistic($event) { // Código para atualizar as estatísticas - - // Code to update statistics } } - // Anexa o objeto UserStatistic para o gerenciador de evento da Order + // Do seu controller, anexe o objeto UserStatistic ao gerenciador de evento da Order $statistics = new UserStatistic(); $this->Orders->getEventManager()->on($statistics); -Como você pôde ver nó código acima, o metodo ``on()`` aceita instancias da interface -``EventListener``. Internamente o gerenciador de eventos vai utilizar os +Como você pôde ver no código acima, o método ``on()`` aceita instâncias da interface +``EventListener``. Internamente o gerenciador de eventos vai utilizar ``implementedEvents()`` para anexar ao callback corretamente. +.. versionadded:: 5.1.0 + O hook ``events`` foi adicionado ao ``BaseApplication`` assim como à classe ``BasePlugin`` + +A partir do CakePHP 5.1 é recomendado registrar event listeners adicionando-os através do hook ``events`` na sua classe de aplicação ou plugin:: + + namespace App; + + use App\Event\UserStatistic; + use Cake\Event\EventManagerInterface; + use Cake\Http\BaseApplication; + + class Application extends BaseApplication + { + // O resto da sua classe Application + + public function events(EventManagerInterface $eventManager): EventManagerInterface + { + $statistics = new UserStatistic(); + $eventManager->on($statistics); + + return $eventManager; + } + } + Registrando Listeners Anônimos ------------------------------- +------------------------------- -Enquanto objeto de Event Listerners são geralmente um melhor método para implementar -Listeners você pode utilizar uma ``callable`` como Event Listener. Por exemplo, se nós -quisessemos colocar qualquer pedido nos arquivos de log, nós poderiamos utilizar +Enquanto objetos event listener são geralmente um melhor método para implementar +Listeners você pode utilizar qualquer ``callable`` como Event Listener. Por exemplo, se nós +quiséssemos colocar qualquer pedido nos arquivos de log, nós poderíamos utilizar uma função anônima para isso:: use Cake\Log\Log; - $this->Orders->getEventManager()->on('Model.Order.afterPlace', function ($event) { + // Do controller, ou durante o bootstrap da aplicação. + $this->Orders->getEventManager()->on('Order.afterPlace', function ($event) { Log::write( 'info', 'A new order was placed with id: ' . $event->getSubject()->id @@ -225,30 +286,27 @@ Além de funções anônimas você pode usar qualquer outro callable no qual o P 'inventory' => [$this->InventoryManager, 'decrement'], ]; foreach ($events as $callable) { - $eventManager->on('Model.Order.afterPlace', $callable); + $eventManager->on('Order.afterPlace', $callable); } -Quando trabalhamos com plugins que não dispara eventos especificos, você pode -utilizar Event Listeners dos eventos padrão. Vamos pensar, por exemplo o plugin +Quando trabalhamos com plugins que não disparam eventos específicos, você pode +utilizar Event Listeners dos eventos padrão. Vamos pensar, por exemplo, o plugin 'UserFeedback' que lida com o feedback dos usuários. A partir da sua aplicação, você poderia querer saber quando um feedback foi salvo no banco de dados e intervir nele. -Você pode utilizar o gerenciador de eventos global para pegar o evento -``Model.afterSave``. No entendo, você pode pegar um caminho mais direto. e escutar -somente o que você realmente precisa:: +Você pode utilizar o evento global ``Model.afterSave``. No entanto, você pode pegar um +caminho mais direto e escutar somente o que você realmente precisa:: // Você pode criar o código a seguir antes de persistir os dados no banco // exemplo no config/bootstrap.php - - use Cake\ORM\TableRegistry; + use Cake\Datasource\FactoryLocator; // Se está enviando emails use Cake\Mailer\Email; - TableRegistry::getTableLocator()->get('ThirdPartyPlugin.Feedbacks') + FactoryLocator::get('Table')->get('ThirdPartyPlugin.Feedbacks') ->getEventManager() ->on('Model.afterSave', function($event, $entity) { - // Por exemplo, podemos mandar um email para o admin - // Antes da versão 3.4 use os métodos from()/to()/subject() + // Por exemplo, podemos mandar um email para o admin $email = new Email('default'); $email->setFrom(['info@yoursite.com' => 'Your Site']) ->setTo('admin@yoursite.com') @@ -272,11 +330,11 @@ evento específico pode ser usada como base de alguma ação:: // Em algum outro local da sua aplicação. $events = $this->getEventManager()->matchingListeners('Verification'); if (!empty($events)) { - // Executa a lógica relacionada a precença do Event Listener 'Verification'. + // Executa a lógica relacionada à presença do Event Listener 'Verification'. // Por exemplo, remover o Listener caso esteja presente. $this->getEventManager()->off('User.Verification'); } else { - // Executa a lógica relacionada a ausencia do event listener 'Verification' + // Executa a lógica relacionada à ausência do event listener 'Verification' } .. note:: @@ -285,18 +343,18 @@ evento específico pode ser usada como base de alguma ação:: .. _event-priorities: Estabelecendo Prioridades -------------------------- +-------------------------- Em alguns casos você pode querer controlar a ordem em que os Listeners são invocados, por exemplo, se nós voltarmos ao nosso exemplo das estatísticas do usuários. Seria ideal se esse Listener fosse chamado no final da pilha. Ao chamar no -final do pilha de ouvintes, nós garantimos que o evento não foi cancelado e que, +final da pilha de ouvintes, nós garantimos que o evento não foi cancelado e que nenhum outro listeners retornou exceptions. Nós podemos também pegar o estado final dos objetos, no caso de outros ouvintes possam terem modificado o objeto de assunto ou do evento. Prioridades são definidas como inteiros (integer) quando adicionadas ao ouvinte. -Quando maior for o número, mais tarde esse metodo será disparado. A prioridade padrão +Quanto maior for o número, mais tarde esse método será disparado. A prioridade padrão para todos os listeners é ``10``. Se você precisa que o seu método seja executado antes, utilize um valor menor que o padrão. Por outro lado se você deseja rodar o seu callback depois dos outros, usando um número acima de ``10`` será suficiente. @@ -309,7 +367,7 @@ os Event Listeners:: // Definindo a prioridade para um callback $callback = [$this, 'doSomething']; $this->getEventManager()->on( - 'Model.Order.afterPlace', + 'Order.afterPlace', ['priority' => 2], $callback ); @@ -317,10 +375,10 @@ os Event Listeners:: // Definindo a prioridade para um Listener class UserStatistic implements EventListenerInterface { - public function implementedEvents() + public function implementedEvents(): array { return [ - 'Model.Order.afterPlace' => [ + 'Order.afterPlace' => [ 'callable' => 'updateBuyStatistic', 'priority' => 100 ], @@ -329,51 +387,51 @@ os Event Listeners:: } Como você pôde ver, a principal diferença entre objetos ``EventListener`` é que você -precisa usar uma array para especificar o metodo callable e a preferência de +precisa usar um array para especificar o método callable e a preferência de prioridade. A chave ``callable`` é uma array especial que o gerenciador vai ler para saber qual função na classe ele deverá chamar. Obtendo Dados do Evento como Argumentos da Função -------------------------------------------------- +-------------------------------------------------- Quando eventos tem dados definidos no seu construtor, esses dados são convertidos em -argumentos para os ouvintes. Um exemplo da camada ViewView é o afterRender callback:: +argumentos para os ouvintes. Um exemplo da camada View é o afterRender callback:: $this->getEventManager() ->dispatch(new Event('View.afterRender', $this, ['view' => $viewFileName])); Os ouvintes do callback ``View.afterRender`` devem ter a seguinte assinatura:: - function (Event $event, $viewFileName) + function (EventInterface $event, $viewFileName) Cada valor fornecido ao construtor Event será convertido em parâmetros de função na ordem em que aparecem na matriz de dados. Se você usar uma matriz associativa, o resultado ``array_values`` determinará a ordem dos argumentos da função. .. note:: - Diferente do CakePHP 2.x, converter dados para os arqumentos do listener é o + Diferente do CakePHP 2.x, converter dados para os argumentos do listener é o comportamento padrão e não pode ser desativado. Disparando Eventos ================== -Uma vez que você tem uma instancia do event manager você pode disparar eventos +Uma vez que você tem uma instância do event manager você pode disparar eventos utilizando :php:meth:`~Cake\\Event\\EventManager::dispatch()`. Esse método aceita uma -instancia da class :php:class:`Cake\\Event\\Event`. Vamos ver como disparar um evento:: +instância da classe :php:class:`Cake\\Event\\Event`. Vamos ver como disparar um evento:: // Um event listener tem que ser instanciado antes de disparar um evento. // Crie um evento e dispare ele. - $event = new Event('Model.Order.afterPlace', $this, [ + $event = new Event('Order.afterPlace', $this, [ 'order' => $order ]); $this->getEventManager()->dispatch($event); :php:class:`Cake\\Event\\Event` aceita três argumentos no seu construtor. O primeiro é o nome do evento, você deve tentar manter esse nome o mais único possível, ainda assim, -deve ser de fácil entendimento . Nós sugerimos a seguinte convenção: +deve ser de fácil entendimento. Nós sugerimos a seguinte convenção: ``Camada.nomeDoEvento`` para eventos acontecendo a nível de uma camada (ex. ``Controller.startup``, ``View.beforeRender``) e ``Camada.Classe.NomeDoEvento`` para -eventos que acontecen em uma classe especifica em uma camada, exemplo +eventos que acontecem em uma classe específica em uma camada, exemplo ``Model.User.afterRegister`` ou ``Controller.Courses.invalidAccess``. O segundo argumento é o ``subject``, ou seja, o objeto associado ao evento, geralmente @@ -387,7 +445,7 @@ evento. Esses dados podem ser qualquer coisa que você considere útil enviar ao listeners. Enquanto esse argumento pode ser de qualquer tipo, nós recomendamos que seja uma array associativa. -O medoto :php:meth:`~Cake\\Event\\EventManager::dispatch()` aceita um objeto de +O método :php:meth:`~Cake\\Event\\EventManager::dispatch()` aceita um objeto de evento como argumento e notifica a todos os listeners inscritos. .. _stopping-events: @@ -397,8 +455,8 @@ Parando Eventos Assim como nos eventos do DOM, você pode querer parar um evento para previnir que outros listeners sejam notificados. Você pode ver isso em ação nos Callbacks do model -(ex. beforeSave) onde é possível parar o operação de persistir os dados se o código -decidir que não pode continuar +(ex. beforeSave) onde é possível parar a operação de persistir os dados se o código +decidir que não pode continuar. Para parar um evento você pode retornar ``false`` nos seus callbacks ou chamar o método ``stopPropagation()`` no objeto do evento:: @@ -415,17 +473,17 @@ chamar o método ``stopPropagation()`` no objeto do evento:: $event->stopPropagation(); } -Parar um evento vai previnir que qualquer callback adicional seja chamado. +Parar um evento vai prevenir que qualquer callback adicional seja chamado. Além disso o código que disparou o evento pode se comportar de maneira diferente baseado no evento sendo parado ou não. Geralmente não faz sentido parar 'depois' do evento, mas parar 'antes' do evento costuma ser usado para impedir toda a operação de acontecer. -Para verificar se um evento foi parado você pode chamar o metodo ``isStopped()`` no -objeto do evento object:: +Para verificar se um evento foi parado você pode chamar o método ``isStopped()`` no +objeto do evento:: public function place($order) { - $event = new Event('Model.Order.beforePlace', $this, ['order' => $order]); + $event = new Event('Order.beforePlace', $this, ['order' => $order]); $this->getEventManager()->dispatch($event); if ($event->isStopped()) { return false; @@ -439,13 +497,13 @@ objeto do evento object:: No exemplo anterior o pedido não será salvo se o evento for parado durante o processamento do callback ``beforePlace``. -Parando o Resultado de um Evento --------------------------------- +Obtendo o Resultado de um Evento +--------------------------------- Toda vez que um callback retorna um valor não nulo ou não falso, ele é armazenado na propriedade ``$result`` do objeto do evento. Isso é útil quando você quer permitir -callbacks a modificar a execução do evento. Vajamos novamente nosso exemplo -``beforePlace``e vamos deixar os callbacks modififcar os dados de ``$order``. +callbacks a modificar a execução do evento. Vejamos novamente nosso exemplo +``beforePlace`` e vamos deixar os callbacks modificarem os dados de ``$order``. Resultados de eventos podem ser alterados utilizando o resultado do objeto do evento diretamente ou retornando o valor no próprio callback:: @@ -458,6 +516,7 @@ diretamente ou retornando o valor no próprio callback:: return $alteredData; } + // Outro callback public function doSomethingElse($event) { @@ -468,7 +527,7 @@ diretamente ou retornando o valor no próprio callback:: // Utilizando o resultado do evento public function place($order) { - $event = new Event('Model.Order.beforePlace', $this, ['order' => $order]); + $event = new Event('Order.beforePlace', $this, ['order' => $order]); $this->getEventManager()->dispatch($event); if (!empty($event->getResult()['order'])) { $order = $event->getResult()['order']; @@ -485,8 +544,8 @@ como dados ou resultado de eventos e alterar diretamente o objeto é a melhor solução, pois a referência é mantida a mesma e as modificações são compartilhadas em todas as chamadas de retorno de chamada. -Removento Callbacks e Ouvintes ------------------------------- +Removendo Callbacks e Ouvintes +------------------------------- Se por qualquer motivo você desejar remover os callbacks do gerenciador de eventos é só chamar o método :php:meth:`Cake\\Event\\EventManager::off()` utilizando como argumentos os @@ -512,7 +571,7 @@ dois primeiros parâmetros usados para anexá-lo:: // Removendo uma única chave de um evento em um ouvinte $this->getEventManager()->off('My.event', $listener); - // Removento todos os callbacks implemantados por um ouvinte + // Removendo todos os callbacks implementados por um ouvinte $this->getEventManager()->off($listener); Eventos são uma ótima maneira de separar responsabilidades na sua aplicação e fazer @@ -526,6 +585,7 @@ Leitura Adicional ================= * :doc:`/orm/behaviors` +* :doc:`/console-commands/commands` * :doc:`/controllers/components` * :doc:`/views/helpers` * :ref:`testing-events` diff --git a/pt/core-libraries/form.rst b/pt/core-libraries/form.rst index a51d5051c4..a624168a33 100644 --- a/pt/core-libraries/form.rst +++ b/pt/core-libraries/form.rst @@ -5,18 +5,18 @@ Formulários sem Models .. php:class:: Form -Muitas vezes você precisará ter formulários associados ao :doc:`ORM entities ` -e :doc:`ORM tables ` ou outras persistência de dados, -mas há vezes quando você precisará validar um campo de usuário e então realizar uma -ação se o dado é válido. O exemplo mais comum para esta situação é o formulário de contato. +Na maioria das vezes você terá formulários associados ao :doc:`ORM entities ` +e :doc:`ORM tables ` ou outras persistências de dados, +mas há vezes quando você precisará validar dados de usuário e então realizar uma +ação se os dados forem válidos. O exemplo mais comum para isto é um formulário de contato. Criando o Formulário ==================== -Geralmente quando se usa a classe Form será necessário utilizar uma sub-classe para definir -seu formulário. Isso facilita o teste, e permite o reuso do formulário. Formulários ficam dentro -de **src/Form** e comumente tem ``Form`` como sufixo da classe. Por exemplo, -um simples formulário de contato poderia ficar assim:: +Geralmente quando se usa a classe Form você vai querer usar uma sub-classe para definir +seu formulário. Isso facilita o teste, e permite reusar seu formulário. Formulários ficam dentro +de **src/Form** e comumente têm ``Form`` como sufixo da classe. Por exemplo, +um simples formulário de contato ficaria assim:: // em src/Form/ContactForm.php namespace App\Form; @@ -27,26 +27,22 @@ um simples formulário de contato poderia ficar assim:: class ContactForm extends Form { - - protected function _buildSchema(Schema $schema) + protected function _buildSchema(Schema $schema): Schema { return $schema->addField('name', 'string') ->addField('email', ['type' => 'string']) ->addField('body', ['type' => 'text']); } - protected function _buildValidator(Validator $validator) + public function validationDefault(Validator $validator): Validator { - return $validator->add('name', 'length', [ - 'rule' => ['minLength', 10], - 'message' => 'A name is required' - ])->add('email', 'format', [ - 'rule' => 'email', - 'message' => 'A valid email address is required', - ]); + $validator->minLength('name', 10) + ->email('email'); + + return $validator; } - protected function _execute(array $data) + protected function _execute(array $data): bool { // Envie um email. return true; @@ -57,20 +53,20 @@ No exemplo acima vemos os 3 métodos hooks que o formulário fornece: * ``_buildSchema`` é usado para definir o esquema de dados usado pelo FormHelper para criar um formulário HTML. Você pode definir o tipo de campo, tamanho, e precisão. -* ``_buildValidator`` Pega uma instância do :php:class:`Cake\\Validation\\Validator` - que você pode você juntar com os validadores. -* ``_execute`` permite definir o comportamento desejado quando o - ``execute()`` é chamado e o dado é válido. +* ``validationDefault`` Pega uma instância do :php:class:`Cake\\Validation\\Validator` + que você pode anexar validadores. +* ``_execute`` permite definir o comportamento que você deseja que aconteça quando + ``execute()`` é chamado e os dados são válidos. -Você sempre pode adicionar métodos públicos a sua maneira. +Você sempre pode definir métodos públicos adicionais conforme necessário também. -Processando Requisição de Dados -=============================== +Processando Dados de Requisição +================================ -Uma vez definido o formulário, é possível usá-lo em seu controller para processar -e validar os dados:: +Uma vez que você definiu seu formulário, é possível usá-lo em seu controller para processar +e validar os dados de requisição:: - // No Controller + // Em um controller namespace App\Controller; use App\Controller\AppController; @@ -83,27 +79,40 @@ e validar os dados:: $contact = new ContactForm(); if ($this->request->is('post')) { if ($contact->execute($this->request->getData())) { - $this->Flash->success('We will get back to you soon.'); + $this->Flash->success('Retornaremos o contato em breve.'); } else { - $this->Flash->error('There was a problem submitting your form.'); + $this->Flash->error('Houve um problema ao enviar seu formulário.'); } } $this->set('contact', $contact); } } -No exemplo acima, usamos o método ``execute()`` para chamar o nosso método -``_execute()`` do formulário apenas quando o dado é válido, e definimos as mensagens flash -adequadas. Poderíamos também ter usado o método ``validate()`` apenas para validar -a requisição de dados:: +No exemplo acima, usamos o método ``execute()`` para executar o método +``_execute()`` do nosso formulário apenas quando os dados são válidos, e definimos as mensagens flash +adequadas. Se quisermos usar um conjunto de validação não padrão, podemos usar a +opção ``validate``:: + + if ($contact->execute($this->request->getData(), 'update')) { + // Lidar com sucesso do formulário. + } + +Esta opção também pode ser definida como ``false`` para desabilitar a validação. + +Poderíamos também ter usado o método ``validate()`` apenas para validar +os dados de requisição:: $isValid = $form->validate($this->request->getData()); + // Você também pode usar outros conjuntos de validação. O seguinte + // usaria as regras definidas por `validationUpdate()` + $isValid = $form->validate($this->request->getData(), 'update'); + Definindo os Valores do Formulário -================================== +=================================== -Na sequência para definir os valores para os campos do formulário sem modelo, basta apenas definir -os valores usando ``$this->request->getData()``, como em todos os outros formulários criados pelo FormHelper:: +Você pode definir valores padrão para formulários sem models usando o método ``setData()``. +Valores definidos com este método sobrescreverão dados existentes no objeto de formulário:: // Em um controller namespace App\Controller; @@ -125,38 +134,57 @@ os valores usando ``$this->request->getData()``, como em todos os outros formul } if ($this->request->is('get')) { - //Values from the User Model e.g. - $this->request->getData('name', 'John Doe'); - $this->request->getData('email','john.doe@example.com'); + $contact->setData([ + 'name' => 'John Doe', + 'email' => 'john.doe@example.com' + ]); } $this->set('contact', $contact); } } -Valores devem apenas serem definidos se a requesição é do tipo GET, caso contrário -você sobreescreverá os dados anteriormente passados via POST que de certa forma -poderiam estar incorretos e não salvos. +Valores devem apenas ser definidos se o método de requisição é GET, caso contrário +você sobrescreverá seus dados POST anteriores que podem ter erros de validação +que precisam ser corrigidos. Você pode usar ``set()`` para adicionar ou substituir campos individuais +ou um subconjunto de campos:: -Pegando os Erros do Formulário -============================== + // Definir um campo. + $contact->set('name', 'John Doe'); + + // Definir múltiplos campos; + $contact->set([ + 'name' => 'John Doe', + 'email' => 'john.doe@example.com', + ]); + +Obtendo Erros do Formulário +============================ -Uma vez sido validado, o formulário pode recuperar seus próprios erros:: +Uma vez que um formulário foi validado, você pode recuperar os erros dele:: - $errors = $form->errors(); - /* $errors contains + $errors = $form->getErrors(); + /* $errors contém [ - 'email' => ['A valid email address is required'] + 'name' => ['length' => 'Nome deve ter pelo menos dois caracteres'], + 'email' => ['format' => 'Um endereço de email válido é necessário'], + ] + */ + + $error = $form->getError('email'); + /* $error contém + [ + 'format' => 'Um endereço de email válido é necessário', ] */ Invalidando Campos Individuais do Formulário no Controller -========================================================== +=========================================================== -É possível invalidar campos únicos do controller sem o uso da classe Validator. -O Uso mais comum neste caso é quando a validação -é feita no servidor remoto. Neste caso, você deve manualmente invalidar -os campos de acordo com a resposta do servidor:: +É possível invalidar campos individuais do controller sem o uso da classe Validator. +O uso mais comum para isto é quando a validação +é feita em um servidor remoto. Neste caso, você deve manualmente invalidar +os campos de acordo com o feedback do servidor remoto:: // em src/Form/ContactForm.php public function setErrors($errors) @@ -164,34 +192,33 @@ os campos de acordo com a resposta do servidor:: $this->_errors = $errors; } -Conforme como a classe validadora poderia ter retornado os erros, ``$errors`` +Conforme como a classe validadora teria retornado os erros, ``$errors`` deve estar neste formato:: - ["fieldName" => ["validatorName" => "The error message to display"]] + ['fieldName' => ['validatorName' => 'A mensagem de erro para exibir']] -Agora você pode invalidar os campos determinar o fieldName, e então +Agora você será capaz de invalidar campos do formulário definindo o fieldName, e então definir as mensagens de erro:: // Em um controller $contact = new ContactForm(); - $contact->setErrors(["email" => ["_required" => "Seu email é necessário"]]); + $contact->setErrors(['email' => ['_required' => 'Seu email é necessário']]); -Prossiga para Criação do HTML com o FormHelper para ver o resultado. +Prossiga para Criação do HTML com FormHelper para ver os resultados. Criando o HTML com FormHelper -============================= +============================== -Uma vez sido criado uma class Form, -Once you've created a Form class, você provavelmente vai querer criar um formulário -HTML para isso. FormHelper compreende objetos Form apenas como entidades ORM:: +Uma vez que você criou uma classe Form, você provavelmente vai querer criar um formulário +HTML para ela. FormHelper compreende objetos Form assim como entidades ORM:: echo $this->Form->create($contact); - echo $this->Form->input('name'); - echo $this->Form->input('email'); - echo $this->Form->input('body'); + echo $this->Form->control('name'); + echo $this->Form->control('email'); + echo $this->Form->control('body'); echo $this->Form->button('Submit'); echo $this->Form->end(); -O código acima criar um formulário HTML para o ``ContactForm`` definidos anteriormente. -Formulários HTML criados com FormHelper usará o esquema definido -e validador para determinar os tipos de campos, tamanhos máximos, e validação de erros. +O código acima criaria um formulário HTML para o ``ContactForm`` que definimos anteriormente. +Formulários HTML criados com FormHelper usarão o esquema definido e validador para +determinar os tipos de campos, tamanhos máximos, e erros de validação. diff --git a/pt/core-libraries/global-constants-and-functions.rst b/pt/core-libraries/global-constants-and-functions.rst index ec1507d453..1d84f6574c 100644 --- a/pt/core-libraries/global-constants-and-functions.rst +++ b/pt/core-libraries/global-constants-and-functions.rst @@ -1,203 +1,216 @@ Constantes e Funções #################### -A maior parte do seu trabalho diário com o CakePHP será feito utilizando classes -e métodos do *core*. O CakePHP disponibiliza funções -globais de conveniência que podem ajudar. Muitas dessas funções são usadas em -classes do CakePHP (carregando um *model* ou um *component*), mas outras tornam -mais fácil o trabalho de lidar com *arrays* ou *strings*. +Embora a maior parte do seu trabalho diário no CakePHP seja utilizando classes e +métodos principais, o CakePHP possui várias funções de conveniência globais que podem ser +úteis. Muitas dessas funções são para uso com classes do CakePHP (carregando +classes de modelo ou componente), mas muitas outras tornam o trabalho com arrays ou +strings um pouco mais fácil. -Nós também vamos cobrir algumas das constantes existentes em aplicações CakePHP. -Constantes essas, que facilitam *upgrades* e apontam convenientemente para -arquivos e diretórios chaves da sua aplicação. +Também cobriremos algumas das constantes disponíveis em aplicações CakePHP. Usar +essas constantes ajudará a tornar as atualizações mais suaves, mas também são +formas convenientes de apontar para certos arquivos ou diretórios em sua aplicação CakePHP. -Funções globais -=============== +Funções Globais +================ -Aqui estão as funções disponíveis globalmente no CakePHP. A maioria delas são -*wrappers* de conveniência para funcionalidades do CakePHP, como por exemplo, -*debug* e localização de conteúdo. +Aqui estão as funções globalmente disponíveis do CakePHP. A maioria delas são apenas +wrappers de conveniência para outras funcionalidades do CakePHP, como depuração e +tradução de conteúdo. Por padrão, apenas funções com namespace são carregadas automaticamente, +no entanto, você pode opcionalmente carregar aliases globais adicionando:: + + require CAKE . 'functions.php'; + +No arquivo ``config/bootstrap.php`` da sua aplicação. Fazer isso carregará aliases globais +para *todas* as funções listadas abaixo. + +.. php:namespace:: Cake\I18n .. php:function:: \_\_(string $string_id, [$formatArgs]) - Essa função lida com a localização da sua aplicação. O ``$string_id`` - identifica o ID usado para a tradução. *Strings* são tratadas seguindo o - formato usado no ``sprintf()``. Você pode fornecer argumentos adicionais - para substituir *placeholders* na sua string:: + Esta função lida com a localização em aplicações CakePHP. O + ``$string_id`` identifica o ID de uma tradução. Você pode fornecer + argumentos adicionais para substituir marcadores de posição em sua string:: + + __('You have {0} unread messages', $number); - __('Você tem {0} mensagens', $number); + Você também pode fornecer um array de substituições indexado por nome:: + + __('You have {unread} unread messages', ['unread' => $number]); .. note:: - Verifique a seção - :doc:`/core-libraries/internationalization-and-localization` para mais - informações. + + Confira a seção + :doc:`/core-libraries/internationalization-and-localization` para + mais informações. .. php:function:: __d(string $domain, string $msg, mixed $args = null) - Permite sobrescrever o domínio atual por uma mensagem simples. + Permite que você substitua o domínio atual para uma única pesquisa de mensagem. + + Útil ao internacionalizar um plugin: + ``echo __d('plugin_name', 'This is my plugin');`` + + .. note:: - Muito útil ao localizar um *plugin*: - ``echo __d('PluginName', 'Esse é meu plugin');`` + Certifique-se de usar a versão sublinhada do nome do plugin aqui como domínio. .. php:function:: __dn(string $domain, string $singular, string $plural, integer $count, mixed $args = null) - Permite sobrescrever o domínio atual por uma mensagem no plural. Retorna - a forma corrreta da mensagem no plural identificada por ``$singular`` e - ``$plural``, pelo contador ``$count`` e pelo domínio ``$domain``. + Permite que você substitua o domínio atual para uma única pesquisa de mensagem + no plural. Retorna a forma plural correta da mensagem identificada por ``$singular`` + e ``$plural`` para a contagem ``$count`` do domínio ``$domain``. .. php:function:: __dx(string $domain, string $context, string $msg, mixed $args = null) - Permite sobrescrever o domínio atual por uma mensagem simples. Também - permite a especificação de um contexto. + Permite que você substitua o domínio atual para uma única pesquisa de mensagem. Também + permite que você especifique um contexto. - O contexto é um identificador único para as *strings* de tradução que a - tornam únicas sob um mesmo domínio. + O contexto é um identificador único para a string de traduções que a torna + única dentro do mesmo domínio. .. php:function:: __dxn(string $domain, string $context, string $singular, string $plural, integer $count, mixed $args = null) - Permite sobrescrever o domínio atual por uma mensagem no plural. Também - permite a especificação de um contexto. Retorna a forma corrreta - da mensagem no plural identificada por ``$singular`` e - ``$plural``, pelo contador ``$count`` e pelo domínio ``$domain``. Alguns - idiomas tem mais de uma forma para o plural dependendo do contador. + Permite que você substitua o domínio atual para uma única pesquisa de mensagem + no plural. Também permite que você especifique um contexto. Retorna a forma plural + correta da mensagem identificada por ``$singular`` e ``$plural`` para a contagem + ``$count`` do domínio ``$domain``. Algumas línguas têm mais de uma forma + para mensagens no plural dependentes da contagem. - O contexto é um identificador único para as *strings* de tradução que a - tornam únicas sob um mesmo domínio. + O contexto é um identificador único para a string de traduções que a torna + única dentro do mesmo domínio. .. php:function:: __n(string $singular, string $plural, integer $count, mixed $args = null) - Retorna a forma corrreta da mensagem no plural identificada por - ``$singular`` e ``$plural``, pelo contador ``$count`` e pelo domínio - ``$domain``. Alguns idiomas tem mais de uma forma para o plural dependendo - do contador. + Retorna a forma plural correta da mensagem identificada por ``$singular`` e + ``$plural`` para a contagem ``$count``. Algumas línguas têm mais de uma forma para + mensagens no plural dependentes da contagem. .. php:function:: __x(string $context, string $msg, mixed $args = null) - O contexto é um identificador único para as *strings* de tradução que a - tornam únicas sob um mesmo domínio. + O contexto é um identificador único para a string de traduções que a torna + única dentro do mesmo domínio. .. php:function:: __xn(string $context, string $singular, string $plural, integer $count, mixed $args = null) - Retorna a forma corrreta da mensagem no plural identificada por - ``$singular`` e ``$plural``, pelo contador ``$count`` e pelo domínio - ``$domain``. Alguns idiomas tem mais de uma forma para o plural dependendo - do contador. + Retorna a forma plural correta da mensagem identificada por ``$singular`` e + ``$plural`` para a contagem ``$count`` do domínio ``$domain``. Também permite que você + especifique um contexto. Algumas línguas têm mais de uma forma para + mensagens no plural dependentes da contagem. + + O contexto é um identificador único para a string de traduções que a torna + única dentro do mesmo domínio. - O contexto é um identificador único para as *strings* de tradução que a - tornam únicas sob um mesmo domínio. +.. php:namespace:: Cake\Collection .. php:function:: collection(mixed $items) - *Wrapper* de conveniência para instanciar um novo objeto - :php:class:`Cake\\Collection\\Collection`, re-passando o devido argumento. - O parâmetro ``$items`` recebe tanto um objeto ``Traversable`` quanto um - *array*. + Wrapper de conveniência para instanciar um novo objeto :php:class:`Cake\\Collection\\Collection`, + envolvendo o argumento passado. O parâmetro ``$items`` aceita tanto + um objeto ``Traversable`` quanto um array. + +.. php:namespace:: Cake\Core .. php:function:: debug(mixed $var, boolean $showHtml = null, $showFrom = true) - .. versionchanged:: 3.3.0 - Esse método retorna a ``$var`` passada para que você possa, por - instância, colocá-la em uma declaração de retorno. + Se a variável ``$debug`` do núcleo for ``true``, ``$var`` é impresso. + Se ``$showHTML`` for ``true`` ou deixado como ``null``, os dados são renderizados para serem + amigáveis ao navegador. Se ``$showFrom`` não for definido como ``false``, a saída de depuração + começará com a linha de onde foi chamada. Veja também + :doc:`/development/debugging` - Se a variável do core ``$debug`` for ``true``, ``$var`` será imprimida. - Se ``$showHTML`` for ``true``, ou for deixada como ``null`` os dados serão - renderizados formatados para melhor exibição em navegadores. Se - ``$showFrom`` não for definida como ``false``, o *debug* começará a partir - da linha em que foi chamado. Também veja :doc:`/development/debugging` +.. php:function:: dd(mixed $var, boolean $showHtml = null) -.. php:function:: pr(mixed $var) + Comporta-se como ``debug()``, mas a execução também é interrompida. + Se a variável ``$debug`` do núcleo for ``true``, ``$var`` é impresso. + Se ``$showHTML`` for ``true`` ou deixado como ``null``, os dados são renderizados para serem + amigáveis ao navegador. Veja também :doc:`/development/debugging` - .. versionchanged:: 3.3.0 - Chamar esse método vai retornar a ``$var`` passada, então, você pode, - por instância, colocá-la em uma declaração de retorno. +.. php:function:: pr(mixed $var) - *Wrapper* de conveniência para ``print_r()`` com a adição das *tags* - ``
`` ao redor da saída.
+    Wrapper de conveniência para ``print_r()``, com a adição de
+    envolver tags ``
`` ao redor da saída.
 
 .. php:function:: pj(mixed $var)
 
-    .. versionchanged:: 3.3.0
-        Chamar esse método vai retornar a ``$var`` passada, então, você pode,
-        por instância, colocá-la em uma declaração de retorno.
-
-    Função de conveniência para formatação de JSON, com a adição das *tags*
-    ``
`` ao redor da saída.
+    Função de conveniência para impressão bonita de JSON, com a adição de
+    envolver tags ``
`` ao redor da saída.
 
-    Deve ser usada com o intuito de *debugar* JSON de objetos e *arrays*.
+    É destinada para depurar a representação JSON de objetos e arrays.
 
 .. php:function:: env(string $key, string $default = null)
 
-    .. versionchanged:: 3.1.1
-        O parâmetro ``$default`` será adicionado.
+    Obtém uma variável de ambiente de fontes disponíveis. Usada como backup se
+    ``$_SERVER`` ou ``$_ENV`` estiverem desabilitadas.
 
-    Recebe uma variável de ambiente de fontes disponíveis. Usada como *backup*
-    se ``$_SERVER`` ou ``$_ENV`` estiverem desabilitados.
-
-    Essa função também emula ``PHP_SELF`` e ``DOCUMENT_ROOT`` em servidores
-    não suportados. De fato, é sempre uma boa ideia usar ``env()`` ao invés de
-    ``$_SERVER``ou ``getenv()`` (especialmente se você planeja distribuir o
-    código), pois é um *wrapper* completo de emulação.
+    Esta função também emula ``PHP_SELF`` e ``DOCUMENT_ROOT`` em
+    servidores sem suporte. Na verdade, é uma boa ideia sempre usar ``env()``
+    em vez de ``$_SERVER`` ou ``getenv()`` (especialmente se você planeja
+    distribuir o código), já que é um wrapper de emulação completo.
 
 .. php:function:: h(string $text, boolean $double = true, string $charset = null)
 
-    *Wrapper* de conveniência para ``htmlspecialchars()``.
+    Wrapper de conveniência para ``htmlspecialchars()``.
 
 .. php:function:: pluginSplit(string $name, boolean $dotAppend = false, string $plugin = null)
 
-    Divide um nome de plugin que segue o padrão de sintaxe de pontos e o
-    transforma em um nome de classe ou do *plugin*. Se ``$name`` não tem um
-    ponto, então o índice 0 será ``null``.
+    Divide um nome de plugin com sintaxe de ponto em seu plugin e nome de classe. Se ``$name``
+    não tiver um ponto, então o índice 0 será ``null``.
 
-    Comumente usada assim: ``list($plugin, $name) = pluginSplit('Users.User');``
+    Comumente usado como ``list($plugin, $name) = pluginSplit('Users.User');``
 
 .. php:function:: namespaceSplit(string $class)
 
-    Divide o *namespace* do nome da classe.
+    Divide o namespace do nome da classe.
 
-    Comumente usada assim: ``list($namespace, $className) = namespaceSplit('Cake\Core\App');``
+    Comumente usado como ``list($namespace, $className) = namespaceSplit('Cake\Core\App');``
 
-Constantes de definição do Core
-===============================
+Constantes de Definição do Núcleo
+==================================
 
-A maior parte das constantes a seguir referem-se a caminhos da sua aplicação.
+A maioria das seguintes constantes se refere a caminhos em sua aplicação.
 
 .. php:const:: APP
 
-    Caminho absoluto para o diretório de sua aplicação, incluindo a barra final.
+   Caminho absoluto para o diretório da sua aplicação, incluindo uma barra final.
 
 .. php:const:: APP_DIR
 
-    Igual a ``app`` ou ao nome do diretório de sua aplicação.
+    Igual a ``app`` ou o nome do diretório da sua aplicação.
 
 .. php:const:: CACHE
 
-    Caminho para o diretório de arquivos de cache. Pode ser compartilhado entre
-    hosts em uma configuração multi-servidores.
+    Caminho para o diretório de arquivos de cache. Pode ser compartilhado entre hosts em uma
+    configuração multi-servidor.
 
 .. php:const:: CAKE
 
-    Caminho para o diretório do CakePHP.
+    Caminho para o diretório cake.
 
 .. php:const:: CAKE_CORE_INCLUDE_PATH
 
-    Caminho para o diretório raiz de bibliotecas.
+    Caminho para o diretório lib raiz.
 
 .. php:const:: CONFIG
 
-    Caminho para o diretório de configurações.
+   Caminho para o diretório config.
 
 .. php:const:: CORE_PATH
 
-    Caminho para o diretório raiz com contra-barra no final.
+   Caminho para o diretório CakePHP com barra de diretório final.
 
 .. php:const:: DS
 
-    Atalho para o ``DIRECTORY_SEPARATOR`` do PHP, que é ``/`` no Linux e ``\\``
+    Abreviação para ``DIRECTORY_SEPARATOR`` do PHP, que é ``/`` no Linux e ``\``
     no Windows.
 
 .. php:const:: LOGS
 
-    Caminho para o diretório de logs.
+    Caminho para o diretório logs.
+
+.. php:const:: RESOURCES
+
+   Caminho para o diretório resources.
 
 .. php:const:: ROOT
 
@@ -205,51 +218,23 @@ A maior parte das constantes a seguir referem-se a caminhos da sua aplicação.
 
 .. php:const:: TESTS
 
-    Caminho para o diretório de testes.
+    Caminho para o diretório tests.
 
 .. php:const:: TMP
 
     Caminho para o diretório de arquivos temporários.
 
-.. php:const:: WWW\_ROOT
+.. php:const:: WWW_ROOT
 
-    Caminho completo para o diretório webroot.
+    Caminho completo para o webroot.
 
-Constantes de definição de tempo
-================================
+Constantes de Definição de Tempo
+=================================
 
 .. php:const:: TIME_START
 
-    Timestamp unix em microsegundos como *float* de quando a aplicação começou.
-
-.. php:const:: SECOND
-
-    Igual a 1
-
-.. php:const:: MINUTE
-
-    Igual a 60
-
-.. php:const:: HOUR
-
-    Igual a 3600
-
-.. php:const:: DAY
-
-    Igual a 86400
-
-.. php:const:: WEEK
-
-    Igual a 604800
-
-.. php:const:: MONTH
-
-    Igual a 2592000
-
-.. php:const:: YEAR
-
-    Igual a 31536000
+    Timestamp Unix em microssegundos como um float de quando a aplicação iniciou.
 
 .. meta::
-    :title lang=pt: Constantes globais e funções
-    :keywords lang=pt: constantes,funções,internacionalização,diretórios,caminhos
+    :title lang=pt: Constantes e Funções Globais
+    :keywords lang=pt: internacionalização e localização,constantes globais,exemplo config,array php,funções de conveniência,bibliotecas do núcleo,classes de componente,número opcional,funções globais,string string,classes do núcleo,strings de formato,mensagens não lidas,marcadores de posição,funções úteis,arrays,parâmetros,existência,traduções
diff --git a/pt/core-libraries/hash.rst b/pt/core-libraries/hash.rst
index 356c82fd3e..fcbfe2b947 100644
--- a/pt/core-libraries/hash.rst
+++ b/pt/core-libraries/hash.rst
@@ -5,94 +5,95 @@ Hash
 
 .. php:class:: Hash
 
-O gerenciamento de matrizes, se feito da maneira certa, pode ser uma 
-ferramenta muito poderosa e útil para construir um código mais inteligente 
-e otimizado. O CakePHP oferece um conjunto muito útil de utilitários estáticos 
-na classe Hash que permitem que você faça exatamente isso.
+O gerenciamento de arrays, se feito corretamente, pode ser uma ferramenta muito poderosa e útil
+para construir código mais inteligente e otimizado. O CakePHP oferece um
+conjunto muito útil de utilitários estáticos na classe Hash que permitem fazer
+exatamente isso.
 
-A classe Hash do CakePHP pode ser chamada de qualquer template ou controlador da 
-mesma forma que o Inflector é chamado. Exemplo: :php:meth:`Hash::combine()`.
+A classe Hash do CakePHP pode ser chamada de qualquer model ou controller da
+mesma forma que Inflector é chamado. Exemplo: :php:meth:`Hash::combine()`.
 
 .. _hash-path-syntax:
 
-Sintaxe do Caminho de Hash
-==========================
+Sintaxe de Caminho do Hash
+===========================
 
-A sintaxe de caminho descrita abaixo é usada por todos os métodos em ``Hash``. 
-Nem todas as partes da sintaxe do caminho estão disponíveis em todos os métodos. 
-Uma expressão de caminho é feita de qualquer número de tokens. Os tokens são compostos 
-por dois grupos. Expressões são usadas para percorrer os dados da matriz, enquanto as
-expressões são usadas para qualificar elementos.
+A sintaxe de caminho descrita abaixo é usada por todos os métodos em ``Hash``. Nem todas
+as partes da sintaxe de caminho estão disponíveis em todos os métodos. Uma expressão de caminho é
+composta por qualquer número de tokens. Tokens são compostos de dois grupos. Expressões
+são usadas para atravessar os dados do array, enquanto matchers são usados para qualificar
+elementos. Você aplica matchers a elementos de expressão.
 
 Tipos de Expressão
-------------------
+-------------------
 
 +--------------------------------+--------------------------------------------+
 | Expressão                      | Definição                                  |
 +================================+============================================+
 | ``{n}``                        | Representa uma chave numérica. Irá         |
-|                                | corresponder a qualquer string ou chave    |
-|                                | numérica                                   |
+|                                | corresponder qualquer chave string ou      |
+|                                | numérica.                                  |
 +--------------------------------+--------------------------------------------+
-| ``{s}``                        | Representa uma string. Irá corresponder a  |
-|                                | qualquer valor de string, incluindo        |
-|                                | valores de string numéricos.               |
+| ``{s}``                        | Representa uma string. Irá corresponder    |
+|                                | qualquer valor string incluindo valores de |
+|                                | string numéricos.                          |
 +--------------------------------+--------------------------------------------+
-| ``{*}``                        | Corresponde a qualquer valor.              |
+| ``{*}``                        | Corresponde qualquer valor.                |
 +--------------------------------+--------------------------------------------+
-| ``Foo``                        | Corresponde às chaves exatamente com o     |
-|                                | mesmo valor.                               |
+| ``Foo``                        | Corresponde chaves com exatamente o mesmo  |
+|                                | valor.                                     |
 +--------------------------------+--------------------------------------------+
 
-Todos os elementos de expressão são suportados por todos os métodos. Além de 
-elementos de expressão, você pode usar a correspondência de atributos com certos 
-métodos. Eles são: ``extract()``, ``combine()``, ``format()``, ``check()``, ``map()``, ``reduce()``,
+Todos os elementos de expressão são suportados por todos os métodos. Além dos elementos de expressão,
+você pode usar correspondência de atributos com certos métodos. Eles são ``extract()``,
+``combine()``, ``format()``, ``check()``, ``map()``, ``reduce()``,
 ``apply()``, ``sort()``, ``insert()``, ``remove()`` e ``nest()``.
 
 Tipos de Correspondência de Atributos
--------------------------------------
+--------------------------------------
 
 +--------------------------------+--------------------------------------------+
-| Expressão                      | Definição                                  |
+| Matcher                        | Definição                                  |
 +================================+============================================+
-| ``[id]``                       | Combine elementos com uma determinada      |
+| ``[id]``                       | Corresponde elementos com uma determinada  |
 |                                | chave de array.                            |
 +--------------------------------+--------------------------------------------+
-| ``[id=2]``                     | Combine elementos com id igual a 2.        |
+| ``[id=2]``                     | Corresponde elementos com id igual a 2.    |
 +--------------------------------+--------------------------------------------+
-| ``[id!=2]``                    | Combine elementos com id diferente de 2.   |
+| ``[id!=2]``                    | Corresponde elementos com id diferente     |
+|                                | de 2.                                      |
 +--------------------------------+--------------------------------------------+
-| ``[id>2]``                     | Combine elementos com id maior que 2.      |
+| ``[id>2]``                     | Corresponde elementos com id maior que 2.  |
 +--------------------------------+--------------------------------------------+
-| ``[id>=2]``                    | Combine elementos com id maior ou          |
+| ``[id>=2]``                    | Corresponde elementos com id maior ou      |
 |                                | igual a 2.                                 |
 +--------------------------------+--------------------------------------------+
-| ``[id<2]``                     | Combine elementos com id menor que 2       |
+| ``[id<2]``                     | Corresponde elementos com id menor que 2   |
 +--------------------------------+--------------------------------------------+
-| ``[id<=2]``                    | Combine elementos com id menor ou          |
+| ``[id<=2]``                    | Corresponde elementos com id menor ou      |
 |                                | igual a 2.                                 |
 +--------------------------------+--------------------------------------------+
-| ``[text=/.../]``               | Combine elementos que possuem valores      |
-|                                | correspondentes à expressão regular        |
-|                                | dentro de ``...``.                         |
+| ``[text=/.../]``               | Corresponde elementos que têm valores      |
+|                                | correspondentes à expressão regular dentro |
+|                                | de ``...``.                                |
 +--------------------------------+--------------------------------------------+
 
 .. php:staticmethod:: get(array|\ArrayAccess $data, $path, $default = null)
 
-    ``get()`` é uma versão simplificada de ``extract()``, ele só suporta expressões 
-    de caminho direto. Caminhos como ``{n}``, ``{s}``, ``{*}`` ou expressões não 
-    são suportados. Use ``get()`` quando quiser exatamente um valor de uma matriz. 
-    Se um caminho correspondente não for encontrado, o valor padrão será retornado.
+    ``get()`` é uma versão simplificada de ``extract()``, suporta apenas
+    expressões de caminho direto. Caminhos com ``{n}``, ``{s}``, ``{*}`` ou matchers não são
+    suportados. Use ``get()`` quando você deseja exatamente um valor de um array. Se
+    um caminho correspondente não for encontrado, o valor padrão será retornado.
 
 .. php:staticmethod:: extract(array|\ArrayAccess $data, $path)
 
-    ``Hash::extract()`` suporta todas as expressões e componentes de correspondência 
-    :ref:`hash-path-syntax`. Você pode usar a extração para recuperar dados de matrizes
-    ou objetos que implementam a interface ``ArrayAccess``, ao longo de caminhos arbitrários 
-    rapidamente, sem ter que percorrer as estruturas de dados. Em vez disso, você usa expressões 
-    de caminho para qualificar quais elementos você deseja que sejam retornados::
+    ``Hash::extract()`` suporta todos os componentes de expressão e matcher de
+    :ref:`hash-path-syntax`. Você pode usar extract para recuperar dados de arrays
+    ou objetos que implementam a interface ``ArrayAccess``, ao longo de caminhos arbitrários
+    rapidamente sem ter que fazer loop pelas estruturas de dados. Em vez disso, você
+    usa expressões de caminho para qualificar quais elementos você deseja retornados::
 
-        // Uso comum:
+        // Uso Comum:
         $users = [
             ['id' => 1, 'name' => 'mark'],
             ['id' => 2, 'name' => 'jane'],
@@ -105,13 +106,13 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: Hash::insert(array $data, $path, $values = null)
 
-    Insere ``$values`` em uma matriz conforme definido por ``$path``::
+    Insere ``$values`` em um array conforme definido por ``$path``::
 
         $a = [
             'pages' => ['name' => 'page']
         ];
         $result = Hash::insert($a, 'files', ['name' => 'files']);
-        // $result agora parece:
+        // $result agora parece com:
         [
             [pages] => [
                 [name] => page
@@ -121,11 +122,12 @@ Tipos de Correspondência de Atributos
             ]
         ]
 
-    Você pode usar caminhos usando ``{n}``, ``{s}`` e ``{*}`` para inserir dados em vários pontos::
+    Você pode usar caminhos usando ``{n}``, ``{s}`` e ``{*}`` para inserir dados em múltiplos
+    pontos::
 
         $users = Hash::insert($users, '{n}.new', 'value');
 
-    As expressões de atributos funcionam com ``insert()`` também::
+    Matchers de atributos também funcionam com ``insert()``::
 
         $data = [
             0 => ['up' => true, 'Item' => ['id' => 1, 'title' => 'first']],
@@ -135,7 +137,7 @@ Tipos de Correspondência de Atributos
             4 => ['Item' => ['id' => 5, 'title' => 'fifth']],
         ];
         $result = Hash::insert($data, '{n}[up].Item[id=4].new', 9);
-        /* $result agora se parece:
+        /* $result agora parece com:
             [
                 ['up' => true, 'Item' => ['id' => 1, 'title' => 'first']],
                 ['Item' => ['id' => 2, 'title' => 'second']],
@@ -147,14 +149,14 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: remove(array $data, $path)
 
-    Remove todos os elementos de uma matriz que corresponde a ``$path``. ::
+    Remove todos os elementos de um array que correspondem a ``$path``. ::
 
         $a = [
             'pages' => ['name' => 'page'],
             'files' => ['name' => 'files']
         ];
         $result = Hash::remove($a, 'files');
-        /* $result agora se parece:
+        /* $result agora parece com:
             [
                 [pages] => [
                     [name] => page
@@ -163,8 +165,8 @@ Tipos de Correspondência de Atributos
             ]
         */
 
-    Usando ``{n}``, ``{s}`` e ``{*}`` permitirá que você remova múltiplos valores 
-    de uma vez. Você também pode usar expressões de atributo com ``remove()``::
+    Usar ``{n}``, ``{s}`` e ``{*}`` permitirá que você remova vários valores de uma vez.
+    Você também pode usar matchers de atributos com ``remove()``::
 
         $data = [
             0 => ['clear' => true, 'Item' => ['id' => 1, 'title' => 'first']],
@@ -174,7 +176,7 @@ Tipos de Correspondência de Atributos
             4 => ['Item' => ['id' => 5, 'title' => 'fifth']],
         ];
         $result = Hash::remove($data, '{n}[clear].Item[id=4]');
-        /* $result agora se parece:
+        /* $result agora parece com:
             [
                 ['clear' => true, 'Item' => ['id' => 1, 'title' => 'first']],
                 ['Item' => ['id' => 2, 'title' => 'second']],
@@ -186,11 +188,11 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: combine(array $data, $keyPath, $valuePath = null, $groupPath = null)
 
-    Cria uma matriz associativa usando um ``$keyPath`` como o caminho para construir 
-    suas chaves, e opcionalmente ``$valuePath`` como o caminho para obter os valores. 
-    Se ``$valuePath`` não for especificado, ou não corresponder a nada, os valores 
-    serão inicializados como nulos. Você pode opcionalmente agrupar os valores pelo 
-    que é obtido ao seguir o caminho especificado em ``$groupPath``.::
+    Cria um array associativo usando um ``$keyPath`` como o caminho para construir suas chaves,
+    e opcionalmente ``$valuePath`` como caminho para obter os valores. Se ``$valuePath`` não for
+    especificado, ou não corresponder a nada, os valores serão inicializados como null.
+    Você pode opcionalmente agrupar os valores pelo que é obtido ao seguir o
+    caminho especificado em ``$groupPath``. ::
 
         $a = [
             [
@@ -216,7 +218,7 @@ Tipos de Correspondência de Atributos
         ];
 
         $result = Hash::combine($a, '{n}.User.id');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [2] =>
                 [14] =>
@@ -224,7 +226,7 @@ Tipos de Correspondência de Atributos
         */
 
         $result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.user');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [2] => 'mariano.iglesias'
                 [14] => 'phpnut'
@@ -232,7 +234,7 @@ Tipos de Correspondência de Atributos
         */
 
         $result = Hash::combine($a, '{n}.User.id', '{n}.User.Data');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [2] => [
                         [user] => mariano.iglesias
@@ -246,7 +248,7 @@ Tipos de Correspondência de Atributos
         */
 
         $result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [2] => Mariano Iglesias
                 [14] => Larry E. Masters
@@ -254,7 +256,7 @@ Tipos de Correspondência de Atributos
         */
 
         $result = Hash::combine($a, '{n}.User.id', '{n}.User.Data', '{n}.User.group_id');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [1] => [
                         [2] => [
@@ -272,7 +274,7 @@ Tipos de Correspondência de Atributos
         */
 
         $result = Hash::combine($a, '{n}.User.id', '{n}.User.Data.name', '{n}.User.group_id');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [1] => [
                         [2] => Mariano Iglesias
@@ -283,17 +285,17 @@ Tipos de Correspondência de Atributos
             ]
         */
 
-        // A partir de 3.9.0 $keyPath pode ser nulo 
+        // A partir da versão 3.9.0 $keyPath pode ser null
         $result = Hash::combine($a, null, '{n}.User.Data.name');
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [0] => Mariano Iglesias
                 [1] => Larry E. Masters
             ]
         */
 
-    Você pode fornecer matrizes para ``$keyPath`` e ``$valuePath``. Se você fizer isso, 
-    o primeiro valor será usado com o formato de string, para valores extraídos por 
+    Você pode fornecer arrays para ambos ``$keyPath`` e ``$valuePath``. Se você fizer isso,
+    o primeiro valor será usado como uma string de formato, para valores extraídos pelos
     outros caminhos::
 
         $result = Hash::combine(
@@ -302,7 +304,7 @@ Tipos de Correspondência de Atributos
             ['%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'],
             '{n}.User.group_id'
         );
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [1] => [
                         [2] => mariano.iglesias: Mariano Iglesias
@@ -318,7 +320,7 @@ Tipos de Correspondência de Atributos
             ['%s: %s', '{n}.User.Data.user', '{n}.User.Data.name'],
             '{n}.User.id'
         );
-        /* $result agora se parece com:
+        /* $result agora parece com:
             [
                 [mariano.iglesias: Mariano Iglesias] => 2
                 [phpnut: Larry E. Masters] => 14
@@ -327,8 +329,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: format(array $data, array $paths, $format)
 
-    Retorna uma série de valores extraídos de uma matriz, formatados 
-    com uma string::
+    Retorna uma série de valores extraídos de um array, formatados com uma
+    string de formato::
 
         $data = [
             [
@@ -380,7 +382,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: contains(array $data, array $needle)
 
-    Determina se um Hash ou matriz contém as chaves e valores exatos de outro::
+    Determina se um Hash ou array contém as chaves e valores exatos
+    de outro::
 
         $a = [
             0 => ['name' => 'main'],
@@ -390,7 +393,7 @@ Tipos de Correspondência de Atributos
             0 => ['name' => 'main'],
             1 => ['name' => 'about'],
             2 => ['name' => 'contact'],
-            'a' => 'b'
+            'a' => 'b',
         ];
 
         $result = Hash::contains($a, $a);
@@ -402,7 +405,7 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: check(array $data, string $path = null)
 
-    Verifica se um determinado caminho está definido em uma matriz::
+    Verifica se um caminho específico está definido em um array::
 
         $set = [
             'My Index 1' => ['First' => 'The first item']
@@ -438,10 +441,9 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: filter(array $data, $callback = ['Hash', 'filter'])
 
-    Filtra os elementos vazios da matriz, excluindo '0'. Você também pode 
-    fornecer um ``$callback`` personalizado para filtrar os elementos da matriz. 
-    O retorno de chamada deve retornar ``false`` para remover elementos da matriz
-    resultante::
+    Filtra elementos vazios do array, excluindo '0'. Você também pode fornecer um
+    ``$callback`` personalizado para filtrar os elementos do array. O callback deve
+    retornar ``false`` para remover elementos do array resultante::
 
         $data = [
             '0',
@@ -452,7 +454,7 @@ Tipos de Correspondência de Atributos
         ];
         $res = Hash::filter($data);
 
-        /* $res agora se parece:
+        /* $res agora parece com:
             [
                 [0] => 0
                 [2] => true
@@ -467,7 +469,7 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: flatten(array $data, string $separator = '.')
 
-    Nivela uma matriz multidimensional em uma única dimensão::
+    Colapsa um array multidimensional em uma única dimensão::
 
         $arr = [
             [
@@ -480,7 +482,7 @@ Tipos de Correspondência de Atributos
             ],
         ];
         $res = Hash::flatten($arr);
-        /* $res now looks like:
+        /* $res agora parece com:
             [
                 [0.Post.id] => 1
                 [0.Post.title] => First Post
@@ -495,7 +497,7 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: expand(array $data, string $separator = '.')
 
-    Expande uma matriz que foi previamente achatada com 
+    Expande um array que foi previamente achatado com
     :php:meth:`Hash::flatten()`::
 
         $data = [
@@ -509,7 +511,7 @@ Tipos de Correspondência de Atributos
             '1.Author.user' => Crystal,
         ];
         $res = Hash::expand($data);
-        /* $res agora se parece com:
+        /* $res agora parece com:
         [
             [
                 'Post' => ['id' => '1', 'title' => 'First Post'],
@@ -524,16 +526,16 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: merge(array $data, array $merge[, array $n])
 
-    Esta função pode ser considerada um híbrido entre ``array_merge`` e 
-    ``array_merge_recursive`` do PHP. A diferença entre as duas é que se 
-    uma chave da matriz contém outra matriz, então a função se comporta 
-    recursivamente (ao contrário de ``array_merge``), mas não se comporta 
-    do mesmo jeito para chaves contendo strings (ao contrário de ``array_merge_recursive``).
+    Esta função pode ser pensada como um híbrido entre
+    ``array_merge`` e ``array_merge_recursive`` do PHP. A diferença para os dois
+    é que se uma chave de array contiver outro array, então a função
+    se comporta recursivamente (ao contrário de ``array_merge``), mas não faz isso para chaves
+    contendo strings (ao contrário de ``array_merge_recursive``).
 
     .. note::
 
-        Esta função funcionará com uma quantidade ilimitada de argumentos 
-        e casting de parâmetros primitivos para matrizes.
+        Esta função funcionará com uma quantidade ilimitada de argumentos e
+        converte parâmetros não-array em arrays.
 
     ::
 
@@ -541,7 +543,7 @@ Tipos de Correspondência de Atributos
             [
                 'id' => '48c2570e-dfa8-4c32-a35e-0d71cbdd56cb',
                 'name' => 'mysql raleigh-workshop-08 < 2008-09-05.sql ',
-                'description' => 'Importing an sql dump'
+                'description' => 'Importing an sql dump',
             ],
             [
                 'id' => '48c257a8-cf7c-4af2-ac2f-114ecbdd56cb',
@@ -554,7 +556,7 @@ Tipos de Correspondência de Atributos
         $arrayD = ["cats" => "felines", "dog" => "angry"];
         $res = Hash::merge($array, $arrayB, $arrayC, $arrayD);
 
-        /* $res agora se parece com:
+        /* $res agora parece com:
         [
             [0] => [
                     [id] => 48c2570e-dfa8-4c32-a35e-0d71cbdd56cb
@@ -576,7 +578,7 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: numeric(array $data)
 
-    Verifica se todos os valores da matriz são numéricas::
+    Verifica se todos os valores no array são numéricos::
 
         $data = ['one'];
         $res = Hash::numeric(array_keys($data));
@@ -588,8 +590,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: dimensions (array $data)
 
-    Conta as dimensões de uma matriz. Este método irá considerar 
-    apenas a dimensão do primeiro elemento na matriz::
+    Conta as dimensões de um array. Este método considerará apenas
+    a dimensão do primeiro elemento no array::
 
         $data = ['one', '2', 'three'];
         $result = Hash::dimensions($data);
@@ -613,8 +615,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: maxDimensions(array $data)
 
-    Semelhante a :php:meth:`~Hash::dimensions()`, no entanto, este método 
-    retorna, o maior número de dimensões de qualquer elemento na matriz::
+    Semelhante a :php:meth:`~Hash::dimensions()`, porém este método retorna
+    o número mais profundo de dimensões de qualquer elemento no array::
 
         $data = ['1' => '1.1', '2', '3' => ['3.1' => '3.1.1']];
         $result = Hash::maxDimensions($data);
@@ -626,29 +628,29 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: map(array $data, $path, $function)
 
-    Cria uma nova matriz, extraindo ``$path``, e mapeando ``$function`` nos 
-    resultados. Você pode usar expressões e elementos correspondentes com este método::
+    Cria um novo array, extraindo ``$path`` e mapeando ``$function``
+    através dos resultados. Você pode usar elementos de expressão e correspondência com
+    este método::
 
-        // Chame a função noop $this->noop() em cada elemento de $data
+        // Chama a função noop $this->noop() em cada elemento de $data
         $result = Hash::map($data, "{n}", [$this, 'noop']);
 
         public function noop(array $array)
         {
-            // Faça coisas para a matriz e retorne o resultado
+            // Faça algo com o array e retorne o resultado
             return $array;
         }
 
 .. php:staticmethod:: reduce(array $data, $path, $function)
 
-    Cria um único valor, extraindo ``$path``, e reduzindo os resultados extraídos 
-    com ``$function``. Você pode usar expressões e elementos correspondentes com 
-    este método.
+    Cria um único valor, extraindo ``$path`` e reduzindo os resultados extraídos
+    com ``$function``. Você pode usar elementos de expressão e correspondência
+    com este método.
 
 .. php:staticmethod:: apply(array $data, $path, $function)
 
-    Aplique um retorno de chamada a um conjunto de valores extraídos 
-    usando ``$function``. A função obterá os valores extraídos do 
-    primeiro argumento::
+    Aplica um callback a um conjunto de valores extraídos usando ``$function``. A função
+    receberá os valores extraídos como o primeiro argumento::
 
         $data = [
             ['date' => '01-01-2016', 'booked' => true],
@@ -665,8 +667,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: sort(array $data, $path, $dir, $type = 'regular')
 
-    Classifica uma matriz por qualquer valor, determinado por :ref:`hash-path-syntax`
-    Somente elementos de expressão são suportados por este método::
+    Ordena um array por qualquer valor, determinado por uma :ref:`hash-path-syntax`
+    Apenas elementos de expressão são suportados por este método::
 
         $a = [
             0 => ['Person' => ['name' => 'Jeff']],
@@ -688,18 +690,18 @@ Tipos de Correspondência de Atributos
             ]
         */
 
-    ``$dir`` pode ser ``asc`` ou ``desc``. ``$type`` pode 
-    ser um dos seguintes valores:
+    ``$dir`` pode ser ``asc`` ou ``desc``. ``$type``
+    pode ser um dos seguintes valores:
 
-    * ``regular`` para ordenamento padrão
-    * ``numeric`` para classificar valores como seus equivalentes numéricos.
-    * ``string`` para classificar valores como seu valor de string.
-    * ``natural`` para classificar valores de uma forma amigável ao humano. 
-      Classificará ``foo10`` abaixo de ``foo2`` por exemplo.
+    * ``regular`` para ordenação regular.
+    * ``numeric`` para ordenar valores como seus equivalentes numéricos.
+    * ``string`` para ordenar valores como seu valor de string.
+    * ``natural`` para ordenar valores de forma amigável ao usuário. Irá
+      ordenar ``foo10`` abaixo de ``foo2`` como exemplo.
 
 .. php:staticmethod:: diff(array $data, array $compare)
 
-    Calcula a diferença entre duas matrizes::
+    Calcula a diferença entre dois arrays::
 
         $a = [
             0 => ['name' => 'main'],
@@ -722,8 +724,8 @@ Tipos de Correspondência de Atributos
 
 .. php:staticmethod:: mergeDiff(array $data, array $compare)
 
-    Essa função mescla duas matrizes e empurra as diferenças nos 
-    dados para a parte inferior da matriz resultante.
+    Esta função mescla dois arrays e empurra as diferenças nos
+    dados para o final do array resultante.
 
     **Exemplo 1**
     ::
@@ -762,12 +764,12 @@ Tipos de Correspondência de Atributos
             ]
         */
 
-.. php:staticmethod:: normalize(array $data, $assoc = true)
+.. php:staticmethod:: normalize(array $data, $assoc = true, $default = null)
 
-    Normaliza uma matriz. Se ``$assoc`` for ``true``, a matriz resultante 
-    será normalizada para ser uma matriz associativa. Chaves numéricas com 
-    valores serão convertidas em chaves de string com valores nulos. Normalizar 
-    uma matriz torna o uso dos resultados com :php:meth:`Hash::merge()` mais fácil::
+    Normaliza um array. Se ``$assoc`` for ``true``, o array resultante será
+    normalizado para ser um array associativo. Chaves numéricas com valores serão
+    convertidas em chaves string com valores ``$default``. Normalizar um array
+    torna o uso dos resultados com :php:meth:`Hash::merge()` mais fácil::
 
         $a = ['Tree', 'CounterCache',
             'Upload' => [
@@ -795,7 +797,7 @@ Tipos de Correspondência de Atributos
             'Limit',
             'Bindable',
             'Validator',
-            'Transactional'
+            'Transactional',
         ];
         $result = Hash::normalize($b);
         /* $result agora parece com:
@@ -811,21 +813,24 @@ Tipos de Correspondência de Atributos
             ]
         */
 
+.. versionchanged:: 4.5.0
+    O parâmetro ``$default`` foi adicionado.
+
 .. php:staticmethod:: nest(array $data, array $options = [])
 
-    Pega um conjunto de matriz simples e cria uma estrutura de dados aninhada ou encadeada.
+    Pega um conjunto de array plano e cria uma estrutura de dados aninhada ou encadeada.
 
     **Opções:**
 
-    - ``children`` O nome da chave a ser usada no conjunto de resultados para 
-      os valores aninhados. O padrão é 'children'.
-    - ``idPath`` O caminho para uma chave que identifica cada entrada. Deve ser compatível 
-      com :php:meth:`Hash::extract()`. O padrão é ``{n}.$alias.id``
-    - ``parentPath`` O caminho para uma chave que identifica o pai de cada entrada. Deve ser compatível com 
-      :php:meth:`Hash::extract()`. O padrão é ``{n}.$alias.parent_id``
-    - ``root`` O id do resultado desejado mais alto.
+    - ``children`` O nome da chave a ser usado no conjunto de resultados para filhos. Padrão
+      é 'children'.
+    - ``idPath`` O caminho para uma chave que identifica cada entrada. Deve ser
+      compatível com :php:meth:`Hash::extract()`. Padrão é ``{n}.$alias.id``
+    - ``parentPath`` O caminho para uma chave que identifica o pai de cada entrada.
+      Deve ser compatível com :php:meth:`Hash::extract()`. Padrão é ``{n}.$alias.parent_id``
+    - ``root`` O id do resultado de nível superior desejado.
 
-    Por exemplo, se você tivesse a seguinte matriz de dados::
+    Por exemplo, se você tivesse o seguinte array de dados::
 
         $data = [
             ['ThreadPost' => ['id' => 1, 'parent_id' => null]],
@@ -884,4 +889,4 @@ Tipos de Correspondência de Atributos
 
 .. meta::
     :title lang=pt: Hash
-    :keywords lang=pt: matriz matriz,caminho de matriz,nome da matriz,chave numerica,expressao regular,configuracao de resultado,nome de pessoas,brackets,sintaxe,cakephp,elementos,php,definir caminho
+    :keywords lang=pt: array array,path array,array name,chave numérica,expressão regular,conjunto de resultados,nome de pessoa,colchetes,sintaxe,cakephp,elementos,php,definir caminho
diff --git a/pt/core-libraries/httpclient.rst b/pt/core-libraries/httpclient.rst
index 3b7bc2c66a..1928f94bb4 100644
--- a/pt/core-libraries/httpclient.rst
+++ b/pt/core-libraries/httpclient.rst
@@ -1,18 +1,18 @@
-Cliente Http
+Cliente HTTP
 ############
 
 .. php:namespace:: Cake\Http
 
 .. php:class:: Client(mixed $config = [])
 
-O CakePHP inclui um cliente HTTP compatível com a PSR-18 que pode ser usado
-para fazer solicitações. É uma ótima maneira de se comunicar com serviços da web e
+O CakePHP inclui um cliente HTTP compatível com PSR-18 que pode ser usado para
+fazer requisições. É uma ótima maneira de se comunicar com webservices e
 APIs remotas.
 
-Fazendo Solicitações
-====================
+Fazendo Requisições
+===================
 
-Fazer solicitações é simples e direto. Fazer uma solicitação GET parece::
+Fazer requisições é simples e direto. Fazer uma requisição GET é assim::
 
     use Cake\Http\Client;
 
@@ -24,24 +24,24 @@ Fazer solicitações é simples e direto. Fazer uma solicitação GET parece::
     // GET simples com querystring
     $response = $http->get('http://example.com/search', ['q' => 'widget']);
 
-    // GET simples com querystring & cabeçalhos adicionais
+    // GET simples com querystring e cabeçalhos adicionais
     $response = $http->get('http://example.com/search', ['q' => 'widget'], [
-      'headers' => ['X-Requested-With' => 'XMLHttpRequest']
+      'headers' => ['X-Requested-With' => 'XMLHttpRequest'],
     ]);
 
-Fazer solicitações POST e PUT é igualmente simples::
+Fazer requisições POST e PUT é igualmente simples::
 
-    // Envie uma solicitação POST com dados codificados em application/x-www-form-urlencoded
+    // Enviar uma requisição POST com dados codificados em application/x-www-form-urlencoded
     $http = new Client();
     $response = $http->post('http://example.com/posts/add', [
       'title' => 'testing',
-      'body' => 'content in the post'
+      'body' => 'content in the post',
     ]);
 
-    // Envie uma solicitação PUT com dados codificados application/x-www-form-urlencoded
+    // Enviar uma requisição PUT com dados codificados em application/x-www-form-urlencoded
     $response = $http->put('http://example.com/posts/add', [
       'title' => 'testing',
-      'body' => 'content in the post'
+      'body' => 'content in the post',
     ]);
 
     // Outros métodos também.
@@ -49,7 +49,7 @@ Fazer solicitações POST e PUT é igualmente simples::
     $http->head(/* ... */);
     $http->patch(/* ... */);
 
-Se você criou um objeto de solicitação PSR-7, pode enviá-lo usando
+Se você criou um objeto de requisição PSR-7, pode enviá-lo usando
 ``sendRequest()``::
 
     use Cake\Http\Client;
@@ -59,58 +59,58 @@ Se você criou um objeto de solicitação PSR-7, pode enviá-lo usando
         'http://example.com/search',
         ClientRequest::METHOD_GET
     );
-    $client = new Client();
-    $response = $client->sendRequest($request);
+    $http = new Client();
+    $response = $http->sendRequest($request);
 
-Criação de Solicitações Multipart com Arquivos
-==============================================
+Criando Requisições Multipart com Arquivos
+===========================================
 
-Você pode incluir arquivos em corpos de solicitação::
+Você pode incluir arquivos nos corpos das requisições incluindo um filehandle no array::
 
     $http = new Client();
     $response = $http->post('http://example.com/api', [
       'image' => fopen('/path/to/a/file', 'r'),
     ]);
 
-O arquivo será lido até o fim; não será rebobinado antes de ser lido.
+O filehandle será lido até o final; ele não será rebobinado antes de ser lido.
 
-Criação de Corpos de Solicitação de Várias Partes Manualmente
--------------------------------------------------------------
+Construindo Corpos de Requisição Multipart
+-------------------------------------------
 
-Pode haver momentos em que você precise criar um corpo de solicitação de
-uma maneira muito específica. Nessas situações, você pode frequentemente usar
-``Cake\Http\Client\FormData`` para criar a solicitação HTTP multipart que você deseja::
+Pode haver momentos em que você precise construir um corpo de requisição de uma maneira muito específica.
+Nessas situações, você pode frequentemente usar ``Cake\Http\Client\FormData`` para criar
+a requisição HTTP multipart específica que você deseja::
 
     use Cake\Http\Client\FormData;
 
     $data = new FormData();
 
-    // Crie uma parte XML
+    // Criar uma parte XML
     $xml = $data->newPart('xml', $xmlString);
-    // Defina o tipo de conteúdo.
+    // Definir o tipo de conteúdo.
     $xml->type('application/xml');
     $data->add($xml);
 
-    // Crie um upload de arquivo com addFile()
-    // Isso irá anexar o arquivo aos dados do formulário também.
+    // Criar um upload de arquivo com addFile()
+    // Isso também adicionará o arquivo aos dados do formulário.
     $file = $data->addFile('upload', fopen('/some/file.txt', 'r'));
     $file->contentId('abc123');
     $file->disposition('attachment');
 
-    // Envie a solicitação.
+    // Enviar a requisição.
     $response = $http->post(
         'http://example.com/api',
         (string)$data,
         ['headers' => ['Content-Type' => $data->contentType()]]
     );
 
-Enviando o Corpo da Solicitação
-===============================
+Enviando Corpos de Requisição
+==============================
 
-Ao lidar com APIs REST, você geralmente precisa enviar corpos de solicitação que
-não são codificados por formulário. Http\\Cliente expõe isso através da opção de tipo::
+Ao lidar com APIs REST, você frequentemente precisa enviar corpos de requisição que não são
+codificados como formulário. Http\\Client expõe isso através da opção type::
 
-    // Envie um corpo de solicitação JSON.
+    // Enviar um corpo de requisição JSON.
     $http = new Client();
     $response = $http->post(
       'http://example.com/tasks',
@@ -119,11 +119,11 @@ não são codificados por formulário. Http\\Cliente expõe isso através da op
     );
 
 A chave ``type`` pode ser 'json', 'xml' ou um tipo MIME completo.
-Ao usar a opção ``type``, você deve fornecer os dados como uma string.
-Se você estiver fazendo uma solicitação GET que precisa de parâmetros de
-string de consulta e um corpo de solicitação, você pode fazer o seguinte::
+Ao usar a opção ``type``, você deve fornecer os dados como uma string. Se você estiver
+fazendo uma requisição GET que precisa de parâmetros de querystring e de um corpo de requisição,
+você pode fazer o seguinte::
 
-    // Envie um corpo JSON em uma solicitação GET com parâmetros de string de consulta.
+    // Enviar um corpo JSON em uma requisição GET com parâmetros de query string.
     $http = new Client();
     $response = $http->get(
       'http://example.com/tasks',
@@ -133,150 +133,170 @@ string de consulta e um corpo de solicitação, você pode fazer o seguinte::
 
 .. _http_client_request_options:
 
-Opções de Método para Solicitação
-=================================
-
-Cada método HTTP leva um parâmetro ``$options`` que é usado para fornecer informações
-adicionais de solicitação. As seguintes chaves podem ser usadas em ``$options``:
-
-- ``headers`` - Matriz de cabeçalhos adicionais
-- ``cookie`` - Matriz de cookies para usar.
-- ``proxy`` - Matriz de informações do proxy.
-- ``auth`` - Matriz de dados de autenticação, a chave ``type`` é usada para delegar a uma estratégia
-  de autenticação. Por padrão, a autenticação básica é usada.
-- ``ssl_verify_peer`` - o padrão é ``true``. Defina como ``false`` para desativar a
-  verificação de certificação SSL (não recomendado).
-- ``ssl_verify_peer_name`` - o padrão é ``true``. Defina como ``false`` para desabilitar a verificação do nome
-  do host ao verificar os certificados SSL (não recomendado).
-- ``ssl_verify_depth`` - o padrão é 5. Profundidade a ser percorrida na cadeia de CA.
-- ``ssl_verify_host`` - o padrão é ``true``. Valide o certificado SSL em relação ao nome do host.
-- ``ssl_cafile`` - o padrão é construído em cafile. Substitua para usar pacotes CA personalizados.
-- ``timeout`` - Duração de espera antes de expirar em segundos.
-- ``type`` - Envie um corpo de solicitação em um tipo de conteúdo personalizado. Requer que ``$data``
-  seja uma string ou que a opção ``_content`` seja definida ao fazer solicitações GET.
-- ``redirect`` - Número de redirecionamentos a seguir. O padrão é ``false``.
-
-O parâmetro options é sempre o terceiro parâmetro em cada um dos métodos HTTP.
-Eles também podem ser usados ao construir ``Client`` para criar :ref:`scoped clients `.
+Opções de Método de Requisição
+===============================
+
+Cada método HTTP recebe um parâmetro ``$options`` que é usado para fornecer
+informações adicionais de requisição. As seguintes chaves podem ser usadas em ``$options``:
+
+- ``headers`` - Array de cabeçalhos adicionais
+- ``cookie`` - Array de cookies a usar.
+- ``proxy`` - Array de informações de proxy.
+- ``auth`` - Array de dados de autenticação, a chave ``type`` é usada para delegar a
+  uma estratégia de autenticação. Por padrão, a autenticação básica é usada.
+- ``ssl_verify_peer`` - padrão é ``true``. Defina como ``false`` para desabilitar a verificação de
+  certificação SSL (não recomendado).
+- ``ssl_verify_peer_name`` - padrão é ``true``. Defina como ``false`` para desabilitar
+  a verificação do nome do host ao verificar certificados SSL (não recomendado).
+- ``ssl_verify_depth`` - padrão é 5. Profundidade para percorrer na cadeia de CA.
+- ``ssl_verify_host`` - padrão é ``true``. Validar o certificado SSL contra o nome do host.
+- ``ssl_cafile`` - padrão é o cafile integrado. Sobrescrever para usar pacotes de CA personalizados.
+- ``timeout`` - Duração a esperar antes de expirar em segundos.
+- ``type`` - Enviar um corpo de requisição em um tipo de conteúdo personalizado. Requer que ``$data`` seja
+  uma string, ou que a opção ``_content`` seja definida ao fazer requisições GET.
+- ``redirect`` - Número de redirecionamentos a seguir. Padrão é ``false``.
+- ``curl`` - Um array de opções adicionais do curl (se o adaptador curl for usado),
+  por exemplo, ``[CURLOPT_SSLKEY => 'key.pem']``.
+
+O parâmetro options é sempre o 3º parâmetro em cada um dos métodos HTTP.
+Eles também podem ser usados ao construir ``Client`` para criar
+:ref:`clientes com escopo `.
 
 Autenticação
 ============
 
-``Cake\Http\Client`` suporta alguns sistemas de autenticação. Diferentes
-estratégias de autenticação podem ser adicionadas pelos desenvolvedores.
-As estratégias de autenticação são chamadas antes do envio da solicitação
-e permitem que cabeçalhos sejam adicionados ao contexto da solicitação.
+``Cake\Http\Client`` suporta alguns sistemas de autenticação diferentes. Diferentes
+estratégias de autenticação podem ser adicionadas por desenvolvedores. As estratégias de autenticação são chamadas
+antes do envio da requisição e permitem que cabeçalhos sejam adicionados ao
+contexto da requisição.
 
 Usando Autenticação Básica
---------------------------
+---------------------------
 
 Um exemplo de autenticação básica::
 
     $http = new Client();
     $response = $http->get('http://example.com/profile/1', [], [
-      'auth' => ['username' => 'mark', 'password' => 'secret']
+      'auth' => ['username' => 'mark', 'password' => 'secret'],
     ]);
 
-Por padrão, o ``Cake\Http\Client`` usará a autenticação básica se não
-houver uma chave ``'type'`` na opção auth.
+Por padrão, ``Cake\Http\Client`` usará autenticação básica se não houver
+chave ``'type'`` na opção auth.
 
-Usando a Autenticação Digest
-----------------------------
+Usando Autenticação Digest
+---------------------------
 
-Um exemplo de autenticação básica::
+Um exemplo de autenticação digest::
 
     $http = new Client();
     $response = $http->get('http://example.com/profile/1', [], [
-      'auth' => [
-        'type' => 'digest',
-        'username' => 'mark',
-        'password' => 'secret',
-        'realm' => 'myrealm',
-        'nonce' => 'onetimevalue',
-        'qop' => 1,
-        'opaque' => 'someval'
-      ]
+        'auth' => [
+            'type' => 'digest',
+            'username' => 'mark',
+            'password' => 'secret',
+            'realm' => 'myrealm',
+            'nonce' => 'onetimevalue',
+            'qop' => 1,
+            'opaque' => 'someval',
+        ],
     ]);
 
-Ao definir a chave 'type' como 'digest', você informa ao subsistema de autenticação
-para usar a autenticação digest.
+Ao definir a chave 'type' como 'digest', você informa ao subsistema de autenticação para
+usar autenticação digest. A autenticação digest suporta os seguintes
+algoritmos:
+
+* MD5
+* SHA-256
+* SHA-512-256
+* MD5-sess
+* SHA-256-sess
+* SHA-512-256-sess
+
+O algoritmo será escolhido automaticamente com base no desafio do servidor.
 
 Autenticação OAuth 1
---------------------
+---------------------
 
-Muitos serviços da web moderna exigem autenticação OAuth para acessar suas APIs.
-A autenticação OAuth incluída pressupõe que você já tenha sua chave e segredo do
-consumidor::
+Muitos webservices modernos exigem autenticação OAuth para acessar suas APIs.
+A autenticação OAuth incluída assume que você já tem sua chave de consumidor
+e segredo de consumidor::
 
     $http = new Client();
     $response = $http->get('http://example.com/profile/1', [], [
-      'auth' => [
-        'type' => 'oauth',
-        'consumerKey' => 'bigkey',
-        'consumerSecret' => 'secret',
-        'token' => '...',
-        'tokenSecret' => '...',
-        'realm' => 'tickets',
-      ]
+        'auth' => [
+            'type' => 'oauth',
+            'consumerKey' => 'bigkey',
+            'consumerSecret' => 'secret',
+            'token' => '...',
+            'tokenSecret' => '...',
+            'realm' => 'tickets',
+        ],
     ]);
 
 Autenticação OAuth 2
---------------------
+---------------------
 
-Como OAuth2 geralmente é um único cabeçalho, não há um adaptador de
-autenticação especializado. Em vez disso, você pode criar um cliente
-com o token de acesso::
+Como OAuth2 é frequentemente um único cabeçalho, não há um adaptador de
+autenticação especializado. Em vez disso, você pode criar um cliente com o token de acesso::
 
     $http = new Client([
-        'headers' => ['Authorization' => 'Bearer ' . $accessToken]
+        'headers' => ['Authorization' => 'Bearer ' . $accessToken],
     ]);
     $response = $http->get('https://example.com/api/profile/1');
 
-Autenticação no Proxy
----------------------
+Autenticação de Proxy
+----------------------
 
-Alguns proxies requerem autenticação para serem usados. Geralmente, essa
-autenticação é Básica, mas pode ser implementada por qualquer adaptador
-de autenticação. Por padrão, o Http\\Client assumirá a autenticação Básica,
-a menos que a chave de tipo seja definida::
+Alguns proxies exigem autenticação para usá-los. Geralmente, essa autenticação
+é básica, mas pode ser implementada por qualquer adaptador de autenticação. Por padrão,
+Http\\Client assumirá autenticação básica, a menos que a chave type seja definida::
 
     $http = new Client();
     $response = $http->get('http://example.com/test.php', [], [
-      'proxy' => [
-        'username' => 'mark',
-        'password' => 'testing',
-        'proxy' => '127.0.0.1:8080',
-      ]
+        'proxy' => [
+            'username' => 'mark',
+            'password' => 'testing',
+            'proxy' => '127.0.0.1:8080',
+        ],
     ]);
 
-O segundo parâmetro de proxy deve ser uma string com um IP ou um domínio
-sem protocolo. As informações de nome de usuário e senha serão passadas
-pelos cabeçalhos da solicitação, enquanto a string do proxy será passada
-por `stream_context_create()
+O segundo parâmetro proxy deve ser uma string com um IP ou um domínio sem
+protocolo. As informações de nome de usuário e senha serão passadas através dos
+cabeçalhos da requisição, enquanto a string proxy será passada através de
+`stream_context_create()
 `_.
 
 .. _http_client_scoped_client:
 
-Criação de Clientes com Escopo
-==============================
+Criando Clientes com Escopo
+============================
 
-Ter que redigitar o nome de domínio, as configurações de autenticação e
-proxy pode se tornar tedioso e sujeito a erros. Para reduzir a chance de
-erro e aliviar um pouco do tédio, você pode criar clientes com escopo::
+Ter que redigitar o nome de domínio, autenticação e configurações de proxy pode se tornar
+tedioso e propenso a erros. Para reduzir a chance de erro e aliviar parte do
+tédio, você pode criar clientes com escopo::
 
-    // Crie um cliente com escopo definido.
+    // Criar um cliente com escopo.
     $http = new Client([
-      'host' => 'api.example.com',
-      'scheme' => 'https',
-      'auth' => ['username' => 'mark', 'password' => 'testing']
+        'host' => 'api.example.com',
+        'scheme' => 'https',
+        'auth' => ['username' => 'mark', 'password' => 'testing'],
     ]);
 
-    // Faça uma solicitação para api.example.com
+    // Fazer uma requisição para api.example.com
     $response = $http->get('/test.php');
 
+Se seu cliente com escopo precisa apenas de informações da URL, você pode usar
+``createFromUrl()``::
+
+    $http = Client::createFromUrl('https://api.example.com/v1/test');
+
+O código acima criaria uma instância de cliente com as opções ``protocol``, ``host`` e
+``basePath`` definidas.
+
 As seguintes informações podem ser usadas ao criar um cliente com escopo:
 
 * host
+* basePath
 * scheme
 * proxy
 * auth
@@ -287,48 +307,47 @@ As seguintes informações podem ser usadas ao criar um cliente com escopo:
 * ssl_verify_depth
 * ssl_verify_host
 
-Qualquer uma dessas opções pode ser substituída, especificando-as ao fazer
-solicitações. host, scheme, proxy, port são substituídos no URL do pedido::
+Qualquer uma dessas opções pode ser sobrescrita especificando-as ao fazer requisições.
+host, scheme, proxy, port são sobrescritos na URL da requisição::
 
-    // Usando o cliente com escopo criado anteriormente.
+    // Usando o cliente com escopo que criamos anteriormente.
     $response = $http->get('http://foo.com/test.php');
 
-O exemplo acima irá substituir o domínio, esquema e porta. No entanto, essa solicitação
-continuará usando todas as outras opções definidas quando o cliente com escopo foi criado.
-Veja :ref:`http_client_request_options` para mais informações sobre as opções suportadas.
+O código acima substituirá o domínio, esquema e porta. No entanto, esta requisição continuará
+usando todas as outras opções definidas quando o cliente com escopo foi criado.
+Veja :ref:`http_client_request_options` para mais informações sobre as opções
+suportadas.
 
-Configuração e Gerenciamento de Cookies
-=======================================
+Definindo e Gerenciando Cookies
+================================
 
-Http\\Client também pode aceitar cookies ao fazer solicitações.
-Além de aceitar cookies, ele também armazenará automaticamente
-cookies válidos definidos nas respostas. Qualquer resposta com
-cookies, os terá armazenados na instância de origem do Http\\Client.
-Os cookies armazenados em uma instância do cliente são incluídos
-automaticamente em solicitações futuras para combinações de
-domínio + caminho que corresponderem::
+Http\\Client também pode aceitar cookies ao fazer requisições. Além de
+aceitar cookies, ele também armazenará automaticamente cookies válidos definidos nas
+respostas. Qualquer resposta com cookies os armazenará na instância
+de origem do Http\\Client. Os cookies armazenados em uma instância de Client são
+automaticamente incluídos em requisições futuras para combinações de domínio + caminho que
+correspondam::
 
     $http = new Client([
         'host' => 'cakephp.org'
     ]);
 
-    // Faça uma solicitação que defina alguns cookies
+    // Fazer uma requisição que define alguns cookies
     $response = $http->get('/');
 
-    // Os cookies da primeira solicitação serão incluídos
+    // Os cookies da primeira requisição serão incluídos
     // por padrão.
     $response2 = $http->get('/changelogs');
 
-Você sempre pode substituir os cookies incluídos automaticamente,
-definindo-os nos parâmetros ``$options`` da solicitação::
+Você sempre pode sobrescrever os cookies incluídos automaticamente definindo-os nos
+parâmetros ``$options`` da requisição::
 
-    // Substitua um cookie armazenado por um valor personalizado.
+    // Substituir um cookie armazenado com um valor personalizado.
     $response = $http->get('/changelogs', [], [
-        'cookies' => ['sessionid' => '123abc']
+        'cookies' => ['sessionid' => '123abc'],
     ]);
 
-Você pode adicionar objetos de cookie ao cliente após criá-lo
-usando o método ``addCookie()``::
+Você pode adicionar objetos de cookie ao cliente após criá-lo usando o método ``addCookie()``::
 
     use Cake\Http\Cookie\Cookie;
 
@@ -337,6 +356,17 @@ usando o método ``addCookie()``::
     ]);
     $http->addCookie(new Cookie('session', 'abc123'));
 
+Eventos do Cliente
+==================
+
+``Client`` emitirá eventos quando requisições forem enviadas. O evento
+``HttpClient.beforeSend`` é disparado antes que uma requisição seja enviada, e
+``HttpClient.afterSend`` é disparado após uma requisição ser enviada. Você pode modificar a
+requisição ou definir uma resposta em um listener ``beforeSend``. O evento ``afterSend``
+é disparado para todas as requisições, mesmo aquelas que tiveram suas respostas definidas por
+um evento ``beforeSend``.
+
+
 .. _httpclient-response-objects:
 
 Objetos de Resposta
@@ -346,88 +376,86 @@ Objetos de Resposta
 
 .. php:class:: Response
 
-Os objetos de resposta têm vários métodos para inspecionar os dados recebidos.
+Os objetos de resposta têm vários métodos para inspecionar os dados da resposta.
 
-Leitura do Corpo da Resposta
-----------------------------
+Lendo Corpos de Resposta
+-------------------------
 
 Você lê todo o corpo da resposta como uma string::
 
-    // Leia toda a resposta como uma string.
+    // Ler toda a resposta como uma string.
     $response->getStringBody();
 
-Você também pode acessar o objeto stream para a resposta e usar seus métodos::
+Você também pode acessar o objeto stream da resposta e usar seus métodos::
 
-    // Obtêm um Psr\Http\Message\StreamInterface contendo o corpo da resposta
+    // Obter um Psr\Http\Message\StreamInterface contendo o corpo da resposta
     $stream = $response->getBody();
 
-    // Leia um fluxo de 100 bytes por vez.
+    // Ler um stream de 100 bytes por vez.
     while (!$stream->eof()) {
         echo $stream->read(100);
     }
 
 .. _http-client-xml-json:
 
-Lendo Corpo de Respostas JSON e XML
------------------------------------
+Lendo Corpos de Resposta JSON e XML
+------------------------------------
 
-Como as respostas JSON e XML são comumente usadas, os objetos de resposta
-fornecem acessores fáceis de usar para ler dados decodificados. Os dados
-JSON são decodificados em uma matriz, enquanto os dados XML são decodificados
-em uma árvore ``SimpleXMLElement``::
+Como respostas JSON e XML são comumente usadas, objetos de resposta fornecem uma maneira
+de usar acessores para ler dados decodificados. Dados JSON são decodificados em um array, enquanto
+dados XML são decodificados em uma árvore ``SimpleXMLElement``::
 
-    // Obtêm algum XML
+    // Obter algum XML
     $http = new Client();
     $response = $http->get('http://example.com/test.xml');
     $xml = $response->getXml();
 
-    // Obtêm algum JSON
+    // Obter algum JSON
     $http = new Client();
     $response = $http->get('http://example.com/test.json');
     $json = $response->getJson();
 
-Os dados de resposta decodificados são armazenados no objeto de resposta,
-portanto, acessá-lo várias vezes não tem custo adicional.
+Os dados de resposta decodificados são armazenados no objeto de resposta, então acessá-los
+várias vezes não tem custo adicional.
 
-Acessando Cabeçalhos da Resposta
---------------------------------
+Acessando Cabeçalhos de Resposta
+---------------------------------
 
-Você pode acessar os cabeçalhos por meio de alguns métodos diferentes. Os nomes
-dos cabeçalhos são sempre tratados como valores que não diferenciam maiúsculas
-de minúsculas ao acessá-los por meio de métodos::
+Você pode acessar cabeçalhos através de alguns métodos diferentes. Os nomes dos cabeçalhos são sempre
+tratados como valores não sensíveis a maiúsculas/minúsculas ao acessá-los através de métodos::
 
-    // Obtenha todos os cabeçalhos como uma matriz associativa.
+    // Obter todos os cabeçalhos como um array associativo.
     $response->getHeaders();
 
-    // Obtenha um único cabeçalho como uma matriz.
+    // Obter um único cabeçalho como um array.
     $response->getHeader('content-type');
 
-    // Obtenha um cabeçalho como uma string
+    // Obter um cabeçalho como uma string
     $response->getHeaderLine('content-type');
 
-    // Obtenha a codificação da resposta
+    // Obter a codificação da resposta
     $response->getEncoding();
 
-Acessando Dados do Cookie
--------------------------
+Acessando Dados de Cookie
+--------------------------
 
-Você pode ler os cookies com alguns métodos diferentes,
-dependendo de quantos dados você precisa sobre os cookies::
+Você pode ler cookies com alguns métodos diferentes, dependendo de quanto
+dado você precisa sobre os cookies::
 
-    // Obtenha todos os cookies (dados completos)
+    // Obter todos os cookies (dados completos)
     $response->getCookies();
 
-    // Obtenha o valor de um único cookie.
+    // Obter o valor de um único cookie.
     $response->getCookie('session_id');
 
-    // Obtenha os dados completos para um único cookie,
-    // incluindo valor, expiração, caminho, httponly, chaves seguras.
+    // Obter os dados completos de um único cookie
+    // inclui as chaves value, expires, path, httponly, secure.
     $response->getCookieData('session_id');
 
 Verificando o Código de Status
-------------------------------
+-------------------------------
 
-Os objetos de resposta fornecem alguns métodos para verificar os códigos de status::
+Objetos de resposta fornecem alguns métodos para verificar códigos de status::
 
     // A resposta foi 20x
     $response->isOk();
@@ -435,21 +463,129 @@ Os objetos de resposta fornecem alguns métodos para verificar os códigos de st
     // A resposta foi 30x
     $response->isRedirect();
 
-    // Obtenha o código de status
+    // Obter o código de status
     $response->getStatusCode();
 
-Alteração de Adaptadores de Transporte
-======================================
+Alterando Adaptadores de Transporte
+====================================
 
-Por padrão, o ``Http\Client`` irá preferir usar um adaptador de transporte
-baseado em ``curl``. Se a extensão curl não estiver disponível, um adaptador
-baseado em fluxo será usado. Você pode forçar a seleção de um adaptador de
-transporte usando uma opção de construtor::
+Por padrão, ``Http\Client`` preferirá usar um adaptador de transporte baseado em ``curl``.
+Se a extensão curl não estiver disponível, um adaptador baseado em stream será usado
+em vez disso. Você pode forçar a seleção de um adaptador de transporte usando uma opção de construtor::
 
     use Cake\Http\Client\Adapter\Stream;
 
-    $client = new Client(['adapter' => Stream::class]);
+    $http = new Client(['adapter' => Stream::class]);
+
+Eventos
+=======
+
+O cliente HTTP dispara alguns eventos antes e depois de enviar uma requisição
+que permitem modificar a requisição ou resposta ou fazer outras tarefas como
+cache, registro, etc.
+
+HttpClient.beforeSend
+---------------------
+
+::
+
+    // Em algum lugar antes de chamar um dos métodos do cliente HTTP que faz uma requisição
+    $http->getEventManager()->on(
+        'HttpClient.beforeSend',
+        function (
+            \Cake\Http\Client\ClientEvent $event,
+            \Cake\Http\Client\Request $request,
+            array $adapterOptions,
+            int $redirects
+        ) {
+            // Modificar a requisição
+            $event->setRequest(....);
+            // Modificar as opções do adaptador
+            $event->setAdapterOptions(....);
+
+            // Pular a requisição real retornando uma resposta.
+            // Você pode usar $event->setResult($response) para obter o mesmo resultado.
+            return new \Cake\Http\Client\Response(body: 'something');
+        }
+    );
+
+HttpClient.afterSend
+--------------------
+
+::
+
+    // Em algum lugar antes de chamar um dos métodos do cliente HTTP que faz uma requisição
+    $http->getEventManager()->on(
+        'HttpClient.afterSend',
+        function (
+            \Cake\Http\Client\ClientEvent $event,
+            \Cake\Http\Client\Request $request,
+            array $adapterOptions,
+            int $redirects,
+            bool $requestSent // Indica se a requisição foi realmente enviada
+                              // ou resposta retornada do evento ``beforeSend``
+        ) {
+            // Obter a resposta
+            $response = $event->getResponse();
+
+            // Retornar uma nova/modificada resposta.
+            // Você pode usar $event->setResult($response) para obter o mesmo resultado.
+            return new \Cake\Http\Client\Response(body: 'something');
+        }
+    );
+
+.. _httpclient-testing:
+
+Testes
+======
+
+.. php:namespace:: Cake\Http\TestSuite
+
+.. php:trait:: HttpClientTrait
+
+Em testes, você frequentemente desejará criar respostas simuladas para APIs externas. Você pode
+usar o ``HttpClientTrait`` para definir respostas para as requisições que sua aplicação
+está fazendo::
+
+    use Cake\Http\TestSuite\HttpClientTrait;
+    use Cake\TestSuite\TestCase;
+
+    class CartControllerTests extends TestCase
+    {
+        use HttpClientTrait;
+
+        public function testCheckout()
+        {
+            // Simular uma requisição POST que será feita.
+            $this->mockClientPost(
+                'https://example.com/process-payment',
+                $this->newClientResponse(200, [], json_encode(['ok' => true]))
+            );
+            $this->post("/cart/checkout");
+            // Fazer asserções.
+        }
+    }
+
+Existem métodos para simular os métodos HTTP mais comumente usados::
+
+    $this->mockClientGet(/* ... */);
+    $this->mockClientPatch(/* ... */);
+    $this->mockClientPost(/* ... */);
+    $this->mockClientPut(/* ... */);
+    $this->mockClientDelete(/* ... */);
+
+.. php:method:: newClientResponse(int $code = 200, array $headers = [], string $body = '')
+
+Como visto acima, você pode usar o método ``newClientResponse()`` para criar respostas
+para as requisições que sua aplicação fará. Os cabeçalhos precisam ser uma lista de
+strings::
+
+    $headers = [
+        'Content-Type: application/json',
+        'Connection: close',
+    ];
+    $response = $this->newClientResponse(200, $headers, $body)
 
 .. meta::
-    :title lang=pt: Cliente Http
-    :keywords lang=pt: nome de matriz,dados de matriz,parametros de consulta,string de consulta,classe php,teste de tipo,string de dado,google,consulta de resultados,webservices,apis,parametros,cakephp,metodos,pesquisando resultados
+    :title lang=pt: HttpClient
+    :keywords lang=pt: array name,array data,query parameter,query string,php class,string query,test type,string data,google,query results,webservices,apis,parameters,cakephp,meth,search results
diff --git a/pt/core-libraries/inflector.rst b/pt/core-libraries/inflector.rst
index 06e44d0998..438f2f08c0 100644
--- a/pt/core-libraries/inflector.rst
+++ b/pt/core-libraries/inflector.rst
@@ -5,24 +5,25 @@ Inflector
 
 .. php:class:: Inflector
 
-A classe ``Inflector`` recebe uma string e a manipula afim de suportar variações
-de palavas como pluralizações ou CamelCase e normalmente é acessada
+A classe Inflector pega uma string e pode manipulá-la para lidar com variações de palavras
+como pluralização ou camelização e normalmente é acessada
 estaticamente. Exemplo:
 ``Inflector::pluralize('example')`` retorna "examples".
 
-Você pode testar as inflexões em `inflector.cakephp.org
-`_.
+Você pode testar as inflexões online em `inflector.cakephp.org
+`_ ou `sandbox.dereuromark.de
+`_.
 
 .. _inflector-methods-summary:
 
-Resumo dos métodos de Inflexão e Suas Saídas
-============================================
+Resumo dos Métodos do Inflector e Suas Saídas
+==============================================
 
-Resumo rápido dos métodos embutidos no Inflector e os resultados que produzem
-quando fornecidos um argumento de palavra composta.
+Resumo rápido dos métodos integrados do Inflector e os resultados que eles produzem
+quando fornecido um argumento com várias palavras:
 
 +-------------------+---------------+---------------+
-| Method            | Argument      | Output        |
+| Método            | Argumento     | Saída         |
 +===================+===============+===============+
 | ``pluralize()``   | BigApple      | BigApples     |
 +                   +---------------+---------------+
@@ -60,27 +61,23 @@ quando fornecidos um argumento de palavra composta.
 +                   +---------------+---------------+
 |                   | big apples    | bigApples     |
 +-------------------+---------------+---------------+
-| ``slug()``        | Big Apple     | big-apple     |
-+                   +---------------+---------------+
-|                   | BigApples     | BigApples     |
-+-------------------+---------------+---------------+
 
-Criando as formas singulares e plurais
-======================================
+Criando Formas Plural e Singular
+=================================
 
 .. php:staticmethod:: singularize($singular)
 .. php:staticmethod:: pluralize($singular)
 
-Tanto ``pluralize()`` quanto ``singularize()`` funcionam para a maioria dos
-substantivos do Inglês. Caso seja necessário o suporte para outras línguas,
-você pode usar :ref:`inflection-configuration` para personalizar as regras usadas::
+Tanto ``pluralize`` quanto ``singularize()`` funcionam na maioria dos substantivos em inglês. Se você precisa
+suportar outros idiomas, você pode usar :ref:`inflection-configuration` para
+personalizar as regras usadas::
 
     // Apples
     echo Inflector::pluralize('Apple');
 
 .. note::
-    ``pluralize()`` pode não funcionar corretamente nos casos onde um substantivo já
-    esteja em sua forma plural.
+
+    ``pluralize()`` não deve ser usado em um substantivo que já está em sua forma plural.
 
 .. code-block:: php
 
@@ -88,16 +85,16 @@ você pode usar :ref:`inflection-configuration` para personalizar as regras usad
     echo Inflector::singularize('People');
 
 .. note::
-    ``singularize()`` pode não funcionar corretamente nos casos onde um substantivo já
-    esteja em sua forma singular.
 
-Criando as formas CamelCase e nome_sublinhado
-=============================================
+    ``singularize()`` não deve ser usado em um substantivo que já está em sua forma singular.
+
+Criando Formas CamelCase e under_scored
+========================================
 
 .. php:staticmethod:: camelize($underscored)
 .. php:staticmethod:: underscore($camelCase)
 
-Estes métodos são úteis para a criação de nomes de classe ou de propriedades::
+Esses métodos são úteis ao criar nomes de classes ou nomes de propriedades::
 
     // ApplePie
     Inflector::camelize('Apple_pie')
@@ -105,32 +102,32 @@ Estes métodos são úteis para a criação de nomes de classe ou de propriedade
     // apple_pie
     Inflector::underscore('ApplePie');
 
-É importante ressaltar que ``underscore()`` irá converter apenas palavras formatadas
-em CamelCase. Palavras com espaços serão convertidas para caixa baixa, mas não serão
-separadas por sublinhado.
+Deve-se notar que underscore converterá apenas palavras formatadas em camelCase.
+Palavras que contêm espaços serão colocadas em minúsculas, mas não conterão um
+underscore.
 
-Criando formas legíveis para humanos
-====================================
+Criando Formas Legíveis para Humanos
+=====================================
 
 .. php:staticmethod:: humanize($underscored)
 
-Este método é útil para converter da forma sublinhada para o "Formato Título" para
-a leitura humana::
+Este método é útil ao converter formas com underscore em formas "Title Case"
+para valores legíveis por humanos::
 
     // Apple Pie
     Inflector::humanize('apple_pie');
 
-Criando formatos para nomes de tabelas e classes
-================================================
+Criando Formas de Nome de Tabela e Classe
+==========================================
 
 .. php:staticmethod:: classify($underscored)
 .. php:staticmethod:: dasherize($dashed)
 .. php:staticmethod:: tableize($camelCase)
 
-Ao gerar o código ou usar as convenções do CakePHP, você pode precisar inferir
-os nomes das tabelas ou classes::
+Ao gerar código ou usar as convenções do CakePHP, você pode precisar inflexionar
+nomes de tabelas ou nomes de classes::
 
-    // UserProfileSettings
+    // UserProfileSetting
     Inflector::classify('user_profile_settings');
 
     // user-profile-setting
@@ -139,66 +136,52 @@ os nomes das tabelas ou classes::
     // user_profile_settings
     Inflector::tableize('UserProfileSetting');
 
-Criando nomes de variáveis
-==========================
+Criando Nomes de Variáveis
+===========================
 
 .. php:staticmethod:: variable($underscored)
 
-Nomes de variáveis geralmente são úteis em tarefas de meta-programação que
-involvem a geração de código ou rotinas baseadas em convenções::
+Nomes de variáveis são frequentemente úteis ao fazer tarefas de meta-programação que envolvem
+gerar código ou fazer trabalho baseado em convenções::
 
     // applePie
     Inflector::variable('apple_pie');
 
-Criando strings de URL seguras
-==============================
-
-.. php:staticmethod:: slug($word, $replacement = '-')
-
-``slug()`` converte caracteres especiais em suas versões normais e converte
-os caracteres não encontrados e espaços em traços. O método ``slug()`` espera
-que a codificação seja UTF-8::
-
-    // apple-puree
-    Inflector::slug('apple purée');
-
-.. note::
-    ``Inflector::slug()`` foi depreciado desde a versão 3.2.7. Procure usar ``Text::slug()``
-    de agora em diante.
 
 .. _inflection-configuration:
 
-Configuração da inflexão
-========================
+Configuração de Inflexão
+=========================
 
-As convenções de nomes do CakePHP podem ser bem confortáveis. Você pode nomear sua
-tabela no banco de dados como ``big_boxes``, seu modelo como ``BigBoxes``, seu
-controlador como ``BigBoxesController`` e tudo funcionará automaticamente. O CakePHP
-entrelaça todos estes conceitos através da inflexão das palavras em suas formas
-singulares e plurais.
+As convenções de nomenclatura do CakePHP podem ser realmente boas - você pode nomear sua tabela
+de banco de dados ``big_boxes``, seu modelo ``BigBoxes``, seu controller
+``BigBoxesController``, e tudo funciona junto automaticamente. A
+maneira como o CakePHP sabe como unir as coisas é *inflexionando* as palavras
+entre suas formas singular e plural.
 
-Porém ocasionalmente (especialmente para os nossos amigos não Anglófonos) podem encontrar
-situações onde o infletor do CakePHP (a classe que pluraliza, singulariza, transforma em
-CamelCase e em nome\_sublinhado) não funciona como você gostaria. Caso o CakePHP não
-reconheça seu "quaisquer" ou "lápis", você pode ensiná-lo a entender seus casos especiais.
+Há ocasiões (especialmente para nossos amigos que não falam inglês) em que você
+pode se deparar com situações em que o inflector do CakePHP (a classe que pluraliza,
+singulariza, cameliza e usa under\_scores) pode não funcionar como você gostaria. Se
+o CakePHP não reconhecer seus Foci ou Fish, você pode informar ao CakePHP sobre seus
+casos especiais.
 
-Carregando inflexões personalizadas
------------------------------------
+Carregando Inflexões Personalizadas
+------------------------------------
 
 .. php:staticmethod:: rules($type, $rules, $reset = false)
 
-Define novas inflexões e transliterações para o ``Inflector`` usar. Geralmente este método
-deve ser chamado no seu **config/bootstrap.php**::
+Defina novas regras de inflexão e transliteração para o Inflector usar. Frequentemente,
+este método é usado em seu **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
+    Inflector::rules('irregular', ['phylum' => 'phyla']); // A chave é a forma singular, o valor é a forma plural
 
-As regras ditadas por este método serão agregadas aos conjuntos de inflexão definidos em ``Cake/Utility/Inflector``,
-onde elas terão prioridade sobre as regras já declaradas por padrão. Você pode usar ``Inflector::reset()``
-para limpar todas as regras e retornar o ``Inflector`` para seu estado original.
+As regras fornecidas serão mescladas nos respectivos conjuntos de inflexão definidos em
+``Cake/Utility/Inflector``, com as regras adicionadas tendo precedência sobre as regras
+principais. Você pode usar ``Inflector::reset()`` para limpar regras e restaurar o
+estado original do Inflector.
 
 .. meta::
     :title lang=pt: 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
-    :keywords lang=pt: inflexão, infletor, variações de palavras, caracteres especiais, conversão, sublinhado, variações, plural, pluralização, singular, singularização, regras, urls seguras
+    :keywords lang=pt: 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/pt/core-libraries/internationalization-and-localization.rst b/pt/core-libraries/internationalization-and-localization.rst
index bf1b286e94..b905886198 100644
--- a/pt/core-libraries/internationalization-and-localization.rst
+++ b/pt/core-libraries/internationalization-and-localization.rst
@@ -1,97 +1,93 @@
-Internacionalização e Localização
-#################################
+Internacionalização & Localização
+##################################
 
-.. note::
-    Atualmente, a documentação desta página não é suportada em português.
-
-    Por favor, sinta-se a vontade para nos enviar um *pull request* para o
-    `Github `_ ou use o botão
-    **IMPROVE THIS DOC** para propor suas mudanças diretamente.
-
-    Você pode consultar a versão em inglês deste tópico através do seletor de
-    idiomas localizado ao lado direito do campo de buscas da documentação.
+Uma das melhores maneiras de uma aplicação alcançar um público maior é atender
+a vários idiomas. Isso pode frequentemente se revelar uma tarefa assustadora, mas os
+recursos de internacionalização e localização no CakePHP tornam isso muito mais fácil.
 
-Uma das melhores maneiras para uma aplicação alcançar uma maior audiência é
-atender a vários idiomas. Isso muitas vezes pode provar ser uma tarefa difícil,
-mas a internacionalização e recursos de localização do CakePHP tornam muito mais
-fácil.
-
-Primeiro, é importante entender a terminologia.  *Internacionalização* refere-se
-à capacidade de um aplicativo ser localizado. O termo *localização* refere-se à
-adaptação de uma aplicação, para atender idioma específico (ou cultura)
-requisitos (Isto é, uma "localidade"). Internacionalização e Localização são
-frequentemente abreviado como i18n (internacionalization) e l10n (localization);
-18 e 10 são o número de caracteres entre a primeira e última letra de cada
-termo.
+Primeiro, é importante entender alguma terminologia. *Internacionalização*
+refere-se à capacidade de uma aplicação ser localizada. O termo *localização*
+refere-se à adaptação de uma aplicação para atender requisitos específicos de idioma (ou
+cultura) (ou seja, uma "locale"). Internacionalização e localização
+são frequentemente abreviadas como i18n e l10n respectivamente; 18 e 10 são o número
+de caracteres entre o primeiro e o último caractere.
 
 Configurando Traduções
 ======================
 
-Existem apenas alguns passos para ir de um aplicativo de um único idioma a uma
-aplicação multi-lingual, o primeiro deles é fazer uso da função
-:php:func: `__()` em seu código. Abaixo está um exemplo de algum código para uma
-aplicação de um único idioma::
+Existem apenas alguns passos para ir de uma aplicação de idioma único para uma
+aplicação multi-idioma, o primeiro dos quais é fazer uso da
+função :php:func:`__()` no seu código. Abaixo está um exemplo de algum código para uma
+aplicação de idioma único::
 
     

Popular Articles

-Para Internacionalizar seu código, tudo que você precisa fazer é refatorar a -string usando :php:func: `__()` por exemplo:: +Para internacionalizar seu código, tudo que você precisa fazer é envolver strings na +função :php:func:`__()` assim::

-Fazendo nada mais, estes dois exemplos de código são funcionalmente idênticos - -ambos irão enviar o mesmo conteúdo para o navegador. A função :php:func: `__()` -irá traduzir a string passada se a tradução estiver disponível, ou devolvê-la -inalterada. +Não fazendo nada mais, esses dois exemplos de código são funcionalmente idênticos - eles +enviarão o mesmo conteúdo para o navegador. A função :php:func:`__()` +traduzirá a string passada se uma tradução estiver disponível, ou a retornará +sem modificações. -Arquivos de Idiomas -------------------- +Arquivos de Idioma +------------------ -Traduções podem ser disponibilizados usando arquivos de idiomas armazenados na -aplicação. O formato padrão para arquivos de tradução do CakePHP é o formato -`Gettext `_. Os arquivos precisam ser -colocado dentro do Diretório **resources/locales/** e dentro deste diretório, deve -haver uma subpasta para cada idioma, por exemplo:: +Traduções podem ser disponibilizadas usando arquivos de idioma armazenados na +aplicação. O formato padrão para arquivos de tradução do CakePHP é o +formato `Gettext `_. Os arquivos precisam ser +colocados em **resources/locales/** e dentro deste diretório, deve haver uma +subpasta para cada idioma que a aplicação precisa suportar:: - /resources - /locales - /en_US + resources/ + locales/ + en_US/ default.po - /en_GB + en_GB/ default.po validation.po - /es + es/ default.po -O domínio padrão é 'default', portanto, a pasta **resources/locales/** deve pelo menos -conter o arquivo **default.po** como mostrado acima. Um domínio refere-se a -qualquer arbitrário agrupamento de mensagens de tradução. Quando nenhum grupo é -usado, o grupo padrão é selecionado. - -As mensagens das Strings do core extraídos da biblioteca CakePHP podem ser -armazenado separadamente em um arquivo chamado **cake.po** em **resources/locales/**. -O `CakePHP localized library `_ possui -traduções para as mensagens traduzidas voltados para o cliente no núcleo (o -domínio Cake). Para usar esses arquivos, baixar ou copiá-los para o seu local -esperado: **resources/locales//cake.po**. Se sua localidade está incompleta ou -incorreta, por favor envie um PR neste repositório para corrigi-lo. - -Plugins também podem conter arquivos de tradução, a convenção é usar o -``under_score`` do nome do plugin como o domínio para a tradução mensagens:: - - MyPlugin - /resources - /locales - /fr +O domínio padrão é 'default', portanto a pasta de locale deve pelo menos +conter o arquivo **default.po** como mostrado acima. Um domínio refere-se a qualquer +agrupamento arbitrário de mensagens de tradução. Quando nenhum grupo é usado, o grupo padrão +é selecionado. + +As mensagens de strings principais extraídas da biblioteca CakePHP podem ser armazenadas +separadamente em um arquivo chamado **cake.po** em **resources/locales/**. +A `biblioteca localizada do CakePHP `_ abriga +traduções para as strings traduzidas voltadas para o cliente no núcleo (o domínio cake). +Para usar esses arquivos, vincule ou copie-os para o local esperado: +**resources/locales//cake.po**. Se sua locale estiver incompleta ou incorreta, +por favor envie um PR neste repositório para corrigi-la. + +Plugins também podem conter arquivos de tradução, a convenção é usar a +versão ``under_scored`` do nome do plugin como domínio para as mensagens +de tradução:: + + MyPlugin/ + resources/ + locales/ + fr/ my_plugin.po - /de + additional.po + de/ my_plugin.po -Pastas de tradução pode ser o código ISO de duas letras do idioma ou nome do -local completo, como ``fr_FR``, ``es_AR``, ``da_DK`` que contém tanto o idioma e -o país onde ele é falado. +As pastas de tradução podem ser o código ISO de duas ou três letras do idioma ou +o nome completo da locale ICU como ``fr_FR``, ``es_AR``, ``da_DK`` que contém +tanto o idioma quanto o país onde é falado. + +Veja https://www.localeplanet.com/icu/ para a lista completa de locales. + +.. versionchanged:: 4.5.0 + A partir de 4.5.0, plugins podem conter múltiplos domínios de tradução. Use + ``MyPlugin.additional`` para referenciar domínios de plugin. -Um exemplo de arquivo de tradução pode ser visto como: +Um exemplo de arquivo de tradução pode ser assim: .. code-block:: pot @@ -102,24 +98,25 @@ Um exemplo de arquivo de tradução pode ser visto como: msgstr "J'ai {0,number} ans" .. note:: - As traduções são armazenadas em cache - Certifique-se de que você sempre limpa - o cache após fazer alterações nas traduções! Você pode utilizar a ferramenta - :doc:`cache tool ` e executar por exemplo - ``bin/cake cache clear _cake_core_``, ou limpar manualmente a pasta - ``tmp/cache/persistent`` (se estiver utilizando o cache baseado em arquivo). + Traduções são armazenadas em cache - Certifique-se de sempre limpar o cache após + fazer alterações nas traduções! Você pode usar a + :doc:`ferramenta de cache ` e executar por exemplo + ``bin/cake cache clear _cake_core_``, ou limpar manualmente a pasta ``tmp/cache/persistent`` + (se estiver usando cache baseado em arquivo). -Extraindo arquivos .pot com I18n Shell --------------------------------------- +Extrair Arquivos Pot com I18n Shell +------------------------------------ -Para criar os arquivos .pot apartir de `__()` e outros tipos de mensagens -internacionalizadas que podem ser encontrados no código do aplicativo, você pode -usar o shell i18n. Por favor, leia o :doc:`Capítulo Seguinte -` para saber mais. +Para criar os arquivos pot a partir de `__()` e outros tipos internacionalizados de +mensagens que podem ser encontradas no código da aplicação, você pode usar o comando i18n. +Por favor leia o :doc:`capítulo seguinte ` para +aprender mais. -Definir a localidade padrão ---------------------------- -A localidade padrão pode ser definida em no arquivo **config/app.php**, -definindo ``App.default Locale``:: +Definindo a Locale Padrão +-------------------------- + +A locale padrão pode ser definida no seu arquivo **config/app.php** definindo +``App.defaultLocale``:: 'App' => [ ... @@ -127,99 +124,110 @@ definindo ``App.default Locale``:: ... ] -Isto vai controlar vários aspectos da aplicação, incluindo o padrão da linguagem -de traduções, o formato da data, formato de número e moeda sempre que qualquer -daqueles é exibida usando as bibliotecas de localização que o CakePHP fornece. +Isso controlará vários aspectos da aplicação, incluindo o idioma padrão das +traduções, o formato de data, formato de número e moeda sempre que qualquer +um deles for exibido usando as bibliotecas de localização que o CakePHP fornece. -Alterando o local em tempo de execução --------------------------------------- +Alterando a Locale em Tempo de Execução +---------------------------------------- -Para alterar o idioma para as mensagens traduzidas você pode chamar esse -método:: +Para alterar o idioma das strings traduzidas, você pode chamar este método:: use Cake\I18n\I18n; I18n::setLocale('de_DE'); -Isso também irá alterar a forma como números e datas são formatadas quando -usamos uma das ferramentas de localização. +Isso também mudará como números e datas são formatados ao usar uma das +ferramentas de localização. -Usando funções de tradução -========================== +Usando Funções de Tradução +=========================== -CakePHP fornece várias funções que o ajudarão a internacionalizar sua aplicação. -O mais utilizado é :php:func: `__()`. Esta função é usada para recuperar uma -única mensagem de tradução ou devolver a mesma String se não houver tradução:: +O CakePHP fornece várias funções que ajudarão você a internacionalizar sua +aplicação. A mais frequentemente usada é :php:func:`__()`. Esta função +é usada para recuperar uma única mensagem de tradução ou retornar a mesma string se nenhuma +tradução foi encontrada:: echo __('Popular Articles'); -Se você precisa agrupar suas mensagens, por exemplo, traduções dentro de um -plugin, você pode usar a função :php:func: `__d()` para buscar mensagens de -outro domínio:: +Se você precisa agrupar suas mensagens, por exemplo, traduções dentro de um plugin, +você pode usar a função :php:func:`__d()` para buscar mensagens de outro +domínio:: echo __d('my_plugin', 'Trending right now'); -Às vezes traduções de Strings podem ser ambíguos para as pessoas traduzindo-os. -Isso pode acontecer se duas sequências são idênticas, mas referem-se a coisas -diferentes. Por exemplo, "letter" tem vários significados em Inglês. Para -resolver esse problema, você pode usar a função :php:func: `__x()`:: +.. note:: + + Se você quiser traduzir plugins que têm namespace de vendor, você deve usar + a string de domínio ``vendor/plugin_name``. Mas o arquivo de idioma relacionado + será ``plugins///resources/locales//plugin_name.po`` + dentro da pasta do seu plugin. + +Às vezes, strings de tradução podem ser ambíguas para as pessoas que as traduzem. +Isso pode acontecer se duas strings são idênticas mas se referem a coisas diferentes. Por +exemplo, 'letter' tem vários significados em inglês. Para resolver esse problema, você +pode usar a função :php:func:`__x()`:: echo __x('written communication', 'He read the first letter'); echo __x('alphabet learning', 'He read the first letter'); -O primeiro argumento é o contexto da mensagem e a segunda é a mensagem a ser -traduzida. +O primeiro argumento é o contexto da mensagem e o segundo é a mensagem +a ser traduzida. -Usando variáveis em mensagens de tradução ------------------------------------------ +.. code-block:: pot -Funções de tradução permitem que você interpole variáveis para as mensagens -usando marcadores especiais definidos na própria mensagem ou na string -traduzida:: + msgctxt "written communication" + msgid "He read the first letter" + msgstr "Er las den ersten Brief" - echo __("Hello, my name is {0}, I'm {1} years old", ['Jefferson', 19]); +Usando Variáveis em Mensagens de Tradução +------------------------------------------ -Marcadores são numéricos, e correspondem às teclas na matriz passada. Você pode -também passar variáveis como argumentos independentes para a função:: +Funções de tradução permitem que você interpole variáveis nas mensagens usando +marcadores especiais definidos na própria mensagem ou na string traduzida:: + + echo __("Hello, my name is {0}, I'm {1} years old", ['Sara', 12]); + +Marcadores são numéricos e correspondem às chaves no array passado. Você também +pode passar variáveis como argumentos independentes para a função:: echo __("Small step for {0}, Big leap for {1}", 'Man', 'Humanity'); -Todas as funções de tradução apoiam as substituições de espaço reservado:: +Todas as funções de tradução suportam substituições de placeholder:: __d('validation', 'The field {0} cannot be left empty', 'Name'); __x('alphabet', 'He read the letter {0}', 'Z'); -O caracter ``'`` (aspas simples) age como um código de escape na mensagem de -tradução. Todas as variáveis entre aspas simples não serão substituídos e é -tratado como texto literal. Por exemplo:: +O caractere ``'`` (aspas simples) age como um código de escape em mensagens +de tradução. Quaisquer variáveis entre aspas simples não serão substituídas e são +tratadas como texto literal. Por exemplo:: __("This variable '{0}' be replaced.", 'will not'); -Ao usar duas aspas adjacentes suas variáveis e serão substituídos -adequadamente:: +Ao usar duas aspas adjacentes, suas variáveis serão substituídas adequadamente:: __("This variable ''{0}'' be replaced.", 'will'); -Estas funções tiram vantagem do -`UTI MessageFormatter `_ -para que possa traduzir mensagens e localizar datas, números e moeda, ao mesmo -tempo:: +Essas funções tiram proveito do +`ICU MessageFormatter `_ +para que você possa traduzir mensagens e localizar datas, números e moeda ao +mesmo tempo:: echo __( - 'Hi {0,string}, your balance on the {1,date} is {2,number,currency}', - ['Charles', '2014-01-13 11:12:00', 1354.37] + 'Hi {0}, your balance on the {1,date} is {2,number,currency}', + ['Charles', new DateTime('2014-01-13 11:12:00'), 1354.37] ); // Returns Hi Charles, your balance on the Jan 13, 2014, 11:12 AM is $ 1,354.37 -Os números em espaços reservados podem ser formatados, bem como com o controle -de grão fino da saída:: +Números em placeholders também podem ser formatados com controle refinado da +saída:: echo __( - 'You have traveled {0,number,decimal} kilometers in {1,number,integer} weeks', + 'You have traveled {0,number} kilometers in {1,number,integer} weeks', [5423.344, 5.1] ); @@ -231,61 +239,54 @@ de grão fino da saída:: // Returns There are 6,100,000,000 people on earth -Esta é a lista de especificadores de formato que você pode colocar após -a palavra ``number``: +Esta é a lista de especificadores de formatação que você pode colocar após a palavra ``number``: -* ``integer``: Remove a parte Decimal -* ``decimal``: Formata o número como um float -* ``currency``: Coloca o local do símbolo de moeda e números de casas decimais +* ``integer``: Remove a parte decimal +* ``currency``: Coloca o símbolo de moeda da locale e arredonda decimais * ``percent``: Formata o número como porcentagem -Datas também pode ser formatadas usando a palavra ``date`` após o número do -espaço reservado. Uma lista de opções adicionais a seguir: +Datas também podem ser formatadas usando a palavra ``date`` após o número +do placeholder. Uma lista de opções extras segue: * ``short`` * ``medium`` * ``long`` * ``full`` -A palavra ``time`` após o número de espaço reservado também é aceito e -compreende as mesmas opções que ``date``. - -.. note:: +A palavra ``time`` após o número do placeholder também é aceita e ela +entende as mesmas opções que ``date``. - Espaços reservados nomeados são suportados no PHP 5.5+ e são formatados como - ``{name}``. Ao usar espaços reservados nomeados para passar as variáveis em - uma matriz usando pares de chave/valor, por exemplo ``['name' => - 'Jefferson', 'age' => 19]``. +Você também pode usar placeholders nomeados como ``{name}`` nas strings de mensagem. +Ao usar placeholders nomeados, passe o placeholder e a substituição em um array usando pares chave/valor, +por exemplo:: - Recomenda-se usar o PHP 5.5 ou superior ao fazer uso de recursos de - internacionalização no CakePHP. A extensão ``php5-intl`` deve ser instalada - e a versão UTI deve estar acima 48.x.y (para verificar a versão UTI - ``Intl::getIcuVersion ()``). + // echos: Hi. My name is Sara. I'm 12 years old. + echo __("Hi. My name is {name}. I'm {age} years old.", ['name' => 'Sara', 'age' => 12]); Plurais ------- -Uma parte crucial de internacionalizar sua aplicação é a pluralização das suas -mensagens corretamente, dependendo do idioma que eles são mostrados. O CakePHP -fornece algumas maneiras de selecionar corretamente plurais em suas mensagens. +Uma parte crucial da internacionalização da sua aplicação é fazer com que suas mensagens +sejam pluralizadas corretamente dependendo do idioma em que são exibidas. O CakePHP fornece +algumas maneiras de selecionar corretamente plurais em suas mensagens. -Usando UTI para Seleção de Plural -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Usando Seleção de Plural ICU +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -O primeiro está aproveitando o formato de mensagem ``ICU`` que vem por padrão -nas funções de tradução. Nos arquivos de traduções você pode ter as seguintes -cadeias +A primeira é tirar proveito do formato de mensagem ``ICU`` que vem por +padrão nas funções de tradução. No arquivo de traduções você poderia ter +as seguintes strings .. code-block:: pot msgid "{0,plural,=0{No records found} =1{Found 1 record} other{Found # records}}" - msgstr "{0,plural,=0{Nenhum resultado} =1{1 resultado} other{# resultados}}" + msgstr "{0,plural,=0{Ningún resultado} =1{1 resultado} other{# resultados}}" msgid "{placeholder,plural,=0{No records found} =1{Found 1 record} other{Found {1} records}}" - msgstr "{placeholder,plural,=0{Nenhum resultado} =1{1 resultado} other{{1} resultados}}" + msgstr "{placeholder,plural,=0{Ningún resultado} =1{1 resultado} other{{1} resultados}}" -E na aplicação utilize o seguinte código para a saída de uma das traduções para -essa seqüência:: +E na aplicação use o seguinte código para exibir qualquer uma das +traduções para tal string:: __('{0,plural,=0{No records found }=1{Found 1 record} other{Found # records}}', [0]); @@ -300,40 +301,41 @@ essa seqüência:: // Returns "many resultados" because the argument {placeholder} is 2 and // argument {1} is 'many' -Um olhar mais atento para o formato que acabamos utilizado tornará evidente -como as mensagens são construídas:: +Uma olhada mais de perto no formato que acabamos de usar tornará evidente como as mensagens são +construídas:: { [count placeholder],plural, case1{message} case2{message} case3{...} ... } -O ``[count placeholder]`` pode ser o número-chave de qualquer das variáveis que -você passar para a função de tradução. Ele será usado para selecionar o plural -correto. +O ``[count placeholder]`` pode ser o número da chave do array de qualquer uma das variáveis +que você passa para a função de tradução. Ele será usado para selecionar a forma +plural correta. -Note que essa referência para ``[count placeholder]`` dentro de ``{message}`` -você tem que usar ``#``. +Note que para referenciar ``[count placeholder]`` dentro de ``{message}`` você tem que +usar ``#``. -Você pode usar ids de mensagem mais simples se você não deseja digitar a plena -seqüência de seleção para plural em seu código +Você pode, é claro, usar IDs de mensagem mais simples se não quiser digitar a sequência +completa de seleção de plural no seu código .. code-block:: pot msgid "search.results" - msgstr "{0,plural,=0{Nenhum resultado} =1{1 resultado} other{{1} resultados}}" + msgstr "{0,plural,=0{Ningún resultado} =1{1 resultado} other{{1} resultados}}" -Em seguida, use a nova string em seu código:: +Then use the new string in your code:: __('search.results', [2, 2]); // Returns: "2 resultados" -A última versão tem a desvantagem na qual existe uma necessidade de arquivar -mensagens e precisa de tradução para o idioma padrão mesmo, mas tem a vantagem -de que torna o código mais legível. +A última versão tem a desvantagem de que há necessidade de ter um arquivo +de mensagens de tradução mesmo para o idioma padrão, mas tem a vantagem de que torna +o código mais legível e deixa as strings complicadas de seleção de plural nos +arquivos de tradução. -Às vezes, usando o número de correspondência direta nos plurais é impraticável. -Por exemplo, idiomas como o árabe exigem um plural diferente quando você se -refere a algumas coisas. Nesses casos, você pode usar o UTI correspondentes. Em -vez de escrever:: +Às vezes usar correspondência direta de números em plurais é impraticável. Por exemplo, +idiomas como árabe requerem um plural diferente quando você se refere a +poucas coisas e outra forma plural para muitas coisas. Nesses casos você pode +usar os aliases de correspondência ICU. Em vez de escrever:: =0{No results} =1{...} other{...} @@ -341,16 +343,16 @@ Você pode fazer:: zero{No Results} one{One result} few{...} many{...} other{...} -Certifique-se de ler a -`Language Plural Rules Guide `_ +Certifique-se de ler o +`Guia de Regras de Plural de Idiomas `_ para obter uma visão completa dos aliases que você pode usar para cada idioma. -Usando Gettext para Seleção de Plural -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Usando Seleção de Plural Gettext +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -O segundo formato para seleção de plural aceito é a utilização das capacidades -embutidas de Gettext. Neste caso, plurais será armazenado nos arquivos ``.po``, -criando uma linha de tradução de mensagens separada por forma de plural: +O segundo formato de seleção de plural aceito é usar os recursos integrados +do Gettext. Neste caso, plurais serão armazenados no arquivo ``.po`` +criando uma linha de tradução de mensagem separada por forma plural: .. code-block:: pot @@ -363,19 +365,19 @@ criando uma linha de tradução de mensagens separada por forma de plural: # Translation in plural msgstr[1] "{0} ficheros eliminados" -Ao usar este outro formato, você é obrigado a usar outra tradução de forma -funcional:: +Ao usar este outro formato, você deve usar outra função +de tradução:: // Returns: "10 ficheros eliminados" $count = 10; __n('One file removed', '{0} files removed', $count, $count); - // Também é possível utilizá-lo dentro de um domínio + // It is also possible to use it inside a domain __dn('my_plugin', 'One file removed', '{0} files removed', $count, $count); -O número dentro de ``msgstr[]`` é o número atribuído pela Gettext para o plural -na forma da língua. Algumas línguas têm mais de duas formas plurais, para -exemplo *Croatian*: +O número dentro de ``msgstr[]`` é o número atribuído pelo Gettext para a forma +plural do idioma. Alguns idiomas têm mais de duas formas plurais, por +exemplo Croata: .. code-block:: pot @@ -385,21 +387,21 @@ exemplo *Croatian*: msgstr[1] "{0} datoteke su uklonjene" msgstr[2] "{0} datoteka je uklonjeno" -Por favor visite a `Launchpad languages page -`_ para uma explicação detalhada -dos números de formulário de plurais para cada idioma. +Por favor visite a `página de idiomas do Launchpad `_ +para uma explicação detalhada dos números de forma plural para cada idioma. -Criar seus próprios Tradutores -============================== +Criando Seus Próprios Tradutores +================================= -Se você precisar a divergir convenções do CakePHP sobre onde e como as mensagens -de tradução são armazenadas, você pode criar seu próprio carregador de mensagem -de tradução. A maneira mais fácil de criar o seu próprio tradutor é através da -definição de um carregador para um único domínio e localidade:: +Se você precisa divergir das convenções do CakePHP em relação a onde e como +mensagens de tradução são armazenadas, você pode criar seu próprio carregador de +mensagens de tradução. A maneira mais fácil de criar seu próprio tradutor é definindo um loader +para um único domínio e locale:: - use Aura\Intl\Package; + use Cake\I18n\Package; + // Prior to 4.2 you need to use Aura\Intl\Package - I18n::translator('animals', 'fr_FR', function () { + I18n::setTranslator('animals', function () { $package = new Package( 'default', // The formatting strategy (ICU) 'default' // The fallback domain @@ -412,31 +414,288 @@ definição de um carregador para um único domínio e localidade:: ]); return $package; - }); + }, 'fr_FR'); -O código acima pode ser adicionado ao seu **config/bootstrap.php** de modo que -as traduções podem ser encontradas antes de qualquer função de tradução é usada. -O mínimo absoluto que é necessário para a criação de um tradutor é que a função -do carregador deve retornar um ``Aura\Intl\Package`` objeto. Uma vez que o -código é no lugar que você pode usar as funções de tradução, como de costume:: +O código acima pode ser adicionado ao seu **config/bootstrap.php** para que +traduções possam ser encontradas antes de qualquer função de tradução ser usada. O mínimo +absoluto necessário para criar um tradutor é que a função loader +deve retornar um objeto ``Cake\I18n\Package`` (antes de 4.2 deveria ser um objeto ``Aura\Intl\Package``). +Uma vez que o código esteja no lugar, você pode usar as funções de tradução como de costume:: I18n::setLocale('fr_FR'); - __d('animals', 'Dog'); // Retorna "Chien" + __d('animals', 'Dog'); // Returns "Chien" -Como você vê objetos, ``Package`` carregam mensagens de tradução como uma -matriz. Você pode passar o método ``setMessages()`` da maneira que quiser: com -código inline, incluindo outro arquivo, chamar outra função, etc. CakePHP -fornece algumas funções da carregadeira que podem ser reutilizadas se você só -precisa mudar para onde as mensagens são carregadas. Por exemplo, você ainda -pode usar **.po**, mas carregado de outro local:: +Como você vê, objetos ``Package`` recebem mensagens de tradução como um array. Você pode +passar o método ``setMessages()`` como quiser: com código inline, incluindo +outro arquivo, chamando outra função, etc. O CakePHP fornece algumas funções loader +que você pode reutilizar se precisar apenas mudar de onde as mensagens são carregadas. +Por exemplo, você ainda pode usar arquivos **.po**, mas carregados de outro local:: use Cake\I18n\MessagesFileLoader as Loader; // Load messages from resources/locales/folder/sub_folder/filename.po + I18n::setTranslator( + 'animals', + new Loader('filename', 'folder/sub_folder', 'po'), + 'fr_FR' + ); + +Criando Parsers de Mensagem +---------------------------- + +É possível continuar usando as mesmas convenções que o CakePHP usa, mas usar +um parser de mensagem diferente de ``PoFileParser``. Por exemplo, se você quisesse carregar +mensagens de tradução usando ``YAML``, você primeiro precisará criar a classe +do parser:: + + namespace App\I18n\Parser; - I18n::translator( + class YamlFileParser + { + public function parse($file) + { + return yaml_parse_file($file); + } + } + +O arquivo deve ser criado no diretório **src/I18n/Parser** da sua +aplicação. Em seguida, crie o arquivo de traduções em +**resources/locales/fr_FR/animals.yaml** + +.. code-block:: yaml + + Dog: Chien + Cat: Chat + Bird: Oiseau + +E finalmente, configure o loader de tradução para o domínio e locale:: + + use Cake\I18n\MessagesFileLoader as Loader; + + I18n::setTranslator( 'animals', - 'fr_FR', - new Loader('filename', 'folder/sub_folder', 'po') + new Loader('animals', 'fr_FR', 'yaml'), + 'fr_FR' ); +.. _creating-generic-translators: + +Criando Tradutores Genéricos +----------------------------- + +Configurar tradutores chamando ``I18n::setTranslator()`` para cada domínio e +locale que você precisa suportar pode ser tedioso, especialmente se você precisa suportar mais +de algumas locales diferentes. Para evitar este problema, o CakePHP permite que você defina +loaders de tradutor genéricos para cada domínio. + +Imagine que você queira carregar todas as traduções para o domínio padrão e para +qualquer idioma de um serviço externo:: + + use Cake\I18n\Package; + // Prior to 4.2 you need to use Aura\Intl\Package + + I18n::config('default', function ($domain, $locale) { + $locale = Locale::parseLocale($locale); + $lang = $locale['language']; + $messages = file_get_contents("http://example.com/translations/$lang.json"); + + return new Package( + 'default', // Formatter + null, // Fallback (none for default domain) + json_decode($messages, true) + ) + }); + +O exemplo acima chama um serviço externo de exemplo para carregar um arquivo JSON com as +traduções e então apenas constrói um objeto ``Package`` para qualquer locale que seja +solicitada na aplicação. + +Se você quiser mudar como os pacotes são carregados para todos os pacotes que não +têm loaders específicos definidos, você pode substituir o loader de pacote de fallback usando +o pacote ``_fallback``:: + + I18n::config('_fallback', function ($domain, $locale) { + // Custom code that yields a package here. + }); + +Plurais e Contexto em Tradutores Personalizados +------------------------------------------------ + +Os arrays usados para ``setMessages()`` podem ser criados para instruir o tradutor +a armazenar mensagens em diferentes domínios ou para acionar seleção de plural +estilo Gettext. O seguinte é um exemplo de armazenar traduções para a mesma chave +em diferentes contextos:: + + [ + 'He reads the letter {0}' => [ + 'alphabet' => 'Él lee la letra {0}', + 'written communication' => 'Él lee la carta {0}', + ], + ] + +Da mesma forma, você pode expressar plurais estilo Gettext usando o array de mensagens +tendo uma chave de array aninhada por forma plural:: + + [ + 'I have read one book' => 'He leído un libro', + 'I have read {0} books' => [ + 'He leído un libro', + 'He leído {0} libros', + ], + ] + +Usando Diferentes Formatadores +------------------------------- + +Em exemplos anteriores vimos que Packages são construídos usando ``default`` como +primeiro argumento, e foi indicado com um comentário que ele correspondia ao +formatador a ser usado. Formatadores são classes responsáveis por interpolar +variáveis em mensagens de tradução e selecionar a forma plural correta. + +Se você está lidando com uma aplicação legada, ou não precisa do poder oferecido +pela formatação de mensagem ICU, o CakePHP também fornece o formatador ``sprintf``:: + + return Package('sprintf', 'fallback_domain', $messages); + +As mensagens a serem traduzidas serão passadas para a função ``sprintf()`` para +interpolar as variáveis:: + + __('Hello, my name is %s and I am %d years old', 'José', 29); + +É possível definir o formatador padrão para todos os tradutores criados pelo +CakePHP antes de serem usados pela primeira vez. Isso não inclui tradutores criados +manualmente usando os métodos ``setTranslator()`` e ``config()``:: + + I18n::setDefaultFormatter('sprintf'); + +Localizando Datas e Números +============================ + +Ao exibir Datas e Números na sua aplicação, você frequentemente precisará que +eles sejam formatados de acordo com o formato preferido para o país ou região +em que você deseja que sua página seja exibida. + +Para mudar como datas e números são exibidos, você só precisa mudar +a configuração de locale atual e usar as classes corretas:: + + use Cake\I18n\I18n; + use Cake\I18n\DateTime; + use Cake\I18n\Number; + + I18n::setLocale('fr-FR'); + + $date = new DateTime('2015-04-05 23:00:00'); + + echo $date; // Displays 05/04/2015 23:00 + + echo Number::format(524.23); // Displays 524,23 + +Certifique-se de ler as seções :doc:`/core-libraries/time` e :doc:`/core-libraries/number` +para aprender mais sobre opções de formatação. + +Por padrão, datas retornadas para resultados ORM usam a classe ``Cake\I18n\DateTime``, +então exibi-las diretamente na sua aplicação será afetado ao mudar a +locale atual. + +.. _parsing-localized-dates: + +Analisando Dados de Datetime Localizados +----------------------------------------- + +Ao aceitar dados localizados da requisição, é bom aceitar informações de datetime +no formato localizado do usuário. Em um controller, ou +:doc:`/controllers/middleware` você pode configurar os tipos Date, Time e +DateTime para analisar formatos localizados:: + + use Cake\Database\TypeFactory; + + // Enable default locale format parsing. + TypeFactory::build('datetime')->useLocaleParser(); + + // Configure a custom datetime format parser format. + TypeFactory::build('datetime')->useLocaleParser()->setLocaleFormat('dd-M-y'); + + // You can also use IntlDateFormatter constants. + TypeFactory::build('datetime')->useLocaleParser() + ->setLocaleFormat([IntlDateFormatter::SHORT, -1]); + +O formato de análise padrão é o mesmo que o formato de string padrão. + +.. _converting-request-data-from-user-timezone: + +Convertendo Dados de Requisição do Fuso Horário do Usuário +----------------------------------------------------------- + +Ao lidar com dados de usuários em fusos horários diferentes, você precisará converter +os datetimes nos dados da requisição para o fuso horário da sua aplicação. Você pode usar +``setUserTimezone()`` de um controller ou :doc:`/controllers/middleware` para +tornar este processo mais simples:: + + // Set the user's timezone + TypeFactory::build('datetime')->setUserTimezone($user->timezone); + +Uma vez definido, quando sua aplicação cria ou atualiza entidades a partir de dados de requisição, +o ORM irá automaticamente converter valores datetime do fuso horário do usuário para +o fuso horário da sua aplicação. Isso garante que sua aplicação esteja sempre +trabalhando no fuso horário definido em ``App.defaultTimezone``. + +Se sua aplicação lida com informações de datetime em várias actions, você pode +usar um middleware para definir tanto a conversão de fuso horário quanto a análise de locale:: + + namespace App\Middleware; + + use Cake\Database\TypeFactory; + use Psr\Http\Message\ResponseInterface; + use Psr\Http\Message\ServerRequestInterface; + use Psr\Http\Server\MiddlewareInterface; + use Psr\Http\Server\RequestHandlerInterface; + + class DatetimeMiddleware implements MiddlewareInterface + { + public function process( + ServerRequestInterface $request, + RequestHandlerInterface $handler + ): ResponseInterface { + // Get the user from the request. + // This example assumes your user entity has a timezone attribute. + $user = $request->getAttribute('identity'); + if ($user) { + TypeFactory::build('datetime') + ->useLocaleParser() + ->setUserTimezone($user->timezone); + } + + return $handler->handle($request); + } + } + +Escolhendo Automaticamente a Locale Baseado em Dados da Requisição +=================================================================== + +Ao usar o ``LocaleSelectorMiddleware`` na sua aplicação, o CakePHP irá +automaticamente definir a locale baseada no usuário atual:: + + // in src/Application.php + use Cake\I18n\Middleware\LocaleSelectorMiddleware; + + // Update the middleware function, adding the new middleware + public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue + { + // Add middleware and set the valid locales + $middlewareQueue->add(new LocaleSelectorMiddleware(['en_US', 'fr_FR'])); + // To accept any locale header value + $middlewareQueue->add(new LocaleSelectorMiddleware(['*'])); + } + +O ``LocaleSelectorMiddleware`` usará o cabeçalho ``Accept-Language`` para +automaticamente definir a locale preferida do usuário. Você pode usar a opção de lista de locale +para restringir quais locales serão usadas automaticamente. + +Traduzir Conteúdo/Entidades +============================ + +Se você quiser traduzir conteúdo/entidades, então você deve ver o :doc:`Translate Behavior `. + +.. meta:: + :title lang=en: Internationalization & Localization + :keywords lang=en: internationalization localization,internationalization and localization,language application,gettext,l10n,pot,i18n,translation,languages diff --git a/pt/core-libraries/logging.rst b/pt/core-libraries/logging.rst index ed8bafcc39..12e1d15dc5 100644 --- a/pt/core-libraries/logging.rst +++ b/pt/core-libraries/logging.rst @@ -1,17 +1,550 @@ Logging ####### +Embora as configurações da Classe Configure do núcleo do CakePHP possam realmente ajudar você a ver +o que está acontecendo internamente, há certos momentos em que +você precisará registrar dados no disco para descobrir o que está +acontecendo. Com tecnologias como SOAP, AJAX e APIs REST, a depuração pode ser +bastante difícil. + +O logging também pode ser uma maneira de descobrir o que está acontecendo em sua +aplicação ao longo do tempo. Quais termos de pesquisa estão sendo usados? Que tipos +de erros meus usuários estão vendo? Com que frequência uma consulta específica +está sendo executada? + +O registro de dados no CakePHP é feito com a função ``log()``. Ela é fornecida pelo +``LogTrait``, que é o ancestral comum para muitas classes do CakePHP. Se o +contexto é uma classe CakePHP (Controller, Component, View,...), você pode registrar seus +dados. Você também pode usar ``Log::write()`` diretamente. Veja :ref:`writing-to-logs`. + .. _log-configuration: -Logging Configuration +Configuração de Logging +======================== + +Configurar o ``Log`` deve ser feito durante a fase de bootstrap da sua aplicação. +O arquivo **config/app.php** é destinado exatamente para isso. Você pode definir +quantos loggers sua aplicação precisar. Loggers devem ser +configurados usando :php:class:`Cake\\Log\\Log`. Um exemplo seria:: + + use Cake\Log\Engine\FileLog; + use Cake\Log\Log; + + // Classname using logger 'class' constant + Log::setConfig('info', [ + 'className' => FileLog::class, + 'path' => LOGS, + 'levels' => ['info'], + 'file' => 'info', + ]); + + // Short classname + 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', + ]); + +O exemplo acima cria três loggers, chamados ``info``, ``debug`` e ``error``. +Cada um é configurado para lidar com diferentes níveis de mensagens. Eles também armazenam suas +mensagens de log em arquivos separados, para que possamos separar logs de debug/notice/info +de erros mais sérios. Veja a seção sobre :ref:`logging-levels` para mais +informações sobre os diferentes níveis e o que eles significam. + +Uma vez que uma configuração é criada, você não pode alterá-la. Em vez disso, você deve remover +a configuração e recriá-la usando :php:meth:`Cake\\Log\\Log::drop()` e +:php:meth:`Cake\\Log\\Log::setConfig()`. + +Também é possível criar loggers fornecendo uma closure. Isso é útil +quando você precisa de controle total sobre como o objeto logger é construído. A closure +tem que retornar a instância do logger construída. Por exemplo:: + + Log::setConfig('special', function () { + return new \Cake\Log\Engine\FileLog(['path' => LOGS, 'file' => 'log']); + }); + +Opções de configuração também podem ser fornecidas como uma string :term:`DSN`. Isso é +útil ao trabalhar com variáveis de ambiente ou provedores :term:`PaaS`:: + + Log::setConfig('error', [ + 'url' => 'file:///full/path/to/logs/?levels[]=warning&levels[]=error&file=error', + ]); + +.. warning:: + Se você não configurar engines de logging, as mensagens de log não serão armazenadas. + +Logging de Erros e Exceções +============================ + +Erros e Exceções também podem ser registrados. Configurando os valores correspondentes +no seu arquivo **config/app.php**. Erros serão exibidos quando debug estiver +``true`` e registrados quando debug estiver ``false``. Para registrar exceções não capturadas, defina a +opção ``log`` como ``true``. Veja :doc:`/development/configuration` para mais +informações. + +.. _writing-to-logs: + +Escrevendo nos Logs +=================== + +Escrever nos arquivos de log pode ser feito de duas maneiras diferentes. A primeira +é usar o método estático :php:meth:`Cake\\Log\\Log::write()`:: + + Log::write('debug', 'Something did not work'); + +A segunda é usar a função de atalho ``log()`` disponível em qualquer +classe usando o ``LogTrait``. Chamar ``log()`` chamará internamente +``Log::write()``:: + + // Executing this inside a class using LogTrait + $this->log('Something did not work!', 'debug'); + +Todos os streams de log configurados são escritos sequencialmente cada vez que +:php:meth:`Cake\\Log\\Log::write()` é chamado. Se você não tiver configurado nenhum +engine de logging, ``log()`` retornará ``false`` e nenhuma mensagem de log será +escrita. + +Usando Placeholders em Mensagens +--------------------------------- + +Se você precisa registrar dados definidos dinamicamente, você pode usar placeholders em suas +mensagens de log e fornecer um array de pares chave/valor no parâmetro ``$context``:: + + // Will log `Could not process for userid=1` + Log::write('error', 'Could not process for userid={user}', ['user' => $user->id]); + +Placeholders que não têm chaves definidas não serão substituídos. Se você precisa +usar uma palavra entre chaves literal, você deve escapar o placeholder:: + + // Will log `No {replace}` + Log::write('error', 'No \\{replace}', ['replace' => 'no']); + +Se você incluir objetos em seus placeholders de logging, esses objetos devem implementar +um dos seguintes métodos: + +* ``__toString()`` +* ``toArray()`` +* ``__debugInfo()`` + +.. _logging-levels: + +Usando Níveis +------------- + +O CakePHP suporta o conjunto padrão POSIX de níveis de logging. Cada nível representa +um nível crescente de gravidade: + +* Emergency: sistema está inutilizável +* Alert: ação deve ser tomada imediatamente +* Critical: condições críticas +* Error: condições de erro +* Warning: condições de aviso +* Notice: condição normal mas significativa +* Info: mensagens informativas +* Debug: mensagens de nível debug + +Você pode se referir a esses níveis por nome ao configurar loggers e ao escrever +mensagens de log. Alternativamente, você pode usar métodos de conveniência como +:php:meth:`Cake\\Log\\Log::error()` para indicar claramente o nível de +logging. Usar um nível que não está nos níveis acima resultará em uma +exceção. + +.. note:: + Quando ``levels`` é definido como um valor vazio na configuração de um logger, ele + receberá mensagens de qualquer nível. + +.. _logging-scopes: + +Escopos de Logging +------------------ + +Muitas vezes você vai querer configurar diferentes comportamentos de logging para diferentes +subsistemas ou partes da sua aplicação. Tome como exemplo uma loja de e-commerce. +Você provavelmente vai querer lidar com o logging para pedidos e pagamentos de forma diferente do que +faz com outros logs menos críticos. + +O CakePHP expõe este conceito como escopos de logging. Quando mensagens de log são escritas, +você pode incluir um nome de escopo. Se houver um logger configurado para esse escopo, +as mensagens de log serão direcionadas para esses loggers. Por exemplo:: + + use Cake\Log\Engine\FileLog; + + // Configure logs/shops.log to receive all levels, but only + // those with `orders` and `payments` scope. + Log::setConfig('shops', [ + 'className' => FileLog::class, + 'path' => LOGS, + 'levels' => [], + 'scopes' => ['orders', 'payments'], + 'file' => 'shops.log', + ]); + + // Configure logs/payments.log to receive all levels, but only + // those with `payments` scope. + 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']]); + +Escopos também podem ser passados como uma única string ou um array indexado numericamente. +Note que usar este formato limitará a capacidade de passar mais dados como contexto:: + + Log::warning('This is a warning', ['orders']); + Log::warning('This is a warning', 'payments'); + +.. note:: + Quando ``scopes`` é definido como um array vazio ou ``null`` na + configuração de um logger, ele receberá mensagens de qualquer escopo. Defini-lo como ``false`` + só corresponderá a mensagens sem escopo. + +.. _file-log: + +Logging para Arquivos ===================== +Como o nome implica, ``FileLog`` escreve mensagens de log em arquivos. O nível da mensagem de log +sendo escrita determina o nome do arquivo onde a mensagem é armazenada. +Se um nível não for fornecido, :php:const:`LOG_ERR` é usado, que escreve no +log de erro. O local de log padrão é **logs/$level.log**:: + + // Executing this inside a CakePHP class + $this->log("Something didn't work!"); + + // Results in this being appended to logs/error.log + // 2007-11-02 10:22:02 Error: Something didn't work! + +O diretório configurado deve ser gravável pelo usuário do servidor web para +que o logging funcione corretamente. + +Você pode configurar locais FileLog adicionais/alternativos ao configurar +um logger. FileLog aceita um ``path`` que permite que +caminhos personalizados sejam usados:: + + Log::setConfig('custom_path', [ + 'className' => 'File', + 'path' => '/path/to/custom/place/' + ]); + +O engine ``FileLog`` aceita as seguintes opções: + +* ``size`` Usado para implementar rotação básica de arquivo de log. Se o tamanho do arquivo de log + atingir o tamanho especificado, o arquivo existente é renomeado anexando timestamp + ao nome do arquivo e um novo arquivo de log é criado. Pode ser valor inteiro em bytes ou + valores de string legíveis como '10MB', '100KB' etc. Padrão é 10MB. +* ``rotate`` Arquivos de log são rotacionados o número de vezes especificado antes de serem removidos. + Se o valor for 0, versões antigas são removidas em vez de rotacionadas. Padrão é 10. +* ``mask`` Define as permissões de arquivo para arquivos criados. Se deixado vazio, as permissões + padrão são usadas. + +.. note:: + + Diretórios ausentes serão criados automaticamente para evitar + erros desnecessários lançados ao usar o FileEngine. + +.. _syslog-log: + +Logging para Syslog +=================== + +Em ambientes de produção, é altamente recomendado que você configure seu sistema para +usar syslog em vez do logger de arquivo. Isso terá um desempenho muito melhor, pois qualquer +gravação será feita de forma (quase) não bloqueante e o logger do seu sistema operacional +pode ser configurado separadamente para rotacionar arquivos, pré-processar gravações ou usar +um armazenamento completamente diferente para seus logs. + +Usar syslog é praticamente como usar o engine FileLog padrão, você só precisa +especificar ``Syslog`` como o engine a ser usado para logging. O seguinte +snippet de configuração substituirá o logger padrão pelo syslog, isso deve +ser feito no arquivo **config/bootstrap.php**:: + + Log::setConfig('default', [ + 'engine' => 'Syslog' + ]); + +O array de configuração aceito para o engine de logging Syslog entende as +seguintes chaves: + +* ``format``: Uma string template sprintf com dois placeholders, o primeiro + para o nível de erro e o segundo para a própria mensagem. Esta chave é + útil para adicionar informações adicionais sobre o servidor ou processo na + mensagem registrada. Por exemplo: ``%s - Web Server 1 - %s`` parecerá + ``error - Web Server 1 - An error occurred in this request`` após + substituir os placeholders. Esta opção está obsoleta. Você deve usar + :ref:`logging-formatters` em vez disso. +* ``prefix``: Uma string que será prefixada a cada mensagem registrada. +* ``flag``: Uma flag inteira a ser usada para abrir a conexão com o + logger, por padrão ``LOG_ODELAY`` será usado. Veja a documentação ``openlog`` + para mais opções +* ``facility``: O slot de logging a ser usado no syslog. Por padrão ``LOG_USER`` é + usado. Veja a documentação ``syslog`` para mais opções + +Criando Engines de Log +======================= + +Engines de log podem fazer parte da sua aplicação ou parte de +plugins. Se, por exemplo, você tivesse um logger de banco de dados chamado +``DatabaseLog``. Como parte da sua aplicação, ele seria colocado em +**src/Log/Engine/DatabaseLog.php**. Como parte de um plugin, seria colocado em +**plugins/LoggingPack/src/Log/Engine/DatabaseLog.php**. Para configurar +engine de log, você deve usar :php:meth:`Cake\\Log\\Log::setConfig()`. Por exemplo, +configurar nosso DatabaseLog ficaria assim:: + + // For src/Log + Log::setConfig('otherFile', [ + 'className' => 'Database', + 'model' => 'LogEntry', + // ... + ]); + + // For plugin called LoggingPack + Log::setConfig('otherFile', [ + 'className' => 'LoggingPack.Database', + 'model' => 'LogEntry', + // ... + ]); + +Ao configurar um engine de log, o parâmetro ``className`` é usado para +localizar e carregar o manipulador de log. Todas as outras propriedades de +configuração são passadas para o construtor do engine de log como um 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. + } + } + +O CakePHP requer que todos os engines de logging implementem ``Psr\Log\LoggerInterface``. +A classe :php:class:`Cake\Log\Engine\BaseLog` é uma maneira fácil de satisfazer a +interface, pois requer apenas que você implemente o método ``log()``. + +.. _logging-formatters: + +Formatadores de Logging +======================== + +Formatadores de logging permitem que você controle como as mensagens de log são formatadas +independentemente do engine de armazenamento. Cada engine de logging fornecido pelo núcleo vem com +um formatador configurado para manter a saída compatível com versões anteriores. No entanto, você pode +ajustar os formatadores para atender às suas necessidades. Formatadores são configurados +junto com o engine de logging:: + + use Cake\Log\Engine\SyslogLog; + use App\Log\Formatter\CustomFormatter; + + // Simple formatting configuration with no options. + Log::setConfig('error', [ + 'className' => SyslogLog::class, + 'formatter' => CustomFormatter::class, + ]); + + // Configure a formatter with additional options. + Log::setConfig('error', [ + 'className' => SyslogLog::class, + 'formatter' => [ + 'className' => CustomFormatter::class, + 'key' => 'value', + ], + ]); + +Para implementar seu próprio formatador de logging, você precisa estender +``Cake\Log\Format\AbstractFormatter`` ou uma de suas subclasses. O método principal +que você precisa implementar é ``format($level, $message, $context)``, que é +responsável por formatar mensagens de log. + +.. _log-testing: + +Testando Logs +============= + +Para testar logging, adicione ``Cake\TestSuite\LogTestTrait`` ao seu caso de teste. O +``LogTestTrait`` usa hooks do PHPUnit para anexar engines de log que interceptam as mensagens de log +que sua aplicação está fazendo. Uma vez que você capturou logs, você pode realizar +asserções em mensagens de log que sua aplicação está emitindo. Por exemplo:: + + namespace App\Test\TestCase\Controller; + + use Cake\TestSuite\LogTestTrait; + use Cake\TestSuite\TestCase; + + class UsersControllerTest extends TestCase + { + use LogTestTrait; + + public function setUp(): void + { + parent::setUp(); + $this->setupLog([ + 'error' => ['scopes' => ['app.security']] + ]); + } + + public function testResetPassword() + { + $this->post('/users/resetpassword', ['email' => 'bob@example.com']); + $this->assertLogMessageContains('info', 'bob@example.com reset password', 'app.security'); + } + } + +Você usa ``setupLog()`` para definir as mensagens de log que deseja capturar e +realizar asserções. Depois que os logs foram emitidos, você pode fazer asserções sobre +o conteúdo dos logs ou a ausência deles: + +* ``assertLogMessage(string $level, string $expectedMessage, ?string $scope + = null, string $failMsg = '')`` Afirma que uma mensagem de log foi encontrada. +* ``assertLogMessageContains(string $level, string $expectedMessage, ?string + $scope = null, string $failMsg = '')`` Afirma que uma mensagem de log contém a + substring. +* ``assertLogAbsent(string $level, ?string $failMsg = '')`` Afirma que nenhuma mensagem de log + do nível fornecido foi capturada. + +O ``LogTestTrait`` irá automaticamente limpar quaisquer loggers que foram +configurados. + +API de Log +========== + +.. php:namespace:: Cake\Log + +.. php:class:: Log + + Uma classe simples para escrever em logs. + +.. php:staticmethod:: setConfig($key, $config) + + :param string $name: Nome para o logger sendo conectado, usado + para remover um logger posteriormente. + :param array $config: Array de informações de configuração e + argumentos do construtor para o logger. + + Obter ou definir a configuração para um Logger. Veja :ref:`log-configuration` para + mais informações. + +.. php:staticmethod:: configured() + + :returns: Um array de loggers configurados. + + Obter os nomes dos loggers configurados. + +.. php:staticmethod:: drop($name) + + :param string $name: Nome do logger que você deseja que não receba mais + mensagens. + +.. php:staticmethod:: write($level, $message, $scope = []) + + Escrever uma mensagem em todos os loggers configurados. + ``$level`` indica o nível da mensagem de log sendo criada. + ``$message`` é a mensagem da entrada de log sendo escrita. + ``$scope`` é o(s) escopo(s) em que uma mensagem de log está sendo criada. + +.. php:staticmethod:: levels() + +Chame este método sem argumentos, ex: `Log::levels()` para obter a +configuração de nível atual. + +Métodos de Conveniência +------------------------ + +Os seguintes métodos de conveniência foram adicionados para registrar `$message` com o +nível de log apropriado. + +.. 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 = []) + +Trait de Logging +================= + +.. php:trait:: LogTrait + + Um trait que fornece métodos de atalho para logging + +.. php:method:: log($msg, $level = LOG_ERR) + + Registrar uma mensagem nos logs. Por padrão, as mensagens são registradas como + mensagens ERROR. + +Usando Monolog +============== + +Monolog é um logger popular para PHP. Como ele implementa as mesmas interfaces que +os loggers do CakePHP, você pode usá-los em sua aplicação como o logger +padrão. + +Após instalar o Monolog usando o composer, configure o logger usando o +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('path/to/your/combined.log')); + + return $log; + }); + + // Optionally stop using the now redundant default loggers + Log::drop('debug'); + Log::drop('error'); + +Use métodos similares se você quiser configurar um logger diferente para seu console:: + + // config/bootstrap_cli.php + + use Monolog\Logger; + use Monolog\Handler\StreamHandler; + + Log::setConfig('default', function () { + $log = new Logger('cli'); + $log->pushHandler(new StreamHandler('path/to/your/combined-cli.log')); + + return $log; + }); + + // Optionally stop using the now redundant default CLI loggers + Configure::delete('Log.debug'); + Configure::delete('Log.error'); + .. note:: - Atualmente, a documentação desta página não é suportada em português. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. + Ao usar um logger específico do console, certifique-se de configurar condicionalmente + o logger da sua aplicação. Isso evitará entradas de log duplicadas. - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file +.. meta:: + :title lang=pt: Logging + :description lang=pt: Registre dados do CakePHP no disco para ajudar a depurar sua aplicação por períodos mais longos de tempo. + :keywords lang=pt: cakephp logging,registrar erros,debug,logging data,classe cakelog,ajax logging,soap logging,debugging,logs diff --git a/pt/core-libraries/number.rst b/pt/core-libraries/number.rst index c650ea9ef2..df27293b70 100644 --- a/pt/core-libraries/number.rst +++ b/pt/core-libraries/number.rst @@ -1,12 +1,12 @@ -Número +Number ###### .. php:namespace:: Cake\I18n .. php:class:: Number -Se você precisa das funcionalidades do :php:class:`NumberHelper` fora da ``View``, -use a classe ``Number`` :: +Se você precisa das funcionalidades do :php:class:`NumberHelper` fora de uma ``View``, +use a classe ``Number``:: namespace App\Controller; @@ -17,14 +17,15 @@ use a classe ``Number`` :: public function initialize(): void { parent::initialize(); - $this->loadComponent('Auth'); + $this->loadComponent('Authentication.Authentication'); } public function afterLogin() { - $storageUsed = $this->Auth->user('storage_used'); + $identity = $this->Authentication->getIdentity(); + $storageUsed = $identity->storage_used; if ($storageUsed > 5000000) { - // Notify users of quota + // Notificar usuários sobre cota $this->Flash->success(__('You are using {0} storage', Number::toReadableSize($storageUsed))); } } @@ -32,30 +33,29 @@ use a classe ``Number`` :: .. start-cakenumber -Todas essas funções retornam o número formatado; eles não -são impressas automaticamente na visualização de saída. +Todas essas funções retornam o número formatado; elas não +ecoam automaticamente a saída na view. -Formatação de Valores Monetários -================================ +Formatando Valores de Moeda +============================ .. php:method:: currency(mixed $value, string $currency = null, array $options = []) -Este método é usado para exibir um número em formatos monetários comuns -(EUR, GBP, USD), com base no código de moeda ISO 4217 de 3 letras. O uso em uma view se parece com:: +Este método é usado para exibir um número em formatos de moeda comuns +(EUR, GBP, USD), com base no código de moeda ISO 4217 de 3 letras. O uso em uma view é assim:: - // Called as NumberHelper + // Chamado como NumberHelper echo $this->Number->currency($value, $currency); - // Called as Number + // Chamado como Number echo Number::currency($value, $currency); O primeiro parâmetro, ``$value``, deve ser um número de ponto flutuante -que representa a quantidade de dinheiro que você está expressando. -O segundo parâmetro é uma string usada para escolher um esquema de -formatação de monetária predefinida: +que representa a quantidade de dinheiro que você está expressando. O segundo +parâmetro é uma string usada para escolher um esquema de formatação de moeda predefinido: +---------------------+----------------------------------------------------+ -| $ moeda | 1234.56, formatação pelo tipo monetário | +| $currency | 1234.56, formatado por tipo de moeda | +=====================+====================================================+ | EUR | €1.234,56 | +---------------------+----------------------------------------------------+ @@ -64,128 +64,129 @@ formatação de monetária predefinida: | USD | $1,234.56 | +---------------------+----------------------------------------------------+ -O terceiro parâmetro é um conjunto de opções para definir melhor a saída. -As seguintes opções estão disponíveis: +O terceiro parâmetro é um array de opções para definir ainda mais a +saída. As seguintes opções estão disponíveis: +---------------------+----------------------------------------------------+ | Opção | Descrição | +=====================+====================================================+ -| before | Texto a ser exibido antes do número renderizado. | +| before | Texto a exibir antes do número renderizado. | +---------------------+----------------------------------------------------+ -| after | Texto a ser exibido após o número renderizado. | +| after | Texto a exibir após o número renderizado. | +---------------------+----------------------------------------------------+ -| zero | O texto a ser usado para valores zero; pode ser | -| | uma string ou um número. ou seja, 0, 'Grátis!'. | +| zero | O texto a usar para valores zero; pode ser uma | +| | string ou um número. Ex: 0, 'Grátis!'. | +---------------------+----------------------------------------------------+ -| places | Número de casas decimais a serem usadas, ou seja, 2| +| places | Número de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| precision | Número máximo de casas decimais a serem usadas, | -| | ou seja, 2 | +| precision | Número máximo de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| locale | O nome do local a ser usado para formatar o número,| -| | ou seja. “Fr_FR”. | +| locale | O nome da localidade a usar para formatar o | +| | número, ex: "fr_FR". | +---------------------+----------------------------------------------------+ -| fractionSymbol | String a ser usada para números fracionários, ou | -| | seja, 'centavos'. | +| fractionSymbol | String a usar para números fracionários, | +| | ex: ' centavos'. | +---------------------+----------------------------------------------------+ -| fractionPosition | Ou 'antes' ou 'depois' para colocar o símbolo de | +| fractionPosition | 'before' ou 'after' para posicionar o símbolo de | | | fração. | +---------------------+----------------------------------------------------+ -| pattern | Um padrão de número ICU a ser usado para formatar | -| | o número, ou seja. #, ###. 00 | +| pattern | Um padrão de número ICU a usar para formatar o | +| | número, ex: #,###.00 | +---------------------+----------------------------------------------------+ -| useIntlCode | Defina como ``true`` para substituir o símbolo da | -| | moeda pelo código de moeda internacional | +| useIntlCode | Defina como ``true`` para substituir o símbolo de | +| | moeda pelo código de moeda internacional. | +---------------------+----------------------------------------------------+ -Se de ``$currency`` for ``null``, a moeda padrão será retornada em +Se o valor de ``$currency`` for ``null``, a moeda padrão será recuperada de :php:meth:`Cake\\I18n\\Number::defaultCurrency()`. Para formatar moedas em um -formato de contabilidade, você deve definir o formato da moeda:: +formato contábil, você deve definir o formato de moeda:: Number::setDefaultCurrencyFormat(Number::FORMAT_CURRENCY_ACCOUNTING); -Definição da moeda padrão +Definindo a Moeda Padrão ========================= .. php:method:: setDefaultCurrency($currency) -Atribui a moeda padrão. Isso elimina a necessidade de sempre passar a moeda -para :php:meth:`Cake\\I18n\\Number::currency()` e alterar todas as saídas de -moeda definindo outro padrão. Se ``$currency`` atribuído o valor ``null``, -ele apagará o valor armazenado no momento. +Setter para a moeda padrão. Isso remove a necessidade de sempre passar a +moeda para :php:meth:`Cake\\I18n\\Number::currency()` e alterar todas +as saídas de moeda definindo outro padrão. Se ``$currency`` for definido como ``null``, +isso limpará o valor armazenado atualmente. -Obtendo a moeda padrão -====================== +Obtendo a Moeda Padrão +======================= .. php:method:: getDefaultCurrency() -Obtem a moeda padrão. Se a moeda padrão foi definida anteriormente usando -``setDefaultCurrency()``, então esse valor será retornado. Por padrão, ele irá -retornar o valor ``intl.default_locale`` do ini se estiver atribuído e ``'en_US'`` se não estiver. +Getter para a moeda padrão. Se a moeda padrão foi definida anteriormente usando +``setDefaultCurrency()``, então esse valor será retornado. Por padrão, ele +recuperará o valor ini ``intl.default_locale`` se definido e ``'en_US'`` se não. -Formatando números de ponto flutuante -===================================== +Formatando Números de Ponto Flutuante +====================================== .. php:method:: precision(float $value, int $precision = 3, array $options = []) -Este método exibe um número com a quantidade especificada de precisão (casas decimais). -Ele será arredondado para manter o nível de precisão definido. :: +Este método exibe um número com a quantidade especificada de +precisão (casas decimais). Ele arredondará para manter o +nível de precisão definido. :: - // Called as NumberHelper + // Chamado como NumberHelper echo $this->Number->precision(456.91873645, 2); - // Outputs + // Saída 456.92 - // Called as Number + // Chamado como Number echo Number::precision(456.91873645, 2); -Formatação de Porcentagens -========================== +Formatando Porcentagens +======================== .. php:method:: toPercentage(mixed $value, int $precision = 2, array $options = []) -+---------------------+--------------------------------------------------------+ -| Opção | Descrição | -+=====================+========================================================+ -| multiply | Booleano para indicar se o valor deve ser | -| | multiplicado por 100. Útil para porcentagens decimais. | -+---------------------+--------------------------------------------------------+ ++---------------------+----------------------------------------------------+ +| Opção | Descrição | ++=====================+====================================================+ +| multiply | Booleano para indicar se o valor deve ser | +| | multiplicado por 100. Útil para porcentagens | +| | decimais. | ++---------------------+----------------------------------------------------+ -Da mesma forma :php:meth:`Cake\\I18n\\Number::precision()`, ste método formata um -número de acordo com a precisão fornecida (onde os números são arredondados para -atender à precisão fornecida). Este método também expressa o número como uma -porcentagem e anexa a saída com um sinal de porcentagem. :: +Como :php:meth:`Cake\\I18n\\Number::precision()`, este método formata um número +de acordo com a precisão fornecida (onde os números são arredondados para atender à +precisão fornecida). Este método também expressa o número como uma porcentagem +e acrescenta a saída com um sinal de porcentagem. :: - // Called as NumberHelper. Output: 45.69% + // Chamado como NumberHelper. Saída: 45.69% echo $this->Number->toPercentage(45.691873645); - // Called as Number. Output: 45.69% + // Chamado como Number. Saída: 45.69% echo Number::toPercentage(45.691873645); - // Called with multiply. Output: 45.7% + // Chamado com multiply. Saída: 45.7% echo Number::toPercentage(0.45691, 1, [ 'multiply' => true ]); -Interagindo com valores legíveis para humanos -============================================= +Interagindo com Valores Legíveis para Humanos +============================================== .. php:method:: toReadableSize(string $size) -Este método formata o tamanho dos dados em formatos legíveis -por humanos. Ele fornece uma forma de atalho para converter bytes -em KB, MB, GB e TB. O tamanho é exibido com um nível de precisão de dois dígitos, -de acordo com o tamanho dos dados fornecidos (ou seja, tamanhos maiores são -expressos em termos maiores) :: +Este método formata tamanhos de dados em formas legíveis para humanos. Ele fornece +um atalho para converter bytes em KB, MB, GB e TB. O tamanho é +exibido com um nível de precisão de dois dígitos, de acordo com o tamanho +dos dados fornecidos (ou seja, tamanhos maiores são expressos em termos +maiores):: - // Called as NumberHelper + // Chamado 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 - // Called as Number + // Chamado como Number echo Number::toReadableSize(0); // 0 Byte echo Number::toReadableSize(1024); // 1 KB echo Number::toReadableSize(1321205.76); // 1.26 MB @@ -196,155 +197,157 @@ Formatando Números .. php:method:: format(mixed $value, array $options = []) -Este método fornece muito mais controle sobre a formatação de números -para uso em suas visualizações (e é usado como o método principal pela -maioria dos outros métodos NumberHelper). Usar este método pode ser -parecido com:: +Este método oferece muito mais controle sobre a formatação de +números para uso em suas views (e é usado como método principal por +a maioria dos outros métodos do NumberHelper). Usar este método pode +parecer assim:: - // Called as NumberHelper + // Chamado como NumberHelper $this->Number->format($value, $options); - // Called as Number + // Chamado como Number Number::format($value, $options); -O parâmetro ``$value`` é o número que você está planejando formatar para -saída. Sem o formatting for output. Sem o ``$options``, o número -1236.334 produzirá a saída 1,236. Observe que a precisão padrão -é zero casas decimais. +O parâmetro ``$value`` é o número que você está planejando +formatar para saída. Sem ``$options`` fornecidas, o número +1236.334 seria exibido como 1,236. Observe que a precisão padrão é +zero casas decimais. -O parâmetro ``$options`` é onde reside a verdadeira magia desse método. +O parâmetro ``$options`` é onde a verdadeira mágica deste método +reside. -- Se você passar um número inteiro, isso se tornará a quantidade de precisão ou casas para a função. -- Se você passar uma matriz associada, você pode usar as seguintes chaves: +- Se você passar um inteiro, então isso se torna a quantidade de precisão + ou casas para a função. +- Se você passar um array associativo, você pode usar as seguintes chaves: +---------------------+----------------------------------------------------+ -| Opção | Descrição | +| Opção | Descrição | +=====================+====================================================+ -| places | Número de casas decimais a serem usadas, ou seja, 2| +| places | Número de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| precision | Número máximo de casas decimais a serem usadas, ou | -| | seja, 2 | +| precision | Número máximo de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| pattern | Um padrão de número ICU a ser usado para formatar | -| | o número, ou seja. #, ###. 00 | +| pattern | Um padrão de número ICU a usar para formatar o | +| | número, ex: #,###.00 | +---------------------+----------------------------------------------------+ -| locale | O nome do local a ser usado para formatar o número,| -| | ou seja. “Fr_FR”. | +| locale | O nome da localidade a usar para formatar o | +| | número, ex: "fr_FR". | +---------------------+----------------------------------------------------+ -| before | Texto a ser exibido antes do número renderizado. | +| before | Texto a exibir antes do número renderizado. | +---------------------+----------------------------------------------------+ -| after | Texto a ser exibido após o número renderizado. | +| after | Texto a exibir após o número renderizado. | +---------------------+----------------------------------------------------+ Exemplo:: - // Called as NumberHelper + // Chamado como NumberHelper echo $this->Number->format('123456.7890', [ 'places' => 2, 'before' => '¥ ', 'after' => ' !' ]); - // Output '¥ 123,456.79 !' + // Saída '¥ 123,456.79 !' echo $this->Number->format('123456.7890', [ 'locale' => 'fr_FR' ]); - // Output '123 456,79 !' + // Saída '123 456,79 !' - // Called as Number + // Chamado como Number echo Number::format('123456.7890', [ 'places' => 2, 'before' => '¥ ', 'after' => ' !' ]); - // Output '¥ 123,456.79 !' + // Saída '¥ 123,456.79 !' echo Number::format('123456.7890', [ 'locale' => 'fr_FR' ]); - // Output '123 456,79 !' + // Saída '123 456,79 !' .. php:method:: ordinal(mixed $value, array $options = []) -Este método irá gerar um número ordinal. +Este método produzirá um número ordinal. Exemplos:: echo Number::ordinal(1); - // Output '1st' + // Saída '1st' echo Number::ordinal(2); - // Output '2nd' + // Saída '2nd' echo Number::ordinal(2, [ 'locale' => 'fr_FR' ]); - // Output '2e' + // Saída '2e' echo Number::ordinal(410); - // Output '410th' + // Saída '410th' -Diferenças de formato +Diferenças de Formato ===================== .. php:method:: formatDelta(mixed $value, array $options = []) -Este método exibe diferenças de valor como um número assinado:: +Este método exibe diferenças de valor como um número com sinal:: - // Called as NumberHelper + // Chamado como NumberHelper $this->Number->formatDelta($value, $options); - // Called as Number + // Chamado como Number Number::formatDelta($value, $options); -O parâmetro ``$value`` é o número que você está planejando formatar -para saída. Sem o ``$options`` , 1236.334 produziria como saída 1,236. -Observe que a precisão padrão é zero casas decimais. +O parâmetro ``$value`` é o número que você está planejando +formatar para saída. Sem ``$options`` fornecidas, o número +1236.334 seria exibido como 1,236. Observe que a precisão padrão é +zero casas decimais. -O parâmetro ``$options`` usa as mesmas chaves que :php:meth:`Number::format()`: +O parâmetro ``$options`` recebe as mesmas chaves que :php:meth:`Number::format()`: +---------------------+----------------------------------------------------+ -| Opção | Descrição | +| Opção | Descrição | +=====================+====================================================+ -| places | Número de casas decimais a serem usadas, ou seja, 2| +| places | Número de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| precision | Número máximo de casas decimais a serem usadas, ou | -| | seja, 2 | +| precision | Número máximo de casas decimais a usar, ex: 2 | +---------------------+----------------------------------------------------+ -| locale | O nome do local a ser usado para formatar o número,| -| | ou seja. “Fr_FR”. | +| locale | O nome da localidade a usar para formatar o | +| | número, ex: "fr_FR". | +---------------------+----------------------------------------------------+ -| before | Texto a ser exibido antes do número renderizado. | +| before | Texto a exibir antes do número renderizado. | +---------------------+----------------------------------------------------+ -| after | Texto a ser exibido após o número renderizado. | +| after | Texto a exibir após o número renderizado. | +---------------------+----------------------------------------------------+ Exemplo:: - // Called as NumberHelper + // Chamado como NumberHelper echo $this->Number->formatDelta('123456.7890', [ 'places' => 2, 'before' => '[', 'after' => ']' ]); - // Output '[+123,456.79]' + // Saída '[+123,456.79]' - // Called as Number + // Chamado como Number echo Number::formatDelta('123456.7890', [ 'places' => 2, 'before' => '[', 'after' => ']' ]); - // Output '[+123,456.79]' + // Saída '[+123,456.79]' .. end-cakenumber -Configurar formatadores +Configurar Formatadores ======================= .. php:method:: config(string $locale, int $type = NumberFormatter::DECIMAL, array $options = []) -Este método permite configurar padrões do formatador que persistem nas chamadas para vários métodos. +Este método permite configurar padrões de formatador que persistem entre chamadas +para vários métodos. Exemplo:: @@ -354,5 +357,5 @@ Exemplo:: .. meta:: :title lang=pt: NumberHelper - :description lang=pt: O Helper Number contém métodos convenientes que permitem a exibição de números em formatos comuns em suas visualizações. - :keywords lang=pt: number helper, moeda, formato de número, precisão de número, tamanho do arquivo de formato, números de formato + :description lang=pt: O Number Helper contém métodos de conveniência que permitem exibir números em formatos comuns em suas views. + :keywords lang=pt: number helper,currency,number format,number precision,format file size,format numbers diff --git a/pt/core-libraries/plugin.rst b/pt/core-libraries/plugin.rst index 1b0065a13e..77773fab6b 100644 --- a/pt/core-libraries/plugin.rst +++ b/pt/core-libraries/plugin.rst @@ -1,50 +1,50 @@ -Plugin Class -############ +Classe Plugin +############# .. php:namespace:: Cake\Core .. php:class:: Plugin -The Plugin class is responsible for resource location and path management of plugins. +A classe Plugin é responsável pela localização de recursos e gerenciamento de caminhos de plugins. -Locating Plugins -================ +Localizando Plugins +=================== .. php:staticmethod:: path(string $plugin) -Plugins can be located with Plugin. Using ``Plugin::path('DebugKit');`` -for example, will give you the full path to the DebugKit plugin:: +Plugins podem ser localizados com Plugin. Usar ``Plugin::path('DebugKit');`` +por exemplo, lhe dará o caminho completo para o plugin DebugKit:: $path = Plugin::path('DebugKit'); -Check if a Plugin is Loaded -=========================== +Verificar se um Plugin está Carregado +====================================== -You can check dynamically inside your code if a specific plugin has been loaded:: +Você pode verificar dinamicamente dentro do seu código se um plugin específico foi carregado:: $isLoaded = Plugin::isLoaded('DebugKit'); -Use ``Plugin::loaded()`` if you want to get a list of all currently loaded plugins. +Use ``Plugin::loaded()`` se você quiser obter uma lista de todos os plugins atualmente carregados. -Finding Paths to Namespaces -=========================== +Encontrando Caminhos para Namespaces +===================================== .. php:staticmethod:: classPath(string $plugin) -Used to get the location of the plugin's class files:: +Usado para obter a localização dos arquivos de classe do plugin:: $path = App::classPath('DebugKit'); -Finding Paths to Resources -========================== +Encontrando Caminhos para Recursos +=================================== .. php:staticmethod:: templatePath(string $plugin) -The method returns the path to the plugins' templates:: +O método retorna o caminho para os templates do plugin:: $path = Plugin::templatePath('DebugKit'); -The same goes for the config path:: +O mesmo vale para o caminho de config:: $path = Plugin::configPath('DebugKit'); diff --git a/pt/core-libraries/registry-objects.rst b/pt/core-libraries/registry-objects.rst index 4dc81b6d1f..e8f1accb3d 100644 --- a/pt/core-libraries/registry-objects.rst +++ b/pt/core-libraries/registry-objects.rst @@ -1,12 +1,57 @@ Objetos de Registro ################### -.. note:: - Atualmente, a documentação desta página não é suportada em português. +As classes de registro fornecem uma maneira simples de criar e recuperar instâncias +carregadas de um determinado tipo de objeto. Existem classes de registro para Components, +Helpers, Tasks e Behaviors. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +Embora os exemplos abaixo usem Components, o mesmo comportamento pode ser esperado +para Helpers, Behaviors e Tasks, além de Components. - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file +Carregando Objetos +================== + +Objetos podem ser carregados dinamicamente usando add() +Exemplo:: + + $this->loadComponent('Acl.Acl'); + $this->addHelper('Flash') + +Isso resultará no carregamento da propriedade ``Acl`` e do helper ``Flash``. +A configuração também pode ser definida dinamicamente. Exemplo:: + + $this->loadComponent('Cookie', ['name' => 'sweet']); + +Quaisquer chaves e valores fornecidos serão passados para o construtor do Component. A +única exceção a esta regra é ``className``. Classname é uma chave especial que é +usada para criar aliases de objetos em um registro. Isso permite que você tenha nomes de components +que não refletem os nomes de classe, o que pode ser útil ao estender components +do núcleo:: + + $this->Flash = $this->loadComponent('Flash', ['className' => 'MyCustomFlash']); + $this->Flash->error(); // Actually using MyCustomFlash::error(); + +Acionando Callbacks +=================== + +Callbacks não são fornecidos por objetos de registro. Você deve usar o +:doc:`sistema de eventos ` para disparar quaisquer eventos/callbacks +para sua aplicação. + +Desabilitando Callbacks +======================== + +Em versões anteriores, objetos de coleção forneciam um método ``disable()`` para desabilitar +objetos de receber callbacks. Você deve usar os recursos do sistema de eventos para +fazer isso agora. Por exemplo, você poderia desabilitar callbacks de components da +seguinte maneira:: + + // Remove MyComponent from callbacks. + $this->getEventManager()->off($this->MyComponent); + + // Re-enable MyComponent for callbacks. + $this->getEventManager()->on($this->MyComponent); + +.. meta:: + :title lang=pt: Objetos de Registro + :keywords lang=pt: array name,loading components,several different kinds,unified api,loading objects,component names,special key,core components,callbacks,prg,callback,alias,fatal error,collections,memory,priority,priorities diff --git a/pt/core-libraries/security.rst b/pt/core-libraries/security.rst index f1bd39ae1f..4e71f13642 100644 --- a/pt/core-libraries/security.rst +++ b/pt/core-libraries/security.rst @@ -1,92 +1,70 @@ -Segurança -######### +Utilitário de Segurança +####################### .. php:namespace:: Cake\Utility .. php:class:: Security A `biblioteca de segurança -`_ -trabalha com medidas básicas de segurança fornecendo métodos para -`hash` e criptografia de dados. +`_ +lida com medidas básicas de segurança, como fornecer métodos para +fazer hash e criptografar dados. Criptografando e Descriptografando Dados -========================================== +========================================= .. php:staticmethod:: encrypt($text, $key, $hmacSalt = null) .. php:staticmethod:: decrypt($cipher, $key, $hmacSalt = null) -Criptografando ``$text`` usando AES-256. A ``$key`` deve ser um valor com -dados variados como uma senha forte. O resultado retornado será o valor -criptografado com um `HMAC checksum`. +Criptografa ``$text`` usando AES-256. A ``$key`` deve ser um valor com muita +variância nos dados, muito parecido com uma boa senha. O resultado retornado +será o valor criptografado com uma soma de verificação HMAC. -Este método irá usar `openssl `_ ou `mcrypt -`_ dependendo de qual deles estiver disponível em seu sistema. Dados criptografados em uma implementação são portáveis para a outra. +A extensão `openssl `_ é necessária para criptografar/descriptografar. -.. warning:: - A extensão `mcrypt `_ foi considerada obsoleta no PHP7.1 - -Este método **nunca** deve ser usado para armazenar senhas. Em vez disso, você deve usar -o método de ``hash`` de mão única fornecidos por -:php:meth:`~Cake\\Utility\\Security::hash()`. Um exemplo de uso pode ser:: +Um exemplo de uso seria:: - // Assumindo que $key está gravada em algum lugar, ela pode ser reusada para - // descriptografar depois. + // Assumindo que a chave está armazenada em algum lugar onde pode ser reutilizada para + // descriptografia posteriormente. $key = 'wt1U5MACWJFTXGenFoZoiLwQGrLgdbHA'; $result = Security::encrypt($value, $key); -Se você não fornecer um ``HMAC salt``, o valor em ``Security.salt`` será usado. -Os valores criptografados podem ser descriptografados usando +Se você não fornecer um sal HMAC, o valor de ``Security::getSalt()`` será usado. +Valores criptografados podem ser descriptografados usando :php:meth:`Cake\\Utility\\Security::decrypt()`. -Descriptografando um valor criptografado anteriormente. Os parametros ``$key`` e ``$hmacSalt`` -devem corresponder aos valores utilizados para a criptografia senão o processo falhará. +Este método **nunca** deve ser usado para armazenar senhas. -Exemplo:: +Descriptografa um valor criptografado anteriormente. Os parâmetros ``$key`` e ``$hmacSalt`` +devem corresponder aos valores usados para criptografar ou a descriptografia falhará. Um +exemplo de uso seria:: - // Assumindo que $key está gravada em algum lugar, ela pode ser reusada para - // descriptografar depois. + // Assumindo que a chave está armazenada em algum lugar onde pode ser reutilizada para + // Descriptografia posteriormente. $key = 'wt1U5MACWJFTXGenFoZoiLwQGrLgdbHA'; $cipher = $user->secrets; $result = Security::decrypt($cipher, $key); -Se o valor não puder ser descriptografado por mudanças em ``$key`` ou ``HMAC salt`` -o método retornará ``false``. - -.. _force-mcrypt: - -Escolhendo uma implementação de criptografia --------------------------------------------- - -Se você está atualizando sua aplicação do CakePHP 2.x, os dados criptografados -não serão compatíveis com openssl por que seus dados criptografados não são totalmente -compatíveis com AES. Se você não quiser ter o trabalho de refazer a criptografia dos seus -dados, você pode forçar o CakePHP a usar ``mcrypt`` através do método ``engine()``:: +Se o valor não puder ser descriptografado devido a alterações na chave ou sal HMAC, +``false`` será retornado. - // Em config/bootstrap.php - use Cake\Utility\Crypto\Mcrypt; - - Security::engine(new Mcrypt()); - -O código acima permitirá que você leia dados de versões anteriores do CakePHP -e criptografar novos dados para serem compatíveis com OpenSSL. - -Fazendo Hash de dados -===================== +Fazendo Hash de Dados +====================== .. php:staticmethod:: hash( $string, $type = NULL, $salt = false ) -Criando um hash de uma string usando o método acima. Se ``$salt`` estiver -setado como ``true`` o salt da aplicação será utilizado:: +Cria um hash de string usando o método fornecido. Retorna ao próximo +método disponível. Se ``$salt`` for definido como ``true``, o valor de sal da +aplicação será usado:: - // Usando salt definido na aplicação + // Usando o valor de sal da aplicação $sha1 = Security::hash('CakePHP Framework', 'sha1', true); - // Usando um salt customizado + // Usando um valor de sal personalizado $sha1 = Security::hash('CakePHP Framework', 'sha1', 'my-salt'); - // Usando o padrão do algoritmo de hash + // Usando o algoritmo de hash padrão $hash = Security::hash('CakePHP Framework'); O método ``hash()`` suporta as seguintes estratégias de hash: @@ -95,27 +73,34 @@ O método ``hash()`` suporta as seguintes estratégias de hash: - sha1 - sha256 -E qualquer outro algoritmo de hash que a função ``hash()`` do PHP suporta. +E qualquer outro algoritmo de hash que a função ``hash()`` do PHP suporte. .. warning:: - Você não deve usar ``hash()`` para senhas em novas aplicações, o ideal - é usar a classe ``DefaultPasswordHasher`` que usa ``bcrypt`` por padrão. + Você não deve usar ``hash()`` para senhas em novas aplicações. + Em vez disso, você deve usar a classe ``DefaultPasswordHasher`` que usa bcrypt + por padrão. -Gerando dados aleatórios seguros -================================ +Obtendo Dados Aleatórios Seguros +================================= .. php:staticmethod:: randomBytes($length) -Obter ``$length`` número de bytes de uma fonte segura aleatória. Esta função -utiliza um dos seguntes métodos: +Obtenha ``$length`` número de bytes de uma fonte aleatória segura. Esta função extrai +dados de uma das seguintes fontes: * Função ``random_bytes`` do PHP. -* Função ``openssl_random_pseudo_bytes`` da extensão SSL. +* ``openssl_random_pseudo_bytes`` da extensão SSL. + +Se nenhuma fonte estiver disponível, um aviso será emitido e um valor +inseguro será usado por motivos de compatibilidade com versões anteriores. + +.. php:staticmethod:: randomString($length) -Se nenhuma das opções estiverem disponíveis um ``warning`` será emitido e um -valor não seguro será usado por motivos de compatibilidade. +Obtenha uma string aleatória com ``$length`` de comprimento de uma fonte aleatória segura. Este método +extrai da mesma fonte aleatória que ``randomBytes()`` e codificará os dados +como uma string hexadecimal. .. meta:: - :title lang=pt: Segurança - :keywords lang=pt: api segurança,senha,cipher text,php class,class security,text key,security library,object instance,security measures,basic security,security level,string type,fallback,hash,data security,singleton,inactivity,php encrypt,implementation,php security + :title lang=pt: Security + :keywords lang=pt: security api,secret password,cipher text,php class,class security,text key,security library,object instance,security measures,basic security,security level,string type,fallback,hash,data security,singleton,inactivity,php encrypt,implementation,php security diff --git a/pt/core-libraries/text.rst b/pt/core-libraries/text.rst index 08986f1e21..21c2df0d69 100644 --- a/pt/core-libraries/text.rst +++ b/pt/core-libraries/text.rst @@ -1,12 +1,385 @@ Texto ##### -.. note:: - Atualmente, a documentação desta página não é suportada em português. +.. php:namespace:: Cake\Utility - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +.. php:class:: Text - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file +A classe Text inclui métodos de conveniência para criar e manipular +strings e é normalmente acessada estaticamente. Exemplo: +``Text::uuid()``. + +Se você precisa das funcionalidades do :php:class:`Cake\\View\\Helper\\TextHelper` fora +de uma ``View``, use a classe ``Text``:: + + namespace App\Controller; + + use Cake\Utility\Text; + + class UsersController extends AppController + { + public function initialize(): void + { + parent::initialize(); + $this->loadComponent('Auth') + }; + + public function afterLogin() + { + $message = $this->Users->find('new_message')->first(); + if (!empty($message)) { + // Notificar usuário sobre nova mensagem + $this->Flash->success(__( + 'Você tem uma nova mensagem: {0}', + Text::truncate($message['Message']['body'], 255, ['html' => true]) + )); + } + } + } + +Converter Strings para ASCII +============================= + +.. php:staticmethod:: transliterate($string, $transliteratorId = null) + +Transliterate por padrão converte todos os caracteres na string fornecida em +caracteres ASCII equivalentes. O método espera codificação UTF-8. A conversão de caracteres +pode ser controlada usando identificadores de transliteração que você pode +passar usando o argumento ``$transliteratorId`` ou alterar a string de identificador padrão +usando ``Text::setTransliteratorId()``. Identificadores de transliteração ICU +são basicamente da forma `` + +Você pode linkar arquivos com caminhos absolutos também para linkar arquivos +que não estão em **webroot/js**:: + + echo $this->Html->script('/otherdir/script_file'); + +Você também pode linkar para uma URL remota:: + + echo $this->Html->script('https://code.jquery.com/jquery.min.js'); + +Vai produzir: + +.. code-block:: html + + + +O primeiro parâmetro pode ser um array para incluir múltiplos arquivos. :: + + echo $this->Html->script(['jquery', 'wysiwyg', 'scripts']); + +Vai produzir: + +.. code-block:: html + + + + + +Você pode anexar a tag de script a um bloco específico usando a opção ``block``:: + + $this->Html->script('wysiwyg', ['block' => 'scriptBottom']); + +No seu layout, você pode produzir todas as tags de script adicionadas a 'scriptBottom':: + + echo $this->fetch('scriptBottom'); + +Você pode incluir arquivos de script de qualquer plugin carregado usando +:term:`sintaxe de plugin`. Para incluir **plugins/DebugKit/webroot/js/toolbar.js** +você poderia usar o seguinte:: + + echo $this->Html->script('DebugKit.toolbar.js'); + +Se você quiser incluir um arquivo de script que compartilha um nome com um plugin carregado, +você pode fazer o seguinte. Por exemplo, se você tivesse um plugin ``Blog``, +e também quisesse incluir **webroot/js/Blog.plugins.js**, você faria:: + + echo $this->Html->script('Blog.plugins.js', ['plugin' => false]); + +Criando Blocos Javascript Inline +--------------------------------- + +.. php:method:: scriptBlock(string $code, array $options = []) + +Para gerar blocos Javascript a partir do código de view PHP, você pode usar um dos métodos +de bloco de script. Scripts podem ser produzidos no lugar, ou armazenados em buffer em um bloco:: + + // Define um bloco de script de uma vez, com o atributo defer. + $this->Html->scriptBlock('alert("hi")', ['defer' => true]); + + // Armazena em buffer um bloco de script para ser produzido mais tarde. + $this->Html->scriptBlock('alert("hi")', ['block' => true]); + +.. php:method:: scriptStart(array $options = []) +.. php:method:: scriptEnd() + +Você pode usar o método ``scriptStart()`` para criar um bloco de captura que será +produzido em uma tag `` + +Gerando mapas com imports, scopes e integrity:: + + echo $this->Html->importmap([ + 'imports' => [ + 'jquery' => 'jquery-3.7.1.min.js', + 'wysiwyg' => '/editor/wysiwyg.js' + ], + 'scopes' => [ + 'scoped/' => [ + 'foo' => 'inner/foo', + ], + ], + 'integrity' => [ + 'jquery' => 'sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=', + ], + ]); + +Vai produzir: + +.. code-block:: html + + + +Criando Listas Aninhadas +------------------------- + +.. php:method:: nestedList(array $list, array $options = [], array $itemOptions = []) + +Constrói uma lista aninhada (UL/OL) a partir de um array associativo:: + + $list = [ + 'Idiomas' => [ + 'Inglês' => [ + 'Americano', + 'Canadense', + 'Britânico', + ], + 'Espanhol', + 'Alemão', + ] + ]; + echo $this->Html->nestedList($list); + +Saída: + +.. code-block:: html + + // Saída (menos os espaços em branco) +
    +
  • Idiomas +
      +
    • Inglês +
        +
      • Americano
      • +
      • Canadense
      • +
      • Britânico
      • +
      +
    • +
    • Espanhol
    • +
    • Alemão
    • +
    +
  • +
+ +Criando Cabeçalhos de Tabela +----------------------------- + +.. php:method:: tableHeaders(array $names, array $trOptions = null, array $thOptions = null) + +Cria uma linha de células de cabeçalho de tabela para ser colocada dentro de tags . :: + + echo $this->Html->tableHeaders(['Data', 'Título', 'Ativo']); + +Saída: + +.. code-block:: html + + + + + + + +:: + + echo $this->Html->tableHeaders( + ['Data', 'Título','Ativo'], + ['class' => 'status'], + ['class' => 'product_table'] + ); + +Saída: + +.. code-block:: html + + + + + + + +Você pode definir atributos por coluna, estes são usados em vez dos +padrões fornecidos no ``$thOptions``:: + + echo $this->Html->tableHeaders([ + 'id', + ['Nome' => ['class' => 'highlight']], + ['Data' => ['class' => 'sortable']] + ]); + +Saída: + +.. code-block:: html + + + + + + + +Criando Células de Tabela +-------------------------- + +.. php:method:: tableCells(array $data, array $oddTrOptions = null, array $evenTrOptions = null, $useCount = false, $continueOddEven = true) + +Cria células de tabela, em linhas, atribuindo atributos de forma diferente +para linhas ímpares e pares. Envolva uma única célula de tabela dentro de um +[] para atributos + + + +:: + + echo $this->Html->tableCells([ + ['7 de Jul, 2007', ['Melhores Brownies', ['class' => 'highlight']] , 'Sim'], + ['21 de Jun, 2007', 'Cookies Inteligentes', 'Sim'], + ['1 de Ago, 2006', 'Bolo Anti-Java', ['Não', ['id' => 'special']]], + ]); + +Saída: + +.. code-block:: html + + + + + + + + + + + + + + + + + +:: + + echo $this->Html->tableCells( + [ + ['Vermelho', 'Maçã'], + ['Laranja', 'Laranja'], + ['Amarelo', 'Banana'], + ], + ['class' => 'darker'] + ); + +Saída: + +.. code-block:: html + + + + + +Alterando as Tags Produzidas pelo HtmlHelper +============================================= + +.. php:method:: setTemplates(array $templates) + +Carrega um array de templates para adicionar/substituir templates:: + + // Carrega templates específicos. + $this->Html->setTemplates([ + 'javascriptlink' => '' + ]); + +Você pode carregar um arquivo de configuração contendo templates usando o templater +diretamente:: + + // Carrega um arquivo de configuração com templates. + $this->Html->templater()->load('my_tags'); + +Ao carregar arquivos de templates, seu arquivo deve se parecer com:: + + '' + ]; + +.. warning:: + + Strings de template contendo um sinal de porcentagem (``%``) precisam de atenção especial, + você deve prefixar este caractere com outro sinal de porcentagem para que pareça + ``%%``. A razão é que internamente os templates são compilados para serem usados com + ``sprintf()``. Exemplo: ``
{{content}}
`` + +.. meta:: + :title lang=pt: HtmlHelper + :description lang=pt: O papel do HtmlHelper no CakePHP é tornar as opções relacionadas ao HTML mais fáceis, rápidas e resilientes a mudanças. + :keywords lang=pt: html helper,cakephp css,cakephp script,tipo de conteúdo,imagem html,link html,tag html,bloco de script,início de script,url html,estilo cakephp,breadcrumbs cakephp diff --git a/pt/views/helpers/number.rst b/pt/views/helpers/number.rst index 03a30e3e7c..96aca1cea3 100644 --- a/pt/views/helpers/number.rst +++ b/pt/views/helpers/number.rst @@ -5,12 +5,21 @@ Number .. php:class:: NumberHelper(View $view, array $config = []) -.. note:: - Atualmente, a documentação desta página não é suportada em português. +O NumberHelper contém métodos convenientes que permitem exibir +números em formatos comuns em suas views. Esses métodos incluem formas +de formatar moeda, porcentagens, tamanhos de dados, formatar números para +precisões específicas e também oferecem mais flexibilidade com +a formatação de números. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +.. include:: /core-libraries/number.rst + :start-after: start-cakenumber + :end-before: end-cakenumber - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. +.. warning:: + + Todos os símbolos são UTF-8. + +.. meta:: + :title lang=pt: NumberHelper + :description lang=pt: O NumberHelper contém métodos de conveniência que permitem exibir números em formatos comuns em suas views. + :keywords lang=pt: number helper,currency,number format,number precision,format file size,format numbers diff --git a/pt/views/helpers/paginator.rst b/pt/views/helpers/paginator.rst index c3366d0871..9d15d9de54 100644 --- a/pt/views/helpers/paginator.rst +++ b/pt/views/helpers/paginator.rst @@ -5,17 +5,578 @@ Paginator .. php:class:: PaginatorHelper(View $view, array $config = []) +O PaginatorHelper é usado para gerar controles de paginação como números de página +e links de próximo/anterior. + +Veja também :doc:`/controllers/pagination` para informações sobre como +criar datasets paginados e fazer consultas paginadas. + +Definindo o conjunto de resultados paginado +-------------------------------------------- + +.. php:method:: setPaginated($paginated, $options) + +Por padrão, o helper usa a primeira instância de ``Cake\Datasource\Paging\PaginatedInterface`` +que ele encontra nas variáveis de view. (Geralmente o resultado de ``Controller::paginate()``). + +Você pode usar ``PaginatorHelper::setPaginated()`` para definir explicitamente o conjunto +de resultados paginado que o helper deve usar. + .. _paginator-templates: -PaginatorHelper Templates -========================= +Templates do PaginatorHelper +============================ + +Internamente o PaginatorHelper usa uma série de templates HTML simples para gerar +marcação. Você pode modificar esses templates para personalizar o HTML gerado pelo +PaginatorHelper. + +Os templates usam placeholders no estilo ``{{var}}``. É importante não adicionar +espaços ao redor de ``{{}}`` ou as substituições não funcionarão. + +Carregando Templates de um Arquivo +----------------------------------- + +Ao adicionar o PaginatorHelper em seu controller, você pode definir a +configuração 'templates' para definir um arquivo de template a ser carregado. Isso permite que você +personalize vários templates e mantenha seu código DRY:: + + // No seu AppView.php + public function initialize(): void + { + ... + $this->loadHelper('Paginator', ['templates' => 'paginator-templates']); + } + +Isso carregará o arquivo localizado em **config/paginator-templates.php**. Veja o +exemplo abaixo de como o arquivo deve ser. Você também pode carregar templates +de um plugin usando :term:`sintaxe de plugin`:: + + // No seu AppView.php + public function initialize(): void + { + ... + $this->loadHelper('Paginator', ['templates' => 'MyPlugin.paginator-templates']); + } + +Seja seus templates na aplicação principal ou em um plugin, seu +arquivo de templates deve se parecer com algo assim:: + + return [ + 'number' => '{{text}}', + ]; + +Alterando Templates em Tempo de Execução +----------------------------------------- + +.. php:method:: setTemplates($templates) + +Este método permite que você altere os templates usados pelo PaginatorHelper em +tempo de execução. Isso pode ser útil quando você quer personalizar templates para uma +chamada de método específica:: + + // Lê o valor do template atual. + $result = $this->Paginator->getTemplates('number'); + + // Altera um template + $this->Paginator->setTemplates([ + 'number' => '{{text}}' + ]); + +.. warning:: + + Strings de template contendo um sinal de porcentagem (``%``) precisam de atenção + especial, você deve prefixar este caractere com outra porcentagem para que + pareça ``%%``. A razão é que internamente os templates são compilados para + serem usados com ``sprintf()``. + Exemplo: '
{{content}}
' + +Nomes de Template +----------------- + +O PaginatorHelper usa os seguintes templates: + +- ``nextActive`` O estado ativo para um link gerado por next(). +- ``nextDisabled`` O estado desabilitado para next(). +- ``prevActive`` O estado ativo para um link gerado por prev(). +- ``prevDisabled`` O estado desabilitado para prev() +- ``counterRange`` O template que counter() usa quando format == range. +- ``counterPages`` O template que counter() usa quando format == pages. +- ``first`` O template usado para um link gerado por first(). +- ``last`` O template usado para um link gerado por last() +- ``number`` O template usado para um link gerado por numbers(). +- ``current`` O template usado para a página atual. +- ``ellipsis`` O template usado para reticências geradas por numbers(). +- ``sort`` O template para um link de ordenação sem direção. +- ``sortAsc`` O template para um link de ordenação com direção ascendente. +- ``sortDesc`` O template para um link de ordenação com direção descendente. + +Criando Links de Ordenação +=========================== + +.. php:method:: sort($key, $title = null, $options = []) + + :param string $key: O nome da coluna pela qual o conjunto de registros deve ser ordenado. + :param string $title: Título para o link. Se $title for null, $key será + usado convertido para o formato "Title Case" e usado como título. + :param array $options: Opções para link de ordenação. + +Gera um link de ordenação. Define parâmetros querystring para a ordenação e +direção. Os links serão padrão para ordenar por asc. Após o primeiro clique, links +gerados com ``sort()`` irão lidar com a troca de direção automaticamente. Se o +conjunto de resultados está ordenado 'asc' pela chave especificada, o link retornado irá ordenar por +'desc'. Usa os templates ``sort``, ``sortAsc``, ``sortDesc``, ``sortAscLocked`` e +``sortDescLocked``. + +Chaves aceitas para ``$options``: + +* ``escape`` Se você quer o conteúdo codificado como entidade HTML, padrão é + ``true``. +* ``direction`` A direção padrão a usar quando este link não está ativo. +* ``lock`` Bloquear direção. Só usará a direção padrão então, padrão é ``false``. + +Assumindo que você está paginando alguns posts, e está na página um:: + + echo $this->Paginator->sort('user_id'); + +Saída: + +.. code-block:: html + + User Id + +Você pode usar o parâmetro title para criar texto personalizado para seu link:: + + echo $this->Paginator->sort('user_id', 'User account'); + +Saída: + +.. code-block:: html + + User account + +Se você está usando HTML como imagens em seus links, lembre-se de desativar o escape:: + + echo $this->Paginator->sort( + 'user_id', + 'User account', + ['escape' => false] + ); + +Saída: + +.. code-block:: html + + User account + +A opção direction pode ser usada para definir a direção padrão para um link. Uma vez que um +link está ativo, ele irá trocar de direção automaticamente como normal:: + + echo $this->Paginator->sort('user_id', null, ['direction' => 'desc']); + +Saída: + +.. code-block:: html + + User Id + +A opção lock pode ser usada para bloquear a ordenação na direção especificada:: + + echo $this->Paginator->sort('user_id', null, ['direction' => 'asc', 'lock' => true]); + +.. php:method:: sortDir(string $model = null, mixed $options = []) + + Obtém a direção atual pela qual o conjunto de registros está ordenado. + +.. php:method:: sortKey(string $model = null, mixed $options = []) + + Obtém a chave atual pela qual o conjunto de registros está ordenado. + +Criando Links de Números de Página +=================================== + +.. php:method:: numbers($options = []) + +Retorna um conjunto de números para o conjunto de resultados paginado. Usa um módulo para +decidir quantos números mostrar em cada lado da página atual. Por padrão, +8 links em cada lado da página atual serão criados se essas páginas existirem. +Links não serão gerados para páginas que não existem. A página atual também +não é um link. Os templates ``number``, ``current`` e ``ellipsis`` serão +usados. + +Opções suportadas são: + +* ``before`` Conteúdo a ser inserido antes dos números. +* ``after`` Conteúdo a ser inserido depois dos números. +* ``modulus`` quantos números incluir em cada lado da página atual, + padrão é 8. +* ``first`` Se você quer links primeiro gerados, defina como um inteiro para + definir o número de links 'primeiro' a gerar. Padrão é ``false``. Se uma + string for definida, um link para a primeira página será gerado com o valor como + título:: + + echo $this->Paginator->numbers(['first' => 'First page']); + +* ``last`` Se você quer links último gerados, defina como um inteiro para definir + o número de links 'último' a gerar. Padrão é ``false``. Segue a mesma + lógica que a opção ``first``. Há um + método :php:meth:`~PaginatorHelper::last()` para ser usado separadamente também se + você desejar. + +Embora este método permita muita personalização para sua saída, também é +ok apenas chamar o método sem nenhum parâmetro. :: + + echo $this->Paginator->numbers(); + +Usando as opções first e last você pode criar links para o início +e fim do conjunto de páginas. O seguinte criaria um conjunto de links de página que +incluem links para as primeiras 2 e últimas 2 páginas nos resultados paginados:: + + echo $this->Paginator->numbers(['first' => 2, 'last' => 2]); + +Criando Links de Salto +======================= + +Além de gerar links que vão diretamente para números de página específicos, +você frequentemente vai querer links que vão para os links anterior e próximo, primeira e última +páginas no conjunto de dados paginado. + +.. php:method:: prev($title = '<< Previous', $options = []) + + :param string $title: Título para o link. + :param mixed $options: Opções para link de paginação. + + Gera um link para a página anterior em um conjunto de registros paginados. Usa + os templates ``prevActive`` e ``prevDisabled``. + + ``$options`` suporta as seguintes chaves: + + * ``escape`` Se você quer o conteúdo codificado como entidade HTML, + padrão é ``true``. + * ``disabledTitle`` O texto a usar quando o link está desabilitado. Padrão é + o parâmetro ``$title``. + + Um exemplo simples seria:: + + echo $this->Paginator->prev(' << ' . __('previous')); + + Se você estivesse atualmente na segunda página de posts, você obteria o seguinte: + + .. code-block:: html + + + + Se não houver páginas anteriores, você obteria: + + .. code-block:: html + + + + Para alterar os templates usados por este método, veja :ref:`paginator-templates`. + +.. php:method:: next($title = 'Next >>', $options = []) + + Este método é idêntico a :php:meth:`~PaginatorHelper::prev()` com algumas exceções. Ele + cria links apontando para a próxima página em vez da anterior. Ele também + usa ``next`` como o valor do atributo rel em vez de ``prev``. Usa os + templates ``nextActive`` e ``nextDisabled``. + +.. php:method:: first($first = '<< first', $options = []) + + Retorna um primeiro ou conjunto de números para as primeiras páginas. Se uma string for fornecida, + então apenas um link para a primeira página com o texto fornecido será criado:: + + echo $this->Paginator->first('< first'); + + O código acima cria um único link para a primeira página. Não produzirá nada se você + estiver na primeira página. Você também pode usar um inteiro para indicar quantos primeiros + links de paginação você quer gerados:: + + echo $this->Paginator->first(3); + + O código acima criará links para as primeiras 3 páginas, uma vez que você chegue à terceira ou + página maior. Antes disso, nada será produzido. Usa o template ``first``. + + O parâmetro options aceita o seguinte: + + - ``escape`` Se o texto deve ou não ser escapado. Defina como ``false`` se seu + conteúdo contém HTML. + +.. php:method:: last($last = 'last >>', $options = []) + + Este método funciona muito como o método :php:meth:`~PaginatorHelper::first()`. + Mas tem algumas diferenças. Ele não gerará nenhum link se você + estiver na última página para valores string de ``$last``. Para um valor inteiro de + ``$last``, nenhum link será gerado uma vez que o usuário esteja dentro do intervalo de últimas + páginas. Usa o template ``last``. + +Criando Tags de Link de Cabeçalho +================================== + +O PaginatorHelper pode ser usado para criar tags de link de paginação em seus elementos +```` da página:: + + // Cria links próximo/anterior para o model atual. + echo $this->Paginator->meta(); + + // Cria links próximo/anterior e primeiro/último para o model atual. + echo $this->Paginator->meta(['first' => true, 'last' => true]); + +Verificando o Estado da Paginação +================================== + +.. php:method:: current() + + Obtém a página atual do conjunto de registros:: + + // Nossa URL é: /comments?page=3 + echo $this->Paginator->current(); + // A saída é 3 + + Usa o template ``current``. + +.. php:method:: hasNext(string $model = null) + + Retorna ``true`` se o conjunto de resultados fornecido não estiver na última página. + +.. php:method:: hasPrev() + + Retorna ``true`` se o conjunto de resultados fornecido não estiver na primeira página. + +.. php:method:: hasPage(int $page = 1) + + Retorna ``true`` se o conjunto de resultados fornecido tiver o número de página fornecido por ``$page``. + +.. php:method:: total() + + Retorna o número total de páginas para o model fornecido. + +Criando um Contador de Página +============================== + +.. php:method:: counter(string $format = 'pages', array $options = []) + +Retorna uma string de contador para o conjunto de resultados paginado. Usando uma string de formato +fornecida e várias opções, você pode criar indicadores localizados e específicos da aplicação +de onde um usuário está no conjunto de dados paginado. Usa os +templates ``counterRange`` e ``counterPages``. + +Formatos suportados são 'range', 'pages' e custom. O padrão é pages, que +geraria uma saída como '1 of 10'. No modo custom, a string fornecida é analisada e +tokens são substituídos por valores reais. Os tokens disponíveis são: + +- ``{{page}}`` - a página atual exibida. +- ``{{pages}}`` - número total de páginas. +- ``{{current}}`` - número atual de registros sendo mostrados. +- ``{{count}}`` - o número total de registros no conjunto de resultados. +- ``{{start}}`` - número do primeiro registro sendo exibido. +- ``{{end}}`` - número do último registro sendo exibido. +- ``{{model}}`` - A forma humana pluralizada do nome do model. + Se seu model fosse 'RecipePage', ``{{model}}`` seria 'recipe pages'. + +Você também pode fornecer apenas uma string para o método counter usando os tokens +disponíveis. Por exemplo:: + + echo $this->Paginator->counter( + 'Page {{page}} of {{pages}}, showing {{current}} records out of + {{count}} total, starting on record {{start}}, ending on {{end}}' + ); + +Definir 'format' para range geraria uma saída como '1 - 3 of 13':: + + echo $this->Paginator->counter('range'); + +Gerando URLs de Paginação +========================== + +.. php:method:: generateUrl(array $options = [], ?string $model = null, array $url = [], array $urlOptions = []) + +Por padrão, retorna uma string de URL de paginação completa para uso em contextos não-padrão +(ou seja, JavaScript). :: + + // Gera uma URL similar a: /articles?sort=title&page=2 + echo $this->Paginator->generateUrl(['sort' => 'title']); + + // Gera uma URL para um model diferente + echo $this->Paginator->generateUrl(['sort' => 'title'], 'Comments'); + + // Gera uma URL para um controller diferente. + echo $this->Paginator->generateUrl( + ['sort' => 'title'], + null, + ['controller' => 'Comments'] + ); + +Criando um Controle Selectbox de Limite +======================================== + +.. php:method:: limitControl(array $limits = [], $default = null, array $options = []) + +Cria um controle dropdown que altera o parâmetro de consulta ``limit``:: + + // Usa os padrões. + echo $this->Paginator->limitControl(); + + // Define quais opções de limite você quer. + echo $this->Paginator->limitControl([25 => 25, 50 => 50]); + + // Limites personalizados e define a opção selecionada + echo $this->Paginator->limitControl([25 => 25, 50 => 50], $user->perPage); + +O formulário e controle gerados serão enviados automaticamente ao mudar. + +Configurando Opções de Paginação +================================= + +.. php:method:: options($options = []) + +Define todas as opções para o PaginatorHelper. Opções suportadas são: + +* ``url`` A URL da action de paginação. + + A opção permite que você defina/sobrescreva qualquer elemento para URLs geradas pelo + helper:: + + $this->Paginator->options([ + 'url' => [ + 'lang' => 'en', + '?' => [ + 'sort' => 'email', + 'direction' => 'desc', + 'page' => 6, + ], + ] + ]); + + O exemplo acima adiciona o parâmetro de rota ``en`` a todos os links que o helper irá + gerar. Ele também criará links com valores específicos de sort, direction e page. + Por padrão, ``PaginatorHelper`` irá mesclar todos os argumentos passados + atualmente e parâmetros de query string. + +* ``escape`` Define se o campo de título para links deve ser escapado em HTML. + Padrão é ``true``. + +Exemplo de Uso +============== + +Cabe a você decidir como mostrar registros ao usuário, mas na maioria das vezes isso +será feito dentro de tabelas HTML. Os exemplos abaixo assumem um layout tabular, mas +o PaginatorHelper disponível em views nem sempre precisa ser restrito +assim. + +Veja os detalhes sobre +`PaginatorHelper `_ na +API. Como mencionado, o PaginatorHelper também oferece recursos de ordenação que +podem ser integrados aos cabeçalhos das colunas da sua tabela: + +.. code-block:: php + + +
DataTítuloAtivo
DataTítuloAtivo
idNomeData
específicos. :: + + echo $this->Html->tableCells([ + ['7 de Jul, 2007', 'Melhores Brownies', 'Sim'], + ['21 de Jun, 2007', 'Cookies Inteligentes', 'Sim'], + ['1 de Ago, 2006', 'Bolo Anti-Java', 'Não'], + ]); + +Saída: + +.. code-block:: html + +
7 de Jul, 2007Melhores BrowniesSim
21 de Jun, 2007Cookies InteligentesSim
1 de Ago, 2006Bolo Anti-JavaNão
+ 7 de Jul, 2007 + + Melhores Brownies + + Sim +
+ 21 de Jun, 2007 + + Cookies Inteligentes + + Sim +
+ 1 de Ago, 2006 + + Bolo Anti-Java + + Não +
VermelhoMaçã
LaranjaLaranja
AmareloBanana
+ + + + + + + + + + +
Paginator->sort('id', 'ID') ?>Paginator->sort('title', 'Title') ?>
id ?> title) ?>
+ +Os links gerados pelo método ``sort()`` do ``PaginatorHelper`` permitem que +usuários cliquem nos cabeçalhos da tabela para alternar a ordenação dos dados por um +campo específico. + +Também é possível ordenar uma coluna com base em associações: + +.. code-block:: php + + + + + + + + + + + + +
Paginator->sort('title', 'Title') ?>Paginator->sort('Authors.name', 'Author') ?>
title) ?> name) ?>
.. note:: - Atualmente, a documentação desta página não é suportada em português. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. + Ordenar por colunas em models associados requer definir estas na + propriedade ``PaginationComponent::paginate``. Usando o exemplo acima, o + controller que lida com a paginação precisaria definir sua chave ``sortableFields`` + da seguinte forma: + + .. code-block:: php + + $this->paginate = [ + 'sortableFields' => [ + 'Posts.title', + 'Authors.name', + ], + ]; + + Para mais informações sobre o uso da opção ``sortableFields``, por favor veja + :ref:`control-which-fields-used-for-ordering`. + +O ingrediente final para exibição de paginação em views é a adição de +navegação de página, também fornecida pelo PaginationHelper:: + + // Mostra os números de página + Paginator->numbers() ?> + + // Mostra os links próximo e anterior + Paginator->prev('« Previous') ?> + Paginator->next('Next »') ?> + + // Imprime X de Y, onde X é a página atual e Y é o número de páginas + Paginator->counter() ?> + +A redação da saída do método counter() também pode ser personalizada usando +marcadores especiais:: + + Paginator->counter([ + 'format' => 'Page {{page}} of {{pages}}, showing {{current}} records out of + {{count}} total, starting on record {{start}}, ending on {{end}}' + ]) ?> + +.. _paginator-helper-multiple: + +Paginando Múltiplos Resultados +=============================== + +Se você está :ref:`paginando múltiplas consultas ` +você precisará usar ``PaginatorHelper::setPaginated()`` primeiro antes de chamar +outros métodos do helper, para que eles gerem a saída esperada. + +``PaginatorHelper`` usará automaticamente o ``scope`` definido quando a +consulta foi paginada. Para definir parâmetros de URL adicionais para paginação múltipla +você pode incluir os nomes de scope em ``options()``:: + + $this->Paginator->options([ + 'url' => [ + // Parâmetros de URL adicionais para o scope 'articles' + 'articles' => [ + '?' => ['articles' => 'yes'] + ], + // Parâmetros de URL adicionais para o scope 'comments' + 'comments' => [ + 'articleId' => 1234, + ], + ], + ]); - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. +.. meta:: + :title lang=pt: PaginatorHelper + :description lang=pt: O PaginatorHelper é usado para gerar controles de paginação como números de página e links de próximo/anterior. + :keywords lang=pt: helper paginator,paginação,ordenar,links de número de página,paginação em views,link anterior,link próximo,link último,link primeiro,contador de página diff --git a/pt/views/helpers/text.rst b/pt/views/helpers/text.rst index 3680fc6b8a..dfa898b47c 100644 --- a/pt/views/helpers/text.rst +++ b/pt/views/helpers/text.rst @@ -5,12 +5,90 @@ Text .. php:class:: TextHelper(View $view, array $config = []) -.. note:: - Atualmente, a documentação desta página não é suportada em português. +O TextHelper contém métodos para tornar o texto mais utilizável e +amigável em suas views. Ele auxilia na criação de links, formatação de URLs, +criação de trechos de texto em torno de palavras ou frases escolhidas, +destacando palavras-chave em blocos de texto, e truncando graciosamente +longos trechos de texto. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +Vinculando Endereços de E-mail +=============================== - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. +.. php:method:: autoLinkEmails(string $text, array $options = []) + +Adiciona links aos endereços de e-mail bem formados em $text, de acordo +com quaisquer opções definidas em ``$options`` (consulte +:php:meth:`HtmlHelper::link()`). :: + + $myText = 'Para mais informações sobre nossas sobremesas e doces ' . + 'mundialmente famosos, entre em contato info@example.com'; + $linkedText = $this->Text->autoLinkEmails($myText); + +Saída:: + + Para mais informações sobre nossas sobremesas e doces mundialmente famosos, + entre em contato info@example.com + +Este método automaticamente escapa sua entrada. Use a opção ``escape`` +para desabilitar isso se necessário. + +Vinculando URLs +=============== + +.. php:method:: autoLinkUrls(string $text, array $options = []) + +Igual a ``autoLinkEmails()``, apenas este método procura por +strings que começam com https, http, ftp, ou nntp e as vincula +apropriadamente. + +Este método automaticamente escapa sua entrada. Use a opção ``escape`` +para desabilitar isso se necessário. + +Vinculando Tanto URLs quanto Endereços de E-mail +================================================= + +.. php:method:: autoLink(string $text, array $options = []) + +Executa a funcionalidade tanto de ``autoLinkUrls()`` quanto de +``autoLinkEmails()`` no ``$text`` fornecido. Todas as URLs e e-mails +são vinculados apropriadamente de acordo com as ``$options`` fornecidas. + +Este método automaticamente escapa sua entrada. Use a opção ``escape`` +para desabilitar isso se necessário. + +Opções adicionais: + +* ``stripProtocol``: Remove ``http://`` e ``https://`` do início do + rótulo do link. Padrão desligado. +* ``maxLength``: O comprimento máximo do rótulo do link. Padrão desligado. +* ``ellipsis``: A string a ser anexada ao final do rótulo do link. Padrão é + reticências UTF8. + +Convertendo Texto em Parágrafos +================================ + +.. php:method:: autoParagraph(string $text) + +Adiciona

apropriados ao redor do texto onde retornos de linha duplos são encontrados, e
onde +retornos de linha simples são encontrados. :: + + $myText = 'Para mais informações + sobre nossas sobremesas e doces mundialmente famosos. + + entre em contato info@example.com'; + $formattedText = $this->Text->autoParagraph($myText); + +Saída:: + +

Para mais informações
+ sobre nossas sobremesas e doces mundialmente famosos.

+

entre em contato info@example.com

+ +.. include:: /core-libraries/text.rst + :start-after: start-text + :end-before: end-text + +.. meta:: + :title lang=pt: TextHelper + :description lang=pt: O TextHelper contém métodos para tornar o texto mais utilizável e amigável em suas views. + :keywords lang=pt: text helper,autoLinkEmails,autoLinkUrls,autoLink,excerpt,highlight,stripLinks,truncate,string text diff --git a/pt/views/helpers/time.rst b/pt/views/helpers/time.rst index 51dbf9babf..2eef18dfa6 100644 --- a/pt/views/helpers/time.rst +++ b/pt/views/helpers/time.rst @@ -5,12 +5,47 @@ Time .. php:class:: TimeHelper(View $view, array $config = []) -.. note:: - Atualmente, a documentação desta página não é suportada em português. +O TimeHelper permite o processamento rápido de informações relacionadas ao tempo. +O TimeHelper tem duas tarefas principais que pode realizar: - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +#. Ele pode formatar strings de tempo. +#. Ele pode testar tempo. - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. +Usando o Helper +=============== + +Um uso comum do TimeHelper é compensar a data e hora para corresponder ao fuso +horário de um usuário. Vamos usar um fórum como exemplo. Seu fórum tem muitos usuários que +podem postar mensagens a qualquer momento de qualquer parte do mundo. Uma maneira de +gerenciar o tempo é salvar todas as datas e horas como GMT+0 ou UTC. Descomente a +linha ``date_default_timezone_set('UTC');`` em **config/bootstrap.php** para garantir +que o fuso horário da sua aplicação esteja definido como GMT+0. + +Em seguida, adicione um campo de fuso horário à sua tabela de usuários e faça as modificações necessárias +para permitir que seus usuários definam seu fuso horário. Agora que sabemos +o fuso horário do usuário logado, podemos corrigir a data e hora em nossas +postagens usando o TimeHelper:: + + echo $this->Time->format( + $post->created, + \IntlDateFormatter::FULL, + false, + $user->time_zone + ); + // Exibirá 'Saturday, August 22, 2011 at 11:53:00 PM GMT' + // para um usuário em GMT+0. Enquanto exibe, + // 'Saturday, August 22, 2011 at 03:53 PM GMT-8:00' + // para um usuário em GMT-8 + +A maioria dos recursos do TimeHelper são destinados como interfaces compatíveis com versões anteriores +para aplicações que estão atualizando de versões mais antigas do CakePHP. Porque o +ORM retorna instâncias de :php:class:`Cake\\I18n\\Time` para cada coluna ``timestamp`` +e ``datetime``, você pode usar os métodos lá para fazer a maioria das tarefas. +Por exemplo, para ler sobre as strings de formatação aceitas, dê uma olhada no método +`Cake\\I18n\\Time::i18nFormat() +`_. + +.. meta:: + :title lang=pt: TimeHelper + :description lang=pt: O TimeHelper ajudará você a formatar tempo e testar tempo. + :keywords lang=pt: time helper,format time,timezone,unix epoch,time strings,time zone offset,utc,gmt diff --git a/pt/views/helpers/url.rst b/pt/views/helpers/url.rst index 5565c9880b..38d76aafa7 100644 --- a/pt/views/helpers/url.rst +++ b/pt/views/helpers/url.rst @@ -5,12 +5,159 @@ Url .. php:class:: UrlHelper(View $view, array $config = []) -.. note:: - Atualmente, a documentação desta página não é suportada em português. +O UrlHelper ajuda você a gerar URLs de seus outros helpers. +Ele também oferece um único lugar para personalizar como as URLs são geradas +sobrescrevendo o helper principal com um da aplicação. Veja a +seção :ref:`aliasing-helpers` para saber como fazer isso. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +Gerando URLs +============ - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. +.. php:method:: build($url = null, array $options = []) + +Retorna uma URL apontando para uma combinação de controller e action. +Se ``$url`` estiver vazia, retorna o ``REQUEST_URI``, caso contrário +gera a URL para a combinação de controller e action. Se ``fullBase`` for +``true``, a URL base completa será anexada ao resultado:: + + echo $this->Url->build([ + 'controller' => 'Posts', + 'action' => 'view', + 'bar', + ]); + + // Saída + /posts/view/bar + +Aqui estão mais alguns exemplos de uso: + +URL com extensão:: + + echo $this->Url->build([ + 'controller' => 'Posts', + 'action' => 'list', + '_ext' => 'rss', + ]); + + // Saída + /posts/list.rss + +URL com prefixo:: + + echo $this->Url->build([ + 'controller' => 'Posts', + 'action' => 'list', + 'prefix' => 'Admin', + ]); + + // Saída + /admin/posts/list + +URL (começando com '/') com a URL base completa anexada:: + + echo $this->Url->build('/posts', ['fullBase' => true]); + + // Saída + http://somedomain.com/posts + +URL com parâmetros GET e âncora de fragmento:: + + echo $this->Url->build([ + 'controller' => 'Posts', + 'action' => 'search', + '?' => ['foo' => 'bar'], + '#' => 'first', + ]); + + // Saída + /posts/search?foo=bar#first + +O exemplo acima usa a chave especial ``?`` para especificar parâmetros de query string +e a chave ``#`` para fragmento de URL. + +URL para rota nomeada:: + + // Assumindo que uma rota está configurada como rota nomeada: + // $router->connect( + // '/products/{slug}', + // [ + // 'controller' => 'Products', + // 'action' => 'view', + // ], + // [ + // '_name' => 'product-page', + // ] + // ); + + echo $this->Url->build(['_name' => 'product-page', 'slug' => 'i-m-slug']); + // Resultará em: + /products/i-m-slug + +O 2º parâmetro permite que você defina opções controlando o escape de HTML e +se o caminho base deve ser adicionado ou não:: + + $this->Url->build('/posts', [ + 'escape' => false, + 'fullBase' => true, + ]); + +.. php:method:: buildFromPath(string $path, array $params = [], array $options = []) + +Se você quiser usar strings de caminho de rota, pode fazer isso usando este método:: + + echo $this->Url->buildFromPath('Articles::index'); + // saída: /articles + + echo $this->Url->buildFromPath('MyBackend.Admin/Articles::view', [3]); + // saída: /admin/my-backend/articles/view/3 + +URL com timestamp de asset envolvida por ````, aqui pré-carregando +uma fonte. Nota: O arquivo deve existir e ``Configure::read('Asset.timestamp')`` +deve retornar ``true`` ou ``'force'`` para que o timestamp seja anexado:: + + echo $this->Html->meta([ + 'rel' => 'preload', + 'href' => $this->Url->assetUrl( + '/assets/fonts/your-font-pack/your-font-name.woff2' + ), + 'as' => 'font', + ]); + +Se você está gerando URLs para arquivos CSS, Javascript ou de imagem, existem métodos +helper para cada um desses tipos de assets:: + + // Saída /img/icon.png + $this->Url->image('icon.png'); + + // Saída /js/app.js + $this->Url->script('app.js'); + + // Saída /css/app.css + $this->Url->css('app.css'); + + // Força timestamps para uma chamada de método. + $this->Url->css('app.css', ['timestamp' => 'force']); + + // Ou desabilita timestamps para uma chamada de método. + $this->Url->css('app.css', ['timestamp' => false]); + +Personalizando a Geração de URL de Assets +========================================== + +Se você precisar personalizar como as URLs de assets são geradas, ou deseja usar parâmetros +personalizados de cache busting de assets, pode usar a opção ``assetUrlClassName``:: + + // Em view initialize + $this->loadHelper('Url', ['assetUrlClassName' => AppAsset::class]); + +Ao usar ``assetUrlClassName`` você deve implementar os mesmos métodos que +``Cake\Routing\Asset`` implementa. + +Para mais informações, consulte +`Router::url `_ +na API. + +.. meta:: + :title lang=pt: UrlHelper + :description lang=pt: O papel do UrlHelper no CakePHP é ajudar a construir urls. + :keywords lang=pt: url helper,url diff --git a/pt/views/json-and-xml-views.rst b/pt/views/json-and-xml-views.rst index ea762ac3ce..e6a19cd602 100644 --- a/pt/views/json-and-xml-views.rst +++ b/pt/views/json-and-xml-views.rst @@ -1,12 +1,249 @@ -Views JSON & XML +Views JSON e XML ################ -.. note:: - Atualmente, a documentação desta página não é suportada em português. +A integração de ``JsonView`` e ``XmlView`` com os recursos de +:ref:`controller-viewclasses` do CakePHP permitem que você crie respostas JSON e XML. - Por favor, sinta-se a vontade para nos enviar um *pull request* para o - `Github `_ ou use o botão - **IMPROVE THIS DOC** para propor suas mudanças diretamente. +Essas classes de view são mais comumente usadas com :php:meth:`Cake\\Controller\\Controller::viewClasses()`. - Você pode consultar a versão em inglês deste tópico através do seletor de - idiomas localizado ao lado direito do campo de buscas da documentação. \ No newline at end of file +Existem duas maneiras de gerar views de dados. A primeira é usando a +opção ``serialize`` e a segunda é criando arquivos de template normais. + +Definindo Classes de View para Negociar +======================================== + +No seu ``AppController`` ou em um controller individual, você pode implementar o +método ``viewClasses()`` e fornecer todas as views que deseja suportar:: + + use Cake\View\JsonView; + use Cake\View\XmlView; + + public function viewClasses(): array + { + return [JsonView::class, XmlView::class]; + } + +Você pode opcionalmente habilitar as extensões json e/ou xml com +:ref:`file-extensions`. Isso permitirá que você acesse as views ``JSON``, ``XML`` ou +qualquer outro formato especial usando uma URL personalizada terminando com o nome do +tipo de resposta como uma extensão de arquivo, como ``http://example.com/articles.json``. + +Por padrão, quando não estiver habilitando :ref:`file-extensions`, o cabeçalho ``Accept`` +na requisição é usado para selecionar qual tipo de formato deve ser renderizado para o +usuário. Um exemplo de formato ``Accept`` usado para renderizar respostas ``JSON`` é +``application/json``. + +Usando Views de Dados com a Chave Serialize +============================================ + +A opção ``serialize`` indica qual(is) variável(is) de view deve(m) ser +serializada(s) ao usar uma view de dados. Isso permite que você pule a definição de arquivos de template +para as ações do seu controller se não precisar fazer nenhuma formatação personalizada antes +de seus dados serem convertidos em json/xml. + +Se você precisar fazer qualquer formatação ou manipulação de suas variáveis de view antes +de gerar a resposta, deve usar arquivos de template. O valor de +``serialize`` pode ser tanto uma string quanto um array de variáveis de view para +serializar:: + + + namespace App\Controller; + + use Cake\View\JsonView; + + class ArticlesController extends AppController + { + public function viewClasses(): array + { + return [JsonView::class]; + } + + public function index() + { + // Define as variáveis de view + $this->set('articles', $this->paginate()); + // Especifica quais variáveis de view o JsonView deve serializar. + $this->viewBuilder()->setOption('serialize', 'articles'); + } + } + +Você também pode definir ``serialize`` como um array de variáveis de view para combinar:: + + namespace App\Controller; + + use Cake\View\JsonView; + + class ArticlesController extends AppController + { + public function viewClasses(): array + { + return [JsonView::class]; + } + + public function index() + { + // Algum código que criou $articles e $comments + + // Define as variáveis de view + $this->set(compact('articles', 'comments')); + + // Especifica quais variáveis de view o JsonView deve serializar. + $this->viewBuilder()->setOption('serialize', ['articles', 'comments']); + } + } + +Definir ``serialize`` como um array tem o benefício adicional de anexar automaticamente +um elemento ```` de nível superior ao usar :php:class:`XmlView`. +Se você usar um valor string para ``serialize`` e XmlView, certifique-se de que sua +variável de view tenha um único elemento de nível superior. Sem um único elemento de nível +superior, o Xml falhará ao gerar. + +Usando uma View de Dados com Arquivos de Template +================================================== + +Você deve usar arquivos de template se precisar manipular o conteúdo de sua view +antes de criar a saída final. Por exemplo, se tivéssemos articles com um campo contendo HTML gerado, provavelmente gostaríamos de omitir isso de uma +resposta JSON. Esta é uma situação onde um arquivo de view seria útil:: + + // Código do Controller + class ArticlesController extends AppController + { + public function index() + { + $articles = $this->paginate('Articles'); + $this->set(compact('articles')); + } + } + + // Código da View - templates/Articles/json/index.php + foreach ($articles as $article) { + unset($article->generated_html); + } + echo json_encode(compact('articles')); + +Você pode fazer manipulações mais complexas, ou usar helpers para formatação também. +As classes de view de dados não suportam layouts. Elas assumem que o arquivo de view irá +gerar o conteúdo serializado. + +Criando Views XML +================= + +.. php:class:: XmlView + +Por padrão, ao usar ``serialize``, o XmlView envolverá suas variáveis +de view serializadas com um nó ````. Você pode definir um nome personalizado para +este nó usando a opção ``rootNode``. + +A classe XmlView suporta a opção ``xmlOptions`` que permite personalizar +as opções, como ``tags`` ou ``attributes``, usadas para gerar XML. + +Um exemplo de uso de ``XmlView`` seria gerar um `sitemap.xml +`_. Este tipo de documento requer que você +altere ``rootNode`` e defina atributos. Atributos são definidos usando o prefixo ``@``:: + + use Cake\View\XmlView; + + public function viewClasses(): array + { + return [XmlView::class]; + } + + public function sitemap() + { + $pages = $this->Pages->find()->all(); + $urls = []; + foreach ($pages as $page) { + $urls[] = [ + 'loc' => Router::url(['controller' => 'Pages', 'action' => 'view', $page->slug, '_full' => true]), + 'lastmod' => $page->modified->format('Y-m-d'), + 'changefreq' => 'daily', + 'priority' => '0.5', + ]; + } + + // Define um nó raiz personalizado no documento gerado. + $this->viewBuilder() + ->setOption('rootNode', 'urlset') + ->setOption('serialize', ['@xmlns', 'url']); + $this->set([ + // Define um atributo no nó raiz. + '@xmlns' => 'http://www.sitemaps.org/schemas/sitemap/0.9', + 'url' => $urls, + ]); + } + +Criando Views JSON +================== + +.. php:class:: JsonView + +A classe JsonView suporta a opção ``jsonOptions`` que permite personalizar +a máscara de bits usada para gerar JSON. Veja a documentação do +`json_encode `_ para os valores +válidos desta opção. + +Por exemplo, para serializar a saída de erros de validação de entidades CakePHP em uma forma consistente de JSON faça:: + + // Na action do seu controller quando o salvamento falhou + $this->set('errors', $articles->errors()); + $this->viewBuilder() + ->setOption('serialize', ['errors']) + ->setOption('jsonOptions', JSON_FORCE_OBJECT); + +Respostas JSONP +--------------- + +Ao usar ``JsonView`` você pode usar a variável de view especial ``jsonp`` para +habilitar o retorno de uma resposta JSONP. Defini-la como ``true`` faz com que a classe de view +verifique se o parâmetro de query string chamado "callback" está definido e, se estiver, envolve a +resposta json no nome da função fornecida. Se você quiser usar um nome de parâmetro de query string +personalizado em vez de "callback", defina ``jsonp`` para o nome requerido em vez de +``true``. + +Escolhendo uma Classe de View +============================== + +Embora você possa usar o método hook ``viewClasses`` na maioria das vezes, se você quiser +controle total sobre a seleção da classe de view, pode escolher diretamente a classe de view:: + + // src/Controller/VideosController.php + namespace App\Controller; + + use App\Controller\AppController; + use Cake\Http\Exception\NotFoundException; + + class VideosController extends AppController + { + public function export($format = '') + { + $format = strtolower($format); + + // Mapeamento de formato para view + $formats = [ + 'xml' => 'Xml', + 'json' => 'Json', + ]; + + // Erro em tipo desconhecido + if (!isset($formats[$format])) { + throw new NotFoundException(__('Unknown format.')); + } + + // Define a View do Formato de Saída + $this->viewBuilder()->setClassName($formats[$format]); + + // Obtém dados + $videos = $this->Videos->find('latest')->all(); + + // Define a View de Dados + $this->set(compact('videos')); + $this->viewBuilder()->setOption('serialize', ['videos']); + + // Define Download Forçado + return $this->response->withDownload('report-' . date('YmdHis') . '.' . $format); + } + } + +.. meta:: + :title lang=pt: Views JSON e XML + :keywords lang=pt: json,xml,presentation layer,view,ajax,logic,syntax,templates,cakephp diff --git a/pt/views/themes.rst b/pt/views/themes.rst index c273a2a1b6..29cb019b11 100644 --- a/pt/views/themes.rst +++ b/pt/views/themes.rst @@ -6,7 +6,7 @@ Veja a seção em :ref:`plugin-create-your-own`. Você pode tirar vantagem de temas, deixando fácil a troca da aparência da dua página rapidamente. Além de arquivos de templates, eles também podem provers `helpers` e 'cells' se o seu tema assim requerer. Quando usado ``cells`` e ``helpes`` no seu tema, -você precisará continuar usando a :term:`sintaxe plugin`. +você precisará continuar usando a :term:`sintaxe de plugin`. Para usar temas, defina o tema na `action` do seu `controller` ou no método ``beforeRender()``:: From 66430bbb93cf663b53fb0a3ad2aa431424409500 Mon Sep 17 00:00:00 2001 From: kromodoro Date: Tue, 7 Oct 2025 23:29:48 -0300 Subject: [PATCH 46/46] Fix paging related docs #8104 --- pt/views/cells.rst | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pt/views/cells.rst b/pt/views/cells.rst index 9ace8d42b4..699f822171 100644 --- a/pt/views/cells.rst +++ b/pt/views/cells.rst @@ -232,10 +232,6 @@ poderia ser assim:: ] ); - // Define os parâmetros de paginação como um atributo da requisição para uso do PaginatorHelper - $paging = $paginator->getPagingParams() + (array)$this->request->getAttribute('paging'); - $this->request = $this->request->withAttribute('paging', $paging); - $this->set('favorites', $results); } }