Os controllers correspondem ao ‘C’ no padrão MVC. Após o roteamento ter sido aplicado e o controller correto encontrado, a ação do controller é chamada. Seu controller deve lidar com a interpretação dos dados de uma requisição, certificando-se que os models corretos são chamados e a resposta ou view esperada seja exibida. Os controllers podem ser vistos como intermediários entre a camada Model e View. Você vai querer manter seus controllers magros e seus Models gordos. Isso lhe ajudará a reutilizar seu código e testá-los mais facilmente.
Mais comumente, controllers são usados para gerenciar a lógica de um único
model. Por exemplo, se você está construindo um site para uma padaria online,
você pode ter um RecipesController
e um IngredientsController
gerenciando suas receitas e seus ingredientes. No CakePHP, controllers são
nomeados de acordo com o model que manipulam. É também absolutamente possível
ter controllers que usam mais de um model.
Os controllers da sua aplicação são classes que estendem a classe CakePHP
AppController
, a qual por sua vez estende a classe Controller
do CakePHP. A classe AppController
pode ser definida em
/app/Controller/AppController.php
e deve conter métodos que são
compartilhados entre todos os seus controllers.
Os controllers fornecem uma série de métodos que são chamados de ações. Ações são métodos em um controller que manipulam requisições. Por padrão, todos os métodos públicos em um controller são ações e acessíveis por urls.
Como mencionado anteriormente, a classe AppController
é a mãe de todos os
outros controllers da sua aplicação. O próprio AppController
é estendida da
classe Controller
que faz parte da biblioteca do CakePHP. Assim sendo,
AppController
é definido em /app/Controller/AppController.php
como:
class AppController extends Controller {
}
Os atributos e métodos criados em AppController
vão estar disponíveis para
todos os controllers da sua aplicação. Este é o lugar ideal para criar códigos
que são comuns para todos os seus controllers. Componentes (que você vai
aprender mais tarde) são a melhor alternativa para códigos que são usados por
muitos (mas não obrigatoriamente em todos) controllers.
Enquanto regras normais de herança de classes orientadas à objetos são
aplicadas, o CakePHP também faz um pequeno trabalho extra quando se trata de
atributos especiais do controller. A lista de componentes (components)
e helpers usados no controller são tratados diferentemente. Nestes casos, as
cadeias de valores do AppController
são mescladas com os valores de seus
controllers filhos. Os valores dos controllers filhos sempre sobrescreveram
os do AppController
.
Nota
O CakePHP mescla as seguintes variáveis do AppController
em controllers
da sua aplicação:
$components
$helpers
$uses
Lembre-se de adicionar os helpers Html e Form padrões se você incluiu o atributo
$helpers
em seu AppController
.
Também lembre de fazer as chamadas de callbacks do AppController
nos
controllers filhos para obter melhores resultados:
function beforeFilter() {
parent::beforeFilter();
}
Quando uma requisição é feita para uma aplicação CakePHP, a classe
Router
e a classe Dispatcher
do Cake usa a
Routes Configuration para encontrar e criar o controller correto. Os
dados da requisição são encapsulados em um objeto de requisição. O CakePHP
coloca todas as informações importantes de uma requisição na propriedade
$this->request
. Veja a seção Objetos de Requisição e Resposta para mais
informações sobre o objeto de requisição do CakePHP.
Retornando ao nosso exemplo da padaria online, nosso controller
RecipesController
poderia conter as ações view()
, share()
e
search()
e poderia ser encontrado em
/app/Controller/RecipesController.php
contendo o código a seguir:
# /app/Controller/RecipesController.php
class RecipesController extends AppController {
function view($id) {
// a lógica da ação vai aqui
}
function share($customer_id, $recipe_id) {
// a lógica da ação vai aqui
}
function search($query) {
// a lógica da ação vai aqui
}
}
Para que você use de forma eficaz os controllers em sua aplicação, nós iremos cobrir alguns dos atributos e métodos inclusos no controller fornecido pelo CakePHP.
Os controllers do CakePHP vêm equipados com callbacks que você pode usar para inserir lógicas em torno do ciclo de vida de uma requisição:
Este método é executado antes de cada ação dos controllers. É um ótimo lugar para verificar se há uma sessão ativa ou inspecionar as permissões de um usuário.
Nota
O método beforeFilter() será chamado para ações não encontradas e ações criadas pelo scaffold do Cake.
Chamada após a lógica da ação de um controller, mas antes da view ser
renderizada. Este callback não é usado com frequência mas pode ser preciso
se você chamar o método render()
manualmente antes do término de uma
ação.
Chamada após cada ação dos controllers, e após a completa renderização da view. Este é o último método executado do controller.
Em adição aos callbacks dos controllers, os Componentes também fornecem um conjunto de callbacks similares.
Para uma lista completa dos métodos e suas descrições, visite a API do CakePHP. Siga para https://api.cakephp.org.
Os controllers interagem com as views de diversas maneiras. Primeiramente eles
são capazes de passar dados para as views usando o método set()
. Você também
pode no seu controller decidir qual classe de View usar e qual arquivo deve ser
renderizado.
O método set()
é a principal maneira de enviar dados do seu controller
para a sua view. Após ter usado o método set()
, a variável pode ser
acessada em sua view:
// Primeiro você passa os dados do controller:
$this->set('color', 'pink');
//Então, na view, você pode utilizar os dados:
?>
Você selecionou a cobertura <?php echo $color; ?> para o bolo.
O método set()
também aceita um array associativo como primeiro
parâmetro, podendo oferecer uma forma rápida para atribuir uma série de
informações para a view.
Alterado na versão 1.3: Chaves de arrays não serão mais flexionados antes de serem atribuídas à view (‘underscored_key’ não se torna ‘underscoredKey’, etc.):
$data = array(
'color' => 'pink',
'type' => 'sugar',
'base_price' => 23.95
);
// Torna $color, $type e $base_price
// disponível na view:
$this->set($data);
O atributo $pageTitle
não existe mais, use o método set()
para
definir o título na view:
$this->set('title_for_layout', 'This is the page title');
?>
O método render()
é chamado automaticamente no fim de cada ação
requisitada de um controller. Este método executa toda a lógica da view
(usando os dados que você passou usando o método set()
), coloca a view
dentro do seu layout e serve de volta para o usuário final.
O arquivo view usado pelo método render()
é determinado por convenção.
Se a ação search()
do controller RecipesController
é requisitada, o
arquivo view encontrado em /app/View/Recipes/search.ctp
será
renderizado:
class RecipesController extends AppController {
...
function search() {
// Renderiza a view em /View/Recipes/search.ctp
$this->render();
}
...
}
Embora o CakePHP irá chamar o método render
automaticamente (ao menos
que você altere o atributo $this->autoRender
para false
) após cada
ação, você pode usá-lo para especificar um arquivo view alternativo
alterando o nome de ação no controller usando o parâmetro $action
.
Se o parâmetro $action
começar com '/'
é assumido que o arquivo view
ou elemento que você queira usar é relativo ao diretório /app/View
. Isto
permite a renderização direta de elementos, muito útil em chamadas Ajax.
// Renderiza o elemento presente em /View/Elements/ajaxreturn.ctp
$this->render('/Elements/ajaxreturn');
Você também pode especificar uma arquivo view ou elemento usando o terceiro
parâmetro chamado $file
. O parâmetro $layout
permite você
especificar o layout em que a view será inserido.
Em seu controller você pode querer renderizar uma view diferente do que a
convenção proporciona automaticamente. Você pode fazer isso chamando o método
render()
diretamente. Após ter chamado o método render()
, o CakePHP
não irá tentar renderizar novamente a view:
class PostsController extends AppController {
function my_action() {
$this->render('custom_file');
}
}
Isto irá renderizar o arquivo app/View/Posts/custom_file.ctp
ao invés de
app/View/Posts/my_action.ctp
O método de controle de fluxo que você vai usar na maioria das vezes é
o redirect()
. Este método recebe seu primeiro parâmetro na forma de uma
URL relativa do CakePHP. Quando um usuário executou um pedido que altera
dados no servidor, você pode querer redirecioná-lo para uma outra tela
de recepção.:
function place_order() {
// Logic for finalizing order goes here
if ($success) {
$this->redirect(array('controller' => 'orders', 'action' => 'thanks'));
} else {
$this->redirect(array('controller' => 'orders', 'action' => 'confirm'));
}
}
Nota
Você pode aprender mais sobre a importância de redirecionar o usuário após um formulário do tipo POST no artigo Post/Redirect/Get (en).
Você também pode usar uma URL relativa ou absoluta como argumento:
$this->redirect('/orders/thanks'));
$this->redirect('http://www.example.com');
Você também pode passar dados para a ação:
// observe o parâmetro $id
$this->redirect(array('action' => 'edit', $id));
O segundo parâmetro passado no redirect()
permite você definir um
código de status HTTP para acompanhar o redirecionamento. Você pode querer
usar o código 301 (movido permanentemente) ou 303 (siga outro), dependendo
da natureza do redirecionamento.
O método vai assumir um exit()
após o redirecionamento ao menos que
você passe o terceiro parâmetro como false
.
Se você precisa redirecionar o usuário de volta para a página que fez a requisição, você pode usar:
$this->redirect($this->referer());
Assim como o método redirect()
, o método flash()
é usado para
direcionar o usuário para uma nova página após uma operação. O método
flash()
é diferente na forma de transição, mostrando uma mensagem antes
de transferir o usuário para a URL especificada.
O primeiro parâmetro deve conter a mensagem que será exibida e o segundo
parâmetro uma URL relativa do CakePHP. O Cake irá mostrar o conteúdo da
variável $message
pelos segundos especificados em $pause
antes de
encaminhar o usuário para a URL especificada em $url
.
Se existir um template particular que você queira usar para mostrar a
mensagem para o usuário, você deve especificar o nome deste layout passando
o parâmetro $layout
.
Para mensagens flash exibidas dentro de páginas, de uma olhada no método
setFlash()
do componente SessionComponent
.
Em adição ao Ciclo de Vida dos Callbacks em uma Requisição. O CakePHP também suporta callbacks relacionados a scaffolding.
$method
é o nome do método chamado, por exemplo: index, edit, etc.
$method
é o nome do método chamado, por exemplo: index, edit, etc.
$method
é o nome do método chamado, podendo ser: edit ou update.
$method
é o nome do método chamado, podendo ser: edit ou update.
Este método carrega os models requeridos pelo controller. Este processo
de carregamento é feito normalmente pelo CakePHP, mas pode ser útil quando
for acessar controllers de outras perspectivas. Se você precisa de um
controller num script de linha de comando ou para outros lugares,
constructClasses()
pode vir a calhar.
Retorna a URL de referência para a requisição atual. O parâmetro
$default
pode ser usado para fornecer uma URL padrão a ser usada caso o
HTTP_REFERER não puder ser lido do cabeçalho da requisição. Então, ao invés
de fazer isto:
class UserController extends AppController {
function delete($id) {
// delete code goes here, and then...
if ($this->referer() != '/') {
$this->redirect($this->referer());
} else {
$this->redirect(array('action' => 'index'));
}
}
}
Você pode fazer isto:
class UserController extends AppController {
function delete($id) {
// delete code goes here, and then...
$this->redirect($this->referer(array('action' => 'index')));
}
}
Se o parâmetro $default
não for passado, o comportamento padrão é a raiz
do seu domínio - '/'
.
Se o parâmetro $local
for passado como true
, o redirecionamento
restringe a URL de referência apenas para o servidor local.
Usado para dizer ao browser do usuário não fazer cache do resultado exibido. Isto é diferente do cache de views, abordado em um capítulo posterior.
O cabeçalho enviado para este efeito são:
Expires: Mon, 26 Jul 1997 05:00:00 GMT
Last-Modified: [data e hora atual] GMT
Cache-Control: no-store, no-cache, must-revalidate
Cache-Control: post-check=0, pre-check=0
Pragma: no-cache
Use este método para transformar um conjunto de dados POSTados de um model
(vindos de inputs compatíveis com o FormHelper) em um conjunto de condições
de busca. Este método oferece um atalho rápido no momento de construir
operações de busca. Por exemplo, um usuário administrativo pode querer
pesquisar pedidos para saber quais itens precisarão ser enviados. Você
pode usar o FormHelper
do CakePHP para criar um formulário
de busca para o model Order. Assim, uma ação de um controller pode usar
os dados enviados deste formulário e criar as condições de busca
necessárias para completar a tarefa:
function index() {
$conditions = $this->postConditions($this->request->data);
$orders = $this->Order->find('all', compact('conditions'));
$this->set('orders', $orders);
}
Se $this->request->data['Order']['destination']
for igual a
“Old Towne Bakery”, o método postConditions()
converte esta condição em
um array compatível para o uso em um método Model->find(). Neste caso,
array('Order.destination' => 'Old Towne Bakery')
.
Se você quiser usar um operador diferente entre os termos, informe-os usando o segundo parâmetro:
/*
Conteúdo do atributo $this->request->data
array(
'Order' => array(
'num_items' => '4',
'referrer' => 'Ye Olde'
)
)
*/
// Vamos pegar os pedidos que possuem no mínimo 4 itens e que contém 'Ye Olde'
$conditions = $this->postConditions(
$this->request->data,
array(
'num_items' => '>=',
'referrer' => 'LIKE'
)
);
$orders = $this->Order->find('all', compact('conditions'));
O terceiro parâmetro permite você dizer ao CakePHP qual operador SQL booleano usar entre as condições de busca. Strings como ‘AND’, ‘OR’ e ‘XOR’ são todos valores válidos.
Finalmente, se o último parâmetro for passado como true
, e a variável
$op
for um array, os campos não inclusos em $op
não serão
retornados entre as condições.
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
pagination para mais detalhes
sobre como usar o método paginate()
Este método chama uma ação de um controller de qualquer lugar e retorna
os dados da ação requisitada. A $url
passada é uma URL relativa do Cake
(/controller_name/action_name/params). Para passar dados extras para serem
recebidos pela ação do controller, adicione-os no parâmetro options
em
um formato de array.
Nota
Você pode usar o requestAction()
para recuperar uma view totalmente
renderizada passando 'return'
no array de opções:
requestAction($url, array('return'));
. É importante notar que
fazendo uma requisição usando ‘return’ em um controller podem fazer com
que tags javascripts e css não funcionem corretamente.
Aviso
Se o método requestAction()
for usado sem fazer cache apropriado do
resultado obtido, a performance da ação pode ser bem ruim.
É raro o uso apropriado deste método em um controller ou model.
O uso do requestAction
é melhor usado em conjunto com elementos
(cacheados) como uma maneira de recuperar dados para um elemento antes de
renderizá-los. Vamos usar o exemplo de por o elemento “últimos comentários”
no layout. Primeiro nós precisamos criar um método no controller que irá
retornar os dados:
// Controller/CommentsController.php
class CommentsController extends AppController {
function latest() {
return $this->Comment->find('all', array('order' => 'Comment.created DESC', 'limit' => 10));
}
}
Se agora nós criarmos um elemento simples para chamar este método:
// View/Elements/latest_comments.ctp
$comments = $this->requestAction('/comments/latest');
foreach ($comments as $comment) {
echo $comment['Comment']['title'];
}
Nós podemos por este elemento em qualquer lugar para ter a saída usando:
echo $this->element('latest_comments');
Fazendo desta maneira, sempre que o elemento for renderizado, uma requisição será feita para nosso controller para pegar os dados, processá-los e retorná-los. Porém, de acordo com o aviso acima, é melhor fazer uso de caching do elemento para evitar um processamento desnecessário. Modificando a chamada do elemento para se parecer com isto:
echo $this->element('latest_comments', array('cache' => '+1 hour'));
A chamada para o requestAction
não será feita enquanto o arquivo de
cache do elemento existir e for válido.
Além disso, o requestAction
pode receber uma URL no formato de
array do Cake:
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'featured'),
array('return')
);
Isto permite o requestAction
contornar o uso do método Router::url()
para descobrir o controller e a ação, aumentando a performance. As URLs
baseadas em arrays são as mesmas usadas pelo método
HtmlHelper::link()
com uma diferença, se você está usando
parâmetros nomeados ou passados, você deve colocá-los em um segundo array
envolvendo elas com a chave correta.
Isto deve ser feito porque o requestAction
mescla o array de parâmetros
nomeados (no segundo parâmetro do requestAction) com o array
Controller::params
e não coloca explicitamente o array de parâmetros
nomeados na chave ‘named’. Além disso, membros do array $options
serão
disponibilizados no array Controller::params
da ação que for chamada.:
echo $this->requestAction('/articles/featured/limit:3');
echo $this->requestAction('/articles/view/5');
Um array no requestAction
poderia ser:
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'featured'),
array('named' => array('limit' => 3))
);
echo $this->requestAction(
array('controller' => 'articles', 'action' => 'view'),
array('pass' => array(5))
);
Nota
Diferente de outros lugares onde URLs no formato de arrays são análogas
as URLs no formato de string, o requestAction
tratam elas
diferentemente.
Quando for usar URLs no formato de arrays em conjunto com o
requestAction()
você deve especificar todos os parâmetros que você
vai precisar na requisição da ação. Isto inclui parâmetros como
$this->request->data
. Além disso, todos os parâmetros
requeridos, parâmetros nomeados e “passados” devem ser feitos no segundo
array como visto acima.
O método loadModel
vem a calhar quando você precisa usar um model que
não é padrão do controller ou o seu model não está associado com
este.
$this->loadModel('Article');
$recentArticles = $this->Article->find('all', array('limit' => 5, 'order' => 'Article.created DESC'));
$this->loadModel('User', 2);
$user = $this->User->read();
Para uma completa lista dos atributos dos controllers e suas descrições, visite a API do CakePHP. Siga para https://api.cakephp.org/2.x/class-Controller.html.
O atributo $name
deve ser definido com o nome do controller. Normalmente
é apenas a forma plural do nome do model principal que o controller usa.
Esta propriedade não é requerida mas salva o CakePHP de ter que flexionar
o nome do model para chegar no valor correto:
# Exemplo de uso do atributo $name do controller
class RecipesController extends AppController {
public $name = 'Recipes';
}
Os seguintes atributos do controller usados com mais frequência dizem ao CakePHP
quais helpers, componentes e models você irá usar em conjunto com o controller
corrente. Usar estes atributos faz com que as classes MVC dadas por
$components
e $uses
sejam disponibilizadas como atributos no controller
(por exemplo, $this->ModelName
) e os dados por $helpers
disponibilizados
como referências para objetos apropriados ($this->{$helpername}
) na view.
Nota
Cada controller possui algumas destas classes disponibilizadas por padrão, então você pode nem ao menos precisar de configurar estes atributos.
Os controllers possuem acesso ao seu model principal por padrão. Nosso
controller Recipes terá a classe model Recipe disposta em $this->Recipe
e nosso controller Products também apresenta o model Product em
$this->Product
. Porém, quando for permitir um controller acessar models
adicionais pela configuração do atributo $uses
, o nome do model do
controller atual deve também estar incluso. Este caso é ilustrado no exemplo
logo abaixo.
Se você não quiser usar um model em seu controller, defina o atributo como
um array vazio (public $uses = array()
). Isto lhe permitirá usar um
controller sem a necessidade de um arquivo model correspondente.
Os helpers Html, Form e Session são disponibilizados por padrão como é
feito o SessionComponent
. Mas se você escolher definir seu próprio
array de $helpers
no AppController
, tenha certeza de incluir o
Html
e o Form
se quiser que eles continuem a estar disponíveis nos
seus controllers. Você também pode passar configurações na declaração de
seus helpers. Para aprender mais sobre estas classes, visite suas
respectivas seções mais tarde neste manual.
Vamos ver como dizer para um controller do Cake que você planeja usar classes MVC adicionais:
class RecipesController extends AppController {
public $uses = array('Recipe', 'User');
public $helpers = array('Js');
public $components = array('RequestHandler');
}
Cada uma destas variáveis são mescladas com seus valores herdados, portanto,
não é necessário (por exemplo) redeclarar o FormHelper
ou qualquer uma
das classes que já foram declaradas no seu AppController
.
O array de componentes permite que você diga ao CakePHP quais
Componentes um controller irá usar. Como o $helpers
e o $uses
, $components
são mesclados com os definidos no
AppController
. E assim como nos $helpers
, você pode passar
configurações para os componentes. Veja Configurando Componentes
para mais informações.
Enquanto você pode conferir todos os detalhes de todos os atributos dos controllers na API, existem outros atributos dos controllers que merecem suas próprias seções neste manual.
O atributo $cacheAction
é usado para definir a duração e outras
informações sobre o cache completo de páginas. Você pode ler mais sobre
o caching completo de páginas na documentação do CacheHelper
.
O atributo $paginate
é uma propriedade de compatibilidade obsoleta.
Usando este atributo, o componente PaginatorComponent
será
carregado e configurado, no entanto, é recomendado atualizar seu código para
usar as configurações normais de componentes:
class ArticlesController extends AppController {
public $components = array(
'Paginator' => array(
'Article' => array(
'conditions' => array('published' => 1)
)
)
);
}