Guia Básico de Conteúdos para a Disciplina de Engenharia de Software
1 Utilizando Git e GitHub
Esta seção aborda os conceitos fundamentais e a importância do Git e do GitHub para o desenvolvimento colaborativo de software. Ela fornece uma visão geral sem entrar em detalhes de execução dos comandos, ajudando a entender o contexto e a finalidade de cada etapa.
1.0.0 Introdução ao Git e GitHub
Git É um sistema de controle de versões distribuído que permite rastrear alterações em arquivos e colaborar em projetos.
GitHub Plataforma baseada em nuvem que hospeda repositórios Git, facilitando o trabalho em equipe e o gerenciamento de projetos.
1.0.1 Conceitos Básicos
-
Configurações Iniciais: Ao instalar o Git, é essencial definir configurações básicas (nome de usuário e email) para identificar as alterações realizadas por cada desenvolvedor.
-
Commits: Um commit representa um ponto de restauração do projeto. Cada commit é identificado por um hash único e pode incluir mensagens descritivas que ajudam a entender as mudanças realizadas.
-
Branches: As branches permitem desenvolver funcionalidades isoladamente. Você pode criar, alternar e unir branches conforme necessário.
-
Trabalho Remoto: Utilizar repositórios remotos (como no GitHub) possibilita que todos os integrantes da equipe acessem a mesma versão do código, facilitando a colaboração.
1.1 Principais comandos Git
Nesta seção, serão apresentados os comandos essenciais do Git, organizados de maneira a facilitar o entendimento tanto dos aspectos conceituais quanto dos procedimentos práticos. As instruções aqui descritas visam promover uma utilização adequada do Git, ferramenta indispensável para o controle de versões em projetos de software.
1.1.0 Configurações iniciais do Git
Após a instalação do Git em sua máquina, recomenda-se iniciar o terminal Git Bash e proceder com a configuração inicial. Execute os comandos abaixo, individualmente, para definir as informações de usuário:
$ git config --global user.name "Seu Nome"
$ git config --global user.email "Seu Email"
Com a execução dos comandos supracitados, serão definidas as configurações básicas para registro de usuário no git em ambiente local. Tais definições são fundamentais para manter a integridade e a rastreabilidade durante o versionamento com git.
1.1.1 Comandos básicos do Git
git add
Usando o git add
, você move os arquivos para a área de preparo, onde os arquivos serão preparados para serem salvos no commit subsequente.
git add .
git commit
Ao executar um commit, o git registra uma versão do arquivo, criando um ponto de restauração, que possibilita um retorno ao estado anterior.
git commit -m "fix: erro de requisição consertado"
Nesta exemplificação, a flag -m
permite a inclusão direta de uma mensagem, evitando a necessidade de abertura de um editor de texto. O prefixo fix
no texto do commit indica que o commit refere-se à correção de um erro no código. Outros prefixos comumente utilizados são:
fix
para correçõesfeat
para novas funcionalidadesrefact
para refatoraçõesrevert
para reversões de commitsdocs
para documentação
git log
Ao criar um commit, o Git associa a ele um identificador único (hash). Para visualizar o histórico de commits, utilize o comando:
git log
A flag --oneline
é uma opção para reduzir as informações mostradas.
git reset
Caso seja necessário reverter para um estado (commit) anterior do repositório, o comando git reset pode ser utilizado. O exemplo a seguir, reverte o repositório para o commit especificado:
git reset --hard hashDoCommit
Este comando elimina todas as alterações efetuadas após o commit selecionado.
git diff
O comando git diff é empregado para comparar o estado atual dos arquivos com o último commit ou para verificar as diferenças entre dois commits específicos:
git diff
Para comparar dois commits:
git diff hashDoPrimeiroCommit hashDoSegundoCommit
git branch
O gerenciamento de branches é uma prática essencial no desenvolvimento colaborativo. Utilize os comandos a seguir para manipulação das branches:
- Para exibir a branch que está atualmente:
git branch
- Para criar uma nova branch:
git branch nomeDaBranchQueVoceQuerCriar
- Para deletar uma branch:
git branch -d nomeDaBranchQueVaiSerDeletada
git checkout
O comando git checkout possibilita a troca entre branches, bem como a criação e imediata alteração para uma nova branch:
- Para alternar para uma branch existente:
git checkout nomeDaBranchQueVoceQuerIr
- Para criar uma nova branch e ir imediatamente para ela:
git checkout -b nomeDaBranchQueVaiSerCriada
git merge
A fusão (merge) de branches é realizada para integrar as alterações de uma branch secundária à branch de origem. Para realizar a fusão, primeiro altere para a branch de destino e, em seguida, execute:
git merge nomeDaBranch
Trabalhando de forma remota
Até este ponto, as operações descritas são executadas de maneira local. Quando se trabalha em equipe, é necessário que todos tenham acesso à mesma versão do código. Para isso, o GitHub permite armazenar seu repositório na nuvem (nos servidores do Github).
Para conectar um repositório local ao remoto, copie o link fornecido pelo GitHub e execute o seguinte comando:
git remote add origin linkQueVoceCopiou
git push
O comando git push é utilizado para enviar os commits locais para o repositório remoto. Caso a branch não exista remotamente, recomenda-se utilizar a opção --set-upstream, conforme orientado pelo Git:
git push
git pull
Para sincronizar as alterações mais recentes do repositório remoto no seu ambiente local:
git pull
🚨 Observação: É de extrema importância realizar o git pull antes do git push, sobretudo quando há possibilidade de alterações concorrentes, para evitar a sobreposição e a perda de dados provenientes de outros colaboradores.
Esta apresentação sistemática e formal de alguns dos comandos do Git pretende proporcionar uma base sólida para o entendimento e a utilização efetiva desta ferramenta, essencial para o desenvolvimento colaborativo.
Code Review e Pull Request no GitHub
Na Engenharia de Software, a colaboração entre desenvolvedores é essencial para garantir qualidade e evolução contínua dos projetos. Em ambientes corporativos e comunidades open-source, a revisão de código (Code Review) e o uso de Pull Requests (PRs) no GitHub são fundamentais para manter padrões, reduzir erros e compartilhar conhecimento dentro da equipe.
Os Pull Requests representam um processo iterativo que envolve a submissão de alterações, análise crítica por pares e integração controlada ao repositório principal. Esse fluxo de trabalho não apenas melhora a qualidade do software, mas também fortalece a comunicação e o aprendizado entre os desenvolvedores.
Além de identificar possíveis bugs e inconsistências, as Code Reviews ajudam a alinhar o código com as diretrizes do projeto e promovem boas práticas de desenvolvimento. Segundo a LinearB, PRs bem documentados não apenas explicam "como" uma mudança foi feita, mas também registram o "porquê" dela, tornando-se um recurso valioso para a manutenção futura.
Neste capítulo, exploraremos em detalhes o que é um Code Review, como realizá-lo de forma eficaz no GitHub, o conceito de Pull Request, o passo a passo para criar um PR e as principais ferramentas que auxiliam nesse processo.
O que é Code Review?
A revisão de código (Code Review) é uma prática essencial no desenvolvimento de software, onde um programador revisa o código escrito por outro antes de sua incorporação ao projeto. O objetivo desse processo é garantir que o código esteja bem estruturado, livre de erros e alinhado com as boas práticas da equipe.
Por que a revisão de código é importante?
- Identifica problemas: Permite encontrar falhas e corrigir bugs antes que o código seja implantado.
- Melhora a qualidade: Mantém o código organizado e em conformidade com os padrões da equipe.
- Facilita a troca de conhecimento: Desenvolvedores compartilham experiências e aprimoram suas habilidades.
- Garante padronização: Seguir um estilo consistente facilita a manutenção do sistema.
- Promove aprimoramento contínuo: O feedback recebido contribui para o crescimento dos programadores.
Como funciona uma revisão de código?
A revisão pode ocorrer de diferentes maneiras:
- Revisão entre colegas: Um desenvolvedor analisa o código e sugere melhorias.
- Revisão informal: Processo mais flexível, sem regras rígidas.
- Revisão estruturada: Análise formal do código com documentação das sugestões.
Ferramentas para revisão de código
- GitHub: Permite revisão através de Pull Requests.
- GitLab: Oferece comentários em partes específicas do código.
- Bitbucket: Possui funcionalidades semelhantes, facilitando o trabalho colaborativo.
A revisão de código é essencial para um software estável e bem desenvolvido, incentivando aprendizado e colaboração na equipe.
Como realizar um Code Review eficaz no GitHub
No GitHub, a revisão de código ocorre principalmente por meio de Pull Requests (PRs). Para torná-la eficiente, algumas boas práticas devem ser seguidas.
1. Antes de Começar, Entenda o Contexto
Antes de revisar um PR:
- Leia a descrição do PR e consulte links para issues ou documentos explicativos.
- Analise outras partes do código para entender o impacto das mudanças.
2. Aproveite as Ferramentas do GitHub
- Comentários diretos no código: Permitem sugerir melhorias de forma clara.
- Histórico de commits: Facilita a compreensão das alterações feitas.
3. O Que Observar Durante a Revisão
- Funcionalidade: O código atende aos requisitos?
- Clareza e organização: É fácil de entender e segue as diretrizes da equipe?
- Testes: Foram incluídos e estão passando?
- Desempenho: As mudanças afetam a eficiência do sistema?
- Segurança: Existe algum risco de segurança na implementação?
4. Dê um Feedback Construtivo
- Seja claro e direto: Em vez de "isso está errado", explique e sugira soluções.
- Evite críticas pessoais: O foco é no código, não no autor.
- Reconheça boas soluções: Valorizar boas práticas motiva a equipe.
5. Trabalhe em Equipe
- Abra discussões quando necessário: Envolver outros membros pode levar a soluções melhores.
- Seja flexível: Não existe uma única maneira correta de escrever código.
6. Só Aprove Quando Tudo Estiver Correto
Antes de aprovar um PR:
- Certifique-se de que todas as alterações estão bem implementadas.
- Verifique se a documentação foi atualizada, caso necessário.
- Solicite ajustes antes da aprovação final, se preciso.
7. Busque Aprimoramento Contínuo
A revisão de código é uma oportunidade de aprendizado, não apenas uma obrigação. Aprenda com cada revisão e incentive a troca de conhecimento na equipe.
Seguindo essas práticas, o Code Review se torna um aliado na construção de um código mais limpo, seguro e eficiente, fortalecendo a colaboração entre os desenvolvedores.
O que é um Pull Request?
O Pull Request (PR) é um recurso amplamente utilizado em plataformas de controle de versão, como GitHub e GitLab, permitindo que desenvolvedores solicitem a integração de suas alterações ao repositório principal. Ele facilita a colaboração e o gerenciamento de mudanças no código, permitindo que diferentes membros da equipe revisem, comentem e aprovem as alterações antes que elas sejam mescladas ao projeto principal.
Como funciona um Pull Request?
-
Criação da branch:
O desenvolvedor realiza as alterações necessárias em uma branch separada, para garantir que as mudanças não afetem diretamente o código principal. -
Abertura do PR:
Após concluir as alterações, o desenvolvedor cria um Pull Request. Este PR notifica a equipe sobre as mudanças feitas e solicita uma revisão antes que as alterações sejam integradas ao repositório principal. -
Revisão do código:
Outros membros da equipe revisam as modificações propostas. Durante a revisão, são feitos comentários, sugestões de melhorias e, se necessário, ajustes são solicitados para corrigir problemas ou melhorar a qualidade do código. -
Integração:
Após a revisão e a implementação de quaisquer ajustes necessários, o PR é aprovado. As mudanças são então mescladas à branch principal do projeto, garantindo que o código atualizado seja incorporado de maneira controlada e segura.
Criando um Pull Request no GitHub
Passos a seguir
Caso você tenha concluído as alterações necessárias em uma branch separada, siga os passos abaixo para criar seu Pull Request:
1. Push das alterações:
# Adicione todas as alterações à staging area:
$ git add .
# Faça o commit com uma mensagem clara e assinada
$ git commit -S -m "<texto-do-commit>"
#Faça o push do branch
$ git push origin <nome-da-branch>
2. Crie o PR no repositório remoto:
Acesse a página do seu repositório no GitHub e Selecione a opção "Pull requests" ou apenas clique em "compare & pull request caso a branch que acabou de fazer push seja a mesma que deseja incluir no PR"
Escolha as branchs de origem e destino (geralmente o destino é main ou develop) e revise as alterações
Adicione um título e uma descrição das alterações feitas para facilitar o code review
Verifique se as branch de origem e destinop estão "able to merge", se sim confirme em "create pull request" se não volte à sua IDE e resolva o(s) conflito(s) de merge
3. Revisão do PR
Após verificar as alterações feitas em cada commit do PR adicione seus comentários
Caso tenha passado no Code Review e esteja "Able to merge" confime o merge
Boas práticas
- Mensagens de Commit Descritivas:
Use mensagens de commit claras e detalhadas para explicar o motivo das alterações. ex.:
$ git commit -S -m "feat: endpoint para login de usuário"
$ git commit -S -m "chore: adição do container docker a partir da imagem do postgres:latest"
$ git commit -S -m "fix: corrige validação de email no formulário de cadastro "
-
Testes Automatizados:
Inclua testes automatizados em uma pipeline de CI (Continous Integration), para evitar bugs em produção. Para isso existem ferramentas como GitHub actions, jenkins e entre outras. -
Feedback Construtivo:
Seja claro e direto nos feedbacks, destaque o que pode melhorar e o que foi bem feito. -
Relacionar PRs a issues: Caso o PR seja a resolução de uma issue, destaque que há esta relação no titulo do PR, ex.: Resolves #123
Dicas para evitar conflito de merge
- Antes de criar um PR, certifique-se de que sua branch está atualizada com a branch de destino
$ git pull origin <branch-de-destino>
- Se houver conflitos de merge, resolva-os em sua máquina antes de enviar o PR. Use ferramentas como git mergetool para facilitar o processo.
Documentação
Para aprofundar o conhecimento sobre Pull Requests e Code Review, é essencial consultar a documentação oficial e outros materiais relevantes. O GitHub Docs oferece guias detalhados sobre criação e gerenciamento de PRs, além de boas práticas para revisão de código.
Aplicando o GitFlow no Git e GitHub com html e css
Neste capítulo, vamos criar um site usando HTML e CSS, que será uma versão estática de uma rede social chamada "Conex". Ao longo dos capítulos deste guia,transformaremos essa rede social estática em uma aplicação funcional, seguindo as boas práticas de desenvolvimento web com php, js, html e css. Mas afinal, o que são HTML e CSS?
HTML - Hyper Text Markup Language
HTML é como o "esqueleto" de qualquer página web. Ele define a estrutura e o conteúdo de um site, permitindo a criação de elementos como parágrafos, links, imagens, listas e muito mais. A criação destes elementos é feita apartir de tags (marcações de hiper-texto) que são interpretados pelo browser.
Quando você digita uma URL (https://www.conex.com) no navegador (cliente), ele inicia uma comunicação com o servidor onde este site está hospedado. Essa comunicação é feita usando o protocolo HTTP (HyperText Transfer Protocol) ou sua versão com criptografia SSL (HTTPS).
Requisição (Request): O navegador envia uma requisição HTTP (verbo GET) ao servidor, solicitando o recurso, neste caso, uma página HTML.
Resposta (Response): O servidor processa a requisição e envia uma resposta HTTP de volta ao browser. Essa resposta contém o código HTML da página, além de outros recursos como CSS, JavaScript, imgs, etc.
🚨 No t07-mvc abordaremos de maneira prática a arquitetura request/response na comunicação entre clientes e servidores
CSS - Cascading Style Sheets
CSS é responsável pela apresentação e estilo dos elementos HTML. Ele permite definir cores, fontes, tamanhos, layouts e outros aspectos visuais, garantindo que o site tenha uma aparência agradável ao usuário final.
O CSS funciona através de seletores e propriedades. Um seletor é usado para selecionar os elementos HTML que você deseja estilizar, e as propriedades definem como esses elementos devem ser estilizados.
O que é o GitFlow?
O GitFlow é uma estratégia de ramificação (branching) para gerenciar o desenvolvimento de projetos de software utilizando o Git. Criado por Vincent Driessen, o GitFlow organiza o processo de desenvolvimento em diferentes tipos de branches, o que facilita a colaboração entre equipes e o controle de versões. Ele define um conjunto de regras que especifica quando e como criar novas branches para diferentes tarefas, como desenvolvimento de novas funcionalidades, correções de bugs e lançamentos de versões.
Estrutura do GitFlow
A estrutura do GitFlow é composta principalmente por cinco tipos de branches:
1. master (ou main)
Contém o código estável e pronto para ser lançado em produção. Cada commit neste branch representa uma versão estável do projeto.
2. develop
Serve como o branch principal de desenvolvimento, onde as novas funcionalidades são integradas antes de serem liberadas para produção. O código no develop
pode não ser 100% estável, mas deve estar funcional para testes.
3. feature
Criado a partir do develop
para o desenvolvimento de novas funcionalidades. Cada funcionalidade tem seu próprio branch feature
, o que facilita o trabalho isolado e sem impacto no código principal.
4. release
Utilizado para preparar o código para uma nova versão estável. O branch release
é criado a partir do develop
e é onde os últimos ajustes, testes e correções de bugs são feitos antes do lançamento.
5. hotfix
Criado a partir da main
quando é necessário corrigir um problema crítico em produção. Depois de corrigido, o hotfix
é mesclado tanto na main
quanto no develop
para garantir que a correção seja aplicada a ambas as versões.
Benefícios do GitFlow
A aplicação do GitFlow ajuda equipes a gerenciar o ciclo de vida do desenvolvimento de forma mais estruturada, evitando problemas de integração e facilitando o controle de versões.
Como Implementar o GitFlow no Git e GitHub (Somente para Windows)
Para implementar o GitFlow no Windows, você precisa seguir alguns passos específicos, desde a instalação do GitFlow até a configuração e uso em seu repositório Git. A seguir, explicamos como realizar isso:
1. Instalando o Git no Windows
Caso ainda não tenha o Git instalado, o primeiro passo é baixar e instalar o Git para Windows. Acesse o site oficial do Git e baixe a versão mais recente. Durante a instalação, é importante selecionar a opção para adicionar o Git ao PATH, para que você possa usá-lo diretamente no terminal do Windows.
2. Instalando o GitFlow no Windows
O GitFlow não é instalado automaticamente com o Git para Windows. Para instalar o GitFlow no Windows, siga os passos abaixo:
-
Baixe o GitFlow para Windows: A maneira mais simples de instalar o GitFlow no Windows é utilizando o Git Bash, que já vem com o Git para Windows.
-
Baixar e instalar o GitFlow: No Git Bash, execute o comando para instalar o GitFlow:
- Abra o Git Bash no seu computador.
- Execute o seguinte comando para baixar e instalar o GitFlow:
git clone https://github.com/nvie/gitflow.git
cd gitflow
make install
3. Inicializando o GitFlow
Após a instalação do GitFlow, você precisa inicializar o GitFlow em seu repositório Git. Para isso:
- No Git Bash, navegue até a pasta do seu projeto, onde o repositório Git está localizado. Se você ainda não inicializou o repositório, execute:
git init
- Agora, inicialize o GitFlow com o comando:
git flow init
Você será solicitado a configurar os nomes dos branches. As opções padrão são:
- Branch principal:
master
(oumain
) - Branch de desenvolvimento:
develop
Pressione Enter para aceitar as configurações padrão ou insira seus próprios nomes de branches, se desejar.
4. Criando Branches de Funcionalidades (feature
)
Após a configuração inicial, você pode começar a criar branches de funcionalidades (feature). Para iniciar o desenvolvimento de uma nova funcionalidade, use o seguinte comando:
git flow feature start nome-da-feature
Isso criará um novo branch feature/nome-da-feature baseado no develop. Quando a funcionalidade estiver concluída, finalize o branch com:
git flow feature finish nome-da-feature
Isso mesclará o branch de funcionalidade de volta ao develop e excluirá o branch feature.
5. Criando Branches de Lançamento (release
)
Quando as funcionalidades estiverem prontas para serem lançadas, crie um branch release a partir do develop com:
git flow release start v1.0.0
Esse comando criará um branch release/v1.0.0. Após realizar os ajustes finais, finalize o release com:
git flow release finish v1.0.0
Isso mesclará o release
tanto na main
quanto no develop
, criando uma nova versão estável na main
e excluindo o branch release
.
6. Criando Branches de Correção (hotfix
)
Se houver um bug crítico em produção, você pode criar um branch hotfix
a partir da main
:
git flow hotfix start v1.0.1
Após corrigir o problema, finalize o hotfix com:
git flow hotfix finish v1.0.1
Isso mesclará o hotfix
de volta na main
e no develop
, garantindo que a correção seja aplicada a ambas as versões.
7. Subindo as Alterações para o GitHub
Sempre que você criar ou finalizar um branch de funcionalidade, release ou hotfix, é importante enviar suas alterações para o repositório remoto no GitHub. Para isso, use os seguintes comandos:
- Para o branch
main
edevelop
:
git push origin main
git push origin develop
- Para branches
feature
,releas
ouhotfix
, use:
git push origin feature/nome-da-feature
git push origin release/v1.0.0
git push origin hotfix/v1.0.1
8. Gerenciando Pull Requests no GitHub
Ao trabalhar com o GitFlow, é uma boa prática criar Pull Requests (PRs) no GitHub para integrar as mudanças dos branches feature
, release
e hotfix
ao develop
ou main
. Isso ajuda a revisar o código e garantir que as alterações sejam bem integradas.
Aplicando o GitFlow em um projeto com HTML e CSS
Aplicar o GitFlow em um projeto com HTML e CSS segue a mesma lógica que em outros projetos de desenvolvimento.
Passos para Aplicar o GitFlow:
1. Inicialize o Repositório Git
git init
2. Crie e Defina a Branch develop
git checkout -b develop
git push -u origin develop
3. Criação de Features
Para cada nova funcionalidade (exemplo: um novo componente HTML/CSS), crie uma branch baseada em develop
:
git checkout -b feature/nova-funcionalidades
4. Desenvolvendo a feature (exemplo)
-
Estrutura do projeto
Na pasta
img
estão armazenadas todas as imagens utilizadas nesta etapa. Na pastaview
, há duas subpastas: uma para os arquivosCSS
e outra para os arquivosHTML
(que estão no formato PHP devido a uma propriedade específica que estamos utilizando). -
Exemplo de código HTML (arquivo
home.php
)
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Página Principal</title>
<link rel="stylesheet" href="../css/home.css" />
</head>
<body>
<!-- Barra Lateral -->
<?php include __DIR__ . '/side-bar.php'; ?>
<!-- Conteúdo Principal -->
<main class="container">
<!-- Seção de Posts -->
<section class="feed">
<article class="post">
<header class="user-info">
<div class="avatar" aria-label="Foto do Usuário"></div>
<span class="username">Usuário1233</span>
</header>
<div class="image-placeholder" aria-label="Imagem do Post"></div>
</article>
<hr class="divider" />
<article class="post">
<header class="user-info">
<div class="avatar" aria-label="Foto do Usuário"></div>
<span class="username">Usuário1233</span>
</header>
<div class="image-placeholder" aria-label="Imagem do Post"></div>
</article>
</section>
</main>
</body>
</html>
- Como o CSS está separado do HTML em um arquivo externo, é necessário importá-lo na página inicial. Para isso, utilizamos a seguinte tag dentro do
<head>
:
<link rel="stylesheet" href="caminho-do-arquivo-css" />
- Observe que dentro do body tem:
<?php include __DIR__ . '/side-bar.php'; ?>
Esta linha utiliza include DIR para importar outro arquivo HTML, que, neste caso, é o side-bar.php
. Como o projeto possui várias páginas que compartilham a mesma funcionalidade, a importação evita a duplicação de código. Assim, essa funcionalidade é carregada apenas nas páginas que precisam dela, como home.php
e profile.php
.
- Arquivo
side-bar.php
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<link rel="stylesheet" href="../css/side-bar.css" />
</head>
<body>
<!-- Barra Lateral -->
<aside class="side-bar">
<img src="../../img/logo.svg" alt="Logo da empresa" class="logo" />
<nav class="side-bar-links">
<a href="./home.php">
<img
src="../../img/home.svg"
class="icon"
alt="Ícone Página Principal"
/>
Página principal
</a>
<a href="">
<img src="../../img/search.svg" class="icon" alt="Ícone Pesquisar" />
Pesquisar
</a>
<a href="./profile.php">
<img src="../../img/profile.svg" class="icon" alt="Ícone Perfil" />
Perfil
</a>
</nav>
</aside>
</body>
</html>
5. Após desenvolver sua feature, faça commits e, quando finalizar, envie para o repositório remoto:
git add .
git commit -m "Novas funcionalidades"
git push origin feature/nova-funcionalidades
- Abra um Pull Request (PR) para mesclar a feature na branch develop.
6. Criação de Releases (Versões do Projeto)
- Quando for preparar uma versão para produção, crie uma branch release baseada na develop:
git checkout -b release/1.0
- Corrija pequenos ajustes e, quando estiver pronto, mescle no main e na develop:
git checkout main
git merge release/1.0
git checkout develop
git merge release/1.0
7. Correção de Bugs (Hotfixes)
- Caso um erro crítico seja encontrado na main, crie uma branch hotfix baseada nela:
git checkout -b hotfix/corrige-erro
- Após corrigir, mescle na main e na develop:
git checkout main
git merge hotfix/corrige-erro
git checkout develop
git merge hotfix/corrige-erro
- Exclua a branch hotfix:
git branch -d hotfix/corrige-erro
Boas Práticas para GitFlow com HTML e CSS
1. Nomeação de Branches
Uma nomenclatura padronizada e clara para as branches facilita a organização do código e a identificação de funcionalidade.
Boas Práticas:
-
Utilize um padrão consistente:
feature/nome-da-funcionalidade
,bugfix/nome-da-correção
. -
Evite nomes genéricos como
feature/alteracao
ouhotfix/ajuste
. -
Use
kebab-case
ousnake_case
para evitar espaços (exemplo:feature/responsive-navbar
ouhotfix/footer_bug
).
Exemplo de criação de branch:
git checkout -b feature/menu-responsivo
2. Commits Claros e Objetivos
Escrever mensagens de commit bem estruturadas facilita a compreensão das alterações feitas no projeto.
Boas Práticas:
-
Seja descritivo e direto: uma boa mensagem de commit resume a mudança de forma clara.
-
Utilize o tempo verbal correto: mensagens no imperativo são recomendadas (
Adiciona
,Corrige
,Remove
). -
Prefira commits pequenos e frequentes ao invés de um grande commit com muitas alterações.
Exemplos de mensagens de commit boas:
-
Adiciona estilo responsivo ao menu
-
Corrige bug no layout do footer em telas menores
3. Uso de Pull Requests para Revisão
Os Pull Requests (PRs) no GitHub permitem que alterações sejam revisadas antes de serem incorporadas à branch principal.
Boas Práticas:
-
Sempre crie um PR ao finalizar uma feature, release ou hotfix.
-
Utilize uma descrição detalhada no PR para explicar as mudanças.
-
Marque membros da equipe para revisão antes de aprovar a fusão.
Exemplo de PR bem documentado:
Título: "Adiciona layout responsivo ao menu de navegação"Descrição:
-
Adiciona media queries para tornar o menu responsivo.
-
Corrige alinhamento do logo e espaçamento dos itens.
-
Testado em dispositivos móveis e desktop.
4. Separação de Código HTML, CSS e JS
Uma estrutura organizada do projeto facilita a manutenção e reutilização do código.
Boas Práticas:
-
Utilize pastas organizadas (
/css
,/js
,/img
). -
Não misture CSS inline no HTML, prefira arquivos externos.
-
Use uma estrutura modular (exemplo:
style.css
para estilos gerais enavbar.css
para estilos específicos do menu).
Exemplo de estrutura recomendada:
/projeto
│── index.html
│── /css
│ │── style.css
│ │── navbar.css
│── /js
│ │── script.js
│── /img
│ │── logo.png
5. Testes Antes de Fazer Merge
Antes de mesclar uma branch ao develop ou main, é fundamental testar o código para evitar bugs.
Boas Práticas:
-
Testar localmente antes de enviar um PR.
-
Verificar compatibilidade em diferentes navegadores.
-
Certificar-se de que o CSS não está quebrando o layout.
Documentação
Para aprofundar os conhecimentos sobre GitFlow, Git e boas práticas no desenvolvimento de projetos com HTML e CSS, é recomendável consultar materiais adicionais.
Documentação Oficial do Git
Documentação do GitFlow
Documentação do GitHub
Boas Práticas em Desenvolvimento com HTML e CSS
Boas práticas recomendadas pelo Google para desenvolvimento web moderno e otimização de desempenho.
Introdução ao PHP Básico aplicando GitFlow no GitHub
O que é PHP?
O PHP (Hypertext Preprocessor) é uma linguagem de programação de código aberto voltada para o desenvolvimento web. Criado originalmente por Rasmus Lerdorf em 1994, o PHP permite a criação de páginas dinâmicas e interativas, sendo amplamente utilizado em conjunto com HTML e bancos de dados como MySQL.
Características do PHP
-
Execução no Servidor: O PHP é processado no servidor, gerando uma página HTML que é enviada ao navegador. Isso significa que o código PHP não aparece no navegador do usuário, apenas o HTML gerado por ele.
-
Integração com HTML: PHP pode ser embutido diretamente no HTML. Isso permite criar páginas dinâmicas com facilidade. Exemplo: Em um sistema de comentários, você pode usar PHP para exibir os comentários armazenados em um banco de dados.
-
Suporte a Vários Bancos de Dados: Compatível com MySQL, PostgreSQL, SQLite, entre outros.
-
Ampla Comunidade: PHP tem uma comunidade enorme que oferece suporte, bibliotecas e frameworks, tornando o desenvolvimento muito mais ágil. Exemplo: A biblioteca Composer facilita a instalação de pacotes externos, e frameworks como Laravel ajudam a construir aplicações mais rápidas e seguras.
Sintaxe Básica do PHP
O PHP pode ser inserido dentro do código HTML utilizando as tags:
<?php ... ?>.
Exemplo de um script básico:
<!DOCTYPE html>
<html>
<head>
<title>Exemplo PHP</title>
</head>
<body>
<h1><?php echo "Hello, World!"; ?></h1>
</body>
</html>
Nesse exemplo, o PHP processa o comando echo
e insere "Hello, World!" na página HTML.
Exemplos de Comandos Básicos no PHP
- Variáveis e Tipos de Dados:
<?php
$nome = "João"; // string
$idade = 30; // inteiro
$altura = 1.75; // float
$ativo = true; // booleano
?>
- Estruturas Condicionais:
<?php
$idade = 20;
if ($idade >= 18) {
echo "Você é maior de idade.";
} else {
echo "Você é menor de idade.";
}
?>
- Laços de Repetição:
<?php
for ($i = 0; $i < 5; $i++) {
echo "Número: $i <br>";
}
$i = 0;
while ($i < 5) {
echo "Número: $i <br>";
$i++;
}
$nomes = ["João", "Maria", "José"];
foreach ($nomes as $nome) {
echo "Nome: $nome <br>";
}
$user = ["id" => 1, "name" => "name", "password" => "123456"]
foreach ($user as $key => $value){
echo "dados do usuário:" . $value;
}
?>
Vantagens do PHP
-
Fácil aprendizado e uso
-
Extensa documentação
-
Alto desempenho para aplicações web
-
Compatibilidade com diversas plataformas
O PHP continua sendo uma das linguagens mais populares para desenvolvimento web, oferecendo uma combinação de flexibilidade, facilidade de uso e integração com outras tecnologias.
Como instalar o PHP no Windows
Baixar o PHP
-
Acesse o site oficial do PHP: https://windows.php.net/download.
-
Baixe a versão Thread Safe compatível com seu sistema (x64 para 64 bits ou x86 para 32 bits).
-
Baixe o arquivo ZIP e extraia-o para um diretório, como
C:\php
.
Configurar as Variáveis de Ambiente
-
Vá em variáveis do sistema, localize a variável
Path
e clique em Editar. -
Clique em Novo e adicione o caminho da pasta do PHP.
-
Clique em OK para salvar.
Configurar o php.ini
-
Vá até a pasta do PHP.
-
Copie o arquivo
php.ini-development
e renomeie paraphp.ini
. -
Abra o arquivo
php.ini
com o Bloco de Notas e descomente (remova o;
antes de) algumas extensões essenciais:
extension=mysqli
extension=pdo_mysql
extension=mbstring
extension=curl
extension=openssl
- Salve o arquivo.
Testar a Instalação
- Abra o Prompt de Comando (CMD) e digite:
php -v
- Se o PHP estiver instalado corretamente, você verá a versão exibida.
O que é XAMPP?
O XAMPP é um ambiente de desenvolvimento integrado e multiplataforma, projetado para facilitar a criação, teste e implantação de aplicações web em um servidor local. Ele é amplamente utilizado por desenvolvedores, analistas de sistemas e profissionais de TI para simular um ambiente de produção em suas máquinas locais, permitindo o desenvolvimento e a validação de projetos web antes de sua publicação em servidores remotos.
Aplicações do XAMPP
O XAMPP é utilizado para:
-
Desenvolvimento de aplicações web: Permite criar e testar sites dinâmicos, sistemas de gerenciamento de conteúdo (CMS), e-commerce e outras aplicações baseadas em PHP, MySQL e Apache.
-
Testes locais: Simula um ambiente de servidor web completo, permitindo a execução de scripts, testes de banco de dados e validação de funcionalidades sem a necessidade de um servidor externo.
-
Prototipagem rápida: Facilita a criação de protótipos funcionais de aplicações web, reduzindo o tempo de desenvolvimento.
-
Aprendizado e treinamento: É uma ferramenta essencial para estudantes e profissionais que estão aprendendo desenvolvimento web, administração de servidores ou gerenciamento de bancos de dados.
-
Ambiente de staging: Permite a configuração de um ambiente de homologação local para testes finais antes da implantação em produção.
Vantagens do XAMPP
-
Portabilidade: É multiplataforma e pode ser executado em diferentes sistemas operacionais.
-
Facilidade de uso: Vem pré-configurado, eliminando a necessidade de configurações manuais complexas.
-
Gratuito e de código aberto: Não há custos associados à sua utilização.
-
Integração de ferramentas: Inclui todos os componentes necessários para um ambiente de desenvolvimento web completo.
Exemplo Prático: Configurando ambiente XAMPP
A configuração correta de um servidor e banco de dados é essencial para o funcionamento de qualquer sistema de software. Neste exemplo prático, vamos demonstrar como configurar um ambiente de desenvolvimento local utilizando XAMPP para o servidor e MySQL para o banco de dados.
Instalação e Configuração do XAMPP
-
Baixe e instale o XAMPP no site oficial: https://www.apachefriends.org/pt_br/download.html
-
Inicie o Painel de Controle do XAMPP e ative os serviços Apache e MySQL.
-
Verifique a instalação acessando
http://localhost
no navegador.
Se ocorrer um erro ao iniciar o MySQL no XAMPP, acesse o painel de controle e encerre o processo do MySQL antes de tentar novamente.
Criação e Configuração do Banco de Dados com XAMPP
-
Acesse o phpMyAdmin via
http://localhost/phpmyadmin/
, ou utilize oMySQL Workbench
como alternativa. -
Crie um novo banco de dados chamado
conex
(vamos usar o exemplo do nosso projeto). -
Execute o seguinte SQL para criar uma tabela de usuários:
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(15) UNIQUE,
password_hash VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
bio TEXT
);
O que é Docker?
Docker é uma ferramenta que permite "empacotar" aplicações em contêineres a partir de um arquivo "Dockerfile", isolando dependências e ambientes. Isso facilita a implantação e distribuição em diferentes sistemas, pois cada contêiner contém tudo que a aplicação precisa para rodar, mas o que exatamente é um contêiner Docker?
Contêiner Docker
Já ouviu a frase: "Mas na minha máquina o código funciona"? então, times que desenvolvem com Docker não passam por problemas como esse, pois com essa ferramenta, o desenvolvedor é capaz de encapsular a aplicação e todas as dependências necessárias para o seu funcionamento , incluindo o sistema operacional em um contêiner isolado, tornando o ambiente de desenvolvimento único para todos os desenvolvedores, prevenindo bug relacionado a mudança de sistema operacional ou versão das dependências utilizadas.
Para quem já trabalhou com o framework Django, é semelhante a criar um “virtual environment” na pasta “venv”. Porém, um contêiner Docker vai além, ele não apenas isola dependências, mas também o ambiente de execução, e também partes do sistema operacional e configurações, tornando o isolamento ainda maior.
DockerHub
DockerHub é um serviço de hospedagem de imagens Docker, semelhante, em algumas partes, ao GitHub. É um repositório onde são armazenadas, versionadas e compartilhadas as imagens de contêiner, permitindo que usuários façam pull (baixem) e push (publiquem) suas imagens em um repositório central.
Imagem é o molde que define como o container será criado. Fazendo uma alusão à orientação a objetos, o container gerado apartir da imagem seria como um objeto e a imagem uma classe
Exemplo:
Uma imagem Docker para um servidor web (como nginx:latest) pode ser usada para criar múltiplos contêineres, cada um rodando de forma independente, assim como uma classe pode ser instanciada em múltiplos objetos.
Docker Compose
O Docker Compose é uma ferramenta que permite definir e gerenciar vários contêineres, e as imagens que os originam, em um único arquivo YAML, chamado "docker-compose.yml". Em vez de executar cada contêiner individualmente, basta declarar todos os serviços necessários (por exemplo, um banco de dados, um servidor web ou uma aplicação) e então executar um único comando, para levantar todo o ambiente de forma coordenada, oque facilita o setup e manutenção do ambiente unificado.
Principais comandos Docker
# Lista os contêineres em execução
docker ps
# Lista todos os contêineres (em execução e parados)
docker ps -a
# Executa um novo contêiner a partir de um Dockerfile
docker run
# Cria e inicia os contêineres em modo desanexado (background), reconstruindo se necessário
docker-compose up --build -d
# Encerra e remove os contêineres e redes criados
docker-compose down
# Para os contêineres sem removê-los
docker-compose stop
# Reinicia contêineres que foram parados
docker-compose start
# Para construir uma imagem Docker a partir de um Dockerfile
docker build -t nome-da-imagem .
# Executa um novo contêiner a partir de uma imagem
docker run -d -p 8080:80 nome-da-imagem
# Para acessar o terminal dentro de um contêiner em execução
docker exec -it <container_name> bash
# Para parar um contêiner
docker stop <container_name>
# Para remover um contêiner
docker rm <container_name>
# Para ver os logs de um contêiner
docker logs <container_name>
# Para visualizar opções gerais do Docker
docker
Observação
Em alguns sistemas operacionais, o comando correto édocker compose
em vez dedocker-compose
.
Exemplo Prático: Configurando ambiente Docker
Criação e Configuração do Banco de Dados
services:
db:
image: postgres:15-alpine
container_name: postgres_db
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: root
POSTGRES_DB: postgres
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
pgadmin:
image: dpage/pgadmin4:8.11.0
ports:
- 5050:80
environment:
- PGADMIN_DEFAULT_EMAIL=admin@mail.com
- PGADMIN_DEFAULT_PASSWORD=root
depends_on:
- db
volumes:
postgres_data:
-
Inicialize o docker deamon (no windows basta abrir o Docker Desktop)
-
No terminal do diretório raiz projeto (onde está localizado o docker-compose.yml) suba o container com o comando "docker-compose up --build -d"
-
use o comando "docker exec -it <container_name> bash" para acessar o terminal dentro de um contêiner em execução
-
no bash do container use o comando psql -U <POSTGRES_USER> -d <POSTGRES_DB> para abrir o terminal do psql
-
Copie o conteúdo do arquivo "db.sql" para o terminal do psql, que automaticamente serão criadas as tabelas do banco
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(15) UNIQUE,
password_hash VARCHAR(255) NOT NULL,
username VARCHAR(255) NOT NULL,
bio TEXT
);
Services (Serviços)
Dentro de um arquivo docker-compose.yml, cada “service” representa um contêiner. Por exemplo:
- db na configuração acima representa um contêiner com banco de dados PostgreSQL.
- pgadmin representa um contêiner que roda a interface web pgAdmin.
Cada serviço define qual imagem Docker será usada, variáveis de ambiente, portas que serão expostas, volumes para persistência de dados e dependências de outros serviços.
Fazendo novamente uma analogia ao paradigma de orientação a objetos, o service "db" instancia a "classe" "postgres:15-alpine" e modifica seus "atributos": POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, ports, volumes.
Volumes
Volumes permitem que você mantenha os dados salvos entre reinicializações dos contêineres. Sem volumes, os dados seriam perdidos toda vez que o contêiner fosse parado ou recriado. Ao configurar “volumes”, o Docker mapeia um diretório interno do contêiner para uma pasta ou volume persistente na máquina host, garantindo que as informações sejam preservadas.
Docker no contexto do projeto
No docker-compose.yml:
• serviço “db”:
- Usa a imagem postgres:15-alpine (versão mais leve do PostgreSQL).
- Define variáveis de ambiente (usuário, senha e nome do banco).
- Expõe a porta 5432 para que outras aplicações possam fazer um bind nessa porta, e se conectar com o servidor do banco postgres.
- Suporta um volume para persistir os dados.
• serviço “pgadmin”:
- Usa a imagem dpage/pgadmin4 para gerenciar o banco via interface visual no navegador.
- Redireciona a porta 80 do contêiner para a 5050 no host, ou seja,
- Define variáveis de ambiente de login e senha do pgAdmin.
- Cria uma dependência com o serviço “db”, ou seja, o contêiner pgAdmin só sobe quando “db” estiver disponível.
Desse modo o banco de dados e a interface de gerenciamento são definidos em um único arquivo de configuração (YAML), facilitando a manutenção, escalabilidade e portabilidade do seu projeto.
Outra forma
Uma alternativa à configuração direta no docker-compose.yml é criar um Dockerfile para encapsular a aplicação e, em seguida, referenciá-lo no docker-compose.yml. Isso é útil quando você precisa personalizar a imagem Docker da aplicação.
Arquivo Dockerfile:
FROM php:5.6-apache
# instala as dependências do PHP: mbstring, pdo, pdo_mysql e pdo_pgsql
RUN apt-get update && apt-get install -y libpq-dev && rm -rf /var/lib/apt/lists/* \ && docker-php-ext-install mbstring pdo pdo_mysql pdo_pgsql
# instala o módulo de reescrita de URL do Apache
RUN a2enmod rewrite
# copia o código-fonte da aplicação para o diretório raiz do servidor web
# fazer um COPY . /var/www/html/ necessitaria de um Dockerignore, pois copia arquivos desnecessários
COPY public/ /var/www/html/
COPY src/ /var/www/html/src/
COPY view/ /var/www/html/view/
# define o diretório raiz do servidor web
WORKDIR /var/www/html
# expõe a porta 80 do contêiner para o host (necessário para acessar a aplicação via navegador)
EXPOSE 80
# define o comando que será executado quando o contêiner for iniciado
CMD ["apache2-foreground"]
Arquivo docker-compose.yml:
php:
build: . # usa o Dockerfile do diretório atual
container_name: php
ports:
- "80:80"
volumes:
- ./src:/var/www/html
depends_on:
- db
Conex - tópico 04
Tarefas:
- Criar conexão com banco de dados via PDO
- Implementar backend da view "signup.php"
Funcionalidade:
- Cadastro de usuários no sistema
Visão do usuário
Cadastro de usuários no sistema
- Como um usuário visitante, quero preencher um formulário de cadastro fornecendo minhas informações (nome, e-mail, senha, telefone), para que eu possa criar uma conta no sistema e acessar todas as funcionalidades exclusivas para usuários cadastrados.
Cadastro de usuários no sistema
Criar conexão com banco de dados via PDO (exemplo do projeto conex)
Arquivo: t04-db/database.php
class Database
{
private static $instance = null;
private $conn;
private function __construct()
{
$config = require __DIR__ . '/config.php';
if (!isset($config['database']['host'], $config['database']['port'], $config['database']['username'], $config['database']['password'], $config['database']['dbname'])) {
throw new Exception("Configuração do banco de dados incompleta");
}
$dsn = "mysql:host={$config['database']['host']};port={$config['database']['port']};dbname={$config['database']['dbname']};charset=utf8mb4";
try {
$this->conn = new PDO($dsn, $config['database']['username'], $config['database']['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
throw new Exception("Falha na conexão com o banco de dados: " . $e->getMessage());
}
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection()
{
return $this->conn;
}
}
Código responsável por estabelecer a conexão com o banco de dados.
No exemplo apresentado, a classe "Database" é implementada utilizando o design pattern "Singleton". Esse pattern tem como objetivo garantir que exista apenas uma instância da classe durante o tempo de execução da aplicação, facilitando o gerenciamento e o reuso da conexão com o banco de dados.
Observações
$config = require __DIR__ . '/config.php';
Aqui, o código carrega um arquivo chamado config.php que contém as configurações do banco de dados.
if (!isset($config['database']['host'], $config['database']['port'], $config['database']['username'], $config['database']['password'], $config['database']['dbname'])) {
throw new Exception("Configuração do banco de dados incompleta");
}
Aqui, o código verifica se todas as chaves necessárias (host, port, username, password, dbname) existem no array $config['database'].
$dsn = "mysql:host={$config['database']['host']};port={$config['database']['port']};dbname={$config['database']['dbname']};charset=utf8mb4";
O DSN é a string usada pelo PDO para conectar ao banco de dados.
Host (host
), Porta (port
), Nome do banco (dbname
), Charset (utf8mb4
)
try {
$this->conn = new PDO($dsn, $config['database']['username'], $config['database']['password'], [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
]);
} catch (PDOException $e) {
throw new Exception("Falha na conexão com o banco de dados: " . $e->getMessage());
}
Aqui, o código tenta criar uma conexão com o banco de dados usando a classe PDO.
Arquivo: t04-db/config.php
<?php
return [
'database' => [
'host' => 'localhost',
'port' => '3306',
'username' => 'root',
'password' => '',
'dbname' => 'conex'
]
];
🚨 Para usuários de XAMPP:
- Teste e Validação:
- Salve o arquivo
database.php
no diretório do seu servidor (htdocs no XAMPP).- Acesse
http://localhost/database.php
no navegador.- Caso a conexão seja bem-sucedida, a página ficará em branco.
Implementar backend da view "signup.php" (exemplo do projeto conex)
Arquivo: t04-db\view\signup.php
<form class="form-group" method="POST" action="../src/post-user.php">
<div class="icon">
<img src="../public/img/logo.svg" alt="logo" class="logo" />
</div>
<!-- resto do código... -->
Com esssa alteração o form de cadastro agora está referenciado ao codigo de controle de cadastro de usuários, seguido do verbo http POST
Arquivo: Arquivo: t04-db\src\post-user.php
<?php
require_once __DIR__ . '/../database.php';
require_once __DIR__ . '/users.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$name = trim($_POST['name']);
$phone = trim($_POST['phone']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$password_confirm = $_POST['password-confirm'];
if (empty($name) || empty($phone) || empty($email) || empty($password) || empty($password_confirm)) {
$_SESSION['error'] = "Todos os campos são obrigatórios!";
header("Location: /view/signup.php?error=todos-os-campos-obrigatorios");
exit();
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$_SESSION['error'] = "Email inválido!";
header("Location: /view/signup.php?error=email-invalido");
exit();
}
if ($password !== $password_confirm) {
$_SESSION['error'] = "As senhas não coincidem!";
header("Location: /view/signup.php?error=confirmação-de-senha-diferente");
exit();
}
$database = Database::getInstance();
$users = new Users($database);
if ($users->checkEmailExists($email)) {
$_SESSION['error'] = "Este email já está registrado!";
header("Location: /view/signup.php?error=email-ja-registrado");
exit();
}
$password_hash = password_hash($password, PASSWORD_ARGON2I);
if ($users->createUser($name, $email, $password_hash, $phone)) {
$_SESSION['success'] = "Usuário cadastrado com sucesso!";
header("Location: /");
exit();
} else {
$_SESSION['error'] = "Erro ao cadastrar o usuário!";
header("Location: /view/signup.php?error=erro-ao-cadastrar-usuario");
exit();
}
}
Aqui, o código processa o formulário de cadastro de usuários (signup.php
) enviado via método POST. Ele valida os dados recebidos, verifica se o e-mail já está cadastrado, cria um novo usuário no banco de dados e redireciona o usuário com mensagens de sucesso ou erro.
O objetivo principal deste código é:
-
Processar o formulário de cadastro de usuários.
-
Validar os dados fornecidos pelo usuário (campos obrigatórios, formato de e-mail, confirmação de senha).
-
Verificar se o e-mail já está cadastrado no banco de dados.
-
Criar um novo usuário no banco de dados com os dados fornecidos.
-
Redirecionar o usuário com feedback adequado (sucesso ou erro).
Arquivo: t04-db\src\users.php
class Users
{
private $db;
public function __construct(Database $db)
{
$this->db = $db->getConnection();
}
public function createUser($username, $email, $password, $phone)
{
$sql = "INSERT INTO users (username, email, password_hash, phone) VALUES (:username, :email, :password_hash, :phone)";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
':username' => $username,
':email' => $email,
':password_hash' => $password,
':phone' => $phone
]);
}
public function checkEmailExists($email)
{
$sql = "SELECT id FROM users WHERE email = :email";
$stmt = $this->db->prepare($sql);
$stmt->execute([':email' => $email]);
return $stmt->fetch() ? true : false;
}
}
Aqui, o código define uma classe chamada Users que tem como objetivo gerenciar operações relacionadas a usuários no banco de dados, especificamente a criação de novos usuários e a verificação se um e-mail já está cadastrado.
Conclusão
Este tópico abordou de maneira prática a criação, conexão e cadastro de um usuário em um banco de dados.
O exemplo demonstrou como configurar a conexão ao banco de dados utilizando PDO
no PHP
, garantindo que a comunicação entre o servidor e o banco seja segura e eficiente. Além disso, o código abordou o processo de cadastro de um novo usuário, incluindo validações de entrada, como a verificação de e-mail e confirmação de senha, bem como a utilização de hashing para armazenamento seguro da senha no banco de dados.
Boas Práticas para Desenvolvimento PHP com GitFlow
O uso de GitFlow em projetos PHP ajuda a estruturar o desenvolvimento de forma organizada e colaborativa. No entanto, para garantir um fluxo de trabalho eficiente, é essencial adotar boas práticas que mantenham o código padronizado, seguro e funcional.
Organização do Código PHP no Repositório
Estrutura recomendada:
/project-root
│── app/ # Código principal do aplicativo
│── config/ # Arquivos de configuração
│── public/ # Pasta pública, incluindo index.php
│── resources/ # Views, assets, templates
│── tests/ # Testes unitários e funcionais
│── vendor/ # Dependências do Composer
│── .env.example # Exemplo do arquivo de variáveis de ambiente
│── composer.json # Dependências do projeto
│── README.md # Documentação do projeto
Manter um README atualizado também é essencial para que novos desenvolvedores compreendam rapidamente o projeto.
Assim como foi explicado sobre a aplicação do GitFlow com HTML e CSS, o processo em PHP segue a mesma lógica:
- Criação das branches
main
edevelop
. - Desenvolvimento de
features
. - Criação de
releases
. - Aplicação de
hotfixes
. - Realização de
pull requests
.
Como aplicar o GitFlow.
Documentação
Abaixo estão alguns links para aprofundar seus conhecimentos desse tópico.
Manual do php
Tudo sobre o Xampp
MySQL
Instalação
Docker (Windows) Docker (Ubuntu)
Sessões de usuário no navegador
As sessões são um mecanismo do site que permitem que os servidores lembrem de informações sobre um usuário ao longo de várias telas, mesmo em um ambiente onde cada requisição HTTP é, por si só, independente e stateless.
Conceito de sessão
-
Persistência de estado:
Sessões permitem que uma experiência personalizada seja mantida enquanto o usuário navega por diferentes páginas de um site, armazenando informações como autenticação, preferências e dados de interação. -
ID Único:
Quando o usuário acessa um site, o servidor gera um ID para essa sessão. Esse identificador é usado para associar futuros requests a um conjunto específico de dados armazenados no servidor.
Obs: O que é "estado" no contexto de aplicações web? Com "estado" nos referimos às informações que indicam a situação atual de um usuário ou de uma aplicação em um dado momento. Basicamente, é o conjunto de dados que o sistema lembra sobre os usuários enquanto eles navegam no site.
Como funcionam no navegador
-
Criação e armazenamento de sessão:
Ao acessar um site, o servidor cria uma sessão para o usuário e envia um identificador único de sessão para o navegador. Esse identificador é geralmente armazenado em um cookie. -
Envio Automático do Cookie:
Toda vez que você interage com o site, clicando em um link ou preenchendo um formulário, o navegador envia o cookie junto com a requisição. O servidor usa o identificador do cookie para acessar ou atualizar a sessão e assim ajustar as informações conforme suas ações. -
Atualização do estado:
Toda vez que o usuário interage com o site (clicando em links, submetendo formulários etc.), o navegador envia o cookie contendo o identificador. O servidor utiliza esse identificador para acessar ou atualizar os dados da sessão, garantindo uma experiência personalizada e a persistência dos estados. -
Segurança dos Cookies:
Os cookies de sessão podem contar com atributos comoHttpOnly
(impede que scripts no navegador acessem o cookie, evitando ataques como o XSS) eSecure
(permite que o cookie seja enviado em conexões HTTPS), que ajudam a proteger o seu conteúdo contra ataques e asseguram que os dados só sejam transmitidos através de conexões seguras.
Conclusão
Imagine que você entra em uma biblioteca onde o garçom te entrega um crachá com um número único (seu ID de sessão). Cada vez que você pede um livro (faz uma requisição), você mostra o crachá. Assim, o bibliotecário sabe quais livros você já pegou ou se você tem alguma restrição. No nosso caso, o navegador usa um cookie com o ID da sua sessão para que o servidor lembre de você sempre que visitá-lo.
Este mecanismo de sessões torna possível que, mesmo que o HTTP não lembre de nada entre as requisições (seja stateless), o site possa manter uma experiência personalizada e contínua para cada usuário.
Sessões em PHP
O que são sessões em PHP?
Uma sessão no PHP, assim como visto no tópico anterior, é uma maneira de armazenar dados temporários enquanto o usuário navega entre as páginas de um site. A sessão é iniciada no servidor e um identificador único é gerado para cada usuário. Este identificador é armazenado no navegador como um cookie por exemplo, permitindo que o PHP associe o usuário a uma sessão específica em futuras requisições.
Como iniciar uma sessão com PHP
Para utilizar sessões em PHP, você deve iniciar a sessão com a função session_start()
. Essa função deve ser chamada no início de cada página PHP onde você deseja acessar ou manipular os dados da sessão. Lembre-se de que session_start()
deve ser chamada antes de qualquer saída HTML ou echo, caso contrário, ocorrerá um erro.
<?php
session_start();
?>
Armazenando dados na sessão com PHP
Após iniciar a sessão, você pode armazenar dados utilizando a superglobal $_SESSION
, que é um array associativo. Isso permite que você guarde qualquer tipo de dado durante a sessão, como informações do usuário, configurações, preferências, entre outros.
Exemplo:
$_SESSION['user_id'] = 123; // Armazena o ID do usuário
$_SESSION['username'] = 'Joao'; // Armazena o nome de usuário
Esses dados ficam acessíveis em qualquer página onde a sessão esteja iniciada.
Acessando dados da sessão com PHP
Você pode acessar os dados armazenados na sessão utilizando a superglobal $_SESSION
. Quando você armazena um valor, basta referenciá-lo pelo nome da chave definida.
Exemplo:
echo 'ID do usuário: ' . $_SESSION['user_id'];
echo 'Nome de usuário: ' . $_SESSION['username'];
Esses valores estarão disponíveis enquanto a sessão estiver ativa.
Modificando dados da sessão com PHP
Os dados armazenados na sessão podem ser modificados a qualquer momento. Basta atribuir um novo valor à chave correspondente na superglobal $_SESSION
.
Exemplo:
$_SESSION['username'] = 'Maria'; // Modificando o nome de usuário
Destruindo a sessão com PHP
Quando você não precisa mais dos dados da sessão ou quando o usuário sai da aplicação, você pode destruir a sessão. Isso pode ser feito de duas formas
- Limpar todos os dados da sessão: A função
session_unset()
remove todas as variáveis de sessão.
session_unset(); // Limpa todos os dados da sessão
- Destruir a sessão completamente: A função session_destroy() encerra a sessão e apaga os dados no servidor. No entanto, ela não apaga as variáveis de sessão imediatamente, por isso é comum utilizar session_unset() antes de chamar session_destroy().
session_destroy(); // Destroi a sessão completamente
Gerenciamento de tempo de sessão com PHP
O PHP permite controlar o tempo de vida da sessão, tanto no lado do servidor quanto no lado do usuário.
- Tempo de expiração da sessão: Para definir o tempo de expiração da sessão, você pode configurar a diretiva
session.gc_maxlifetime
no arquivophp.in
. Essa configuração determina por quanto tempo os dados da sessão podem ficar armazenados no servidor antes de serem considerados expirados.
No PHP, também é possível configurar a expiração por meio do código, alterando as configurações da sessão:
ini_set('session.gc_maxlifetime', 3600); // Define 1 hora como tempo de expiração
- Controle de tempo no lado do cliente: O PHP armazena o identificador da sessão no cliente por meio de um cookie. Você pode configurar o tempo de expiração deste cookie utilizando
session.cookie_lifetime
.
ini_set('session.cookie_lifetime', 3600); // Expiração do cookie em 1 hora
Quando se trabalha com sessões, é importante implementar medidas de segurança para evitar ataques como fixação de sessão (session fixation) ou roubo de sessão. Algumas boas práticas incluem:
- Regeneração do ID da sessão: A cada novo login ou ação importante, é recomendado regenerar o ID da sessão para dificultar a captura de uma sessão antiga ou comprometida.
session_regenerate_id(true); // Regenera o ID da sessão
- Uso de cookies seguros: Para garantir que o cookie da sessão seja enviado apenas por conexões seguras (HTTPS), ative a opção
session.cookie_secure
nophp.ini
.
Conclusão
As sessões são um recurso fundamental em PHP para armazenar e gerenciar informações durante a navegação do usuário. Elas são amplamente utilizadas para funcionalidades como autenticação de usuários, preferências de navegação e manutenção de dados entre páginas. Com o uso adequado das funções de sessão, você pode garantir uma experiência de usuário eficiente e segura.
Conex - tópico 05
Tarefas:
- Implementar Backend para autenticação de usuários registrados.
Funcionalidades:
- Login no sistema
Visão do usuário
Login no sistema
- Como um usuário cadastrado, quero preencher o formulário de login, para que eu me autenticar no sistema e acessar todas as funcionalidades exclusivas para usuários logados.
Login no sistema (exemplo do nosso projeto conex)
Nosso exemplo será composto por três páginas principais:
-
login.php
: Página onde o usuário insere suas credenciais (email de usuário e senha). -
profile.php
: Página restrita, acessada somente após o login bem-sucedido. -
login-user.php
: Página responsável por processar os dados de login.
Vamos utilizar a conexão e a tabela users
criada no exemplo de configuração do servidor e banco de dados.
Página de Login (login.php
)
Na página de login, o usuário será solicitado a inserir seu email de e senha. Se as credenciais estiverem corretas, o sistema iniciará uma sessão e redirecionará o usuário para a página restrita (profile.php
), se não será redirecionado novamente para o login.php com a mensagem de erro no query params.
Arquivo: t05-session-php\view\login.php
<form class="form-group" method="POST" action="../src/login-user.php">
<div class="form-control">
<label for="email">Email</label>
<input type="email" id="email" name="email" required />
</div>
<!--resto do código-->
Com esssa alteração o form de login agora está referenciado ao código de controle de login de usuários, seguido do verbo http POST
Arquivo: t05-session-php\src\login-user.php
<?php
session_start();
require_once __DIR__ . '/../database.php';
require_once __DIR__ . '/users.php';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST['email']);
$password = trim($_POST['password']);
if (empty($email) || empty($password)) {
$_SESSION['error'] = "Todos os campos são obrigatórios!";
header("Location: /view/login.php?error=todos-os-campos-obrigatorios");
exit();
}
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$_SESSION['error'] = "Formato de email inválido!";
header("Location: /view/login.php?error=formato-de-email-invalido");
exit();
}
$database = Database::getInstance();
$users = new Users($database);
$user = $users->getUserByEmail($email);
if ($user && password_verify($password, $user['password_hash'])) {
$id = $_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['username'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['LAST_ACTIVITY'] = time();
$_SESSION['success'] = "Login realizado com sucesso!";
header("Location: ../view/profile.php?id={$id}&success=login-realizado-com-sucesso");
exit();
} else {
$_SESSION['error'] = "Credenciais inválidas!";
header("Location: /view/login.php?error=credencial-errada");
exit();
}
}
Este código PHP trata o processo de autenticação de usuários com base no formulário de login login.php
. Ele verifica se o usuário preencheu o formulário corretamente e valida as credenciais.
Observações
session_start();
Inicia uma nova sessão ou retoma a existente. Isso permite armazenar informações do usuário, como dados de login, em variáveis de sessão.
require_once __DIR__ . '/../database.php';
require_once __DIR__ . '/users.php';
Esses arquivos são incluídos para permitir o acesso ao banco de dados e manipulação de usuários.
if ($_SERVER["REQUEST_METHOD"] == "POST")
Verifica se o método de requisição é POST, ou seja, se o formulário foi enviado.
$email = trim($_POST['email']);
$password = trim($_POST['password']);
O código coleta e "limpa" os valores do formulário com trim()
para remover espaços extras.
if (empty($email) || empty($password))
Verifica se os campos de email e senha estão vazios e, se sim, define uma mensagem de erro na sessão e redireciona o usuário de volta para a página de login.
if ($user && password_verify($password, $user['password_hash']))
Se o usuário for encontrado no banco de dados e a senha fornecida corresponder à senha armazenada (com o password_hash
), a autenticação é bem-sucedida.
$id = $_SESSION['user_id'] = $user['id'];
$_SESSION['user_name'] = $user['username'];
$_SESSION['user_email'] = $user['email'];
$_SESSION['LAST_ACTIVITY'] = time();
$_SESSION['success'] = "Login realizado com sucesso!";
header("Location: ../view/profile.php?id={$id}&success=login-realizado-com-sucesso");
exit();
-
Se o login for bem-sucedido, as variáveis de sessão são definidas (
$_SESSION['user_id']
,$_SESSION['user_name']
, etc.) com informações do usuário. -
Define também o tempo de atividade da sessão com
$_SESSION['LAST_ACTIVITY'] = time();
. -
Exibe uma mensagem de sucesso e redireciona o usuário para a página de perfil (
profile.php
).
else {
$_SESSION['error'] = "Credenciais inválidas!";
header("Location: /view/login.php?error=credencial-errada");
exit();
}
Se a senha não corresponder ou o usuário não for encontrado, o código define uma mensagem de erro na sessão e redireciona o usuário de volta para a página de login com um aviso de "Credenciais inválidas".
Como mencionado anteriormente, o login do usuário está incluído em dois arquivos: o database
, responsável pela conexão com o banco de dados, e o users.php
. Vamos entender o que está sendo utilizado na classe users para realizar o login.
Arquivo: t05-session-php\src\users.php
public function getUserByEmail($email)
{
$sql = "SELECT id, username, email, password_hash, phone FROM users WHERE email = :email";
$stmt = $this->db->prepare($sql);
$stmt->execute([':email' => $email]);
return $stmt->fetch();
}
Essa função busca um usuário no banco de dados com base no email fornecido. Ela retorna os dados do usuário (como ID, nome de usuário, email, senha e telefone) caso o email exista no banco.
O código de login usa essa função com a linha:
$user = $users->getUserByEmail($email);
Ele chama getUserByEmail()
para verificar se um usuário existe com o email fornecido no formulário. Se um usuário for encontrado, seus dados (incluindo a senha) são retornados e usados para verificar a senha fornecida.
Conclusão
Este exemplo básico demonstra como implementar um sistema de login com PHP usando sessões. O PHP facilita a criação de sessões com variáveis globais, permitindo que você armazene dados do usuário enquanto ele navega pelas páginas, tornando a experiência flúida.
Boas Práticas para Sessões em PHP
As sessões em PHP são uma ferramenta poderosa e amplamente utilizada para armazenar e gerenciar informações de usuários em uma aplicação web. No entanto, como qualquer outro recurso, seu uso inadequado pode trazer sérios riscos de segurança. Vamos abordar algumas boas práticas para o uso de sessões, visando garantir a segurança, eficiência e confiabilidade.
Iniciar a Sessão de Forma Segura
- Iniciar a sessão no início do script: O comando
session_start()
deve ser chamado antes de qualquer saída para o navegador (como echo, HTML, ou espaços em branco), caso contrário, ocorrerá um erro.
- Verificar a presença de dados de sessão: Sempre que um usuário tentar acessar uma página restrita, verifique se a sessão foi iniciada e se os dados necessários estão presentes.
session_start();
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
- Regeneração do ID da Sessão: Após o login ou em ações críticas, é recomendável regenerar o ID da sessão para evitar ataques de fixação de sessão (session fixation). Isso impede que um atacante use um ID de sessão válido para se passar por outro usuário.
session_regenerate_id(true);
Armazenar Dados Sensíveis de Forma Segura
- Evitar armazenar dados sensíveis diretamente: Nunca armazene dados como senhas ou informações confidenciais diretamente na sessão. Em vez disso, armazene apenas informações que identificam o usuário (por exemplo, um ID de usuário) e recupere os dados sensíveis no banco de dados conforme necessário.
$_SESSION['user_id'] = $user['id'];
- Usar criptografia: Se for necessário armazenar dados sensíveis na sessão (como preferências ou tokens), utilize criptografia para proteger essas informações. O PHP oferece funções como
openssl_encrypt
para criptografar dados antes de armazená-los.
$_SESSION['encrypted_data'] = openssl_encrypt($data, 'aes-256-cbc', $key, 0, $iv);
Configurar Sessões para Funcionar Apenas em Conexões Seguras (HTTPS)
- Definir cookies de sessão como seguros: Use a opção
session.cookie_secure
para garantir que o cookie de sessão seja transmitido apenas por HTTPS.
ini_set('session.cookie_secure', 1);
- Forçar sessões em HTTPS: Para garantir que todas as páginas de login e áreas sensíveis sejam carregadas por HTTPS, defina uma política de segurança que redirecione os usuários automaticamente para HTTPS, caso estejam acessando via HTTP.
if ($_SERVER['HTTPS'] != 'on') {
header('Location: https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']);
exit();
}
Documentação
Alguns links para aprimorar o conhecimento sobre sessões.
CRUD com PHP
o que é CRUD?
CRUD é um acrônimo para os verbos em inglês: Create, Read, Update e Delete, que se referem a operações básicas de manipulação de dados no backend da aplicação. O "create" refere-se a inserção de dados no banco, o "read" à leitura/recuperação desses dados do banco, o "update" a atualização de dados registrados e o "delete" à deleção destes dados. Esses verbos CRUD estão diretamente ligados aos verbos HTTP (GET, POST, PUT e DELETE), que são usados para definir o tipo de ação que o cliente (navegador ou outra aplicação) deseja realizar no servidor. O client (navegador) envia uma requisição ao servidor, contendo algum verbo http no cabeçalho, o backend processa essa requisição (realiza alguma ação) e envia uma resposta HTTP ao cliente, indicando o resultado desta operação.
Na comunicação HTTP, cada requisição vem acompanhada de um status code que indica ao cliente como a operação ocorreu. Por exemplo, "201" (Created) é retornado quando um recurso é criado com sucesso, enquanto "200" (OK) confirma o sucesso da requisição. Já "204" (No Content) significa que o servidor processou a requisição e não há conteúdo a retornar. Esses códigos ajudam o client (navegador ou outra aplicação) a entender se a ação foi concluída corretamente ou se houve erro.
Nos capítulos anteriores vimos como inserir dados no banco com PDO, isso já configuraria o que chamamos de "Create" no CRUD, agora iremos abordar os demais verbos neste capítulo.
exemplo fictício
function create() {
echo "Inserindo dados no banco";
http_response_code(201); //status code 201: Created
}
function read() {
echo "Recuperando dados do banco";
http_response_code(200); //status code 200: OK
}
function update() {
echo "Atualizando dados no banco";
http_response_code(200); //status code 200: OK
}
function delete() {
echo "Deletando dados no banco";
http_response_code(204); //status code 204: No Content
}
//A variável global "$_SERVER" recupera o método da requisição (GET, POST, PUT, DELETE)
$method = $_SERVER['REQUEST_METHOD'];
//Roteamento básico para simular cada operação CRUD
switch ($method) {
case 'POST':
create();
break;
case 'GET':
read();
break;
case 'PUT':
update();
break;
case 'DELETE':
delete();
break;
}
🚨 No t07-mvc abordaremos na prática os conceitos de um sistema de roteamento em um projeto real
Camadas da aplicação
No exemplo prático deste capítulo criaremos um sistema CRUD com PHP dividindo a aplicação em camadas, para tornar o código mais organizado e consequentemente mais fácil de manter a longo prazo. Aplicaremos os conceitos de Controller, DAO e View na arquitetura da nossa aplicação. Porém, antes de implementar, vamos explicar o que são e como se comunicam do momento em que a requisição chega até o retorno dos dados ao usuário.
Controller
Camada de controle, que recebe os dados da view (HTML), processa, faz validações de dados e os retorna para o DAO. Funciona como a porta de entrada das requisições. Recebe dados da View, valida ou transforma essa informação e decide o que fazer em seguida, por exemplo: criar registros ou buscar dados. Em seguida, faz chamadas ao DAO sempre que precisar manipular o banco de dados. Quando obtém uma resposta, o Controller retorna para a View os dados que deverão ser exibidos. O controller jamais deve acessar o banco de dados diretamente ou conter lógica de apresentação.
DAO - Data Access Object
Centraliza as operações de banco de dados, executando consultas SQL de criação, leitura, atualização e exclusão, realizadas por métodos específicos (ex.: createUser
, getUserById
, updateUser
, deleteUser
) que o Controller chama. Assim, se a lógica de conexão com o banco mudar (ex.: de MySQL para PostgreSQL), é necessário alterar apenas o DAO, sem afetar a camada de controle.
View
Representa a parte visual da aplicação, que é exibida no navegador. Responsável pela lógica de apresentação, receber os inputs do usuário em formulários e/ou fazer o display de dados processados nas outras camadas em páginas, listas, mensagens de erro ou sucesso. Esse fluxo de dados para a view é gerenciado pelo controller, A View em hipótese nenhuma pode ter acesso direto à camada de acesso a dados (DAO) ou aos dados em si.
exemplo - fluxo de comunicação entre as camadas:
- Um usuário interage com a View (envia um formulário).
- o navegador (client) envia uma requisição HTTP ao servidor (header: POST, body: dados inseridos pelo usuário).
- O Controller recebe a requisição, valida os dados e chama o metódo da classe DAO.
- O DAO acessa o banco, retorna o resultado ao Controller.
- O Controller pepara uma resposta HTTP para o navegador, contendo o status code, header e o corpo da requisição (um HTML ou Json)
- O navegador interpreta o código de status e os cabeçalhos e renderiza o conteúdo do corpo (HTML, JSON, XML, etc.) para o usuário.
Desse modo cada camada tem uma responsabilidade bem definida, o que torna o sistema mais modular e escalável.
Conex - tópico 06
Tarefas:
- Implementar o Backend de todas as funcionalidades citadas, com divisão de camadas (DAO e Controller)
- Refatorar a camada de view de todas as funcionalidades com a lógica de apresentação
- Adicionar a view de Atualização de perfil do usuário com a lógica de apresentação
Funcionalidades:
- Criação de posts no feed
- Exibição dos posts de todos os usuários no feed
- Atualização de perfil do usuário
- Exibir perfil do usuário
- Exibir no perfil do usuário suas respectivas postagens
Visão do usuário
Cada feature do Conex foi pensanda para criar uma solução que atenda às expectativas e necessidades dos usuários. Vejamos como cada funcionalidade se conecta à experiência de usuário na prática:
Criação de posts no feed
- Como usuário autenticado, Quero criar e publicar um novo post (por exemplo, com imagem e/ou texto) no meu feed, Para que eu possa compartilhar minhas atualizações e experiências com minha rede.
Exibição dos posts de todos os usuários no feed
- Como usuário logado, quero visualizar os posts de todos os usuários em uma única página do feed, para que eu possa acompanhar as novidades e interações da comunidade em tempo real.
Atualização do perfil do usuário
- Como usuário, quero editar minhas informações pessoais (como nome, foto, biografia, e outros dados relevantes), para que meu perfil reflita minhas informações atuais e facilite minha identificação na plataforma.
Exibir perfil do usuário
- Como qualquer usuário (ou visitante), quero acessar e visualizar o perfil de outros usuários, para que eu possa conhecer mais sobre eles, suas atividades e potencialmente interagir.
Exibir no perfil do usuário suas respectivas postagens
- Como usuário dono do perfil, quero ver uma listagem organizada de todos os meus posts, para que eu possa gerenciar minhas publicações, revisitar conteúdos antigos ou excluir posts indesejados.
Criação de posts no feed
Planejamento Arquitetural do Sistema
-
View (profile.php):
-
Exibe um formulário para o usuário fazer upload de uma foto para o feed.
-
O formulário é exibido apenas para o próprio usuário (não para outros usuários).
-
O formulário envia os dados para o controller (upload-feed.php) via método POST.
-
-
Controller (upload-feed.php):
-
Recebe os dados do formulário (arquivo de imagem).
-
Valida e processa o upload da imagem.
-
Chama o DAO para persistir os dados no banco.
-
-
DAO (posts-dao.php):
- Executa a query SQL para inserir o post no banco de dados.
Na prática
Arquivo: t06-crud-php/view/profile.php
<!-- Formulário de upload de foto (apenas para o próprio usuário) -->
<?php if ((int)$user_id === (int)$logged_in_user_id): ?>
<div class="upload-container">
<form
action="<?= BASE_URL ?>src/controllers/posts/upload-feed-photo.php"
method="POST"
enctype="multipart/form-data"
>
<label for="photo">
<img src="<?= BASE_URL ?>public/img/add-photo.svg" class="icon" /> <br />
Adicionar foto
</label>
<input type="file" id="photo" name="photo" accept="image/*" />
<button type="submit" class="btn-upload">Enviar</button>
</form>
</div>
<?php endif; ?>
-
Responsabilidade:
-
Exibe um formulário para upload de fotos no feed.
-
O formulário só é exibido se o usuário logado estiver visualizando seu próprio perfil.
-
O formulário envia o arquivo para o controller (
upload-feed.php
) via POST.
-
Arquivo: src/controllers/posts/upload-feed.php
<?php
session_start();
require_once __DIR__ . "/../../dao/posts-dao.php";
require_once __DIR__ . "/../../../dir-config.php";
require_once __DIR__ . "/../../utils/upload-handler.php";
if (!isset($_SESSION['user_id'], $_SESSION['user_name'])) {
header("Location: " . BASE_URL . "view/login.php");
exit;
}
$user_id = $_SESSION['user_id'];
$postsDao = new PostsDao();
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['photo'])) {
$uploadDir = '../../../uploads/feed/';
$allowedTypes = ['jpg', 'jpeg', 'png', 'gif'];
$dbUpdateCallback = function ($photoUrl) use ($user_id, $postsDao) {
return $postsDao->createPost($user_id, $photoUrl);
};
$uploadResult = UploadHandler::handleUpload($_FILES['photo'], $uploadDir, $allowedTypes, $dbUpdateCallback);
if ($uploadResult['success']) {
$_SESSION['success'] = "Foto do feed atualizada com sucesso!";
} else {
$_SESSION['error'] = $uploadResult['error'];
}
header("Location: " . BASE_URL . "view/profile.php");
exit;
}
?>
-
Responsabilidade:
-
Inicia a sessão e verifica se o usuário está logado.
-
Recebe o arquivo enviado pelo formulário
($\_FILES['photo'])
. -
Define o diretório de upload e os tipos de arquivo permitidos.
-
Utiliza o
UploadHandler
para processar o upload da imagem. -
Após o upload bem-sucedido, chama o método
createPost
do DAO para inserir o post no banco de dados. -
Redireciona o usuário de volta para a página de perfil com uma mensagem de sucesso ou erro.
-
Arquivo: src/dao/posts-dao.php
public function createPost($userId, $photo_url) {
$sql = "INSERT INTO posts (user_id, photo_url) VALUES (:userId, :photo_url)";
$stmt = $this->db->prepare($sql);
return $stmt->execute([':userId' => $userId, ':photo_url' => $photo_url]);
}
-
Responsabilidade:
-
Executa a query SQL para inserir um novo post na tabela
posts
. -
Recebe o ID do usuário e a URL da foto como parâmetros.
-
Retorna
true
se a inserção for bem-sucedida.
-
Arquivo: src/utils/upload-handler.php
public static function handleUpload($file, $uploadDir, $allowedTypes, $dbUpdateCallback) {
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
$fileType = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
if (!in_array($fileType, $allowedTypes)) {
return ['success' => false, 'error' => "Apenas arquivos " . implode(", ", $allowedTypes) . " são permitidos."];
}
$fileName = uniqid() . '_' . basename($file['name']);
$targetFile = $uploadDir . $fileName;
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
$photoUrl = $fileName;
$dbUpdateResult = $dbUpdateCallback($photoUrl);
if ($dbUpdateResult) {
return ['success' => true, 'photoUrl' => $photoUrl];
} else {
return ['success' => false, 'error' => "Erro ao atualizar o banco de dados."];
}
} else {
return ['success' => false, 'error' => "Erro ao fazer upload da imagem."];
}
}
-
Responsabilidade:
-
Valida o tipo de arquivo e move o arquivo para o diretório de upload.
-
Gera um nome único para o arquivo.
-
Chama o callback (
$dbUpdateCallback
) para atualizar o banco de dados com a URL da foto. -
Retorna um array com o status do upload e a URL da foto (em caso de sucesso) ou uma mensagem de erro.
-
Fluxo Completo
-
O usuário acessa a página de perfil (
profile.php
) e vê o formulário de upload de foto (se for o próprio perfil). -
O usuário seleciona uma imagem e clica em "Enviar".
-
O formulário envia a imagem para o controller (
upload-feed.php
). -
O controller processa o upload da imagem usando o
UploadHandler
. -
Após o upload, o controller chama o método
createPost
do DAO para inserir o post no banco de dados. -
O usuário é redirecionado de volta para a página de perfil com uma mensagem de sucesso ou erro.
Exibição dos posts de todos os usuários no feed
Planejamento Arquitetural do Sistema
-
View (feed.php):
-
Exibe os posts de todos os usuários em um feed.
-
Itera sobre a lista de posts e renderiza cada um deles com informações como foto de perfil, nome do usuário e imagem do post.
-
-
Controller (get-all-posts.php):
-
Recupera todos os posts do banco de dados utilizando o DAO.
-
Passa os dados dos posts para a view.
-
-
DAO (posts-dao.php):
- Executa a query SQL para buscar todos os posts, juntamente com as informações do usuário que fez o post.
Na prática
Arquivo: Arquivo: view/feed.php
<?php
require_once __DIR__ . '/../dir-config.php';
require_once __DIR__ . '/../src/controllers/posts/get-all-posts.php';
?>
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Página Principal</title>
<link rel="stylesheet" href="<?= BASE_URL ?>public/css/home.css" />
</head>
<body class="home-page">
<?php include __DIR__ . '/components/side-bar.php'; ?>
<!-- Conteúdo Principal -->
<main class="container">
<!-- Seção de Feed de Posts -->
<section class="feed">
<?php if ($posts): ?>
<!-- Itera sobre cada post -->
<?php foreach ($posts as $post): ?>
<article class="post">
<!-- Cabeçalho do Post: Informações do Usuário -->
<header class="user-info">
<!-- Container da Foto de Perfil -->
<div class="avatar" aria-label="Foto do Usuário">
<?php
// Define a foto de perfil do usuário
$profilePhoto = !empty($post['profile_pic_url'])
? BASE_URL . 'uploads/' . htmlspecialchars($post['profile_pic_url'])
: BASE_URL . 'public/img/profile.svg';
?>
<img
src="<?= $profilePhoto; ?>"
alt="Foto de Perfil"
class="profile-picture"
/>
</div>
<!-- Link para o Perfil do Usuário -->
<a
href="<?= BASE_URL ?>view/profile.php?id=<?= htmlspecialchars($post['user_id'] ?? '') ?>"
class="username"
>
<?= htmlspecialchars($post['username'] ?? '') ?>
</a>
</header>
<!-- Container da Imagem do Post -->
<div class="image-placeholder" aria-label="Imagem do Post">
<?php if (!empty($post['photo_url'])): ?>
<!-- Exibe a imagem do post, se houver -->
<img
src="<?= BASE_URL . 'uploads/feed/' . htmlspecialchars($post['photo_url']) ?>"
alt="Imagem do post"
/>
<?php endif; ?>
</div>
</article>
<!-- Divisor entre os posts -->
<hr class="divider" />
<?php endforeach; ?>
<?php else: ?>
<p>0 postagens no seu feed</p>
<?php endif; ?>
</section>
</main>
</body>
</html>
-
Responsabilidade:
-
Exibe os posts de todos os usuários.
-
Itera sobre a lista de posts (
$posts
) e renderiza cada post com:-
Foto de perfil do usuário.
-
Nome do usuário (com link para o perfil).
-
Imagem do post.
-
-
-
Se não houver posts, exibe uma mensagem informando que não há postagens.
Arquivo: src/controllers/posts/get-all-posts.php
<?php
require __DIR__ . '/../../dao/posts-dao.php';
if (!isset($_SESSION['user_id'])) {
header("Location: " . BASE_URL . "view/login.php");
exit;
}
try {
$postsDao = new PostsDao();
$posts = $postsDao->getAllPosts();
} catch (PDOException $e) {
die("Erro ao buscar dados: " . $e->getMessage());
}
-
Responsabilidade:
-
Verifica se o usuário está logado (caso contrário, redireciona para a página de login).
-
Instancia o
PostsDao
e chama o métodogetAllPosts
para recuperar todos os posts. -
Em caso de erro, exibe uma mensagem de erro.
-
Arquivo: src/dao/posts-dao.php
public function getAllPosts() {
$sql = "SELECT p.id, p.photo_url, p.user_id, p.upload_date, p.description, u.username, u.profile_pic_url
FROM posts p
JOIN users u ON p.user_id = u.id
ORDER BY p.upload_date DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
-
Responsabilidade:
-
Executa uma query SQL para buscar todos os posts, juntamente com as informações do usuário que fez o post (nome e foto de perfil).
-
Ordena os posts pela data de upload (do mais recente para o mais antigo).
-
Retorna um array associativo com os dados dos posts.
-
Fluxo Completo
-
O usuário acessa a página de feed (
feed.php
). -
O controller (
get-all-posts.php
) é chamado para recuperar os posts do banco de dados. -
O controller utiliza o DAO (
posts-dao.php
) para buscar todos os posts. -
Os dados dos posts são passados para a view (
feed.php
). -
A view itera sobre os posts e renderiza cada um deles com as informações do usuário e a imagem do post.
-
Se não houver posts, a view exibe uma mensagem informando que não há postagens.
Atualização de perfil do usuário
Neste tópico, vamos entender como as camadas DAO, Controller e View se comunicam no contexto real de implementação de uma feature, exemplificado pelos arquivos "profile-update.php", "update-user.php" e "user-dao.php".
Planejamento Arquitetural do sistema
- View: Exibirá um formulário para edição dos dados do usuário, com os dados dele já setados como value dos inputs
- Controller: armazenará os resultados nos inputs no HTML em variáreis e passará para a camada de DAO por referência nos seus metodos
- DAO: executará queries SQL para persistir esses dados no banco.
Na prática
Arquivo: t06-crud-php/view/profile-update.php
<?php
require_once __DIR__ . "/../dir-config.php";
require_once __DIR__ . "/../src/controllers/users/update-user.php";
//echo $id;
?>
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Atualizar Perfil</title>
<link rel="stylesheet" href="<?= BASE_URL ?>public/css/profile-update.css">
</head>
<body>
<?php include __DIR__ . '/components/side-bar.php'; ?>
<div class="form-container">
<h1>Atualize seu perfil</h1>
<form method="POST" action="<?= BASE_URL ?>src/controllers/users/update-user.php?id=<?= $id ?>" class="form-group" enctype="multipart/form-data">
<div class="form-wrapper">
<div class="form-control">
<label for="username">Nome: </label>
<input type="text" name="username" value="<?= htmlspecialchars($user['username']) ?>" id="username"/>
</div>
<div class="form-control">
<label for="phone">Telefone: </label>
<input type="tel" name="phone" value="<?= htmlspecialchars($user['phone']) ?>" id="phone"/>
</div>
<div class="form-control">
<label for="email">Email: </label>
<input type="email" name="email" value="<?= htmlspecialchars($user['email']) ?>" id="email"/>
</div>
<div class="form-control">
<label for="bio">Bio: </label>
<input type="text" name="bio" value="<?= htmlspecialchars($user['bio'] || "") ?>" id="bio" />
</div>
</div>
<div class="form-control">
<label for="profile_pic">Foto de Perfil: </label>
<input type="file" name="profile_pic_url" id="profile_pic_url" accept="image/*" />
</div>
<div class="btn-wrapper">
<button class="btn" name="edit">Confirmar</button>
</div>
<div class="btn-wrapper">
<button class="btn btn-danger" name="delete" onclick="return confirm('Tem certeza que deseja excluir sua conta? Esta ação não pode ser desfeita.');">
Deletar Usuário
</button>
<button class="btn btn-danger" name="logout" onclick="return confirm('Tem certeza que deseja sair?');">
Sair
</button>
</div>
</form>
</div>
</body>
</html>
- Exibe um formulário HTML contendo os inputs preenchidos com os dados atuais do usuário (como nome, e-mail, telefone, bio e foto de perfil).
- Inclui o atributo
enctype="multipart/form-data"
para possibilitar o upload de arquivos (foto de perfil). - Define a ação do formulário para o controller de atualização, enviando os dados via método POST.
- Renderiza a barra lateral e demais componentes de layout para manter a consistência visual da aplicação.
Ao enviar o formulário, os dados e o arquivo (imagem) são enviados para o update-user.php.
Arquivo: t06-crud-php/src/controllers/users/update-user.php
<?php
session_start();
require_once __DIR__ . "/../../../dir-config.php";
require_once __DIR__ . "/../../dao/user-dao.php";
require_once __DIR__ . "/../../../database.php";
require_once __DIR__ . "/../../utils/upload-handler.php";
$userDao = new UserDao();
if (!isset($_GET["id"])) {
die("Parâmetro user_id não informado.");
}
$id = $_GET["id"];
$user = $userDao->getUserProfileById($id);
if (!$user) {
die("Usuário não encontrado.");
}
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['edit'])) {
$name = trim($_POST['username']);
$email = trim($_POST['email']);
$phone = trim($_POST['phone']);
$bio = trim($_POST['bio']);
if (isset($_FILES['profile_pic_url']) && $_FILES['profile_pic_url']['error'] === UPLOAD_ERR_OK) {
$uploadDir = __DIR__ . "/../../../uploads/avatars/";
$allowedTypes = ['jpg', 'jpeg', 'png', 'gif'];
$dbUpdateCallback = function ($photoUrl) use ($id, $userDao) {
return $userDao->updateProfilePic($photoUrl, $id);
};
$uploadResult = UploadHandler::handleUpload($_FILES['profile_pic_url'], $uploadDir, $allowedTypes, $dbUpdateCallback);
if (!$uploadResult['success']) {
die($uploadResult['error']);
}
}
try {
$userDao->updateUser($name, $email, $bio, $phone, $id);
$_SESSION['user_name'] = $name;
header("Location: " . BASE_URL . "view/profile.php?id=$id");
exit;
} catch (Exception $e) {
echo 'Erro: ' . $e->getMessage();
}
} else if(isset($_POST['delete'])) {
try {
$userDao->deleteUser($id);
session_destroy();
header("Location: " . BASE_URL . "view/login.php");
exit;
} catch (Exception $e) {
echo 'Erro: ' . $e->getMessage();
}
} else if (isset($_POST['logout'])) {
session_destroy();
header("Location: " . BASE_URL . "view/login.php");
exit;
}
}
O arquivo update-user.php
é responsável por:
- Iniciar a sessão com
session_start()
para garantir que os dados do usuário estejam disponíveis. - Incluir dependências importantes, como as classes do DAO, o arquivo de configuração (
dir-config.php
), e o upload-handler para processar o arquivo enviado.
Fluxo do Controller:
-
Verificação de parâmetros:
- O controller certifica-se de que o parâmetro
id
(identificador do usuário a ser atualizado) está definido. - Recupera os dados atuais do usuário usando o método
getUserProfileById
do UserDao.
- O controller certifica-se de que o parâmetro
-
Processamento da requisição:
- Caso o método HTTP seja POST, o controller identifica qual ação será executada:
-
Editar o perfil (
edit
):- Extrai os dados enviados pelo formulário (nome, e-mail, telefone, bio).
- Verifica se há um arquivo para upload (campo
profile_pic_url
), e em caso afirmativo, chama o upload-handler que:- Processa o arquivo (valida extensão, move para o diretório correto).
- Utiliza um callback para atualizar a foto de perfil no banco de dados (por meio do método
updateProfilePic
do DAO).
- Após o upload (se houver) e a validação dos dados, o controller chama o método
updateUser
, que executa a query SQL para atualizar o registro. - Atualiza variáveis de sessão (por exemplo, o nome do usuário) e redireciona para a página de perfil com um status de sucesso.
-
Deletar o usuário (
delete
):- Caso o usuário opte por excluir a conta, o controller chama o método
deleteUser
, destrói a sessão e redireciona para a tela de login.
- Caso o usuário opte por excluir a conta, o controller chama o método
-
Logout (
logout
):- Se o usuário optar por sair da aplicação, o controller encerra a sessão e redireciona para a página de login.
-
- Caso o método HTTP seja POST, o controller identifica qual ação será executada:
-
Tratamento de erros:
- Caso ocorra alguma exceção (por exemplo, erro de upload ou na execução de uma query SQL), o controller imprime a mensagem de erro para auxiliar na depuração.
Arquivo: t06-crud-php/src/dao/user-dao.php
public function getUserProfileById($id)
{
$query = "SELECT id, username, email, phone, bio, profile_pic_url, count_followers, count_following FROM users WHERE id = :id";
$stmt = $this->db->prepare($query);
$stmt->execute([':id' => $id]);
return $stmt->fetch();
}
- getUserProfileById($id) Recupera os dados id, username, email, phone, bio, profile_pic_url, count_followers, count_following referente ao perfil do usuário apartir do seu id.
public function updateProfilePic($profilePicUrl, $id): bool {
$query = "UPDATE users SET profile_pic_url = :profile_pic_url WHERE id = :id";
$stmt = $this->db->prepare($query);
return $stmt->execute([":profile_pic_url" => $profilePicUrl, ":id" => $id]);
}
- updateProfilePic($profilePicUrl, $id):
Método específico para atualização da foto de perfil, este método recebe a url da imagem processada e executa uma query para persistir o valor no campoprofile_pic_url
da tabela users.
public function updateUser($username, $email, $bio, $phone, $id): bool
{
$query = "UPDATE users SET username = :username, email = :email, bio = :bio, phone = :phone WHERE id = :id";
$stmt = $this->db->prepare($query);
$stmt->execute([":username" => $username,":email" => $email,":bio"=> $bio,":phone" => $phone,":id"=> $id ]);
return true;
}
- updateUser($username, $email, $bio, $phone, $id):
Atualiza os dados básicos do usuário (nome, e-mail, bio, telefone) através de uma query SQL.
public function deleteUser($id): bool
{
$query = "DELETE FROM users WHERE id = :id";
$stmt = $this->db->prepare($query);
$stmt->execute([':id' => $id]);
return true;
}
- deleteUser($id):
Remove o registro do usuário do banco, sendo ativado quando o usuário opta por excluir sua conta.
Exibir perfil do usuário
Planejamento Arquitetural do Sistema
-
View (profile.php):
-
Exibe as informações do perfil do usuário, como nome, foto de perfil, bio, número de seguidores, número de usuários seguidos e posts do usuário.
-
Renderiza os componentes de layout, como a barra lateral e o formulário de upload de foto (apenas para o próprio usuário).
-
-
Controller (profile-user.php):
-
Recupera os dados do perfil do usuário e seus posts utilizando o DAO.
-
Passa os dados para a view.
-
-
DAO (user-dao.php e posts-dao.php):
- Executa as queries SQL para buscar as informações do usuário e seus posts.
Na Prática
Arquivo: view/profile.php
<?php
require_once __DIR__ . "/../dir-config.php";
require_once __DIR__ . '/../src/controllers/users/profile-user.php';
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Perfil</title>
<link rel="stylesheet" href="<?= BASE_URL ?>public/css/profile.css" />
</head>
<body>
<?php include __DIR__ . '/components/side-bar.php'; ?>
<main class="profile-container">
<!-- Seção de informações do usuário -->
<section class="info-section">
<div class="photo-container">
<!-- Exibe a foto de perfil do usuário -->
<img
src="<?= $profilePhoto; ?>"
alt="Foto de Perfil"
class="profile-picture"
/>
<!-- Botão de edição de perfil (apenas para o próprio usuário) -->
<?php if ((int)$user_id === (int)$logged_in_user_id): ?>
<button class="btn-edit">
<a href="<?= BASE_URL ?>view/profile-update.php?id=<?= $user_id; ?>"
>Editar Perfil</a
>
</button>
<?php endif; ?>
</div>
<div class="user-info">
<!-- Exibe o nome do usuário -->
<h1 class="user-name"><?php echo $userName; ?></h1>
<div class="stats-container">
<!-- Exibe o número de seguidores e usuários seguidos -->
<span class="following"
><?= $following; ?>
seguindo</span
>
<span class="followers"
><?= $followers; ?>
seguidores</span
>
</div>
<!-- Formulário para seguir/deixar de seguir (apenas para outros usuários) -->
<?php if ((int)$user_id !== (int)$logged_in_user_id): ?>
<form method="POST">
<input
type="hidden"
name="action"
value="<?= $isFollowing ? 'unfollow' : 'follow' ?>"
/>
<button type="submit" class="btn-follow">
<?= $isFollowing ? 'Deixar de seguir' : 'Seguir' ?>
</button>
</form>
<?php endif; ?>
</div>
<!-- Botão de postagem (apenas para o próprio usuário) -->
<?php if ((int)$user_id === (int)$logged_in_user_id && !empty($userPosts)): ?>
<div class="upload-more-photos">
<div class="upload-container">
<form
action="<?= BASE_URL ?>src/controllers/posts/upload-feed-photo.php"
method="POST"
enctype="multipart/form-data"
>
<label for="photo-top"> Adicionar foto </label>
<input type="file" id="photo-top" name="photo" accept="image/*" />
<button type="submit" class="btn-upload">Enviar</button>
</form>
</div>
</div>
<?php endif; ?>
</section>
<!-- Seção da foto do feed -->
<section class="info-section">
<div class="feed-photo-container">
<?php if ($userPosts): ?>
<?php
if (isset($userPosts[0]['photo_url'])) {
$relativePath = BASE_URL . "uploads/feed/" . htmlspecialchars($userPosts[0]['photo_url']);
?>
<img
src="<?= $relativePath; ?>"
alt="Imagem do Feed"
class="feed-image"
/>
<?php } ?>
<?php endif; ?>
</div>
<!-- Formulário de upload de foto (apenas para o próprio usuário e se não houver foto) -->
<?php if (empty($userPosts) && (int)$user_id === (int)$logged_in_user_id): ?>
<div class="pai-do-upload-container">
<div class="upload-container">
<form
action="<?= BASE_URL ?>src/controllers/posts/upload-feed-photo.php"
method="POST"
enctype="multipart/form-data"
>
<label for="photo">
<img
src="<?= BASE_URL ?>public/img/add-photo.svg"
class="icon"
/>
<br />
Adicionar foto
</label>
<input type="file" id="photo" name="photo" accept="image/*" />
<button type="submit" class="btn-upload">Enviar</button>
</form>
</div>
</div>
<?php endif; ?>
</section>
</main>
</body>
</html>
-
Responsabilidade:
-
Exibe as informações do perfil do usuário, como nome, foto de perfil, bio, número de seguidores e usuários seguidos.
-
Renderiza os posts do usuário (se houver).
-
Exibe um formulário de upload de foto apenas para o próprio usuário.
-
Inclui a barra lateral e outros componentes de layout.
-
Arquivo: src/controllers/users/profile-user.php
<?php
require_once __DIR__ . '/../../../database.php';
require_once __DIR__ . '/../../dao/follow-dao.php';
require_once __DIR__ . '/../../dao/user-dao.php';
require_once __DIR__ . '/../../../dir-config.php';
require_once __DIR__ . '/../../dao/posts-dao.php';
require_once __DIR__ . "/../../utils/follow-handler.php";
session_start();
if (!isset($_SESSION['user_id'])) {
header("Location: " . BASE_URL . "view/login.php");
exit;
}
$user_id = isset($_GET['id']) ? intval($_GET['id']) : $_SESSION['user_id'];
$logged_in_user_id = $_SESSION['user_id'];
$userDao = new UserDao();
$user = $userDao->getUserProfileById($user_id);
if (!$user) {
die("<p>Usuário não encontrado.</p>");
}
$userName = $user['username'];
$profilePhoto = !empty($user['profile_pic_url'])
? BASE_URL . 'uploads/avatars/' . htmlspecialchars($user['profile_pic_url'])
: BASE_URL . 'public/img/profile.svg';
$followDao = new FollowDao();
$isFollowing = $followDao->isFollowing($user_id, $logged_in_user_id);
$followers = $followDao->getFollowers($user_id);
$following = $followDao->getFollowing($user_id);
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
$followHandler = new FollowHandler();
$followHandler->handleFollow($user_id, $logged_in_user_id, $isFollowing, $action);
}
$postsDao = new PostsDao();
$userPosts = $postsDao->getPostsById($user_id);
$userProfileData = [
'username' => htmlspecialchars($user['username']),
'user_id' => $user_id,
'logged_in_user_id' => $logged_in_user_id,
'profile_Pic_Url' => htmlspecialchars($profilePhoto ?? ''),
'is_Following' => $isFollowing,
'followers' => $followers,
'following' => $following,
'bio' => htmlspecialchars($user['bio'] ?? ''),
'photos' => $userPosts,
];
?>
-
Responsabilidade:
-
Inicia a sessão e verifica se o usuário está logado.
-
Recupera o ID do usuário a partir da URL ou da sessão.
-
Busca as informações do perfil do usuário utilizando o UserDao.
-
Verifica se o usuário logado está seguindo o perfil exibido.
-
Busca o número de seguidores e usuários seguidos.
-
Busca os posts do usuário utilizando o PostsDao.
-
Passa os dados para a view.
-
Arquivo: src/dao/user-dao.php
public function getUserProfileById($id) {
$query = "SELECT id, username, email, phone, bio, profile_pic_url, count_followers, count_following FROM users WHERE id = :id";
$stmt = $this->db->prepare($query);
$stmt->execute([':id' => $id]);
return $stmt->fetch();
}
-
Responsabilidade:
-
Executa uma query SQL para buscar as informações do perfil do usuário (nome, e-mail, telefone, bio, foto de perfil, número de seguidores e usuários seguidos).
-
Retorna um array associativo com os dados do usuário.
-
Arquivo: src/dao/posts-dao.php
public function getPostsById($userId) {
$sql = "SELECT p.id, p.photo_url, p.upload_date, p.description, u.username, u.profile_pic_url
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.user_id = :userId
ORDER BY p.upload_date DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute([':userId' => $userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
-
Responsabilidade:
-
Executa uma query SQL para buscar os posts do usuário, juntamente com as informações do usuário que fez o post.
-
Ordena os posts pela data de upload (do mais recente para o mais antigo).
-
Retorna um array associativo com os dados dos posts.
-
Fluxo Completo
-
O usuário acessa a página de perfil (
profile.php
). -
O controller (
profile-user.php
) é chamado para recuperar os dados do perfil e os posts do usuário. -
O controller utiliza o
UserDao
para buscar as informações do perfil e oPostsDao
para buscar os posts. -
Os dados são passados para a view (
profile.php
). -
A view renderiza as informações do perfil e os posts do usuário
Exibir no perfil do usuário suas respectivas postagens
Planejamento Arquitetural do Sistema
-
View (profile.php):
-
Exibe as postagens do usuário em uma seção específica do perfil.
-
Renderiza cada post com a imagem, data de upload e descrição (se houver).
-
-
Controller (profile-user.php):
-
Recupera as postagens do usuário utilizando o DAO.
-
Passa os dados das postagens para a view.
-
-
DAO (posts-dao.php):
- Executa a query SQL para buscar as postagens do usuário, juntamente com as informações do usuário que fez o post.
Na Prática
Arquivo: view/profile.php
<!-- Seção de Feed de Posts -->
<section class="info-section">
<?php if ($userPosts): ?>
<!-- Itera sobre cada post do usuário -->
<?php foreach ($userPosts as $post): ?>
<div class="feed-photo-container">
<?php if (!empty($post['photo_url'])): ?>
<!-- Exibe a imagem do post -->
<img
src="<?= BASE_URL . 'uploads/feed/' . htmlspecialchars($post['photo_url']) ?>"
alt="Imagem do post"
class="feed-image"
/>
<?php endif; ?>
<!-- Exibe a descrição do post (se houver) -->
<?php if (!empty($post['description'])): ?>
<p class="post-description">
<?= htmlspecialchars($post['description']) ?>
</p>
<?php endif; ?>
<!-- Exibe a data de upload do post -->
<p class="post-date">
Postado em:
<?= date('d/m/Y H:i', strtotime($post['upload_date'])) ?>
</p>
</div>
<?php endforeach; ?>
<?php else: ?>
<!-- Mensagem caso o usuário não tenha postagens -->
<p>Nenhuma postagem encontrada.</p>
<?php endif; ?>
</section>
-
Responsabilidade:
-
Exibe as postagens do usuário em uma seção específica do perfil.
-
Itera sobre a lista de postagens ($userPosts) e renderiza cada post com:
-
Imagem do post.
-
Descrição do post (se houver).
-
Data de upload do post.
-
-
-
Se não houver postagens, exibe uma mensagem informando que não há postagens.
Arquivo: src/controllers/users/profile-user.php
<?php
// ... (código anterior para buscar informações do perfil)
$postsDao = new PostsDao();
$userPosts = $postsDao->getPostsById($user_id);
$userProfileData = [
// ... (outros dados do perfil)
'photos' => $userPosts, // Passa as postagens para a view
];
?>
-
Responsabilidade:
-
Após buscar as informações do perfil, o controller utiliza o PostsDao para buscar as postagens do usuário.
-
Passa os dados das postagens para a view.
-
Arquivo: src/dao/posts-dao.php
public function getPostsById($userId) {
$sql = "SELECT p.id, p.photo_url, p.upload_date, p.description, u.username, u.profile_pic_url
FROM posts p
JOIN users u ON p.user_id = u.id
WHERE p.user_id = :userId
ORDER BY p.upload_date DESC";
$stmt = $this->db->prepare($sql);
$stmt->execute([':userId' => $userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
-
Responsabilidade:
-
Executa uma query SQL para buscar as postagens do usuário, juntamente com as informações do usuário que fez o post.
-
Ordena os posts pela data de upload (do mais recente para o mais antigo).
-
Retorna um array associativo com os dados dos posts.
-
Fluxo Completo
-
O usuário acessa a página de perfil (
profile.php
). -
O controller (
profile-user.php
) é chamado para recuperar os dados do perfil e as postagens do usuário.
3.O controller utiliza o PostsDao
para buscar as postagens do usuário.
-
Os dados das postagens são passados para a view (
profile.php
). -
A view renderiza as postagens do usuário em uma seção específica do perfil.