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.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 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 `.