Desenvolvimento Web Completo 2020 - 20 cursos + 20 projetos

Seção 10 - Iniciando com servidor Apache, PHP e MySQL

Aula ## - TITULO
DESCRICAO

Seção 11 - PHP 7

Aula 298 a 300 - O que é o PHP / Embutindo blocos PHP em páginas HTML / Habilitando e testando tags curtas (short_open_tag)
Introdução a linguagem PHP.
Os modos de injeção de código PHP nas páginas são:
Tag padrão: <?php código aqui ?> Habilitada
Tag de impressão: <?= código aqui ?> Habilitada
Tag curta: <? código aqui ?> Desabilitada
Asp tag: <% código aqui %> Descontinuada na versão 7
A tag de impressão apenas faz a projeção do conteudo na tela como ao utilizar o comando echo.
A tag curta vem desabilitada por padrão no arquivo php.ini. Podemos habilitá-la editanddo esse arquivo e alterando a propriedade short_open_tag=Off.
Aula 301 a 303 - Saída de dados com echo() e print() / Funcionamento do PHP um pouco mais a fundo (páginas estáticas x dinâmicas) / Comentários
Os comandos echo e print retornam valores no ambiente de execução.
A diferença entre eles é que o echo é um construtor da função já o print é uma função. Atualmente podemos executar a função print() sem o '()' mas devido a sua característica ele tem um retorno. Quando executamos um echo na função print além de imprimir o valor do parametro também recebemos um retorno true '1'.
É importante entender que o PHP fucniona no servidor e o acesso dos usuários é via navegador e espera apanas como resposta códigos que podem ser processados pelo navegador (html, css, js). Quando o servidor recebe um REQUEST de um arquivo PHP, o servidor irá processar o script PHP e inseri-lo em um arquivo html antes de retornar a RESPONSE.
Podemos fazer comentários de linhas únicas com os caracteres // ou #.
Para comsntários de várias linhas inserimos o comentário entre /* e */.
Aula 304 a 306 - Variáveis - Introdução / Variáveis - Prática (string, int, float e boolean) / Alterando valores de variáveis
Introdução a variáveis: tipos, definição de tipo, regras para declaração, case sensitive.
Criação de variáveis do tipo string, int, float e boolean.
Alteração de valores e tipos das variáveis (tipagem fraca).
Aula 307 a 308 - Concatenação / Variáveis constantes
Concatenação com '.' e com "" passando as variáveis internamente.
Criando constantes com a função define('nome_constante', valor). O padrão para constantes é que seu nome seja em maiúsculas. A atribuição do seu valor é obrigatória.
Aula 309 a 313 - if/else parte 1 - Introdução / Operadores de comparação (condicionais) / if/else parte 2 - Praticando com operadores de comparação / Operadores lógicos / if/else parte 3 - praticando com operadores lógicos
Introdução sobre a condicional if/else.
Operadores de comparação (==, ===, != ou <>, !==, <, >, <=, >=).
Utilizando if/else com operadores de comparação.
Operadores lógicos (AND ou &&, OR ou ||, XOR e !).
Precedencia de operações com ().
Aula 314 a 316 - if/else parte 4 - praticando um pouco mais / if/else parte 4 - Condições encadeadas / Operador ternário
Criando uma aplicação para o if/else e operadores.
Criando uma aplicação para o if/else encadeado e operadores.
Operador ternário.
Convertendo as condicionais if/else para operador ternário.
Aula 318 a 322 - Switch / Switch na prática / Casting de tipos com (int),(bool),(float) e (string) / Operadores aritméticos / Praticando com operadores aritméticos
Utilização do switch/case.
Ateração dos tipos de variáveis. Podemos alterar os tipos das variáveis utilizando a notação (tipo) antes do valor da variável:
$valor = 15.35;
$valor2 = (int) $valor; //converte float para inteiro
$valor3 = (string) $valor; //converte float para string
$valor4 = (bool) $valor; //converte float para boolean (retorna 1 'true' poi é uma valor válido)

Operadores aritméticos (+ , - , * , / , %)
Aula 323 a 325 - Operações aritméticas na atribuição de valores / Operadores de incremento/decremento / Praticando com operadores de incremento/decremento
Operações de atribuição com +=, -=, *= e /=.
Operadores de incremento e decremento ++ e --.
Pré incremento e pós incremento.
Aula 326 - Funções Introdução / Funções Prática / Atividades para fixação do conteúdo
Funções são utilizadas para encapsular um bloco de códigos com um objetivo definido.
Sintaxe para criação de funções e utilização de parametros.
Funções que não tem return são chamadas funções tipo void.
Aula 329 a 330 - Funções nativas para manipular strings / Funções nativas para tarefas matemáticas
Principais funções para strings:
strtolower($texto): Transforma todos os caracteres da string em minúsculos.
strtoupper($texto): Transforma todos os caracteres da string em maiúsculos.
ucfirst($texto): Transforma o primeiro caractere da string em maiúsculo.
strlen($texto): Conta a quantidade de caracteres da string.
str_replace(procura_por, susbtitui_por, $texto): Substitui uma cadeia de caracteres por outra em uma string.
substr($texto, posicao_inicial, qtd_caracteres): Retorna parte de uma string.

Principais funções matemáticas:
ceil($numero): Arredonda o valor para cima.
floor($numero): Arredonda o valor para baixo.
round($numero): Arredonda o valor com base nas casas decimais.
rand(): Gera um inteiro aleatório.
sqrt($numero): Retorna a raiz quadrada.
Aula 331 - Funções nativas para manipular datas
Principais funções para datas:
date(formato): Recupera a data atual.
date_default_timezone_get(timezone): Recuperar a timezone default da aplicação.
date_default_timezone_set(timezone): Atualizar a timezone default da aplicação.
strtotime(data): Transformar datas textuais em segundos.
A função date_default_timezone_set() permite ajustar o timezone para nossa região. Para isso devemos definir:
date_default_timezone_set('America/Sao_Paulo')
A função strtotime() permite converter uma data (em formato internacional YYYY-MM-DD) para o valor de timestamp o que permite o cálculo com datas:
$dataAtual = date('Y-m-d');
$aniversario = '2022-11-29';
$calcAniversario = strtotime($aniversario) - strtotime($dataAtual);
$diasAniversario = ($calcAniversario / (60 * 60 * 24));
Aula 332 a 334 - Array básico - Introdução / Array básico - Prática / Array multidimensional
Conhecendo arrays. Arrays sequenciais são aqueles que tem indices numéricos e sequenciais. Podemos iniciar arrays utilizando o método array() ou apenas incluindo os valores entre [ ].
Arrays associativos são os que tem um conjunto chave/valor que não precisa ser sequencial. Incluimos os valores nesse tipo de array passando o a chave seguida de '=>' e o valor:
array('chave'=>'valor').
Arrays multidimensionais são arrays de arrays. Criamos o array principal e atribuimos um novo array como valor.
array_principal = [];
array_principal['array_secundário'] = [];
Acessamos os dados utilizando as chaves ddos dois arrays:
array_principal['array_secundário'][chave_array_secundário];
Aula 335 a 336 - Métodos de pesquisa / False, null e empty
Podemos pesquisar valores em arrays com os métodos in_array('valor', $array) e array_search('valor', $array). A diferença entre eles é que o primeiro retorna um booleano (true ou false) e o segundo retorna o índice em que o valor se encontra ou null caso não exista.
Atenção para a diferença de retornos false e null Os valores null, '' (vazio) e false tem tratamentos diferentes no PHP. Podemos verificar esses valores com is_null e empty().
Quando testamos um valor com o método is_null() apenas os retornos null serão válidos.
Já o teste empty() retorna true para todos os casos, assim como a negação !.
Já para o teste isset() apenas o valor null retorna false, os demais retornam true
Aula 337 - Funções nativas para manipular arrays
Principais funções para manipular arrays:
is_array(array): Verifica se o parametro é um array.
array_keys(array): Retorna todas as chaves de um array.
sort(array): Ordena um array e reajusta seus índices. Retorna true/false e modifica o array (método destrutivo).
asort(array): Ordena um array preservando os índices. Retorna true/false e modifica o array (método destrutivo) porém não altera os índices.
count(array): Conta a quantidade de elementos de um array.
array_merge(array1, array2,...arrayn): Funde um ou mais arrays.
explode('delimitador', string): Divide uma string baseada em um delimitador.
implode(array): Junta elementos de um array em uma string.
Aula 338 a 341 - Loops parte 1 - introdução / Loops parte 2 - While / Loops parte 3 - Do while / Loops parte 4 - For
Conceituando os loops.
while(): Utilização do loop while, break e continue.
do/while(): Utilização do loop do/while.
for(): Utilização do loop for.
Aula 342 - Praticando um pouco - Percorrendo arrays com While, Do while e For
Extraindo dados de arrays utilizando os loops while, do while e for.
Aqui criei uma rotina para percorrer um array multidimensional com chaves associativas. Como as chaves não permitem que utilizemos valores numéricos sequenciais, utilizei o método array_keys() para recuperar os nomes das chaves e passa-las como índice para recuperar o valor correto. Na aula foi utilizado o nome das chaves estáticos (declarados manualmente).
Aula 343 a 345 - Loops parte 5 - Foreach / Praticando um pouco - Foreach em Arrays associativos e Foreach encadeados / Atividades para fixação do conteúdo
O loop foreach() é uma função específica para percorrer valores em arrays e objetos.
Podemos utilizar o foreach apenas para recuperar os valores dos campos ou para recuperar o indice e valor.
Utilizando foreach encadeado para recuperação de valores em arrays multidimensionais.
Aula 348 - App Help Desk - Iniciando o projeto / Formulários (Desvendando os métodos GET e POST) / Autenticando usuário / Protegendo páginas restritas com SESSION
Os arquivos para esse projeto já estavam preparados. São 4 arquivos em bootstrap.
Criação do arquivo PHP para processamento do login, inclusão da action no <form> e explicação sobre os métodos GET e POST e as super variáveis $_GET e $_POST. Recuperando dados através do array $_POST. Lembrando que podemos recuperar os dados através do array da supervariável $_POST['chave_desejada'] ou através do método filter_input(INPUT_POST, 'chave_desejada').
Para a autenticação de usuários como não estamos utilizando banco de dados foi criado um array de usuários com dados de email e senha. Para validar os dados utilizamos o método foreach() para percorrer o array de usuários e fizemos a verificação através de um condicional para verificar se os valores enviados em POST são os mesmos dos valores do array.
Com a validação de usuário, em caso de erro fizemos o redirecionamento através da função header('Location:') porém passando a página index.php?login=error, ou seja, enviando um parametro tipo GET de erro no login para ser tratado na página.
Recebemos esse GET na página e imprimimos uma mensagem de erro após os campos de login. Lembrando de utilizar o teste de isset() para a variável $_GET.
Agora foi feito o início da sessão com session_start() e criada a lógica para validar a sessão através da variável $_SESSION. Fiz a sessão utilizando o nome de usuário, mas pela lógica da aula foi utilizado como valor para a chave da sessão SIM e NAO. O interessante aqui foi a utilização do GET para enviar uma mensagem de erro diferente para o caso de tentativa de acesso sem login. Com isso temos duas mensagens de erro diferentes sendo tratadas (uma para login inválido e outra para tentativa de acesso sem login).
Aula 352 - Incorporando scripts com include, include_once, require e require_once / Refactoring do projeto com require_once / Navegação entre páginas / Encerrando a sessão (logoff)
Como utilizar o include e o require. As diferenças entre eles, relembrando é em relação aos erros. Enquanto o primeiro retorna apenas warning o segundo retorna fatal error. As opções com once indicam que o arquivo só deve ser inserido uma vez.
Com essas informações passamos os códigos de sessão para um arquivo separado e inserimos em cada página apenas o require_once() com o arquivo de lógica da sessão.
Agora foi incluido o direcionamento para a página home em caso de login com sucesso (início de sessão), incluidos links nas tags <img> e substituidas as tags <button> por tags <a>.
Podemos encerrar a sessão de dois modos (na verdade tres). Podemos utilizar o método unset() na variável $_SESSION['chave'] selecionando a chave que queremos limpar. Podemos também utilizar o método session_destroy() que limpa todas as variáveis de sessão. Entretanto esses método somente tem efeito quando redirecionameos a página pois se permanecermos na sessão a variável continua na memória.
O terceiro método é reatribuir um valor para a chave de sessão (no nosso caso passando o valor 'NAO' que foi definido no método de autenticação).
Aula 356 a 357 - Registrando chamados / Consultando chamados
Criamos um arquivo para processar os registros de inclusão de chamados que irá receber os dados através do método POST.
Incluimos o método, a action e os names dos campos e passamos a recuperar os valores através de suas chaves no array $_POST.
Uma sugestão para estudo é tentar fazer a criação dessa string utilizando a função implode().
Para gravar os dados nesse momento vamos gerar um arquivo tipo txt. Para isso utilizaremos o método fopen('nome_do_arquivo', 'opção_de_manipulação_do_arquivo').
A opção_de_manipulação_do_arquivo recebe um caractere que define de que modo o PHP vai tratar o arquivo ('r': somente leitura, 'w': escrita, 'a': escrita no final do arquivo).
Com isso criamos uma string com as tres variáveis recebidas pelo POST separadas por um '#' para facilitar o tratamento posterior. Como utilizaremos esse sinal como separador incluimos um método str_replace() em cada valor recebido para substituir o '#' caso ele seja digitado no texto, evitando que isso comprometa a separação da string.
A função fopen() deve ser atribuida a uma variável que será passada como parametro para a função fwrite(variável_que_recebe_fopen, texto_a_ser_gravado). Como o parametro determinado no fopen() foi o 'a' o texto será gravado no final do arquivo.
Por fim executamos a função fclose(variável_que_recebe_fopen) para fechar o arquivo.
Por fim para que o PHP envie um sinal de quebra de linha para cada nova mensagem utilizamos uma constante do PHP. O PHP_EOL informa o fim de linha de acordo com o SO em execução.
Para a exibição da consulta de chamados, vamos manipular o arquivo gravado.
Para isso abrimos o arquivo com a função fopen() mas dessa vez passando o parametro 'r' (apenas leitura).
Feito isso fazemos um loop com while() para percorrer os dados do arquivo verificando se o arquivo chegou ao fim com a função feof(). Essa função retorna 'true' caso seja o fim do arquivo assim devemos fazer o teste invertido (!) ou seja, enquanto não for o fim do arquivo repetimos o laço.
Feito o laço, recuperamos cada linha com a função fgets(). O retorno será uma string. Como indicamos na construção do arquivo o caractere '#' como separador, utilizamos a função explode() para criar um array com os dados de cada item separados.
Feito isso basta incluir cada item do array no respectivo campo do formulário html.
Fiz um teste no primeiro valor do array para verificar se ele está vazio e nesse caso ignorar o registro para não imprimir a ultima linha do arquivo de texto.
Encerrado o loop devemos fechar o arquivo com a função fclose().
Na aula o valor recuperado pelo fget() é atribuido a um array que é tratado no corpo do html com o foreach() e após isso o explode().
$arquivo = fopen('arquivo.txt', 'r');
while (!feof($arquivo)) {
$registro = fgets($arquivo);
$chamados[] = $registro;
}
fclose($arquivo);

No corpo do html:
foreach($chamados as $chamado){
$chamado_dados = explode('#', $chamado);
...
$chamado_dados[0];
$chamado_dados[1];
$chamado_dados[2];
}
Aula 358 - App Help Desk - Aplicando controle de perfil de usuários
Aqui incluimos mais algumas variáveis para filtrar o tipo de usuário de forma a limitar o acesso às informações (administradores e usuários). Para isso tive que alterar para o formato proposto em aula, pois do modo que eu tinha feito não seria possível filtrar os dados.
Criamos variáveis de sessão para verificar qual o usuário está logado e se ele é do grupo Administradores ou Usuários. Essas informações são armazenadas no arquivo txt junto com cada registro incluido. Ajustado isso no loop foreach() fazemos o teste para verificar se o usuário da sessão é administrador e caso negativo recuperamos apenas os dados em que o id de usuário seja igual ao id da sessão.
Como nesse exemplo não estamos utilizando DB tudo é feito manipulando os dados em arrays.
Aula 359 a 360 - Atividade para fixação do conteudo / App Help Desk - Segurança no back-end de aplicações web
Fizemos uma alteração no filtro de usuários, passando ele para a criação do array que é demonstrado na página.
Em seguida para segurança alteramos a estrutura dos arquivos para remover os arquivos de dados e da rotina de login da pasta public do sevidor.
Esse é um início para os padrões do tipo MVC.

Seção 12 - PHP 7 e Orientação a Objetos

Aula 363 - Pilar da Abstração
Na Orientação a Objetos criamos Entidades (classes) que possuem Identidade (objeto). Essas entidades possuem Características (atributos) e Ações (métodos).
Criação de uma classe, com atributos e métodos. Instanciar a classe em um objeto. Utilização do operador de ajuste de contexto $this para referenciar aos atributos da classe.
Aula 364 - Getters e Setters
A utilização de métodos getters e setters para manipular os atributos de objetos é uma conveção da utilização do paradigma, mas não é a única forma de faze-lo.
Criando os métodos getters e setters para os atributos do exemplo anterior.
Aula 365 - Getters e Setters mágicos (overloading de atributos e métodos) / Chamando métodos internamente
Podemos criar métodos getters e setters dinâmicos para não ter que faze-lo para cada atributo da classe. Para isso criamos os métodos com a sintaxe __get() e __set() passando por parametro o nome do atributo a ser manipulado e, no caso do setter o valor a ser atribuido.
É possível executar métodos do próprio objeto dentro e outros métodos. É o que foi feito no exemplo com o método __get() no método resumirCadFunc().
Aula 367 - Método construtor e destrutor (construct e destruct)
O método __construct() é executado quando o objeto é instanciado e pode receber parametros, que nesse caso se tornam obrigatórios para a inicialização.
Já o método __destruct() é executado quando removemos um objeto da memória, ou seja, quando por exemplo executamos um unset() no objeto ou quando a execução do script é finalizada.
Aula 368 a 369 - OO Pilar da Herança / Pilar do Polimorfismo
Criando objetos pai e filho e aplicando a herança com extends. Utilizando o método __construct para atribuir valores aos atributos herdados.
O Polimorfismo é o paradigma que permite a mudança de métodos da classe pai quando necessário na classe filha especializando o método.
Aula 370 - Pilar do Encapsulamento parte 1
Encapsulamento é uma forma de controlar a visibilidade de atributos e métodos de acordo com a regra de negócio criando uma camada de segurança para a aplicação.
Os operadores de visibilidade são public, private e protected.
Quando alteramos a visibilidade dos atributos ele ficam inacessíveis fora da classe, sendo necessário que utilizemos métodos da classe para acessa-los como os métodos setters e getters.
Quando utilizamos os métodos 'mágicos' __get() e __set() podemos acessar os atributos protected e private como se fossem public. Isso não é possível quando criamos os setters e getters do modo tradicional.
Da mesma forma que com os atributos os métodos com visibilidade private e protected também dependem de um método público para serem executados fora do contexto da classe. Para isso devemos criar métodos que intermediam a execução conforme a regra de negócio.
Na herança entre classes, todos os métodos e atributos public e protected são transmitidos ao objeto filho. Já os private aparecem no objeto filho mas apenas como contexto do objeto pai.
Quando utilizamos os métodos __set() e __get() na classe pai a herança funciona mesmo com elementos em private
Os atributos herdados do tipo public podem ser alterados diretamente pelo objeto. Já os protected só são alterados através de métodos setters e getters. Já os atributos do tipo private quando são chamados através de um método set() cria um novo atributo e não aproveita o atributo private da classe pai.
Já com relação aos métodos, os de visibilidade public são passados para o filho, assim como o protected, sendo que esse último fica restrito ao escopo da classe. Nesse caso quando utilizamos a função PHP get_class_methods() fora da classe visualizamos apenas os métodos public. Já quando executamos essa mesma função no __construct() da classe filha temos a visibilidade dos métodos protected.
Quando executamos um método em uma classe filha, que referencia a outro método de uma classe pai, a execução desse método (quando ele utiliza métodos private ou protected) seguirá o contexto do objeto pai e não será alterado para o contexto local, mesmo que tenhamos um método de mesmo nome na classe filha no caso de private. Caso tenhamos realizado o polimorfismo de um método protected ele passará a executar o novo método implementado.
Aula 372 - Atributos e métodos estáticos
Métodos e atributos estáticos podem ser acessados fora da classe mesmo que esta não tenha sido instanciada. Declaramos os atributos e métodos estáticos através do operador static após o operador de visibilidade.
Para acessar esses atributos e métodos devemos digitar o nome da classe seguida por :: e o nome do que se deseja acessar.
Os atributos estáticos não aceitam o operador '->' para ser acessado e os métodos estáticos não aceitam o operador de contexto $this.
Aula 373 - Interfaces
Interfaces atuam como um contrato entre ela e a classe que a implementa indicando quais os métodos que devem ser implementados por elas.
As interfaces são declaradas com o operador interface e são implementadas nas classes através do operador implements.
As interfaces não implementam os métodos elas apenas definem a assinatura do método. A implementação das instruções dos métodos devem ser feitas diretamente nas classes.
Todos os métodos nomeados na interface devem ser public.
Podemos implementar mais de uma interface em uma classe bastando separar cada interface por ','.
Por fim podemos extender uma interface para outra interface e passar os métodos por herança.
Aula 374 - Namespaces parte 1 - Utilizando namespaces para Classes e Interfaces / parte 2 - Importando e apelidando namespaces (Use e Aliasing)
Namespaces possibilitam o agrupamente de classes, interfaces funções e constantes visando evitar o conflito entre seus nomes.
Podemos utilizar mais de um namespace no mesmo arquivo, embora isso não seja comum. Para isso basta definir o operador namespace onde começa o primeiro trecho e definir novamente um nome diferente no segundo bloco. Desse modo podemos utilizar nomes iguais para as entidades.
namespace A;

class Cliente
{
public $nome = 'Jean';
public function __get($attr)
{
return $this->$attr;
}
}

namespace B;

class Cliente
{
public $nome = 'Andreza';
public function __get($attr)
{
return $this->$attr;
}
}

Note que temos duas classes com o mesmo nome no script. Para acessar um ou outro devemos utilizar '\nome_namespace\elemento_a_executar':
$a = new \A\Cliente();
Se não fizermos essa definição será considerado o namespace em que o elemento for executado.
Para utilizar bibliotecas ou arquivos com namespaces devemos primeiramente importar o arquivo com include ou require. Feito isso utilizamos o operador use para definir qual namespace iremos utilizar. Nesse caso entretanto não podemos utilizar classes com nomes iguais. Para isso podemos criar alias para cada namespace diferente. Desse modo passamos a referenciar as classes através do apelido e não mais do nome da classe.
require_once './bibliotecas/Lib1/Lib1.php';
require_once './bibliotecas/Lib2/Lib2.php';

use \Lib1\Cliente as A;
use \Lib2\Cliente as B;

$c = new A();
$d = new B();

Aula 376 - Tratamento de erros - Try, Catch, Finally e Throw
O tratamento de erros com try/catch permitem que interceptemos pontos de erros potenciais e com isso façamos o tratamento adequado caso eles ocorram.
Para utilizar esse método devemos utilizar o operador try seguido da lógica que desejamos verificar. Caso a lógica seja executada comm sucesso podemos seguir com a execução da aplicação. Em caso de erro executamos a função catch(error $e) que retornará a mensagem do erro obtida na variável definida ($e).
Em ambos os casos podemos utilizar o operador finally para executar uma rotina após o processamento com sucesso ou não. O erro pode além de ser informado ser armazenado para posterior analise.
Com o operador throw podemos personalisar mensagens de erro ou exceções. Para isso dentro da rotina do try podemos capturar uma situação de erro e instanciar as classes Error() ou Exception() encaminhando como parametro a mensagem de erro desejada. Dessa forma podemos criar mais que um catch() sendo um para erros e outro para exceções:
try {
if (!file_exists('require_arquivo_a.php')) {
// throw new Exception(ou Error)('O arquivo deveria estar disponivel em ' . date('d/m/Y') . ' mas não está disponível.');
}
} catch (Error $e) {
echo ' <h3>*** Catch Error ***</h3> ';
echo ' <p style="color: red">' . $e . '</p> ';
} catch (Exception $e) {
echo ' <h3>*** Catch Execption ***</h3> ';
echo ' <p style="color: red">' . $e . '</p> ';
} finally {
echo ' <h3>*** Finally ***</h3> ';
}

Aula 377 - Tratamento de erros - Exceções customizadas
Como visto anteriormente o PHP possui métodos Error e Exception. É possível personalizar as mensagens de erro e exceção criando classes personalizadas que extendem as classes padrão do PHP para que as mensagens sejam exibidas de forma mais customizada.
class MinhaExceptionCustomizada extends Exception
{
private $erro = '';

public function __construct($erro)
{
$this->erro = $erro;
}

public function exibirMensagemErroCustomizada()
{
echo '<div style="border:solid 1px black; padding:15px; background: red; text-align:center">';
echo $this->erro;
echo '</div>';
}
}

try {
throw new MinhaExceptionCustomizada('Esse é um erro de teste');
} catch (MinhaExceptionCustomizada $e) {
echo $e->exibirMensagemErroCustomizada();
}

Conforme podemos observar criamos uma classe quew extende a classe Exception e que possui um método que cria uma <div> para mostrar a mensagem de erro enviada por parametro pelo operador throw.
Devemos evitar customizar as mensagens de Erro pois geralmente são mensagens padrão do PHP. Já as Exceções podem ser customizadas de modo a facilitar a experiencia do usuário.
Outra coisa a se observar é que em extensões de terceiros é possível que hajam classes com métodos específicos para o tratamento de erros e exceções que devem ser verificadas na documentação.
Aula 378 a 381 - App Send Mail - Introdução / Iniciando o projeto / Enviando dados do front-end para o back-end via método POST / Criando e instanciando a classe Mensagem
Iniciando projeto de envio e emails. Criação da página inicial com formulário em Bootstrap.
Direcionamento da action do <form> e do método POST. Criação das propriedades name nas tags do formulário.
A seguir criamos uma classe para receber os dados da mensagem. Nessa classe criamos os atributos conforme os campos do formulário todos private. Aqui temos duas opções. A primeira é criar um método __constructor() passando os atributos por parametro. A segunda que foi a utilizada é criando os métodos __set() e __get(). Foi criado um outro método que valida a mensagem verificando se algum dos campos está vazio através da função empty().
Após isso instaciamos a classe, atribuimos os valores recebidos via POST com o método __set() e executamos o método para validar os campos do formulário.
Aula 382 a 387 - App Send Mail - Adicionando a biblioteca PHPMailer ao projeto / Configurando o PHPMailer e enviando e-mails / Enviando e-mails com base nos parametros do front-end / Melhorando o feedback visual / De olho na segurança do back-end
Para instalação do PHPMailer buscamos seu pacote no site Packagist. A instalação preferencial é através do composer, entretanto como esse ainda não é o escopo do curso será feita a instalação manual. Para isso acessamos o repositório da biblioteca no GitHub/PHPMailer e fizemos o download da versão desejada (no caso baixei a versão 6.6 para mater próximo a versão da aula).
Com a biblioteca incluida no diretório do projeto importamos seus arquivos através do require.
Para testar a biblioteca importamos os dados de exemplo de utilização que selecionam os namespaces com o use e cria uma reotina try/catch para executar o envio de e-mail. A mesagem de Exception foi personalizada.
Ajustamos a configuração do PHPMailer para o envio de e-mails definindo os nomes de servidor SMTP e dados da conta (no meu caso utilizei os dados do meu e-mail mas podemos utilizar, por exemplo, o servidor SMTP do GMail com essas configurações ) nos atributos correnpondentes.
Após isso passamos os parametros do objeto mensagem para os métodos do PHPMailer.
Por fim foi isolado o arquivo de processamento do envio de e-mail do diretório público para protege-lo de acesso. Eu já tinha feito dessa forma mantendo o arquivo fora do diretório public. A diferença foi que ele manteve um arquivo de referencia que importa com o require o arquivo de lógica e os arquivos da biblioteca.

Seção 13 - Banco de Dados MySQL

Aula 388 a 395 - O que é MySQL / Um pouco mais sobre SQL / Utilizando o PHPMyAdmin para manipulação do MySQL / Criando e Excluindo bancos de dados / Tabelas de tipos de dados parte 1 - Um pouco de teoria / parte 2 - Partindo para a prática / Entendendo a diferença entre os tipos char e varchar / Editando nomes de tabelas
Uma rápida introdução ao SQL As subdivisões da linguagem SQL são, conforme o tipo de instrução:
DDL Data Definition Language Linguagem de definição de dados
DML Data Manipulation Language Linguagem de manipulação de dados
DCL Data Control Language Linguagem de Controle de dados
DTL Data Transaction Language Linguagem de transação de dados
DQL Data Query Language Linguagem de consulta de dados
Utilizando o phpmyadmin padrão do XAMPP. Acessando via localhost
Aplicações podem acessar um ou mais bancos de dados simultaneamente assim como mais de uma aplicação podem acessar o mesmo DB.
Comandos CREATE DATABASE e DROP DATABASE. Criando DB com a interface do phpMyAdmin.
Explicação sobre tabelas e tipos de dados:
TEXT: tamanho variável que armazena uma grande quantidade de caracteres.
VARCHAR: tamanho variável que armqzena de 0 a 255 caracteres.
CHAR: tamanho fixo que armazena de 0 a 255 caracteres.
INT: valores numéricos inteiros.
FLOAT: valores numéricos fracionados.
DATE: data no formato YYYY/mm/dd.
TIME: hora.
DATETIME: combinação de date e time em um mesmo campo.
Criando tabelas com a interface do phpMyAdmin e com o comando CREATE TABLE.
Diferenças entre CHAR e VARCHAR.
Renomeando tabelas com RENAME TABLE nome_tabela TO novo_nome
Aula 396 - Incluindo, editando e removendo colunas de tabelas
Utilizando o ALTER TABLE para adicionar colunas ADD, alterar nome e propriedades de colunas com CHANGE e remover colunas com DROP.
Aula 397 a 405 - INSERT - Inserindo dados em tabelas / SELECT - Consultando dados / Filtrando registros (WHERE) / Populando o banco de dados com registros para testes / SELECT - Filtros com Operadores de Comparação / Filtros com Operadores Lógicos / Filtros com o operador BETWEEN / Filtros com o operador IN
Inserção de dados na tabela com a interface do PHPMyAdmin e através do comando SQL INSERT.
Consultando dados da tabela com o PHPMyAdmin e através do comando SELECT. Selecionando colunas individuais a serem exibidas e a ordem de exibição.
Fazendo coonsultas com seletor WHERE, operadores lógicos AND/OR e de comparação =, < <=, >, >=.
Inserção de uma base de dados de usuários. Os dados do DB foram gerados pelo Generate Data. Foram criadas 100 inserções para testes.
Executando querys com seleção de conteudo.
Utilizando o operador BETWEEN ... AND .... Os campos de datas devem ser passados como string.
Utilizando o operador IN para fazer seleções de vários valores no mesmo campo. É possível ainda a seleção contrária utilizando o operador NOT.
SELECT * FROM tb_alunos WHERE interesse IN('jogos', 'esporte', 'musica');
Aula 406 a 408 - SELECT - Filtros com o operador LIKE / Ordenando resultado / Limitando retorno
Utilização do filtro LIKE com os coringas:
%: Indica que pode haver qualquer conjunto de caracter no texto.
_: Indica que pode haver um ou mais caracteres em uma posição específica do texto de acordo com o número de coringas posicionados.
Ordenar os resultados com ORDER BY e selecionando o mode de retorno com ASC e DESC.
Para limitar a quantidade de resultados podemos utilizar o operador LIMIT. Esse operador recebe o numero de resultados que queremos apresentar. Ele pode ser combinado com o operador OFFSET que indica a partir de qual registro queremos o retorno, ou seja pula o numero de registros indicados em seu valor (começando com o valor 0 para o primeiro registro).
Podemos fazer essa limitação de duas formas:
LIMIT x OFFSET y
ou:
LIMIT y,x
note que na sintaxe reduzida o OFFSET vem antes do numero do LIMIT.
Aula 409 a 412 - SELECT - Funções de agregação parte 1: MAX, MIN e AVG / Funções de agregação parte 2: SUM e COUNT / Agrupando seleção de registros (GROUP BY)
Funções de agregação tem por objetivo uma operação aritmética em determinada coluna de uma tabela.
MIN(): Retorna o menor valor de todos os registros com base em uma coluna.
MAX(): Retorna o maior valor de todos os registros com base em uma coluna.
AVG(): Retorna o valor médio de todos os registros com base em uma coluna.
SUM(): Retorna a soma dos valores de todos os registros com base em uma coluna. COUNT(): Retorna a quantidade de todos os registros de uma tabela.
GROUP BY: Agrupa valores com base em uma ou mais colunas cujos valores sejam iguais. Permite realizar funções de agregação em cada subconjunto agrupado de registros.
HAVING: Filtro realizado sobre os resultados dos agrupamentos (GROUP BY).
SELECT estado, COUNT(*) AS total_por_estado FROM tb_alunos GROUP BY estado HAVING total_por_estado >= 5;
Aula 413 - UPDATE - Atualizando registros / DELETE - Excluindo registros
Utilizando o comando UPDATE e SET para atualizar registros em tabelas.
Utilizando o comando DELETE FROM.
Aula 415 a 418 - Introdução ao relacionamento entre tabelas, chave primária e estrangeira / Projeto Loja Virtual - Relacionamento Um para Um / Relacionamento Um para Um (populando tabelas) / Relacionamento Uma para Muitos / Relacionamento Muitos para Muitos (populando tabelas)
Relacionamento entre tabelas 1 x 1, 1 x n e n x n. Noçoes sobre chaves primária e secundária.
Criando novas tabelas com PRIMARY KEY para relacionamentos 1 x 1 e utilizando o AUTO_INCREMENT.
A segunda tabela foi criada com um relacionamento utilizando o comando FOREIGN KEY(chave) REFERENCES tabela(chave).
Inserção de dados nas tabelas criadas.
Criando uma tabela com relacionamento 1 x n. Inserção de dados na tabela.
Criação de tabelas para relacionamentos muitos para muitos. Para o campo do tipo DATETIME setamos como valor default uma constante chamada CURRENT_DATETIME, para ter um valor padrão para o campo.
Criação da tabela associativa do relacionamento n x n. Na aula não foram definidas as chaves dessa tabela como primárias. Fiz porque verifiquei em aulas anteriores O curso completo de Banco de Dados e SQL, sem mistérios ver aula 77.
Inserindo dados nas tabelas criadas, inclusive na tabela associativa.
Aula 421 a 425 - Introdução as Junções (JOINS) entre tabelas / JOIN - Junção à esquerda (LEFT JOIN) / Junção à direita (RIGHT JOIN) / Junção interna (INNER JOIN) / Alias - Apelidando tabelas
Junções de tabelas com JOIN, ON.
LEFT JOIN: Faz as junções tomando como base os índices da tabela da esquerda (tabela de FROM).
RIGHT JOIN: Faz as junções tomando como base os índices da tabela da direita (tabela de JOIN).
INNER JOIN: Faz a junção dos valores que estão na intersecção entre as duas tabelas.
Utilizando o AS para criar apelidos (ALIAS) para tabelas.

Seção 14 - Ajax

Aula 427 a 429 - Introdução ao AJAX / Ajax - O que são requisições síncronas e assíncronas / Download dos arquivos auxiliares da seção
Acrônimo de Asynchronous JavaScript And XML.
Uma metodologia de programação que possibilita a comunicação assíncrona entre front-end e back-end de aplicações Web.
Nas requisições síncronas o browser deve aguardar a resposta de uma requisição antes de disparar a próxima requisição. Já nas requisições assíncronas o browser pode enviar várias requisições e, quando a resposta for enviada ele intercepta esses dados e apresenta para o usuário.
Aula 430 - Requisições síncronas Aula 431 a 432 - Requisições asíncronas parte 1 - Efetuando requisições HTTP via XMLHttpRequest / Estados da requisição
Para entender as requisições síncronas podemos observar no exemplo dessa aula que o navegador recarrega a página a cada seleção no link. Inserimos um pequeno loop PHP para verificar o tempo de carregamento. link.
As requisições AJAX são feitas com a função XMLHttpRequest.
Para iniciar um processo assíncrono com AJAX primeiramente devemos instanciar em uma variável a classe XMLHttpRequest().
Feito isso teremos um objeto que terá os métodos necessários para fazer as requisições.
O método open() espera receber dois parâmetros. O método da requisição (GET ou POST) e a URL que se está requisitando.
Para enviar a requisição aberta utilizamos o método send().
let ajax = new XMLHttpRequest();
ajax.open('GET', pagina.http);
ajax.send();
Nesse momento ao verificarmos o conteudo da variável no console teremos um objeto com vários atributos, entre eles o responseURL que tem o caminho para a URL requisitada.
Outro atributo importante é o ReadyState. Não devemos confundir o State com o atributo status.
O objeto XMLHttpRequest pode retornar 5 estados diferentes:
0: request not initialized: requisição não iniciada.
1: server connection established: conexão estabelecida com o servidor.
2: request received: requisição recebida.
3: processing request: processando requisição;
4: request finished and response ready: requisição finalizada.
Quando instanciamos a classe XMLHttpRequest mas sem executar nenhuma requisição temos o estado 0.
Quando iniciamos uma conexão com o método open() passamos para o estado 1.
Já para verificarmos os próximos 3 estados precisamos utilizar um atributo do XMLHttpRequest chamado onreadystatechange. Esse atributo pode receber uma função que manipula cada um dos 3 estados processados pelo servidor, entretanto o tempo desse processo é muito curto.
O fato de a requisição chegar no estado 4 não significa entretanto que a resposta esperada foi processada com sucesso. Significa apenas que a requisição foi completada podendo ter um retorno de erro. Esse retorno é verificado no status.
Aula 433 a 435 - Requisições assincronas parte 3 - Aplicando um loading / Status da requisição / ResponseText
Aplicamos uma imagem de loading utilizando um gif animado. Para isso inserimos um id na tag que irá receber o conteudo e fizemos a manipulação do DOM de modo a inserir essa imagem no html. Eu fiz para treinar de um jeito completamente diferente, entretanto errei em um ponto importante. Quem deve receber o atraso de tempo para fazermos o teste é o arquivo que está sendo requisitado e não a função do onreadystatechange. Eu estava colocando um atraso dentro da função que criamos para esse atributo, quando o delay deveria estar no arquivo requisitado.
No mais além de ter sido utilizado a inserção de html através de JS (na minha tentativa fiz apenas manipulação do estilo display e inseri a img diretamente no html) também foi utilizado o Bootstrap para ajustar a posição do elemento.
Com essas alterações e o delay ajustado no arquivo requisitado temos a apresentação do gif na tela até a requisição receber o estado 4. Entretanto verifiquei um problema quando clicamos várias vezes no link que faz a requisição. Nesse caso a animação acaba mas ainda temos requisições em aberto. Para solucionar isso fiz uma lógica que verifica se existe o elemento antes de remove-lo e criei um array para armazenar o numero de requisições acumuladas. Com isso a animação só deixa de ser apresentada caso não haja mais requisições sendo executadas. Isso vai ser um problema quando passarmos a carregar a página, porém posso bloquear a exibição da página enquanto o array não estiver vazio.
Concluido o tratamento do estado da requisição precisamos agora verificar qual o status da requisição. Essa informação é retonada pelo atributo status e segue o padrão de códigos do http. A lista de códigos de resposta http pode ser consultada em MDN Web Docs. Os principais códigos que serão tratados serão o 200 (requisição bem sucedida) e os códigos da categoria 400 (respostas de erro).
Para verificar a resposta de status do servidor verificamos o atributo status.
Com isso podemos executar a lógica de acordo com o valor retornado. No exemplo da aula foi inserido no corpo do html uma mensagem em caso de sucesso e outra em caso de erro. Como esse mensagem é incluida diretamente na tag de conteudo a tag filha inserida que contem o gif animado é removida, portanto não é necessário fazer a remoção desse elemento. No meu caso isso não influenciou em nada pois já tinha feito um tratamento diferente.
Por fim inserimos uma linha para excluir o valor inserido no conteudo cada vez que é iniciada uma nova requisição.
Para apresentar os dados solicitados pelo AJAX (que nesse caso é um texto HTML) basta recuperarmos o valor do atributo responseText e inserirmos no nosso documento através da manipulação do DOM (no caso com o innerHTML).
Aula 436 a 439- XML parte 1 - O que é? / Recuperando arquivo XML do servidor via HTTP / Convertendo o XML para JSON e por que / Listando os filmes na página
O XML é uma linguagem de marcação que tem como objetivo estruturar e descrever informações. Ele pode ser utilizado para troca de informações entre quaisquer sistemas.
A sintaxe de uma arquivo XML é simples bastando estruturar os dadso em nós como em um documento html. Para saber mais ver W3Schools
Criamos em seguida um arquivo html simples com um script de recuperação de dados em AJAX para requisitar um arquivo XML.
Recuperado o arquivo, precisamos converte-lo para JSON pois o XML não é interpretado nativamente pelo JS.
Esse tipo de conversão é facilmente realizada com a biblioteca jQuery, entretanto nesse momento será feito apenas com JS puro. Para isso utilizamos um script que faz essa conversão. Esse script pode ser encontrado na internet. Localizei um parecido no Stackoverflow.
Antes de passar esse elemento para JSON precisamos transforma-lo em uma árvore de elementos. Para isso devemos instanciar uma classe nativa do JS que é o DOMParser(). Com o objeto instanciado utilizamos o método parseFromString(arquivo_a_converter, 'tipo_de_arquivo') onde o tipo de arquivo nesse caso é text/xml.
Feita essa primeira conversão utilizamos a função principal do xmlToJson para fazer a conversão do texto XML para JSON.
let parser = new DOMParser();
let domFilmes = parser.parseFromString(XMLFilmes, 'text/xml');
console.log(domFilmes);
let jsonFilmes = xmlToJson(domFilmes);
console.log(jsonFilmes);
Com o arquivo JSON criado passamos a fazer a inclusão dos dados no HTML via manipulação do DOM. Como explicado em aula, tudo foi feito da maneira mais complexa, sem o uso de bibliotecas que facilitam o processo. Assim criamos todos os elementos dentro de um loop for...in e percorremos cada chave do arquivo JSON individualmente, criando os elementos e inclusive utilizando novos loops para percorrer os arrays internos de determinadas chaves.
Para inclui tudo no html fizemos um appendChild de todos os elementos e um novo appendChild do bloco de elementos criados dentro da página a ser exibida.
Aula 440 - Refactoring da comunicação com o back-end utilizando JSON
Nesta aula vamos trabalhar diretamente com um arquivo nativo em JSON. Com isso entenderemos as diferenças entre o padrão SOAP (baseado em HTML e XML) e REST (que não possui um padrão oficial e aceita diversos padrões como HTTP, JSON, URL e XML) para a comunicação entre sistemas.
Quando trabalhamos com arquivos JSON ele já vem estruturado e formatado de modo que o tratamento dos dados recebidos é mais simples.
Basta ao recebermos o arquivo executarmos o método JSON.parse no arquivo para podermos acessar todos os elementos de forma direta.
Por se tratar de um objeto os itens do arquivo JSON podem ser acessados diretamente com a notação '.'. Desse modo podemos acessar cada volr através da notação objeto.chave.
Aula 441 - Pesquisa endereço
Fiz a aplicação por minha conta. O WebService utilizado para consulta foi o ViaCEP.
Uma das diferenças que teve na aula foi o evento utilizado para monitorar o campo do formulário (onblur). Eu utilizei o onkeyup. Uma dica interessante foi passar como parametro na função o valor this.value de modo que não foi necessário utilizar o getElementById para recuperar o valor.
No mais tudo que eu fiz foi para melhorar a interface, o que não foi feito na aula pois não era o objetivo.

Seção 15 - PHP com PDO e MySQL

Aula 442 a 444 - Introdução ao PDO / Criando uma conexão entre o PHP e o MySQL com o PDO / Tratando Exceptions (PDOException)
PDO é um conjunto de objetos que nos auxiliam no trabalho com Bancos de Dados. Ele cria um padrão no modo como o PHP se comunica com os Bancos através de extensões que podem ser habilitadas conforme a necessidade. Entre as extensões temos:
pdo_oci: Oracle; pdo_mysql: MySQL; pdo_pgsql: PostgreSQL; pdo_odbc / lib: Microsoft SQL Server (não é suportado nativamente, necessita do odbc mais uma biblioteca disponibilizada pela Microsoft).
Para iniciarmos a utilização do PDO devemos criar a conexão instanciando a classe PDO(). O primeiro parametro a configurar é o Data Source Name(DSN) que é o nome do SGBD que iremos utilizar, seguido do host, do dbname, do nome do usuário do DB e da senha.
Para fazer o tratamento de erro no PDO utilizamos o comando try...catch() passando o parametro PDOException no catch.
Para verificar os métodos que podemos executar no PDOException devemos consultar a documentação em PHP Documentation.
Entre os métodos podemos executar para capturar o tipo de erro o getCode() e o getMessage().
A vantagem de fazer esse procedimento é que evitamos um Fatal Error do PHP.
$dsn = 'mysql:host=localhost;dbname=xphp_com_pdo';
$user = 'root';
$passwd = '';

try {
$conexao = new PDO($dsn, $user, $passwd);
} catch (PDOException $e) {
echo 'Erro: ' . $e->getCode() . ': ' . $e->getMessage();
}
Aula 445 - Executando instruções SQL (Exec)
Foi feita uma query para criar uma tabela através do PDO. Para executar as querys no PDO utilizamos o método exec(). O método exec() retorna o numero de linhas modificadas ou removidas na tabela. Em casos como criação de tabelas ou SELECT de dados o retorno é '0', entretanto não utilizamos o exec() para operações de CRUD, pois o método mais indicado é com o statement para evitar o SQL injection.
Aula 446 - PDOStatement Object (Query) com fetchAll
Como visto anteriormente o método exec() retorna apenas o numero de linhas alteradas pela query. Para recebermos dados de retorno utilizamos o método query(). O método query() retorna um statement que é apenas um objeto com a query selecionada. A partir desse objeto podemos executar métodos para obter os resultados desejados.
O método fetchAll() retorna todos os registros recuperados da consulta em um array de dados.
Esse array retorna os dados em duas formas. Através de valores associativos com chave e valor, e com índices numéricos com chaves sequenciais e os valores de cada chave. Desse modo temos os valores duplicados que podem ser tratados de formas diferentes:

Retorno do fetchAll()

[0] => Array
(
[id] => 7
[0] => 7
[nome] => Jean
[1] => Jean
[email] => [email protected]
[2] => [email protected]
[senha] => 123456
[3] => 123456
)
Através do array obtido podemos acessar os valores tanto pela chave associativa quanto pela chave numérica.
Aula 447 a 448 - FetchAll - Trabalhando os tipos de retorno / Fetch - Retornando apenas um registro
Para selecionar os parametro de modo de retorno utilizamos o nome da classe PDO, com o operador de escopo de resolução :: seguido do nome da constante que se refere ao modo de retorno que desejamos obter (por padrão FETCH_BOTH).
Para receber apenas os valores no modo associativo utilizamos a constante FETCH_ASSOC e para o modo numérico FETCH_NUM.
Podemos também recuperar um array de objetos (e não um array de arrays) com a constante FETCH_OBJ. Note que para cada tipo de retorno temos uma forma de recuperar os dados.

Fetch Association

$lista = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo $lista[0]['nome'];

Fetch Object

$lista = $stmt->fetchAll(PDO::FETCH_OBJ);
echo $lista[0]->nome;
Para ver as demais opções podemos consultar a documentação em PHP Documentation.
O método fetch() retorna apenas um elemento da query na forma de array ou objeto. Assim, diferentemente do fetchAll() não retorna um array com os elementos e sim apenas o array ou objeto do elemento. Caso a seleção tenha mais de um elemento será apresentado apenas o primeiro. Já quando fazemos uma seleção de apenas um registro utilizando o fetchAll() temos um array com apenas um array ou objeto.
Aula 449 - Listando registro com foreach
Podemos percorrer os registros recuperados pelo fetchAll() utilizando o método foreach() diretamente no retorno da query.
$lista = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($lista as $key => $value){

}
Podemos ainda executar o foreach() diretamente com o statement, desse modo sem precisar executar o fetchAll():
$query = 'SELECT * FROM tb_usuarios;';
foreach ($conexao->query($query) as $chave => $valor) {
print_r($valor);
}
Aula 450 - SQL Injection
Aqui fizemoss um exemplo sobre como é feito o SQL Injection.
Criamos um formulário para login onde utilizamos os dados de usuario e senha para fazer a seleção no DB. Caso os dados de usuário e senha existam no DB retornamos um array com os dados do usuário.
Nesse momento na tela de login digitamos o nome do usuário e junto com a senha digitamos o código SQL que queremos injetar:
Usuário: Jean
Senha: 123456';DELETE FROM tb_usuarios WHERE 'a' = 'a
O código SQL em vermelho é injetado no campo de senha e entra como um complemento da query. Desse modo conseguimos apagar todos os dados da tabela tb_usuarios.
Aula 451 - Prepare Statement
Para evitar o SQL Injection devemos utilizar o método prepare() ao invés de utilizar diretamente o método query(). Com isso não passamos diretamente os valores digitados nos campos disponíveis para o usuário. Em vez disso passamos esses dados para o método bindValue() que irá tratar o valor recebido antes de inseri-lo na query.
Após os valores serem tratados pelo bindValue() devemos utilizar o método execute() para processar a query.
$query = "SELECT * FROM tb_usuarios WHERE email = :usuario AND senha = :senha";

$stmt = $conexao->prepare($query);

$stmt->bindValue(':usuario', $_POST['usuario']);
$stmt->bindValue(':senha', $_POST['senha']);
$stmt->execute();

$usuario = $stmt->fetch();
Para o método bindValue() podemos ainda passar um terceiro parametro que é o tipo de dado esperado na variável que por padrão é PDO::PARAM_STR. Para saber quais os parametros possiveis podemos consultar as constante do PHP em PHP Documentation
Aula 452 a 453 - App Lista de Tarefas - Introdução / Iniciando o projeto Modo em AJAX
Introdução ao projeto de lista de tarefas. A base do projeto é em Bootstrap e já estava preparada. Fiz uma alteração para trabalhar com ele em ajax mantendo o menu lateral e alterando apenas o conteudo central da página. Vou tentar trabalhar com os dois modos em paralelo.
No DB temos duas tabelas criadas com uma chave estrangeira.
Aula 454 a 457 - App Lista Tarefas - Criando a classe Tarefa / Criando a Classe TarefaService / Criando a classe Conexão / Enviando dados para o Back-End Modo em AJAX
Aqui criamos as classes para encapsular os registros do banco de dados, com os métodos getter e setter e a classe que contém os métodos de CRUD.
Foi criada também a classe para a conexão. Atenção para essa classe pois ela foi construida de um modo simples, mas com alguns detalhes que é bom memorizar. Por útimo criamos os arquivos para recuperar os dados do formulário de inclusão de dados. Foram criados dois arquivos de controller para receber os dados de POST do formulário, sendo um no diretório público que apenas inclui o arquivo com a lógica que está em um diretório protegido.
Aula 458 - Inserindo registros Modo em AJAX
No arquivo de controller incluimos as referencias para os arquivos de model, service e conexao. Instanciamos a classe Tarefa para receber os dados do POST, a classe Conexao e a classe TarefaService que irá realizar as tarefas de CRUD. Nessa classe criamos um construtor que irá receber como parametro os objetos instanciados no Controller de conexao e a tarefa. Esses objetos irão passar os dados necessarios para efetuarmos as operações de CRUD. Para definir os parametros do construtor da classe TarefaService passamos o nome da classe pai do objeto de modo a garantir que os dados recebidos sejam realmente objetos das classes esperadas.
Ainda na classe TarefaService, o objeto conexao recebido por parametro deverá executar o seu método que executa a conexao propriamente dita pois o objeto conexao só tem os atributos da conexao, mas o que desejamos é a conexão estabelecida. Assim ao executar o método o valor recebido pelo objeto tarefaService é um PDO Object ou seja a conexão estabelecida com o DB.
Feito isso criamos o método inserir() para gravar os dados no DB. Esse método utiliza o prepare statement para proteger os dados a serem enviados ao DB.
Note que o valor a ser inserido não vem diretamente do controller, mas sim deve ser obtido com o método __get do objeto tarefa.
public function inserir()
{
$query = 'INSERT INTO tb_tarefas(tarefa) VALUES(:tarefa);';
$stmt = $this->conexao->prepare($query);
$stmt->bindValue(':tarefa', $this->tarefa->__get('tarefa'));
$stmt->execute();
}
Após a inclusão da tarefa fazemos o redirecionamento para a página de nova_tarefa com o Location, passando um parametro na URL para ser recebido pela página com o GET de modo a podermos mostrar uma mensagem de inclusão com sucesso.
Aula 460 a 461 - App Lista de Tarefas - Listando todos os registros parte 1 / Listando todos os registros parte 2 Modo em AJAX
Aqui foi feita uma refatoração do código para que através de métodos GET possamos definir qual a ação do controller.
A primeira modificação foi na action do formulário de inclusão. Aqui passamos um parametro que passa a encapsular a rotina de inclusão de tarefas no controller.
Em um primeiro momento fizemos um teste para verificar se há algum valor na variável $_GET e caso positivo verificar seu valor.
No entanto criamos uma nova variável para ser utilizada pelo script de listar as tarefas. Assim para que possamos tratar a mesma variável de duas origens criamos um operador ternário para verificar se existe a variável $_GET e caso contrário utilizar o valor da variável recebida.
Com isso podemos criar a rotina do controller para alimentar a página de exibição de tarefas. Aqui precisamos instanciar as três classes (Tarefa, Conexao e TarefaService), pois embora não precisemos utilizar a classe Tarefa ela faz parte do constructor da classe TarefaService.
Feito isso executamos o método recuperar de TarefaService, que passamos a implementar.
Aqui foi feita um query de SELECT com um LEFT JOIN para recuperar o nome do status constante em tabela própria. Feito isso incluimos as informações recuperadas na página com o foreach(). Detalhe para a constante utilizada para o fetchAll() do PDO que no nosso exemplo foi em OBJ
public function recuperar()
{
$query = 'SELECT tb_tarefas.id, id_status, tarefa, tb_status.status as status FROM tb_tarefas LEFT JOIN tb_status ON (tb_tarefas.id_status = tb_status.id);';
$stmt = $this->conexao->prepare($query);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_OBJ);
}
Aula 462 a 464 - App Lista de Tarefas - Atualizando registros parte 1 / Atualizando registros parte 2 / Extra - Prepare com marcadores ? e métodos setters com return $this Modo em AJAX
Para iniciar a opção de edição dos itens da lista primeiramente criamos um evento de click no icone correspondente. Esse evento irá executar uma função que fará a criação de uma estrutura html de um formulário que será responsável por tratar a edição.
A seguir inserimos na <div> de cada elemento da lista que é criado com o foreach um id que recebe o valor do id da tabela do DB de modo a criar um valor único para cada item da lista.
Com esse id criado passamos ele como parametro na função de click, e selecionamos o elemento com esse parametro utilizando o getElementById(), limpamos o seu conteudo e inserimos nesse nó do documento o trecho html criado com o método insertBefore(). Esse método inclui itens no HTML depois que ele já foi renderizado e recebe dois parametros: o valor a ser incluido e o nó aonde devemos fazer a inserção.
Inserimos alguma classes Bootstrap nos elementos para melhorar a apresentação e em seguida criamos um novo parametro para a função onclick que irá armazenar o texto recuperado do DB de modo a inserirmos esse texto no input que irá sofrer alteração.
Para finalizar a construção do formulário de alteração criamos um novo campo <input> para armazenar o valor do id do elemento, que será necessário para fazer a alteração no DB.
function editar(id, txt_tarefa) {
let form = document.createElement('form');
form.action = '#';
form.method = 'POST';
form.className = 'row'

let inputTarefa = document.createElement('input');
inputTarefa.type = 'text';
inputTarefa.name = 'tarefa';
inputTarefa.className = 'col-9 form-control';
inputTarefa.value = txt_tarefa;

let inputId = document.createElement('input');
inputId.type = 'hidden';
inputId.name = 'id';
inputId.value = id;

let button = document.createElement('button');
button.type = 'submit';
button.className = 'col-3 btn btn-info';
button.innerHTML = 'Atualizar';

form.appendChild(inputTarefa);
form.appendChild(inputId);
form.appendChild(button);

let tarefa = document.getElementById('tarefa_' + id);
tarefa.innerHTML = '';
tarefa.insertBefore(form, tarefa[0]);
}
Para a lógica de alteração consegui fazer todas as rotinas. Primeiramente inclui o direcionamento do action do formulário de atualização criado anteriormente passando na URL o parametro 'alterar'.
form.action = './tarefa_controller.php?acao=alterar';
Em seguida criei a rotina do controller para iniciar o método de atualização:
else if ($acao == 'alterar') {
$tarefa = new Tarefa();
$tarefa->__set('tarefa', $_POST['tarefa']);
$tarefa->__set('id', $_POST['id']);
$conexao = new Conexao();

$tarefaService = new TarefaService($conexao, $tarefa);
if ($tarefaService->atualizar()) {
header('Location:todas_tarefas.php');
}
Fazemos o teste lógico do valor retornado pelo método atualizar() pois ele retorna '1' caso o processamento do statememt estiver ok.
Para a query utilizamos o prepare statement para tratar os valores recebidos.
public function atualizar()
{
$query = 'UPDATE tb_tarefas SET tarefa=(:tarefa) WHERE id=(:id)';
$stmt = $this->conexao->prepare($query);
$stmt->bindValue(':tarefa', $this->tarefa->__get('tarefa'));
$stmt->bindValue(':id', $this->tarefa->__get('id'));
return $stmt->execute();
}
Uma consideração sobre a utilização do bindValue() com a notação '?' ao invés de um bind nomeado. Podemos substituir a sintaxe acima por:
$query = 'UPDATE tb_tarefas SET tarefa= ? WHERE id= ?';
$stmt = $this->conexao->prepare($query);
$stmt->bindValue(1, $this->tarefa->__get('tarefa'));
$stmt->bindValue(2, $this->tarefa->__get('id'));
Temos ainda outra notação mas ela funciona com a conexão via mysqli.
Outra dica é sobre o método __set() que utilizamos no model. Nessse momento o método é do tipo void ou seja, não retorna nada. Entretanto se adicionarmos um return $this no método ele passa a retornar o próprio objeto. Dessa forma podemos vincular os setters na mesma instrução:
$tarefa->__set('tarefa', $_POST['tarefa'])->__set('id', $_POST['id']);
Aula 465 a 467 - App Lista de Tarefas - Removendo registros / Marcando tarefas como realizada / Listando tarefas pendentes Modo em AJAX
Para a função de remover tarefas criamos uma função para onclick que passa um novo valor para a URL com dois parametros:
location.href = 'todas_tarefas.php?acao=remover&id=' + id;
Na sequencia criamos a rotina do controller passando apenas o id para a exclusão porém dessa vez recuperando o valor através do $_GET e após a execução do método retornamos para a página com o header
else if ($acao == 'remover') {
$tarefa = new Tarefa();
$tarefa->__set('id', $_GET['id']);
$conexao = new Conexao();
$tarefaService = new TarefaService($conexao, $tarefa);
$tarefaService->remover();

header('Location:todas_tarefas.php');
}
A query não tem nada demais e apenas exclui o registro.
public function remover()
{
$query = 'DELETE FROM tb_tarefas WHERE id=:id';
$stmt = $this->conexao->prepare($query);
$stmt->bindValue(':id', $this->tarefa->__get('id'));
return $stmt->execute();
}
O próximo elemento a ser tratado é a opção de marcar a tarefa como pendente ou realizado. Para isso criamos um evento de onclick da mesma forma que no remove mas com a opção de marcarRealizada. Na minha aplicação passei mais um parametro para capturar o estado atual do status.
location.href = 'todas_tarefas.php?acao=marcarRealizada&id=' + id + '&status=' + status;
No controller recebemos os parametros e aqui fiz algo diferente. Com o valor do status atual caso a tarefa esteja '1'(pendente) alteramos para '2'(realizada) e vice versa. Na lógica da aula não é feito esse tratamento e apenas alteramos uma vez o status da tarefa.
else if ($acao == 'marcarRealizada') {
$tarefa = new Tarefa();
$tarefa->__set('id', $_GET['id']);
if ($_GET['status'] == 1) {
$novoStatus = 2;
} else {
$novoStatus = 1;
}
$tarefa->__set('id_status', $novoStatus);

$conexao = new Conexao();
$tarefaService = new TarefaService($conexao, $tarefa);
$tarefaService->marcarRealizada();

header('Location:todas_tarefas.php');
}
Com isso passamos os valores para o service:
$query = 'UPDATE tb_tarefas SET id_status=:id_status WHERE id=:id';
$stmt = $this->conexao->prepare($query);
$stmt->bindValue(':id', $this->tarefa->__get('id'));
$stmt->bindValue(':id_status', $this->tarefa->__get('id_status'));
return $stmt->execute();
No HTML foi realizado um teste lógico para verificar se o status está pendente. Caso não esteja omite os icones de remover e marcar como realizado removendo as <div> desses icones.
Na minha lógica apenas removi o icone de remover e alterei os icones de pendente e realizdo para far fa-square ou far fa-check-square (icones do fontawesome).
A última etapa é ajustar a página de tarefas pendentes, apresentando apenas essas tarefas.
O pior aqui foi ajustar as ações do CRUD na página index.php que é a responsável por mostrar apenas as tarefas pendentes.
A solução que eu estava tentando foi a utilizada no curso, que é passar um parametro que indica qual página solicitou o serviço (se a index ou se a todas_tarefas).

Seção 16 - Publicação de aplicações Web na Internet

Aula 468 a 477 - Refrescando a memória (Aplicações Web vs Internet) / DNS - Domain Nama System / Servidores de hospedagem / Conecendo o famoso cPanel / FileZilla Client FTP (Instalação) / (Configuração) / Enviando arquivos da aplicação via File Manager / Configurando a hospedagem para PHP 7 / Configurando o banco de dados MySQL
Conceitos sobre internet e Web, breve explicação sobre DNS e registro de domínio, o que são servidores de hospedagem e como escolher.
Cadastro no Infinity Free (servidor gratuito), pequena amostra do CPanel para administração do site.
Instalação e configuração do Filezilla. No Filezilla podemos criar a conexão por site. Basta selecionar na aba Arquivos a opção Gerenciador de sites e incluir os dados do servidor FTP do site.
Transferencia de arquivos para instalação do serviço na hospedagem. As hospedagens no Infinity Free não permitem acesso ao diretório raiz da aplicação, apenas ao diretório publico. Outro detalhe é que nessa hospedagem é necessário habilitar a opção de mostrar mensagens de erro do PHP. É possível que isso occorra em outras hospedagens também.
Demonstrando a criação de DBs com o phpMyAdmin e o cPanel.

Seção 17 - jQuery

Aula 478 - Introdução ao jQuery
jQuery é uma biblioteca JavaScript que fornece diversos recursos prontos para facilitar o desenvolvimento. Entre os recursos aprimorados pelo jQuery estão:
  • Seleção e manipulação de elementos HTML
  • Navegação pelo DOM
  • Manipulação de CSS
  • Eventos
  • Efeitos e animações
  • Ajax
  • Plugins
Aula 479 - Configurando e testando o jQuery
A biblioteca jQuery pode ser incluida através de um script ou através de um link CDN .
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
Existem repositórios de bibliotecas em diversos servidores, entre eles o Google Hosted Libraries.
Entre as versões do jQuery temos o arquivo minificado, o arquivo normal o map file para debug e uma versão simplificada que não possui as extensões para Ajax e efeitos.
Incluimos a biblioteca com o arquivo interno e executamos um código para alterar o conteudo da tag inicial, apenas para teste.
Aula 480 - Seletores - Selecionando elementos HTML parte 1
No jQuery podemos selecionar os elementos de diversas formas, que facilitam o método de seleção usual do JavaScript com menos código do que através da API do DOM tradicional (getElementBy...).
O '$' Permite selecionar elementos, permite executar ações da biblioteca jQuery e permite acesso aos recursos da biblioteca jQuery.
Selecionamos os elementos com $('tag'), $('.class') ou $('#id').
Com o/os elementos selecionados podemos acessar seus atributos (é possível visualiza-los utilizando o console).
Aula 481 - Seletores - Selecionando elementos HTML parte 2
Quando temos uma mesma classe para vários elementos podemos fazer a seleção utilizando a combinação do nome do elemento e da classe $('tag .classe'), ou ainda de duas classes $('.classe1 .classe2').
O retorno das seleções em jQuery são um array de elementos. Podemos assim fazer seleções com operadores :first, :last, :even e :odd
Aula 482 - Sequencia de execução de código e $(document).ready()
A seleção dos elementos pelo jQuery obedece a regra de execcução do script, sendo assim, se selecionarmos um elemento antes de ele ser renderizado no DOM não receberemos nenhuma resposta.
Com o JS puro podemos execcutar funções que carregam o script apenas após o documento ser totalmente carregado (<body onload="function()" /> ou window.onload = function(){...}).
Já com o jQuery podemos fazer isso das seguintes formas:
$(document).ready(function(){...})
ou
$(function(){...}).
Na primeira opção encapsulamos o código que depende da renderização dentro da função que indica que o documento deve estar inteiramente carregado.
Na segunda opção chamamos a função através do indicador do jQuery de modo que seja feito o apontamento para a função a ser executada. Nesse caso a função não será executada (não utilizamos os () após a decclaração) mas apenas apontada para execução.
function teste() {
console.log($('#exemplo'));
}
$(teste);
Assim podemos criar arquivos de JS externos e iniciá-los dentro do <head> do documento HTML sem problemas de carregamento dos elementos, desde que passemos a função que aguarda a execução do DOM.
Aula 483 - Recuperando e manipulando os atributos dos elementos HTML
Podemos manipular todos os atributos contidos em tags HTML selecionando o elemento e utilizando a propriedade .attr(). Essa propriedade pode receber dois parametros. O atributo que queremos verificar e o valor que queremos atribuir a ele.
Aula 484 - Recuperando e manipulando o conteúdo interno de elementos HTML
Podemos manipular o conteudo interno de elementos com a propriedade .html(). Atenção pois esse elemento modifica o conteudo das tags e não o value.
Podemos substituir o conteudo interno de um elemento passando inclusive outras tags para ele. Entretanto não podemos passar por exemplo style pois esse é um atributo do elemento e a propriedade html() injeta apenas texto puro.
Abaixo alguns exemplos:
$('#div1 a').html('Outro teste');
$('#div2').html('Eu sou o valor novo');
$('#div3').html('Meu estilo é black');
$('#div3').attr('style','border: solid 1px black; width: 150px; height: 150px; color: red');
Aula 485 - Recuperando e manipulando valores de inputs (text) e selects
Para recuperar valores de campos utilizamos a propriedade .val(). Essa propriedade só fará sentido quando manipulamos eventos.
$('#nome').click(function (e) {
e.preventDefault();
console.log($('#nome').val());
console.log($('#origem').val());
$('#nome').val('Tom e Fernando');
$('#origem').val(2);
});
Aula 486 - Recuperando e manipulando valores de checkboxes/radios e Loop com $.each()
Para inputs do tipo radio que tem apenas uma opção de seleção, podemos acessar o valor utilizando o seletor seguido de :checked.
Já para inputs do tipo checkbox devemos utilizar o método $.each() que recebe como parametro o seletor (também com o :checked) e uma função que tem como parametro o índice e valor (da mesma forma que o foreach()). Assim podemos percorrer o array de elementos selecionados:
console.log($('.sexo:checked').val());
console.log($('.interesse'));
console.log($('.interesse:checked'));
console.log($('.interesse:checked').val());
$.each($('.interesse:checked'), (indice, valor) => {
console.log(indice, valor);
console.log(indice, valor.value);
Aula 487 - Inserindo e removendo elementos HTML
Podemos inserir elementos HTML selecionando a posição em relação a um elemento já existente. Para isso podemos utilizar os métodos .append() para inserção internamente no final do elemento e .prepend() para inserção internamente no início do elemento (os novos elementos entram como nós filhos).
Podemos ainda inserir elementos antes ou depois de outro elemento (fora do nó) com os métodos .before() e .after().
Lembrando que o método .html() substitui o conteudo interno de um elemento.
Por fim podemos remover elementos com o método .remove().
Aula 488 - Navegando entre elementos HTML
Podemos utilizar métodos específiccos para navegar pela árvore de elementos do DOM. PAra isso temos os métodos:
  • parent(): elemento pai
  • closest(): procura por elementos pais
  • find(): procura por elementos filhos
Podemos combinar esses métodos para navegar de diversas formas entre as tags do documentto:
console.log($('.secao2').parent());
console.log($('.subSecao').closest('#pagina'));
console.log($('.item').closest('#topo'));
console.log($('.item').closest('ul'));
console.log($('.item').parent());
console.log($('#rodape').find('p'));
console.log($('#topo').find('.item'));
console.log($('.secao1').parent().find('h1'));
console.log($('.subSecao').parent().parent().find('h1'));
console.log($('.subSecao').closest('#pagina').find('h1'));
Aula 489 - Manipulando CSS
Métodos para manipular CSS com o jQuery:
  • .css(): adiciona propriedades de estilo inline
  • .addClass(): adiciona classe
  • .hasClass: verifica a existência de uma classe no elemento
  • .removeClass(): remove classes
Podemos manipular os estilos CSS passando para o método a propriedade e o valor que queremos manipular. Esses dados podem ser passados squencialmente, executando o método várias vezes:
$('#topo').css('background-color', '#155661').css('margin', '0px').css('padding', '10px 20px 10px 20px');
Ou podemos ainda passar os dados como um objeto literal em pares chave:valor da forma que segue:
$('#topo').css({'background-color': '#155661', margin: '0px', padding: '10px 20px 10px 20px'});
Já com os métodos de manipulação de classes podemos inserir, consultar ou remover classes. Com o método .hasClass() podemos, por exemplo, criar lógicas para verificar se existe uma classe atribuida a um elemento.
if ($('textarea').hasClass('erro')) {
alert('campo invalido');
$('textarea').removeClass('erro');
$('textarea').addClass('campo alert');
}
Aula 490 - Eventos - Introdução
Podemos capturar com o jQuery os mesmos eventos que fazemos com o HTML e o JS. Eventos de Mouse, Teclado, Formulário e Janela são manipulados com facilidade pelo jQuery.
Aula 491 - Eventos do browser (navegador)
Entre os eventos do navegador, o primeiro que sempre utilizamos é o .onload().
Outro método de navegador é o .scroll(). Ele é acionado a partir do evento window e recebe uma função de callback que executa a ação necessária.
O método .resize() monitora o redimensionamento da janela do navegador.
Podemos utilizar esses eventos também para outros elementos. Note que quando utilizamos os métodos que exigem uma função de callback a utilização de arrow functions pode gerar certos inconvenientes. Quando utilizamos as arrow functions temos uma mudança no escopo da função. Se passarmos a função de modo declarativo podemos utilizar o this para referenciar o elemento que está sendo manipulado. O mesmo não ocorre quando utilizamos as arrow functions
Aula 492 - Eventos do mouse parte 1
Os principais eventos de mouse são:
.mousedown(): captura o momento que se aperta o botão do mouse;
.mouseup(): captura o momento que se solta o botão do mouse;
.click(): captura o ciclo completo de clique (quando o botão é solto), desse modo tem a mesma função que o .mouseup().
.dblclick(): monitora o click duplo no elemento.
.mousemove(): monitora a posição do mouse nos eixos X (offsetX) e Y (offsetY) através do evento de uma função de callback.
Aula 493 - Eventos do mouse parte 2
Outros eventos de mouse são:
.mouseover() e .mouseout(): são eventos mais antigos do jQuery que manipulam o evento ao passar o mouse sobre o elemento na entrada e na saida. São semelhantes aos próximos eventos, porém possuem um problema. Quando encapsulamos dois elementos ao entrar no elemento mais interno esses eventos disparam uma saida para o elemento externo e duas entradas: no elemento interno e no elemento externo novamente.
.mouseenter() e .mouseleave(): são eventos mais novos que foram criados para resolver a falha apontada acima.
Aula 494 - Eventos do teclado
Para o teclado temos os eventos .keydown() e .keyup().
Podemos recuperar o parametro desse método para realizar diversas operações.
O atributo keyCode retorna o código correspondente da tecla pressionada.
Já com o atributo target podemos direcionar para o campo e obter seu conteudo com o método val().
$('#teclado2').keyup((e) => {
if (e.keyCode == 13) {
let txt = $(e.target).val();
$('#resultadoTeclado2').html(txt);
$(e.target).val('');
}
Aula 495 - Eventos de formulário
Alguns eventos uteis em formulários que podemos manipular com jQuery são:
.focus(): foco sobre o elemento HTML.
.blur(): retirada do foco sobre o elemento HTML.
.change(): modificação do valor de um campo tipo radio, checkbox ou select.
.submit(): envio de formulário.
Podemos utilizar os métodos .focus() e .blur() para destacar os campos que o usuário está manipulando:
$('#nome').focus((e) => {
$(e.target).addClass('input-focado');
});
$('#nome').blur((e) => {
$(e.target).removeClass('input-focado');
});
Podemos verificar as mudanças em campo de opção e recuperar seu valor com o método .change()
$('#opcao').change((e) => {
console.log('Opcao foi modificada');
console.log($(e.target).val());
});
Já com o método .submit() podemos capturar o evento de envio de um formulário e tratar esse valor de modos diferentes do comportamento padrão do HTML.
Para isso utilizamos o método .preventDefault()
Esse método pode ser utilizado com qualquer tipo de evento que tem algum comportamento que desejamos capturar.
$('#form').submit((e) => {
e.preventDefault();
console.log('Formulario enviado');
});
Aula 496 - Aplicando eventos a diversos elementos html simultaneamente
Podemos aplicar eventos iguais a mais de um elemento simultaneamente apenas definindo quais elementos devem receber aquela ação:
$('#nome, #email')
.focus((e) => {
console.log('O elemento recebeu foco (agrupado)');
$(e.target).addClass('input-focado');
})
.blur((e) => {
console.log('O elemento perdeu foco (agrupado)');
$(e.target).removeClass('input-focado');
});
Note que nesse exemplo pudemos inclusive agrupar os dois eventos.
Aula 497 - Eventos - Registrando/removendo eventos com on/off
Podemos utilizar os métodos .on() e .off() para monitorar todos os eventos estudados anteriormente.
O método .on() recebe dois parametros: ('evento', cb()), sendo o evento qualquer um dos anteriores e cb() a função de claaback a ser executada. Porém quando inserimos um elemento posteriormente em nosso html via JS, ele não é atingido por essa atribuição. Para isso podemos utilizar uma sintaxe com três parametros. Nesse caso a sintaxe ficará como segue:
$('body').on('focus', 'input', (e) => {
$(e.target).removeClass('desfocado');
$(e.target).addClass('focado');
});
$('body').on('blur', 'input', (e) => {
$(e.target).removeClass('focado');
$(e.target).addClass('desfocado');
});
Perceba que nesse caso o elemento seletor é o elemento pai dos elementos que queremos que seja monitorado pelo evento, e esse á passado como parametro do método .on()
Já o método .off() tem como seletor o mesmo elemento que o definiu e recebe como parametro o evento e o elemento que deve ser desativado.
Aula 498 - Eventos - Disparando eventos de forma programática e eventos auxiliadores
Outras funções do jQuery muito utilizadas quando trabalhamos com eventos são:
.trigger(): Aciona evento de modo programático.
.hover(): Função auxiliar para captura de eventos mouseenter/mouseleave/mouseover/mouseout.
toggleClass(): Função auxiliar para inclusão remoção de classe.
A função trigger do através de uma ação disparar o evento de outro elemento, como por exemplo o click de um outro botão.
$('#btn2').on('click', () => {
alert('BTN 2 clicado');
});
$('#btn1').on('click', () => {
$('#btn2').trigger('click');
});
O método .hover() recebe como parametro duas funções. Uma que é executada no mounseenter/mouseover e outra no mouseleave/mouseout
$('#div1').hover(
(e) => {
$(e.target).addClass('emDestaque');
},
(e) => {
$(e.target).removeClass('emDestaque');
},
);
Já o método .toggleClass() inclui uma classe quando ela não está setada e exclui quando está.
Com ela podemos por exemplo simplificar o exemplo acima:
$('#div2').hover((e) => {
$(e.target).toggleClass('emDestaque');
});
Aula 499 - Efeitos especiais
Os Efeitos Especiais no jQuery realizam as transições mais utilizadas em elementos HTML de modo simplificado.
Os métodos .show(), .hide() e .toggle() mostram ou escondem os elementos selecionados. Podemos passar como parametro para esses métodos a velocidade que queremos para a transição (slow, fast, ou um valor numérico em ms).
Os métodos .fadeOut(), .fadeIn(), .fadeToggle() e .fadeTo() controlam a transparencia dos elementos, sendo o último precisa de um segundo parametro além do tempo para indicar quanto de opacidade se quer.
Por fim os métodos .slideUp(), .slideDown() e .slideToggle() fazem as transições para cima e para baixo.
Aula 500 - Animações
A função .animate() permite que façamos transições como as executadas na aula anterior, porém de forma personalizada. Essa função espera dois parametros:
propriedades visuais e opções da transição.
Essa função pode atuar sobre qualquer propriedade CSS que seja numérica.
O primeiro parametro é um objeto literal onde passamos todas as propriedades que queremos que sejam alteradas no elemento inicial.
Já o segundo pode ser apenas um valor numérico que será interpretado como o tempo a ser realizada a transição, ou pdemos passar um objeto literal com os parametros:
duration: tempo da animação.
start: recebe uma função que é executada no início da animação.
complete: recebe uma função que é executada ao fim da animação.
Com isso podemos encapsular uma animação na outra e iniciar duas animações ao mesmo tempo ou ainda uma ao final da outra.
$('button').on('click', () => {
$('#div1').animate(
{
width: '300px',
height: '300px',
'margin-left': '200px',
'margin-top': '200px',
'border-radius': '75px',
},
{
duration: 2000,
start: () => {
console.log('animação iniciada');
},
complete: () => {
$('#div2').animate(
{
width: '300px',
height: '300px',
'margin-left': '200px',
'margin-top': '200px',
'border-radius': '75px',
},
{
duration: 2000,
start: () => {
console.log('animação 2 iniciada');
},
complete: () => {
console.log('animação finalizada');
},
},
);
},
},
);
});
Por fim podemos encapsular os objetos em variáveis e passar apenas as variáveis como parametro da função.
Aula 501 - Ajax com jQuery - Conceitos básicos - Iniciando o App Dashboard
Copiamos os arquivos disponibilizados na aula. Aqui criamos o DB necessário para a aplicação com alguns dados para consulta.
A interface foi criada em Bootstrap e te além de um menu lateral (nav-side-menu) quatro linhas com quadros que foram posicionados utilizando o grid da biblioteca (col-md-4 para até 3 colunas)
Aula 502 - Ajax com jQuery - Requisições para o back-end com load(), get() e post()
Criamos ids para os links que direcionam para as páginas de documentação e suporte. Aqui o que faremos é capturar o click nesses links e substituir o conteudo da página index a partir da <div> com id=pagina. Desse modo apenas substituimos o conteudo da página com o método jQuery .load().
O método jQuery .load() faz toda a lógica de request do Ajax sem a necessidade de instanciar a classe XMLHttpRequest() e recupera o arquivo desejado no back-end de forma automática. Esse método aceita inclusive navegação por diretórios da aplicação.
$('#documentacao').on('click', () => {
$('#pagina').load('documentacao.html');
Da mesma forma que o .load(), as funções .get() e .post() também executam as requisições Ajax, porém definindo qual método deve ser utilizado.
A diferença desses métodos em relação ao .load() é que eles esperam uma callback para executar a ação. Essa callback pode executar o método .html() para incluir os dados no documento.
$('#documentacao').on('click', () => {
$.get('documentacao.html', (data) => {
$('#pagina').html(data);
});
});
$('#suporte').on('click', () => {
$.post('suporte.html', (data) => {
$('#pagina').html(data);
});
});
Aula 503 - Ajax com jQuery - Preparando o back-end da aplicação
Aqui vamos criar um script app em PHP para fazer a comunicação com do DB e através do AJAX com jQuery faremos requisições assíncronas para obter os registros do DB e atualizar o front-end sem fazer o refresh da página.
Primeiramente criamos uma classe Dashboard que contem as variáveis utilizadas para a construção das informações da página principal. Outra classe criada é a de Conexão. A próxima classe é a responsável pelas querys com o banco de dados. Nessa classe definimos um construtor que receberá o objeto da conexão e o objeto da dashboard.
Na classe de conexão com o DB criamos os métodos com as querrys necessárias para o front-end utilizando o prepared statement
Em seguida instanciamos todos os objetos necessários e começamos a setar os dados do objeto Dashboard, com os valores que serão recebidos para as datas e os resultados das querrys.
Aula 504 - Ajax com jQuery - Conceitos básicos - Utilizando o método $.ajax()
O método .ajax() diferente dos métodos vistos acima (.load(), .get() e .post()) permite que passemos mais parametros para a nossa requisição.
Criamos uma id para a tag <select> e passamos a monitorar o evento change. Com esse evento recuperamos o valor do item selecionado através do atributo $(e.target).val().
O método .ajax() recebe um objeto literal e espera as seguintes informações:
type: o método de comunicação com o back-end (GET ou POST);
url: o caminho para onde deve ser encaminhada a requisição;
dataType: o tipo de dados a ser recebido pelo .ajax();
data: o dado que deve ser enviado para o back-end;
sucesso: recebe uma função cb com a ação em caso de sucesso;
erro: recebe uma função cb com a ação em caso de erro;
No campo data nesse momento passamos o dado no formato x-www-form-urlencoded, que é o tipo de dado passado pela url.
Como estamos passando os dados pelo método GET recuperamos esse valor na aplicação e fazemos um tratamento nesse valor para formatar o periodo de modo automático. O app recebe apenas o mes e o ano e para saber quantos dias temos no mes podemos utilizar um método PHP chamado cal_days_in_month(). Essa função espera tres parametros: o tipo de calendário (no caso uma constante 'CAL_GREGORIAN'), o mes e o ano.
Com isso podemos construir as datas de inicio e fim da querry.
Porém, por padrão o método .ajax() encaminha os dados no formato HTML. Para ajustar o tipo de dados a ser recebido, devemos setar a propriedade dataType para json.
Com isso devemos converter o dado no arquivo PHP para JSON através do comando:
echo json_encode(dado_a_codificar).
Note que é necessário utilizar o echo, pois o ajax espera um retorno "em tela" ou "impresso" do PHP.
Com isso basta inserir os dados nas tags correspondentes com o método .html().
Aula 505 - Ajax com jQuery - Serialize
Nessa aula criamos um pequeno formulário em bootstrap. Os dados do formulário serão enviados através de um botão submit.
A primeira ação a fazer é cancelar o comportamento padrão com o método preventDefault().
A seguir criamos a requisição Ajax que irá encaminhar os dados através do método GET. Para criar a estrutura do x-www-form-urlencoded com todos os dados do fromulário para ser incluido na URL utilizamos o método jQuery .serialize(). Esse método gera a linha completa com todos os dados do formulário para serem enviados.
$('button').on('click', (e) => {
e.preventDefault();

let dados = $('form').serialize();
console.log(dados);

$.ajax({
type: 'GET',
url: 'app.php',
data: dados,
success: (data) => {
console.log(data);
},
error: (err) => {
console.log(err);
},
});
});
Importante saber que quando temos campos no formulario que permitem múltiplos valores é necessário definir a propriedade name da tag como um array, para que o serialize crie um array com todos os elementos. Ver aula 486 sobre o tratamento com o método .each()
Lembrando que o Ajax envia uma requisição e espera uma resposta. Por isso no PHP quando convertemos os valores recebidos em GET para o formato JSON com o json_encode devemos utilizar o echo.
Todo esse procedimento pode ser realizado com o método POST bastanto alterar para esse método nos dois lados da comunicação.

Seção 18 - Padrão de arquitetura MVC com PHP

Aula 506 - Introdução ao padrão de arquitetura MVC
Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
MVC é um padrão de arquitetura (Architetural Pattern). Temos em programação dois tipos de padrões. O Architectural Pattern e o Design Pattern. O primeiro trata dos requisitos não funcionais da aplicação enquanto o segundo trata dos funcionais.
Aula 507 - Um pouco mais sobre MVC e a preparação da estrutura do projeto Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
As camadas do MVC são divididas de acordo com sua responsabilidade na aplicação:
Controller: recebe todas as requisições e controla o que deve acontecer e quando.
Model: Camada de banco de dados e regras de negócio.
View: Exibição dos dados (html, xml, json).
No modelo MVC utilizamos um gerenciador de dependencias para evitar que tenhamos que fazer diversos require entre os arquivos da aplicação. Para isso podemos utilizar o autoloader e criar sistemas de rotas.
Para o servidor, vamos utilizar a partir dessa aula a função interna do PHP pois o XAMPP (como já vimos anteriormente em outros cursos) não lida muito bem com esses tipos dde aplicação pela forma como organizamos os arquivos.
Para o sistema de arquivos criamos um diretório public e um diretório app. No diretório public criamos um arquivo index.php. No diretório app criamos os diretórios Model, View e Controller.
Para executar o servidor do PHP utilizamos o comando:
php -S localhost:porta
Aula 508 - Composer e Autoload PSR-4 Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
O Composer é um gerenciador de dependencias que permite com facilidade trazer pacotes externos para nossa aplicação além de trazer um sistema de autoloader que facilita a utilização de scripts facilitando o acesso às classes através do namespace.
Podemos instalar o Composer diretamente em nosso projeto conforme a documentação da biblioteca.
Criamos um arquivo composer.json com as configurações do Composer manualmente. Esse arquivo indica o básico que precisamos utilizar em nossa aplicação e principalmente os diretórios em que queremos que seja aplicado o namespace:
{
"name": "jean\miniframework",
"require": {
"php": ">= 7.0"
},
"authors": [
{
"name": "Jean",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"App\\": "App/",
"MF\\": "vendor/MF"
}
}
}
Note que nesse arquivo apenas configuramos o básico para o composer, sendo a informação mais importante os diretórios para o namespace.
Download Composer.
Criado esse arquivo podemos iniciar o Composer de acordo com o método escolhido (como eu já tenho ele instalado na máquina rodei apenas com o comando '$ composer install').
Um dos problemas é que na nova versão do Composer precisamos colocar um nome do autor para o projeto (na instalação o Composer estava informando que o name não estava combinando com o valor esperado).
Feito isso para testarmos a instalação incluimos através do require o arquivo autoloader_psr4.php no arquivo index.php e verificamos se não temos nenhum erro.
Aula 509 - Iniciando a configuração de rotas do miniframework Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
O arquivo de roteamento é importante para direcionar as requisições do cliente para as opções corretas no Controller. Ele não se enquadra diretamente em nenhum dos módulos do MVC. A arquivo de Route será o responsável por indicar o caminho de cada action do sistema.
Para iniciar criamos o arquivo Route.php que terá o namespace App. Nesse arquivo criamos a classe Route e para testes criamos um método que retorna a super variável global $_SERVER.
Feito isso podemos instanciar a classe Route em qualquer arquivo da aplicação através do namespace. Desse modo instanciamos a classe no arquivo index.php:
$route = new \App\Route;
Nesse momento o parametro que nos interessa é o REQUEST_URI.
Com essa variável podemos utilizar o método usr_parse() para recuperar um array com o valor desse atributo e através da constante PHP PHP_URL_PATH recuperar apenas o valor sem o array.
public function getUrl()
{
return parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
}
Para entender o método parse_url() se passamos algo como:
parse_url('http://www.google.com.br?x=10')
temos o seguinte retorno:
Array ( [scheme] => http
[host] => www.google.com.br
[query] => x=10 )

A seguir criamos um novo método na classe Route que conterá as variáveis de rotas que serão um array indicando seu route, o arquivo de controller responsável pela execução e a action a ser executada pelo controller. Em nosso exemplo:
public function initRoutes()
{
$routes['home'] = array(
'route' => '/',
'controller' => 'indexController',
'action' => 'index'
);
$routes['sobre_nos'] = array(
'route' => '/sobre_nos',
'controller' => 'indexController',
'action' => 'sobreNos'
);
}
Por fim criamos o arquivo .htaccess que é responsável por algumas configurações do Apache mas que por hora ficará vazio.
Aula 510 - Controllers Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para o Controller da aplicação iniciamos pelo arquivo IndexController.php que já está referenciado no aurquivo Route.
Nesse arquivo definimos o namespace e criamos a classe IndexController que conterá os métodos das action também definidas no Route.
Em seguida iniciamos as rotas criadas no arquivo Route. Para isso criamos um atributo privado para armazenar os valores das rotas com seus respectivos getter/setter e uma classe construtora que irá apenas executar o método interno initRoutes que além de criar os arrays de cada uma das rotas irá executar o método setter do atributo privado $routes
Nesse momento o atributo $routes recuperado pelo método getter é um array multimensional:
Array
(
[home] => Array
(
[route] => /
[controller] => IndexController
[action] => index
)

[sobre_nos] => Array
(
[route] => /sobre_nos
[controller] => IndexController
[action] => sobreNos
)
)
Como estamos iniciando o método initRoutes através do método contruct da classe Route precisamos identificar qual a URL que estáa sendo acessada no momento. Para isso criamos um novo método run() que irá receber como parametro essa URL. Esse método será executado também pelo constructor e receberá o método getUrl() que já havíamos criado anteriormente e que separa o PATH da variável global $_SERVER.
Ainda no método run() vamos executar um foreach() no getRoutes() para separar cada uma das rotas do array.
Agora fazemos um teste lógico para verificar se a URL recebida por parametro existe na nossa variável de $routes e caso positivo criamos uma variável com o nome recuperado na chave 'controller' do nosso array. O valor dessa chave faz referencia ao nome do Controller que será responsável pela execução do script de ação e deve ser precedido pelo namespace onde ele está localizado e passado pela função PHP ucfirst para garantir que a notação de primeiro caracter maiusculo seja respeitado.
Tendo essa variável criada temos o valor necessário para instanciar um objeto com a classe apontada pela rota. Caso nesse momento tivessemos mais de um arquivo de Controller seria instanciada a classe indicada pelo route.
Uma vez instanciada a classe (no nosso exemplo IndexController) podemos executar o método recuperado no atributo action do array.
Aula 511 - Abstração da classe Routes Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
A classe Route nesse momento possui alguns métodos que fazem parte da aplicação. Todos o métodos da classe que não são os métodos de apontamento das rotas devem ser removidos pois podem ser utilizados em qualquer cenário. Para isso temos o diretório vendor\MF que faz parte do autoload e deve armazenar os métodos que são genéricos para a aplicação.
Para isso criamos um diretório Init e uma arquivo Bootstrap para armazenar a classe responsável pela inicialização das rotas. O nome de arquivo Bootstrap é utilizado para scripts de inicialização.
Nessse arquivo criamos uma classe do tipo abstract para armazenar os métodos de direcionamento das rotas. As classes abstract são classes que não podem ser instanciadas, podem apenas ser herdadas. Assim os métodos aqui incluidos só poderão ser utilizados quando extendemos essa classe em outra classe funcional.
Com isso transferimos todos os métodos não funcionais para a classe Bootstrap. Alguns métodos podem ter sua visibilidade alterada para protected e o método initRoutes deve ser incluido na classe como abstract pois ele é referenciado no método construct e deve ser instanciado na classe que herdar a classe Bootstrap.
POr fim devemos utilizar o extends para aplicar a herança dessa classe na classe Route.
Aula 512 - Views Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Dentro do diretório App criamos o diretório View. Esse diretório será responsável pelos arquivos de interface com o navegador. Para manter os arquivos ordenados, criamos um novo diretório index que irá conter os arquivos tratados pelo arquivo indexController. Caso tenhamos outros Controllers podemos criar diretórios individuais para cada um deles.
Nesse diretório criamos os arquivos para cada uma das actions que já definimos, no caso index.phtml e sobreNos.phtml. A extensão .phtml é utilizada pelos frameworks para indicar arquivos HTML com scripts em PHP.
A seguir incluimos um pequeno script HTML nos arquivos para testar o encaminhamento das rotas.
Para entender um pouco sobre como irá funcionar a camada de Model incuimos um array de dados no arquivo do Controller para simular a recepção de dados de um DB e apresentar esses dados na View pela comunicação através do namespace.
Como estamos trabalhando com otimização de código sempre que temos a repetição de scripts devemos criar métodos para isso. Desse modo criaremos um método render() para evitar a utilização do require_once() para cada uma das actions do Controller. Criado esse método passamos como parametro o valor da action para a função render() e a executamos no lugar de utilizar o require_once em cada método. Porém agora temos problemas para a recuperação dos dados de cada método pois eles passam a ficar fora do escopo do método render(). Para resolver isso devemos passar também os dados como parametro da função render().
Ainda para automatização do render() desde que mantido o padrão de nomes dos arquivos e diretórios do Controller fizemos alguns tratamentos nos nomes das classes com o método str_replace() para recuperar apenas o nome do diretório para o caminho do arquivo da View.
public function render($view, $dados)
{
$actualClass = str_replace('App\\Controllers\\', '', get_class($this));
$viewLocation = str_replace('Controller', '', $actualClass);
$viewLocation = lcfirst($viewLocation);
require_once "../app/Views/" . $viewLocation . "/" . $view . ".phtml";
}
Atenção para o ajuste do case dos caracteres pois o Windows não é case sensitive ao contrário de sistemas baseados em Linux e isso pode gerar erros.
Veja que recuperamos o nome da classe com o namespace (App\Controllers\IndexController) removemos o caminho e removemos a palavra Controller do arquivo. Por fim incluimos o nome do prefixo do Controller como diretório do caminho para o arquivo da View. Para que isso funcione corretamente é necessário manter os padrões de nomes de arquivos e diretórios.
O próximo ajuste a fazer é com os dados passados por parametro. Como estamos trabalhando com OO podemos criar um novo objeto para armazenar esses dados e dessa forma passa-los para fora do escopo local.
Para isso criamos um atributo private e criamos um método construct onde instanciaremos no atributo criado um objeto vazio do PHP utilizando a propriedade stdClass(). Esse objeto vazio pode receber como atributo 'dados' e seu array de valores. Desse modo temos um objeto do tipo stcClass que pode ser transferido para fora do escopo do método em que ele foi criado para os outros métodos da mesma classe (no caso do método index ou sobreNos para o método render) e assim não precisamos mais utilizar o valor desses dados como parametro do método render.
Uma útima observação é que o método render() não é um requisito funcional da aplicação portanto pode ser removido dessa classe.
Aula 513 - Abstração dos Controllers Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Continuando com a abstração das classes, na classe IndexController temos dois métodos não funcionais para a aplicação, que são o contructor e o render. Esses métodos são de utilização genérica e portanto devem ser movidos para o diretório criado em vendor. Desse modo criamos um diretório Controller e nele criamos um arquivo chamado Action. Esse arquivo receberá todos os atributos e métodos não funcionais da classe IndexController (no caso o atributo de dados e os métodos constructor e render).
A classe criada deve ser extendida para a classe IndexController, bem como a inclusão do namespace com o use.
Por fim devemos ajustar a visibilidade do método render() e do atributo de dados para protected.
Aula 514 - Definindo e reutilizando um Layout Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Vamos criar aqui um template para o layout padrão do sistema. Para isso criamos um documento phtml no diretório Views com a inclusão do Bootstrap. Estou utilizando o CDN dos Bootstrap 4 dessa vez invés da versão local.
O arquivo layout1 irá receber apenas um método chamado content().
Esse método por sua vez passará a executar a lógica que anteriormente estava no render() que é a renderização do conteúdo da página de acordo com a action recebida pelo Route e executada pelo indexController.
Já o método render() passará a incluir o arquivo layout que por sua vez será o responsável por renderizar o conteúdo do Controller.
Aqui o problema passa a ser que o parametro recebido pelo método render() não é mais encaminhado para o content() que é o responsável pela renderização da view correta. Para passar esse parametro do render() para o content() eu criei uma variável no escopo global e passei essa variável para o content() na página de layout.
Já na aula foi utilizado o objeto padrão criado no método contruct() para receber esse parametro e desse modo executá-lo diretamente no require do método content(). Assim ao invés de passar o parametro $view como fazíamos diretamente no método render() passamos o atributo do objeto padrão diretamente no método content().
Com essa abordagem podemos agora criar diversos layouts para o app e selecioná-lo diretamente no método render(). Podemos inclusive passar o layout desejado como parâmetro pelo indexController, alterando caso necessário o layout dinamicamente. Por fim criamos uma lógica para que caso não exista o arquivo de layout selecionado apresentemos na tela apenas o conteudo sem layout, o que poderia ser substituido por um layout básico.
Aula 515 - Conexão com banco de dados e Models Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para a criação da camada de Model criamos um pequeno banco de dados para testes.
Em seguida criamos um arquivo de conexão definindo o namespace para App. Nesse arquivo criamos a classe Connection que é responsável por estabelecer a conexão, via PDO com o banco de dados. Nessa classe criamos um método do tipo public static para facilitar a execução do método sem a necessidade de instanciar a classe:
Utilizando método static: public static function getDB(){}
...
$conn = Connection::getDB();
Sem utilizar o método static: public function getDB(){}
...
$conn = new Connection();
$conn->getDB();
Criamos a conexão com a função try/catch.
Quando utilizamos namespace caso precisemos utilizar classes nativas do PHP devemos identifica-las com o sinal de '\' no início caso contrário teremos um erro pois a aplicação irá procurar a classe no namespace.
Em seguida criamos um novo arquivo para a classe Produto dentro do diretório Models. Esse arquivo irá receber a conexão com o DB pelo Controller em seu construtor e será o responsável por executar a query junto ao banco.
O método criado nesse Model irá executar a query no banco de dados. Nesse exemplo apenas faremos o SELECT dos dados do DB. A seguir passamos como retorno do método a execução da query e o fetchAll do valor recebido do PDO. Note que como já passamos a conexão instanciada executamos os métodos do PDO diretamente no return do método do Model:
public function getProdutos()
{
$query = "SELECT id, descricao, preco FROM tb_produtos";
return $this->db->query($query)->fetchAll();
}
Os dados recebidos pelo método do Model devem agora ser tratados para exibição diretamente na View (ou ser tratados no Controller).
Aula 516 - Melhorando a forma como os Controllers trabalham com os Models Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Nessa aula inserimos uma nova tabela ao DB e incluimos alguns registros. Criamos um novo arquivo no Model com a classe Info que tem os mesmos métodos que a classe Produto.
Feito isso instanciamos novamente a conexão na agora no método sobreNos() da classe IndexController. Em seguida instanciamos a classe Info e executamos o seu método para executar a query no DB. Essas informações são recuperadas no arquivo da View sobreNos.
Nesse momento o Controller está repetindo as instruções para instanciar a conexão e as classes de Model. Devemos então abstrair essa classe para automatizar essas instancias.
Método da View index:
public function index()
{
$conn = Connection::getDB();

$produto = new Produto($conn);
...
Método da View sobreNos:
public function sobreNos()
{
$conn = Connection::getDB();

$info = new Info($conn);
...
Para esse ajuste o padrão é a criação de uma nova classe Container que será a responsável por executar o instanciamento conforme a rota selecionada.
Para essa classe importamos a conexão no namespace e criamos apenas um método responsável por executar o método da conexão e inserir o valor recebido por parametro no caminho para instanciar a classe do Model desejada. Esse método retorna a classe do Model já instanciada. Aqui para esse método também utilizamos um método do tipo static.
Aula 517 - Abstração dos Models e considerações finais Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para finalizar as abstrações de códigos repetidos criamos uma nova classe Model que conterá uma classe abstract responsável por criar o atributo $db e criar o método construct que inicializa o PDO.
Essa classe será extendida em todas as classes do Model que necessitam de conexão ao DB.

Seção 19 - PHP7 & MySQL - Projeto aplicado Twitter Clone

Aula 518 - Iniciando o projeto e configurando as páginas index e inscrever-se Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Neste projeto vamos aproveitar o miniframework MVC criado na seção anterior.
Basicamente ajustamos i IndexController, removemos os Models, copiei o conteudo dos arquivos da View (incluindo o novo layout), icluimos arquivos de estilo CSS e imagens no diretório public e fiz o ajuste do arquivo Route de acordo com os novos componentes.
Aula 519 - Registrando usuarios Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Aqui iniciamos o projeto construindo o DB com a primeira tabela (usuarios).
A seguir ajustamos o nome do DB no arquivo de conexão e criamos o arquivo de Model com a classe User. Essa classe tem como atributos os mesmos valores dos registros da tabela no DB. A seguir iniciamos a criação dos métodos dessa classe. Primeiramente criamos os métodos setters/getters utilizando os super métodos genéricos (__set() e __get()).
Com isso podemos iniciar a criação dos métodos do CRUD, sendo o primeiro o método de criação de usuário. Para esse método utilizamos o prepared statement do PDO passando as váriáveis no modo bind. Esses valores são todos recuperados do objeto User através do método __get() para cada um dos atributos.
public function salvar()
{
$query = "INSERT INTO usuarios (nome, email, senha) VALUES(:nome, :email, :senha)";
$stmt = $this->db->prepare($query);
$stmt->bindValue(':nome', $this->__get('nome'));
$stmt->bindValue(':email', $this->__get('email'));
$stmt->bindValue(':senha', $this->__get('senha'));
$stmt->execute();

return $this;
}
Criada a query precisamos trabalhar na View do formulário de inscrever-se para indicar a action do formulário. Os dados desse formulário serão direcionados para um novo arquivo chamado registrar que deve portanto ser incluido no Route.
Feitos os ajustes no formulário de inscreverse para que os dados sejam encaminhados pelo método POST criamos o método registrar() na classe IndexController para que seja feito o tratamento desses dados. Esses dados devem ser incluidos na classe User utilizando o método setter dessa classe. Para isso primeiramente devemos instanciar o objeto User como um objeto da classe Model uma vez que ele faz parte dessa classe. Para isso já temos os métodos de inclusão das querys no mini-framework que instanciam a classe de acordo com o valor passado por parametro no método getModel() da classe Container (ver Aula 516). Desse modo podemos instaciar a classe User diretamente através Container (lembrando que o método getModel() é static) e definir o valor dos atributos recebidos via POST no objeto com o método __set() para que esses dados sejam incluidos no DB com o método salvar():
public function registrar()
{
$user = Container::getModel('User');
$user->__set('nome', $_POST['nome']);
$user->__set('email', $_POST['email']); $user->__set('senha', $_POST['senha']);
$user->salvar();
}
A seguir fizemos um novo método para testar os dados enviados (verificando o valor através do método __get()) e no caso não executar caso o numero de caracteres de cada campo for menor que 3. Esse método passa a ser executado antes da execução do método salvar(). Criamos outro método para erificar se já existe no DB o email a ser cadastrado. Para esse método executamos uma query de pesquisa para verificar se existe o email digitado. Com o resultado dessa query podemos fazer uma nova condicional que verifica impede o método salvar() de ser executado caso o retorno seja diferente de 0 ou null (no meu caso utilizei a '!' do retorno da função, já na aula foi utilizado a função PHP count() para verificar se o retorno é maior que 0).
Para tratar o sucesso ou erro no cadastro do usuário, realizamos duas inserções no código. Para o caso de inclusão com sucesso, renderizamos uma nova View chamada Cadastro que apresenta uma mensagem de sucesso para o usuário. Para os casos de erro detectados pelas condicionais criadas redirecinamos o usuário para a página de cadastro novamente, renderizando a View de inscrição, porém criamos uma variável que verifica o estado de erro. Essa variável tem o valor inicial false e caso seja executado a condicional de erro passa a ser true. Com essa variável podemos executar uma inserção na View que indica que houve um erro de dados. Por fim para que os dados preenchidos não sejam perdidos armazenamos seus valores em um array para que eles possam ser mantidos na View recarregada de modo a permitir que o usuário edite os dados digitados.
No projeto da aula quando acessamos o formulário inscreverse diretamente temos o preenchimento dos valores com mensagens de erro pois o value está setado, portanto é feito a limpeza dos dados das tags. Na minha execução esse erro não acontece.
Aula 520 - Autenticação de usuários Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Agora que já estamos cadastrando os usuários no DB, vamos criar as rotinas de autenticação e acesso dos usuários ao sistema. Para isso vamos criar 3 novas rotas no sistema:
  • autenticar
  • sair
  • timeline
Para a autenticação iremos utilizar um Controller diferente que será criado no arquivo AuthController.
Nesse arquivo criamos a classe correspondente e o método autenticar. Esse método será executado conforme o direcionamento criado no arquivo de Route. Em seguida foi feito o apontamento do formulário de login para essa rota através do action.
Esse método irá receber o email e senha do usuário e armazená-los com o método __get() nas váriáveis do objeto user() que deve ter sido instanciado através de Container::getModel('User'). Com isso podemos executar um novo método autenticar() que será executado no Model de User e que fará uma query ao DB recuperando o id, nome, email para tendo como filtro o email e senha
Para essa query podemos apenas recuperar os dados com o método PDO fetch (não é necessário fazer o fetchAll() pois esperamos apenas um resultado). Como temos a condição na query (email AND senha) o retorno só será válido caso os dois valores sejam coincidentes.
Com isso podemos fazer uma verificação ainda no Model para testar se a query retornou o id e nome do usuário que está sendo autenticado. Caso positivo incluimos esses dados no objeto User de modo que podemos recuperar o objeto com todas as informações no AuthController e com isso verificar se existe um id e nome válidos no objeto para iniciar a session.
Em caso de erro no login redirecionamos com o header para a raiz da página passando um parametro na URL para que possamos fazer um tratamento do erro:
header('Location: /?login=erro');
Com esse redirecionamento podemos tratar no método index() da classe IndexController (que é responsável por executar a action index e a renderização da view), o valor recibido por GET na URL.
$this->viewData->login = isset($_GET['login']) ? $_GET['login'] : '';
Aqui verificamos com um operador ternário se existe algum valor recebido através do $_GET com a chave login. Já na View do index incluimos um trecho HTML que será incluido na página caso o valor verificado por esse operador ternário seja diferente de '' e igual a 'erro'.
Para o caso de sucesso iremos iniciar uma session passando o id e nome do usuário e redirecionando para uma nova rota a ser criada.
session_start();
$_SESSION['id'] = $user->__get('id');
$_SESSION['nome'] = $user->__get('nome');

header('Location: /timeline');

A nova rota timeline será controlada por um novo Controller que será exclusivo para tratamento das views autenticadas. Desse modo criamos a nova rota timeline que terá como Controller a classe AppController.
Essa classe irá inicializar a session e verificar se os atributos id e nome estão definidos para a seguir executar o direcionamento para a View da timeline. Caso não hajam os dados de login redirecionamos para a view inicial com a URL de erro.
Como foi criado um novo Controller devemos criar um novo diretório para a View do App pois a classe Action trata o caminho para as views em função da classe do controller. Nesse diretório por enquanto apenas criamos uma view simples com o nome timeline com uma tag de link para sair. Como esse link direciona o endereço para '/sair', criamos uma nova rota para esse caminho que por sua vez irá executar o método com o mesmo nome. Esse método ficará no AuthController e apenas irá destruir a sessão e apontar novamente para a página inicial:
public function sair()
{
session_start();
session_destroy();
header('Location: /');
}

Aula 521 - Incluindo criptografia MD5 para senha de usuários Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para ajustar a forma com que as senhas são armazenadas para o modelo criptografado em md5 utilizamos a função nativa do PHP md5(). Essa função deve ser passada no POST que recebe os dados do HTML, e no que irá transmitir a senha para a query de verificação no DB. Esse método gera uma chave de 32 caracteres, portanto o campo no DB deve ter esse tamanho.
Podemos utilizar esse método também no SQL para atualizar os registros já gravados sem a conversão:
UPDATE usuarios SET senha = md5(senha) WHERE id = 1;
Aula 522 - Criando a Timeline, incluindo e listando Tweets Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Primeiramente incluimos o arquivo com a View do App (html e css). A seguir inserimos uma nova tabela no DB responsável por armazenar os textos dos tweets. Nesse momento não foi criada um FOREIGN KEY para ligar as duas tabelas, embora exista um campo em comum entre as tabelas (verificar se a junção das tabelas será feita apenas na lógica da aplicação).
Com isso devemos agora criar um novo Model para tratar essa nova camada de dados. Nessa classe criamos os atributos private com os campos da tabela e os métodos getter/setter.
Seguindo a metodologia de trabalho criamos uma nova Route para fazer o tratamento do texto inserido, que será responsável por executar a action que irá gravar os dados no DB.
Essa action será um método do AppController, que deverá verificar se a session está iniciada utilizando a mesma lógica que foi definida para a timeline.
Agora devemos configurar o form do HTML para encaminhar os dados via POST e instanciar o Model responsável pelos dados do Tweet.
Com já estamos recebendo os dados via POST devemos definir os atributos com o id_usuario recuperado da SESSION e o tweet recuperado do POST e utilizar o método __set() para defini-los e em seguida criar no Model a query de inclusão do tweet, utilizando o statement.
A seguir vamos implementar o método no Model para recuperar os tweets que serão apresentados na View de acordo com o usuário. Nesse método criamos uma query de todos os tweets e retornamos o valor em um array associativo. Esse array deverá ser tratado no AppController pela action timeline pois é ela que renderiza a View que apresentará os dados no navegador.
Para a query completa fizemos um LEFT JOIN com a tabela de usuários e fizemos o filtro passando o valor do id de ususário recebido na $_SESSION.
$query = "SELECT t.id, t.id_usuario, t.tweet, DATE_FORMAT(t.data, '%d/%m/%Y %H:%i') as data, u.nome FROM tweets as t LEFT JOIN usuarios as u ON (u.id = t.id_usuario) WHERE t.id_usuario = :id_usuario" ORDER BY t.data DESC;
O retorno dessa query é recebido no AppController e é armazenado em nosso Objeto Padrão. Com os dados gravados nesse objeto, basta executar um foreach() no valor do atributo e apresentar cada resultado na view.
Para o retorno da data foi utilizada a função DATE_FORMAT diretamente na query SQL para retornar a data e hora no formato: '%d/%m/%Y %H:%i'.
Por fim, como estamos executando o inicio de sessão e a validação de usuário tanto no método timeline() como no método tweet() podemos abstrair essas ações para um novo método.
Aula 523 - Pesquisando por outros usuarios Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para esse módulo da aplicação criamos uma nova rota quemseguir que fará parte do AppController e terá uma View para pesquisa de usuários. Essa View enviará via GET o nome a ser buscado no DB para incluir como seguidor.
A View quemSeguir faz um teste ternário para verificar se foi enviado algum dado com método GET e caso positivo executa instancia o Container com o model User. No model User criamos um novo método pesquisar() que busca o nome de usuário pesquisado. Essa query é feita com a seleção por LIKE para que possamos utilizar coringas na busca.
Com o retorno da busca devemos apenas executar um foreach() para percorrer todos os dados do array de retorno e apresentar na View.
Aula 524 - Seguindo e deixando de seguir outros usuários Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Nessa aula a primeira coisa a aser feita é alterar o filtro de quem seguir para remover o próprio usuário da busca de usuários.
Eu passei o nome do usuário como parametro no método (embora acredite que não seja o mais correto para esse modelo de programação). Na verdade o melhor é passar o id pois podemos ter homonimos na busca.
Já na resolução foi aproveitado o atributo id do objeto. Não achei muito correto pois estamos fazendo a busca de um nome e enviando um id que não será desse nome, mas talvez seja a melhor maneira.
O indicado agora é criar um Model separado para a tabela usuarios_seguidores entretanto nessa aplicação utilizamos o Model já existente User Nesse ponto minha aplicação ficou bastante diferente do proposta na aula. O primeiro ponto foi que no AppController eu defini o tipo de ação recuperando o valor do método passado pelo GET. Na aula o valor do GET passa por um teste lógico para definir qual operação realizar (seguir ou deixar de seguir) Outra diferença é que eu criei um atributo para receber o valor do id_usuario_seguindo e na aula esse valor foi passado por parametro no método:
Minha solução:
$acao = isset($_GET['acao']) ? $_GET['acao'] : '';

$segue = Container::getModel('User');
$segue->__set('id', $_SESSION['id']);
$segue->__set('id_usuario_seguindo', $_GET['id_usuario']);
$teste = $segue->$acao();
Solução da aula:
$acao = isset($_GET['acao']) ? $_GET['acao'] : '';
$segue = Container::getModel('User');
$segue->__set('id', $_SESSION['id']);
if($acao == 'seguir'){
$segue->seguir($_GET['id_usuario'])
} else if($acao == 'deixar_de_seguir'){
$segue->deixar_de_seguir($_GET['id_usuario'])
}
Com relação a possibilidade de inclusão de mais de um registro de seguindo para o mesmo usuário eu criei um teste a ser executado a cada tentativa de nova inclusão que verifica se já existe o par na tabela de junção. Já na aula a solução foi remover a ação que já foi realizada, ou seja se o usuário não está seguindo apenas será apresentada a opção de seguir e vice-versa.
Nesse ponto o grande passo é criar uma query no método getAll() para que possamos recuperar o status de "seguir" ou "deixar de seguir" dos usuários.
Eu fiz uma query utilizando o LEFT JOIN para recuperar os valores da tabela de junção e caso o valor seja null saber que o usuário não está sendo seguido.
Já o professor fez uma sub-query para contar se existem ocorrencias de seguir (count = 1) ou náo (count = 0) e com isso criar a lógica.
Minha query com LEFT JOIN:
SELECT u.id, u.nome, u.email, s.id_usuario_seguindo, s.id_usuario
FROM usuarios AS u
LEFT JOIN usuarios_seguidores AS s
ON (u.id=s.id_usuario_seguindo AND
:id=s.id_usuario)
WHERE u.nome LIKE :nome
AND NOT u.id = :id;

Query criada na aula:
SELECT u.id, u.nome, u.email,
(SELECT COUNT(*) FROM usuarios_seguidores as s
WHERE s.id_usuario = :id
AND s.id_usuario_seguindo = u.id) AS seguindo_sn
FROM usuarios AS u
WHERE u.nome LIKE :nome
AND NOT u.id = :id";
Feita essa query apenas devemos ajustar a View para mostrar o botão Seguir ou Deixar de seguir em cada caso.
Aula 525 - Exibindo tweets de outros usuários na timeline Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Para apresentar os tweets dos usuários que estão sendo seguidos na timeline do usuário logado incluimos na query de getAll() do Model Tweet um novo seletor para recuperar os tweets dos usuários que estão no relacionamento da tabela de junção.
SELECT t.id, t.id_usuario, t.tweet, DATE_FORMAT(t.data, '%d/%m/%Y %H:%i') as data, u.nome
FROM tweets as t
LEFT JOIN usuarios as u ON (u.id = t.id_usuario)
WHERE t.id_usuario = :id_usuario
OR t.id_usuario IN (SELECT id_usuario_seguindo FROM usuarios_seguidores WHERE id_usuario = :id_usuario)
ORDER BY t.data DESC;

Feito isso precisamos, na View remover o botão Remover dos tweets que não sejam do usuário logado.
Por fim foi sugerido que o método para remover os tweets fosse implementado, o que foi simples de resolver (eu precisei criar um input tipo hidden para armazenar os dados do tweet a ser apagado).
Um detalhe é que eu já implementei os contaddores de tweets e seguidores/seguindo por minha conta.
Aula 526 - Exibindo dados no perfil do usuário e ajustes finais Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Nessa aula foi feita apenas as implementações dos contadores e do nome de usuário que eu já tinha feito anteriormente. A única diferença relvante foi que a query de contagem de tweets foi feita no Model de User e não na de Tweet. Isso facilita pois não é necessário instanciar os dois Model e podemos fazer tudo em apenas um método. Entretanto no exemplo da aula todas as querys foram executadas diretamente no método que vai renderizar a View. No meu caso criei um método separado que é executado em cada um dos método das Views.
Aula 527 - Extra (P&R) - Deploy Twiter Clone parte 1
Informações sobre tipos de servidores web (servidor de hospedagem vs. cloud)
Aula 528 - Extra (P&R) - Deploy Twitter Clone parte 2 (local)
Ajustes de caminhos de diretórios para utilização da aplicação no XAMPP e inclusão do arquivo .htaccess para tratamento das rotas.
A estrutura do .htaccess utilizada á a que segue:
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]
</IfModule>

O tutorial do .htaccess pode ser encontrado em Apache Docs
Aula 529 - Extra (P&R) - Deploy Twiter Clone parte 3 (Servidor de hospedagem com cPanel)
Informações sobre como fazer o deploy da aplicação em ambientes com o gerenciador CPanel.
Inicialmente foi criado o DB, o usuário e a sua vinculação (pela interface cPanel). Note que aqui podemos fazer a seleção dos direitos do usuário sobre o DB conforme nossa necessidade (o ideal é que o usuário utilizado na aplicação tenha apenas os direitos necessários para as querys definidas).
A seguir foram criadas as tabelas no DB através do phpMyAdmin.
Por fim foi feito o upload dos arquivos através do FTP (FileZila). A estrutura de arquivos foi a mesma utilizada no ajuste para deploy local (gravado diretamente na raiz do XAMPP).
Aula 530 - Extra (P&R) - Paginação de registros parte 1 - Introdução, Limit e Offset
Nessa aula tivemos uma introdução ao sistema de paginação utilizando os operadores LIMIT e OFFSET do SQL. Esses operadores permitem limitar a quantidade de registros a serem retornados por um query e definir quantos registros devem ser pulados para iniciar o retorno da mesma. Desse modo de definirmos o LIMIT de um SELECT em '10' teremos apenas 10 registros retornados. Por outro lado se definirmos o OFFSET em '30' teremos apresentados os registros de '31' a '40'.
Aula 531 - Extra (P&R) - Paginação de registros parte 2 Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
Aqui foi criada a rotina para dividir a quantidade de registros apresentados em quantidades pré definidas. Para isso criamos uma nova query que é executada por um método que passa por parametro o valor do LIMIT e do OFFSET. A query resultante ficou como segue:
public function getPorPagina($limit, $offset)
{
$query = "SELECT t.id, t.id_usuario, t.tweet, DATE_FORMAT(t.data, '%d/%m/%Y %H:%i') as data, u.nome
FROM tweets as t
LEFT JOIN usuarios as u ON (u.id = t.id_usuario)
WHERE t.id_usuario = :id_usuario
OR t.id_usuario IN (SELECT id_usuario_seguindo FROM usuarios_seguidores WHERE id_usuario = :id_usuario)
ORDER BY t.data DESC, t.id DESC
LIMIT $limit
OFFSET $offset";
$stmt = $this->db->prepare($query);
$stmt->bindValue(':id_usuario', $this->__get('id_usuario'));
$stmt->execute();

return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
Note que incluí um novo item para o ORDER BY pois nos casos em que o campo de DATETIME tem muitos valores iguais o retorno estava ficando instável. Com isso durante a aula foi criada uma rotina no Controller para definir manualmente os valores de LIMIT e OFFSET.
Além disso foi incluido um trecho do Bootstrap para a barra de páginas. Eu já fiz toda a lógica para esse seletor funcionar, porém isso será executado na próxima aula.
Aula 532 - Extra (P&R) - Paginação de registros parte 3 Para executar essa aplicação utilizar o servidor do PHP ($ php -S localhost:porta)
As lógicas para as páginas ficaram iguais as que eu fiz, com a diferença que não foi implementada a lógica para os deslocamentos Previous e Next mas apenas o apontamento para a primeira e última página.
A única coisa a adicionar foi a lógica na tag <li> para setar a página ativa.

Seção 20 - API com Slim Framework

Aula 533 - O que irei aprender? API com Slim Framework
Criação de APIs para que terceiros acessem o sistema com o Slim Framework.
Aula 534 - Entendendo requisições e API
Os sistemas Web trabalham no sistema de Rquisição (request) e Resposta (response). O padrão de requisição utilizado é o HTTP e os principais tipos de requisições podem ser:
  • GET: Recuperar dados do servidor
  • POST: Criar dado novo no servidor
  • PUT: Atualizar dados no servidor
  • DELETE: Apagar dados no servidor
Essas requisições retornam códigos de status entre eles 404 - Not Found, 200 - Ok, 501 - Bad Gateway, etc.
API (em portugues Interface de Programação de Aplicações) é uma maneira de interagir com outra aplicações como um login através de outra plataforma, serviços de clima, CEP, mapas, etc.
Podemos utilizar o recurso de APIs para mandar os dados para diversos tipos de plataformas diferentes. Assim podemos enviar a resposta das requisições tanto para uma aplicação Desktop quanto para um app Mobile, fazendo com que nosso conteudo esteja disponível para ambas as plataformas.
As APIs podem enviar retornos de diversas formas como em XML ou JSON.
Para a criação de nossa API iremos utilizar o Slim Framework que é um framework PHP indicado para a criação de aplicações Web e APIs.
Para instalação do Slim Framework utilizamos o Composer com o comando a seguir:
$ composer require slim/slim:3.*
Ou caso o composer seja instalado no projeto:
php composer.phar require slim/slim:3.* Feito isso podemos teste a aplicação utilizando um função de teste disponível no site do framework.
Aula 535 - Rotas com Slim
Vamos iniciar estudando a documentação do Slim diretamente na página do framework Slim - First Aplication Walkthrough.
Aqui temos as explicações sobre o arquivo inicial, as classes inicializadas e o método de acesso a esse primeiro exemplo. Note que a aplicação nesse momento só vai apresentar uma resposta se executarmos a URL padrão seguida de /hello/alguma_coisa. Isso pode gerar alguma dúvida quanto ao funcionamento da página.
A primeira configuração a fazer é do arquivo .htaccess para que possamos reescrever os endereços da URL. A configuração sugerida é a seguinte:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule . index.php [L]
Sem essa configuração o servidor php -S funciona sem a indicação do index.html na URL porém o XAMPP não funciona.
Agora vamos iniciar um novo arquivo index.php desde o início.
Primeiramente definimos o require do autoload do composer. Em seguida instanciamos a classe App do Slim (já utilizando o namespace).
Com essa classe instanciada podemos executar os métodos do Slim, entre eles o get() que recebe a rota desejada e uma callback com a lógica que irá retornar as informações para a aplicação. Devemos ainda executar o método run() para que o método anterior seja processado. Com isso podemos encaminhar como retorno dados como texto ou mesmo arquivos JSON como nas APIs tradicionais.
Desse modo podemos criar as rotas de modo bem simples, sem a necessidade da criação manual dos arquivos de direcionamento (como fizemos no miniframework criado anteriormente).
Quando criamos as rotas podemos definir utilizando '{}' um placeholder que irá esperar um valor além da rota para a execução da requisição:
$app->get('/usuarios/{id}'
Nesse caso espera um valor qualquer (no caso o id) na URL.
Podemos passar na funcão de callback dois parametros: request e response. Podemos por exemplo utilizar o parametro request para, através do método getAttribute() recuperar o valor passado no placeholder da URL para tratamento posterior.
$app->get('/usuarios/{id}', function ($req, $res) {
$id = $req->getAttribute('id');
echo 'Lista de usuarios ou ID: ' . $id;
});
Quando utilizamos as '{}' como placeholder estamos indicando para o Slim que aquele valor é obrigatório. Quando queremos indicar um valor opcional utilizamos os '[]'.
$app->get('/usuarios[/{id}]', function ($req, $res) {
$id = $req->getAttribute('id');
echo 'Lista de usuarios ou ID: ' . $id;
});
No exemplo acima o id não é mais obrigatório na URL, entretanto se houver uma '/' após o termo usuários passa a ser necessário. Podemos inclusive criar critérios de seleção mesclados como por exemplo:
'/postagens[/{ano}[/{mes}]]' Outro modo de utilizar o placeholder é passando os valores para um atributo seguido de ':'. Após esse valor podemos definir um RegEx para definir quais valores podem ser digitados. Com essa construção podemos recuperar vários valores e tratá-los do modo que for necessário:
$app->get('/lista/{itens:.*}', function ($req, $res) {
$itens = $req->getAttribute('itens');
var_dump((explode('/', $itens)));
});
No exemplo acima tudo que for digitado separado por '/' será um item em um array.
Podemos também criar nomes para rotas para facilitar o acesso em outras requisições. Isso pode ser feito utilizando o método setName() na rota que queremos aplicar um novo nome.
Feito isso podemos recuperar a rota através do nome alternativo utilizando o parametro router e executando o método pathFor() para passar o nome e os parametros necessários.
$app->get('/blog/postagens/{id}', function ($req, $res) {
$id = $req->getAttribute('id');
echo 'Listar postagem para um ID ';
})->setName('blog');

$app->get('/meusite', function ($req, $res) {
$retorno = $this->get('router')->pathFor('blog', ['id' => '5']);

echo $retorno;
});
Por fim também é possivel agrupar rotas em uma rota principal. Nesse caso a rota que agrupará as demais rotas não utilizará o método get() mas sim o método group():
$app->group('/v1', function () {
$this->get('/usuarios', function () {
echo 'Lista de usuarios';
});
$this->get('/postagens', function () {
echo 'Lista de postagens';
});
});
Aula 536 - Tipos de requisição
O PHP tem alguns padrões de projeto que estão documentados no site PHP_FIG. Aqui podemos verificar alguns padrões para codificação entre eles o que analisaremos nessa aula (PSR-7). A vantagem de trabalhar com Frameworks é que eles são desenvolvidos para obedecer esses padrões.
Agora incluimos o namespace para o Request e Response e indicamos para as variáveis das funções das rotas o caminho para eles. Essa ação não é obrigatória pois o Slim faz isso, mas é uma boa prática.
A resposta das requisições podem ser feitas diretamente pelos métodos padrão do PHP, entretanto utilizando o padrão PSR-7 devemos utilizar o parametro Response para executar os métodos da resposta:
$res->getBody()->write("Listagem de postagens");
Vamos agora estudar os verbos HTTP GET, POST, PUT e DELETE. Esses verbos são equivalentes a ações de DB:
  • GET -> SELECT
  • POST -> INSERT
  • PUT -> UPDATE
  • DELETE -> DELETE
Para trabalhar com as requisições HTTP vamos utilizar algumas ferramentas. Entre as ferramentas sugeridas na aula temos duas extensões do Chrome. O RestEasy que não é mais uma extensão e o Postman que pode ser instalado como extensão ou como aplicativo. Tenho ainda instalados a extensão Boomerang e o aplicativo Insomnia.
Com o Postman podemos testar requisições de qualquer tipo diretamente na API sem a necessidade da criação de formulários ou outras fontes para envio de dados.
Já vimos que podemos recuperar os dados enviados na URL com o método em get() da classe App do Slim. Para recuperarmos dados enviados por POST podemos utilizar o método post(). Os dados serão recebidos no parametro request da callback. Para ler esses dados utilizamos o método getParsedBody() do parametro request:
$app->post('/usuarios/adiciona', function (Request $req, Response $res) {
$post = $req->getParsedBody();
return $res->getBody()->write($post['nome'] . ' ' . $post['email']);
});
Note que utilizamos o parametro response para enviar os dados para a tela. A requisição acima espera receber qualquer dado em POST e no caso gera uma resposta para os parametro nome e email. Se fizermos um var_dump() na super-global $_POST veremos que a aplicação recebe qualquer valor enviado.
Os métodos put() e delete() tem o mesmo funcionamento que o método post(), porém para a remoção de itens com o método delete() o indicado é receber o dado na URL como no método get().
Aula 537 - Serviços e dependencias
Quando criamos classes externas às funções anonimas utilizadas pelo Slim a instancia do objeto dessa classe não fica visível na função. Podemos resolver isso utilizando o método use() ou podemos utilizar um serviço de Dependedncy Injection. No nosso exemplo iremos utilizar o Pimple (no momento desse curso o site do Pimple já não estava mais disponível. A extensão pode ser acessada via Packagist/Pimple e através do GitHub do projeto. Esse projeto está fechado) que é um serviço que acompanha a instalação do Slim.
Para fazer a injeção de dependencia primeiro devemos criar uma variável de Container que irá executar o método getContainer(). Essa variável