Controllers (Controladores) ¶
Os controllers (controladores) fazem parte da arquitetura MVC. São objetos de classes que estendem de yii\base\Controller e são responsáveis pelo processamento das requisições e por gerar respostas. Em particular, após assumir o controle de applications, controllers analisarão os dados de entradas obtidos pela requisição, passarão estes dados para os models (modelos), incluirão os resultados dos models (modelos) nas views (visões) e finalmente gerarão as respostas de saída.
Actions (Ações) ¶
Os controllers são compostos por unidades básicas chamadas de ações que podem ser tratados pelos usuários finais a fim de realizar a sua execução.
No exemplo a seguir mostra um controller post
com duas ações: view
e create
:
namespace app\controllers;
use Yii;
use app\models\Post;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
class PostController extends Controller
{
public function actionView($id)
{
$model = Post::findOne($id);
if ($model === null) {
throw new NotFoundHttpException;
}
return $this->render('view', [
'model' => $model,
]);
}
public function actionCreate()
{
$model = new Post;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
}
Na ação view
(definido pelo método actionView()
), o primeiro código carrega o
model conforme o ID solicitado; Se o model for devidamente
carregado, a ação irá exibi-lo utilizado a view chamada de view
.
Caso contrário, a ação lançará uma exceção.
Na ação create
(definido pelo método actionCreate()
), o código é parecido.
Primeiro ele tenta popular o model usando os dados da requisição
em seguida os salva. Se ambos forem bem sucedidos, a ação redirecionará o navegador
para a ação view
com o novo ID criado pelo model. Caso contrário, a ação exibirá
a view create
na qual os usuário poderão fornecer os dados necessários.
Routes (Rotas) ¶
Os usuários finais abordarão as ações por meio de rotas. Uma rota é uma string composta pelas seguintes partes:
- um ID do módulo: serve apenas se o controller pertencer a um módulo que não seja da aplicação;
- um ID do controller: uma string que identifica exclusivamente o controller dentre todos os controllers da mesma aplicação (ou do mesmo módulo, caso o controller pertença a um módulo);
- um ID da ação: uma string que identifica exclusivamente uma ação dentre todas as ações de um mesmo controller.
As rotas seguem o seguinte formato:
IDdoController/IDdoAction
ou o seguinte formato se o controller estiver em um módulo:
IDdoModule/IDdoController/IDdoAction
Portanto, se um usuário fizer uma requisição com a URL http://hostname/index.php?r=site/index
,
a ação index
do controller site
será executada. Para mais detalhes sobre como
as ações são resolvidas pelas rotas, por favor consulte a seção Roteamento e Criação de URL.
Criando Controllers ¶
Em aplicações Web, os controllers devem estender de yii\web\Controller
ou de suas classes filhas. De forma semelhante, em aplicaçoes console,
os controllers devem estender de yii\console\Controller ou de suas classes filhos. O código a seguir define um controller site
:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
}
IDs de Controllers ¶
Normalmente, um controller é projetado para tratar as requisições relativos a
um determinado tipo de recurso. Por esta razão, os IDs dos controllers geralmente
são substantivos que referenciam-se ao tipo de recurso que será tratado.
Por exemplo, você pode usar o article
como o ID do um controller para tratar
dados de artigos.
Por padrão, os IDs dos controllers devem conter apenas esses caracteres:
letras inglesas em caixa baixa, números, underscores (underline), hífens e barras.
Por exemplo, article
e post-comment
são ambos IDs de controllers válidos,
enquanto article?
, PostComment
, admin\post
não são.
Um ID de controller também pode conter um prefixo para o subdiretório. Por exemplo,
admin/article
representa um controller article
em um subdiretório admin
sob
o namespace do controller
Os caracteres válidos para os prefixos de subdiretórios incluem: letras inglesas
em caixa alto ou caixa baixa, números, underscores (underline) e barras, onde as
barras são usadas para separar os níveis dos subdiretórios (por exemplo, panels/admin
).
Nomenclatura da Classe do Controller ¶
Os nomes da classes dos controllers podem ser derivadas dos IDs dos controllers de acordo com as seguintes procedimentos:
- Colocar em caixa alta a primeira letra de cada palavra separadas por traço. Observe que se o ID do controller possuir barras, a regra é aplicada apenas na parte após a última barra no ID.
- Remover os traços e substituir todas as barras por barras invertidas.
- Adicionar
Controller
como sufixo. - Preceder ao namespace do controller.
Segue alguns exemplos, assumindo que o namespace do controller
tenha por padrão o valor app\controllers
:
article
torna-seapp\controllers\ArticleController
;post-comment
torna-seapp\controllers\PostCommentController
;admin/post-comment
torna-seapp\controllers\admin\PostCommentController
;adminPanels/post-comment
torna-seapp\controllers\adminPanels\PostCommentController
.
As classes dos controllers devem ser autoloadable.
Por esta razão, nos exemplos anteriores, o controller article
deve ser salvo
no arquivo cuja alias é @app/controllers/ArticleController.php
;
enquanto o controller admin/post-comment
deve ser salvo no @app/controllers/admin/PostCommentController.php
.
Informação: No último exemplo
admin/post-comment
, mostra como você pode colocar um controller em um subdiretório do namespace controller. Isto é útil quando você quiser organizar seus controllers em diversas categorias e não quiser usar módulos.
Mapeando Controllers ¶
Você pode configurar um mapeamento de controllers para superar as barreiras impostas pelos IDs de controllers e pelos nomes de classes descritos acima. Isto é útil principalmente quando quiser esconder controllers de terceiros na qual você não tem controle sobre seus nomes de classes.
Você pode configurar o mapeamento de controllers na configuração da aplicação. Por exemplo:
[
'controllerMap' => [
// declara o controller "account" usando um nome de classe
'account' => 'app\controllers\UserController',
// declara o controller "article" usando uma configuração em array
'article' => [
'class' => 'app\controllers\PostController',
'enableCsrfValidation' => false,
],
],
]
Controller Padrão ¶
Cada aplicação tem um controller padrão que é especificado pela propriedade yii\base\Application::$defaultRoute.
Quando uma requisição não especificar uma rota, será utilizada a
rota especificada pela propriedade.
Para as aplicações Web, este valor é 'site'
, enquanto
para as aplicações console é help
. Portanto, se uma
URL for http://hostname/index.php
, o controller site
será utilizado nesta requisição.
Você pode alterar o controller padrão como a seguinte configuração da aplicação:
[
'defaultRoute' => 'main',
]
Criando Ações ¶
Criar ações pode ser tão simples como a definição dos chamados métodos de ação
em uma classe controller. Um método de ação é um método público cujo nome inicia
com a palavra action
. O valor de retorno representa os dados de resposta a serem
enviados aos usuário finais. O código a seguir define duas ações, index
e hello-world
:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public function actionIndex()
{
return $this->render('index');
}
public function actionHelloWorld()
{
return 'Hello World';
}
}
IDs de Actions ¶
Uma ação muitas vezes é projetada para realizar uma manipulação em particular sobre
um recurso. Por esta razão, os IDs das ações geralmente são verbos, tais como view
, update
, etc.
Por padrão, os IDs das ações devem conter apenas esses caracteres: letras inglesas
em caixa baixa, números, underscores (underline) e traços. Os traços em um ID da
ação são usados para separar palavras. Por exemplo, view
, update2
e comment-post
são IDs válidos, enquanto view?
e Update
não são.
Você pode criar ações de duas maneiras: ações inline (em sequência) e ações standalone (autônomas). Uma ação inline é definida pelo método de uma classe controller, enquanto uma ação standalone é uma classe que estende de yii\base\Action ou de uma classe-filha. As ações inline exigem menos esforço para serem criadas e muitas vezes as preferidas quando não se tem a intenção de reutilizar estas ações. Ações standalone, por outro lado, são criados principalmente para serem utilizados em diferentes controllers ou para serem distribuídos como extensions.
Ações Inline ¶
As ações inline referem-se a os chamados métodos de ação, que foram descritos anteriormente.
Os nomes dos métodos de ações são derivadas dos IDs das ações de acordo com os seguintes procedimentos:
- Colocar em caixa alta a primeira letra de cada palavra do ID da ação;
- Remover os traços;
- Adicionar o prefixo
action
.
Por exemplo, index
torna-se actionIndex
e hello-world
torna-se actionHelloWorld
.
Observação: Os nomes dos métodos de ações são case-sensitive. Se você tiver um método chamado
ActionIndex
, não será considerado como um método de ação e como resultado, o pedido para a açãoindex
lançará uma exceção. Observe também que os métodos de ações devem ser públicas. Um método privado ou protegido NÃO será definido como ação inline.
As ações inline normalmente são as mais utilizadas pois demandam pouco esforço para serem criadas. No entanto, se você deseja reutilizar algumas ações em diferentes lugares ou se deseja distribuir uma ação, deve considerar defini-la como uma ação standalone.
Ações Standalone ¶
Ações standalone são definidas por classes de ações que estendem de yii\base\Action ou de uma classe-filha. Por example, nas versões do Yii, existe a yii\web\ViewAction e a yii\web\ErrorAction, ambas são ações standalone.
Para usar uma ação standalone, você deve mapear as ações sobrescrevendo o método yii\base\Controller::actions() em suas classes controllers como o seguinte:
public function actions()
{
return [
// declara a ação "error" usando um nome de classe
'error' => 'yii\web\ErrorAction',
// declara a ação "view" usando uma configuração em array
'view' => [
'class' => 'yii\web\ViewAction',
'viewPrefix' => '',
],
];
}
Como pode ver, o método actions()
deve retornar um array cujas chaves são os IDs
das ações e os valores correspondentes ao nome da classe da ação ou configurações. Ao contrário das ações inline, os IDs das ações standalone
podem conter caracteres arbitrários desde que sejam mapeados no método actions()
.
Para criar uma classe de ação standalone, você deve estender de yii\base\Action ou de duas classes filhas e implementar um método público chamado run()
. A regra para o método run()
é semelhante ao de um método de ação. Por exemplo,
<?php
namespace app\components;
use yii\base\Action;
class HelloWorldAction extends Action
{
public function run()
{
return "Hello World";
}
}
Resultados da Ação ¶
O valor de retorno do método de ação ou do método run()
de uma ação standalone
são importantes. Eles representam o resultado da ação correspondente.
O valor de retorno pode ser um objeto de resposta que será enviado como resposta aos usuários finais.
- Para aplicações Web, o valor de retorno também poder ser algum dado arbitrário que será atribuído à propriedade yii\web\Response::$data e ainda ser convertido em uma string para representar o corpo da resposta.
- Para aplicações console, o valor de retorno também poder ser um inteiro representando o exit status (status de saída) da execução do comando.
Nos exemplos acima, todos os resultados são strings que serão tratados como o corpo das respostas para serem enviados aos usuários finais. No exemplo a seguir, mostra como uma ação pode redirecionar o navegador do usuário para uma nova URL retornando um objeto de resposta (o método redirect() retorna um objeto de resposta):
public function actionForward()
{
// redireciona o navegador do usuário para http://example.com
return $this->redirect('http://example.com');
}
Parâmetros da Ação ¶
Os métodos de ações para as ações inline e os métodos run()
para as ações
standalone podem receber parâmetros, chamados parâmetros da ação.
Seus valores são obtidos a partir das requisições. Para
aplicações Web, o valor de cada parâmetro da ação são
obtidos pelo $_GET
usando o nome do parâmetro como chave; para
aplicações console, eles correspondem aos argumentos
da linha de comando.
No exemplo a seguir, a ação view
(uma ação inline) possui dois parâmetros declarados:
$id
e $version
.
namespace app\controllers;
use yii\web\Controller;
class PostController extends Controller
{
public function actionView($id, $version = null)
{
// ...
}
}
A seguir, os parâmetros da ação serão populados em diferentes requisições:
http://hostname/index.php?r=post/view&id=123
: o parâmetro$id
receberá o valor'123'
, enquanto o$version
continuará com o valor nulo porque não existe o parâmetroversion
na URL.http://hostname/index.php?r=post/view&id=123&version=2
: os parâmetros$id
e$version
serão receberão os valores'123'
e'2'
, respectivamente.http://hostname/index.php?r=post/view
: uma exceção yii\web\BadRequestHttpException será lançada porque o parâmetro obrigatório$id
não foi informado na requisição.http://hostname/index.php?r=post/view&id[]=123
: uma exceção yii\web\BadRequestHttpException será lançada porque o parâmetro$id
foi informado com um valor array['123']
na qual não era esperado.
Se você quiser que um parâmetro da ação aceite valores arrays, deverá declara-lo
explicitamente com array
, como mostro a seguir:
public function actionView(array $id, $version = null)
{
// ...
}
Agora, se a requisição for http://hostname/index.php?r=post/view&id[]=123
, o
parâmetro $id
receberá o valor ['123']
. Se a requisição for
http://hostname/index.php?r=post/view&id=123
, o parâmetro $id
ainda receberá
um array como valor pois o valor escalar '123'
será convertido automaticamente
em um array.
Os exemplo acima mostram, principalmente, como os parâmetros da ação trabalham em aplicações Web. Para aplicações console, por favor, consulte a seção Comandos de Console para mais detalhes.
Default Action ¶
Cada controller tem uma ação padrão especificado pela propriedade yii\base\Controller::$defaultAction. Quando uma rota contém apenas o ID do controller, implica que a ação padrão do controller seja solicitada.
Por padrão, a ação padrão é definida como index
. Se quiser alterar o valor padrão,
simplesmente sobrescreva esta propriedade na classe controller, como o seguinte:
namespace app\controllers;
use yii\web\Controller;
class SiteController extends Controller
{
public $defaultAction = 'home';
public function actionHome()
{
return $this->render('home');
}
}
Ciclo de Vida do Controller ¶
Ao processar uma requisição, a aplicação criará um controller baseada na rota solicitada. O controller, então, se submeterá ao seguinte ciclo de vida para concluir a requisição:
- O método yii\base\Controller::init() é chamado após o controller ser criado e configurado.
- O controller cria um objeto da ação baseada no ID da ação solicitada:
- Se o ID da ação não for especificado, o ID da ação padrão será utilizada.
- Se o ID da ação for encontrada no mapeamento das ações, uma ação standalone será criada;
- Se o ID da ação for encontrada para corresponder a um método de ação, uma ação inline será criada;
- Caso contrário, uma exceção yii\base\InvalidRouteException será lançada.
- De forma sequencial, o controller chama o método
beforeAction()
da aplicação, o módulo (se o controller pertencer a um módulo) e o controller.- Se uma das chamadas retornar
false
, o restante dos métodos subsequentesbeforeAction()
serão ignoradas e a execução da ação será cancelada. - Por padrão, cada método
beforeAction()
desencadeia a execução de um evento chamadobeforeAction
na qual você pode associar a uma função (handler).
- Se uma das chamadas retornar
- O controller executa a ação:
- Os parâmetros da ação serão analizados e populados a partir dos dados obtidos pela requisição;
- De forma sequencial, o controller chama o método
afterAction()
do controller, o módulo (se o controller pertencer a um módulo) e a aplicação.- Por padrão, cada método
afterAction()
desencadeia a execução de um evento chamadoafterAction
na qual você pode associar a uma função (handler).
- Por padrão, cada método
- A aplicação obterá o resultado da ação e irá associá-lo na resposta.
Boas Práticas ¶
Em uma aplicação bem projetada, frequentemente os controllers são bem pequenos na qual cada ação possui poucas linhas de códigos. Se o controller for um pouco complicado, geralmente indica que terá que refaze-lo e passar algum código para outro classe.
Segue algumas boas práticas em destaque. Os controllers:
- podem acessar os dados de uma requisição;
- podem chamar os métodos dos models e outros componentes de serviço com dados da requisição;
- podem usar as views para compor as respostas;
- NÃO devem processar os dados da requisição - isto deve ser feito na camada model (modelo);
- devem evitar inserir códigos HTML ou outro código de apresentação - é melhor que sejam feitos nas views.