Como funcionam as aplicações?
Você está lendo o documento fundamental da documentação do Nette. Aprenderá como as aplicações web funcionam. Do início ao fim, desde o momento do nascimento até o último suspiro do script PHP. Após a leitura, você saberá:
- como tudo funciona
- o que é Bootstrap, Presenter e Contêiner de DI
- como é a estrutura de diretórios
Estrutura de diretórios
Abra o exemplo do esqueleto da aplicação web chamado WebProject e, enquanto lê, pode consultar os arquivos sobre os quais estamos falando.
A estrutura de diretórios se parece com algo assim:
web-project/ ├── app/ ← diretório da aplicação │ ├── Core/ ← classes base necessárias para a execução │ │ └── RouterFactory.php ← configuração de endereços URL │ ├── Presentation/ ← presenters, templates & cia. │ │ ├── @layout.latte ← template de layout │ │ └── Home/ ← diretório do presenter Home │ │ ├── HomePresenter.php ← classe do presenter Home │ │ └── default.latte ← template da ação default │ └── Bootstrap.php ← classe de inicialização Bootstrap ├── bin/ ← scripts executados a partir da linha de comando ├── config/ ← arquivos de configuração │ ├── common.neon │ └── services.neon ├── log/ ← erros registrados ├── temp/ ← arquivos temporários, cache, … ├── vendor/ ← bibliotecas instaladas pelo Composer │ ├── ... │ └── autoload.php ← autoloading de todos os pacotes instalados ├── www/ ← diretório público ou document-root do projeto │ ├── .htaccess ← regras mod_rewrite │ └── index.php ← arquivo inicial pelo qual a aplicação é iniciada └── .htaccess ← proíbe o acesso a todos os diretórios exceto www
Você pode alterar a estrutura de diretórios de qualquer forma, renomear ou mover pastas, é totalmente flexível. Além disso, o Nette possui uma autodeteção inteligente e reconhece automaticamente a localização da aplicação, incluindo sua base de URL.
Para aplicações um pouco maiores, podemos dividir as pastas com presenters e templates em subdiretórios e as classes em namespaces, que chamamos de módulos.
O diretório www/
representa o chamado diretório público ou document-root do projeto. Você pode renomeá-lo
sem a necessidade de configurar mais nada no lado da aplicação. Apenas é necessário configurar a hospedagem para que
o document-root aponte para este diretório.
Você também pode baixar o WebProject diretamente, incluindo o Nette, usando o Composer:
composer create-project nette/web-project
No Linux ou macOS, defina as permissões de
escrita para as pastas log/
e temp/
.
A aplicação WebProject está pronta para ser executada, não é necessário configurar absolutamente nada e você pode
exibi-la imediatamente no navegador acessando a pasta www/
.
Requisição HTTP
Tudo começa no momento em que o usuário abre a página no navegador. Ou seja, quando o navegador faz uma requisição HTTP
ao servidor. A requisição é direcionada para um único arquivo PHP, localizado no diretório público www/
, que
é index.php
. Digamos que seja uma requisição para o endereço https://example.com/product/123
.
Graças à configuração adequada do
servidor, até mesmo esta URL é mapeada para o arquivo index.php
e ele é executado.
Sua tarefa é:
- inicializar o ambiente
- obter a fábrica
- iniciar a aplicação Nette, que tratará da requisição
Que fábrica? Não estamos fabricando tratores, mas sim páginas web! Aguarde, isso será explicado em breve.
Com as palavras “inicialização do ambiente”, queremos dizer, por exemplo, que o Tracy é ativado, que é uma ferramenta incrível para logging ou visualização de erros. No servidor de produção, ele registra os erros; no de desenvolvimento, ele os exibe diretamente. Portanto, a inicialização também inclui a decisão sobre se a web está sendo executada no modo de produção ou de desenvolvimento. Para isso, o Nette usa uma autodeteção inteligente: se você executar a web em localhost, ela será executada no modo de desenvolvimento. Assim, você não precisa configurar nada e a aplicação está imediatamente pronta tanto para o desenvolvimento quanto para a implantação em produção. Esses passos são realizados e detalhadamente descritos no capítulo sobre a classe Bootstrap.
O terceiro ponto (sim, pulamos o segundo, mas voltaremos a ele) é iniciar a aplicação. O tratamento de requisições HTTP
no Nette é responsabilidade da classe Nette\Application\Application
(doravante Application
), então
quando dizemos iniciar a aplicação, queremos dizer especificamente chamar o método com o nome apropriado run()
no objeto desta classe.
O Nette é um mentor que o guia para escrever aplicações limpas de acordo com metodologias comprovadas. E uma das mais
comprovadas é chamada de injeção de dependência, abreviada como DI. Neste momento, não queremos sobrecarregá-lo com a
explicação da DI, para isso existe um capítulo
separado, o resultado essencial é que os objetos chave geralmente serão criados para nós por uma fábrica de objetos,
chamada de Contêiner de DI (abreviado como DIC). Sim, essa é a fábrica da qual falamos há pouco. E ela também nos
fabricará o objeto Application
, por isso precisamos primeiro do contêiner. Obtemo-lo usando a classe
Configurator
e deixamos que ele fabrique o objeto Application
, chamamos o método run()
nele e assim a aplicação Nette é iniciada. É exatamente isso que acontece no arquivo index.php.
Nette Application
A classe Application tem uma única tarefa: responder a uma requisição HTTP.
Aplicações escritas em Nette são divididas em muitos chamados presenters (em outros frameworks, você pode encontrar o termo controller, é a mesma coisa), que são classes, cada uma representando uma página específica do site: por exemplo, a página inicial; um produto em uma loja virtual; um formulário de login; um feed de sitemap, etc. Uma aplicação pode ter de um a milhares de presenters.
A Application começa pedindo ao chamado roteador (router) para decidir a qual dos presenters a requisição atual deve ser
passada para tratamento. O roteador decide de quem é a responsabilidade. Ele olha para a URL de entrada
https://example.com/product/123
e, com base em como está configurado, decide que este é o trabalho, por exemplo,
do presenter Product
, do qual ele desejará como ação a exibição (show
) do produto com
id: 123
. É um bom costume escrever o par presenter + ação separados por dois pontos como
Product:show
.
Portanto, o roteador transformou a URL no par Presenter:action
+ parâmetros, no nosso caso
Product:show
+ id: 123
. Como tal roteador se parece, você pode ver no arquivo
app/Core/RouterFactory.php
e o descrevemos detalhadamente no capítulo Roteamento.
Vamos continuar. A Application já conhece o nome do presenter e pode prosseguir. Criando o objeto da classe
ProductPresenter
, que é o código do presenter Product
. Mais precisamente, ele pede ao Contêiner de
DI para fabricar o presenter, porque fabricar é a função dele.
O presenter pode parecer assim:
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ProductRepository $repository,
) {
}
public function renderShow(int $id): void
{
// obtemos dados do model e passamos para o template
$this->template->product = $this->repository->getProduct($id);
}
}
O tratamento da requisição é assumido pelo presenter. E a tarefa é clara: execute a ação show
com
id: 123
. O que, na linguagem dos presenters, significa que o método renderShow()
é chamado e recebe
123
no parâmetro $id
.
Um presenter pode atender a várias ações, ou seja, ter vários métodos render<Action>()
. Mas
recomendamos projetar presenters com uma ou o mínimo possível de ações.
Então, o método renderShow(123)
foi chamado, cujo código é um exemplo fictício, mas você pode ver nele como
os dados são passados para o template, ou seja, escrevendo em $this->template
.
Posteriormente, o presenter retorna uma resposta. Esta pode ser uma página HTML, uma imagem, um documento XML, o envio de um
arquivo do disco, JSON ou talvez um redirecionamento para outra página. O importante é que, se não dissermos explicitamente
como ele deve responder (o que é o caso de ProductPresenter
), a resposta será a renderização de um template com
uma página HTML. Por quê? Porque em 99% dos casos queremos renderizar um template, então o presenter considera esse
comportamento como padrão e quer facilitar nosso trabalho. Esse é o propósito do Nette.
Nem precisamos indicar qual template renderizar, ele deduzirá o caminho por si só. No caso da ação show
, ele
simplesmente tentará carregar o template show.latte
no diretório com a classe ProductPresenter
. Ele
também tentará localizar o layout no arquivo @layout.latte
(mais detalhes sobre localização de templates).
E então ele renderiza os templates. Com isso, a tarefa do presenter e de toda a aplicação está concluída e o trabalho está finalizado. Se o template não existisse, seria retornada uma página com erro 404. Você pode ler mais sobre presenters na página Presenters.
Para ter certeza, vamos tentar recapitular todo o processo com uma URL ligeiramente diferente:
- A URL será
https://example.com
- Inicializamos a aplicação, o contêiner é criado e
Application::run()
é iniciado - O roteador decodifica a URL como o par
Home:default
- O objeto da classe
HomePresenter
é criado - O método
renderDefault()
é chamado (se existir) - O template, por exemplo,
default.latte
com o layout, por exemplo,@layout.latte
é renderizado
Talvez você tenha encontrado muitos termos novos agora, mas acreditamos que eles fazem sentido. Criar aplicações no Nette é muito fácil.
Templates
Já que falamos de templates, no Nette usa-se o sistema de templates Latte. É por
isso que as extensões .latte
nos templates. O Latte é usado, por um lado, porque é o sistema de templates mais
seguro para PHP e, ao mesmo tempo, o sistema mais intuitivo. Você não precisa aprender muito de novo, basta o conhecimento de
PHP e algumas tags. Você aprenderá tudo na documentação.
No template, links são criados para outros presenters & ações assim:
<a n:href="Product:show $productId">detalhe do produto</a>
Simplesmente, em vez da URL real, você escreve o par conhecido Presenter:action
e especifica quaisquer
parâmetros. O truque está no n:href
, que diz que este atributo será processado pelo Nette. E ele gera:
<a href="/product/456">detalhe do produto</a>
A geração de URLs é responsabilidade do já mencionado roteador. De fato, os roteadores no Nette são excepcionais porque podem realizar não apenas transformações de URL para o par presenter:action, mas também o inverso, ou seja, gerar uma URL a partir do nome do presenter + ação + parâmetros. Graças a isso, no Nette, você pode alterar completamente as formas das URLs em toda a aplicação finalizada, sem alterar um único caractere no template ou presenter. Apenas modificando o roteador. Também graças a isso funciona a chamada canonização, que é outra característica única do Nette, que contribui para um melhor SEO (otimização para motores de busca) ao impedir automaticamente a existência de conteúdo duplicado em URLs diferentes. Muitos programadores consideram isso surpreendente.
Componentes interativos
Sobre os presenters, precisamos contar mais uma coisa: eles têm um sistema de componentes embutido. Algo semelhante pode ser familiar aos veteranos do Delphi ou ASP.NET Web Forms, algo remotamente parecido é a base do React ou Vue.js. No mundo dos frameworks PHP, é uma característica absolutamente única.
Componentes são unidades reutilizáveis independentes que inserimos nas páginas (ou seja, presenters). Podem ser formulários, datagrids, menus, enquetes de votação, na verdade, qualquer coisa que faça sentido usar repetidamente. Podemos criar nossos próprios componentes ou usar alguns da enorme oferta de componentes open source.
Os componentes influenciam fundamentalmente a abordagem para a criação de aplicações. Eles abrirão novas possibilidades para você compor páginas a partir de unidades pré-preparadas. E, além disso, eles têm algo em comum com Hollywood.
Contêiner de DI e configuração
O Contêiner de DI, ou fábrica de objetos, é o coração de toda a aplicação.
Não se preocupe, não é nenhuma caixa preta mágica, como poderia parecer das linhas anteriores. Na verdade, é uma classe
PHP bastante comum, que o Nette gera e salva no diretório de cache. Ela tem muitos métodos nomeados como
createServiceAbcd()
e cada um deles sabe como fabricar e retornar algum objeto. Sim, também existe o método
createServiceApplication()
, que fabrica Nette\Application\Application
, que precisávamos no arquivo
index.php
para iniciar a aplicação. E existem métodos que fabricam os presenters individuais. E assim por
diante.
Objetos que o Contêiner de DI cria são, por algum motivo, chamados de serviços.
O que é realmente especial sobre esta classe é que você não a programa, mas sim o framework. Ele realmente gera
o código PHP e o salva no disco. Você apenas dá instruções sobre quais objetos o contêiner deve saber fabricar e como
exatamente. E essas instruções são escritas nos arquivos de configuração, para os quais
se usa o formato NEON e, portanto, também têm a extensão
.neon
.
Os arquivos de configuração servem puramente para instruir o Contêiner de DI. Então, por exemplo, se eu especificar na
seção sessão a opção expiration: 14 days
,
o Contêiner de DI, ao criar o objeto Nette\Http\Session
representando a sessão, chamará seu método
setExpiration('14 days')
e assim a configuração se tornará realidade.
Há um capítulo inteiro preparado para você descrevendo tudo o que pode ser configurado e como definir seus próprios serviços.
Assim que você se aprofundar um pouco na criação de serviços, encontrará a palavra autowiring. Esta é uma funcionalidade que simplificará sua vida de maneira incrível. Ela pode passar automaticamente objetos para onde você precisa deles (por exemplo, nos construtores de suas classes), sem que você precise fazer nada. Você descobrirá que o Contêiner de DI no Nette é um pequeno milagre.
Para onde ir agora?
Percorremos os princípios básicos das aplicações no Nette. Até agora, muito superficialmente, mas em breve você se aprofundará e, com o tempo, criará aplicações web maravilhosas. Para onde continuar agora? Você já experimentou o tutorial Escrevendo a primeira aplicação?
Além do que foi descrito acima, o Nette possui todo um arsenal de classes úteis, uma camada de banco de dados, etc. Tente apenas navegar pela documentação. Ou pelo blog. Você descobrirá muitas coisas interessantes.
Que o framework lhe traga muita alegria 💙