Projeto implementado durante curso de API com Symfony 5.4 e PHP 7.3.5.
Consiste em uma api que relaciona médicos a especialidades.
Rota | Método | Descrição | BODY PARAMS | QUERY PARAMS |
---|---|---|---|---|
/login | GET | Retorna token obrigatório em todas as outras requisições | { |
- |
O login e senha padrão são "usuario" e "123456". A autenticação é feita passando um Bearer Token como Authorization.
Rota | Método | Descrição | BODY PARAMS |
---|---|---|---|
/medicos | POST | Cadastra um médico | { |
/medicos | GET | Retorna todos os médicos | - |
/medicos/{id} | GET | Retorna médico por id | - |
/medicos/{id} | PUT | Atualiza médico por id | { |
/medicos/{id} | DELETE | Remove médico por id | - |
Rota | Método | Descrição | BODY PARAMS |
---|---|---|---|
/especialidades | POST | Cadastra uma especialidade | { |
/especialidades | GET | Retorna todas as especialidades | - |
/especialidades/{id} | GET | Retorna especialidade por id | - |
/especialidades/{id} | PUT | Atualiza especialidade por id | { |
/especialidades/{id} | DELETE | Remove especialidade por id | - |
É possível ordenar os dados, por exemplo:
http://localhost:8080/medicos?sort[crm]=ASC&sort[nome]=DESC
Também é possível filtrar passando os parâmetros pela url:
http://localhost:8080/medicos?crm=123456
Utilize a páginação da seguinte forma:
http://localhost:8080/medicos?page=1&itensPorPagina=2
composer create-project symfony/skeleton:"^5.4" consultorio-alura
php -S localhost:8080 -t public
composer require annotation
Automaticamente anotações serão interpretadas como rotas.
Exemplo: A rota /ola executa o método olaMundoAction() que faz parte de uma classe criada na pasta de Controller.
/**
* @Route("/ola")
*/
public function olaMundoAction ()
{
echo 'Olá , mundo!';
exit();
}
Para informar os métodos permitidos por essa rota
/**
* @Route("/medicos", methods={"POST"})
*/
Para passar parâmetros para essa rota
/**
* @Route("/medicos/{id}", methods={"GET"})
*/
Esses parâmetros são recuperados no Request.
/**
* @Route("/ola")
*/
public function olaMundoAction ()
{
return new JsonResponse(['mensagem' => 'Olá, mundo!']);
}
Também é possivel passar um segundo parâmetro pro objeto JsonResponse com o status HTTP. Se esse parâmetro não é informado, automaticamente é enviado um status 200.
Pela classe Response é possível obter status como, por exemplo, 204:
Response::HTTP_NO_CONTENT
O método do controller recebe uma Requisição HTTP (Request) e retorna uma resposta HTTP (Response) que são classes que devem ser importadas na classe do Controller.
/**
* @Route("/ola")
*/
public function olaMundoAction (Request $request) : Response
{
$pathInfo = $request->getPathInfo();
return new JsonResponse([
'mensagem' => 'Olá, mundo!',
'pathInfo' => $pathInfo
]);
}
Retornando uma resposta:
/**
* @Route("/medicos")
*/
public function novo(Request $request) : Response
{
$corpoRequisicao = $request->getContent();
return new Response($corpoRequisicao);
}
Retornando uma resposta em Json:
return new JsonResponse($medico);
- pega um parametro especifico da query string (parâmetros da URL)
$parametro = $request->query->get('parametro');
- pega todos os parametros da query string
$parametros = $request->query->all();
- pegar parametros de uma forma genérica
$parametro1 = $request->get('parametro')
- pegar parametro definido na URL da rota
$parametro1 = $request->attributes->get('parametro')
- pegar parametro do corpo da requisição
$parametro1 = $request->request->get('parametro')
$corpoRequisicao = $request->getContent();
composer require symfony/orm-pack
php bin\console doctrine:database:create
Antes é preciso alterar as configurações no arquivo .ENV
Inclua o código ao arquivo config/services.yaml
public: true
Utilizando injeção de dependência é criado um gerenciador de entidade que irá fazer as operações necessárias no banco.
A classe EntityManagerInterface necessita ser importada.
/**
* @var EntityManagerInterface
*/
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
O método persist 'observa' a entidade que recebe como parametro até que seja utilizado o método flush para de fato persistir as alterações no banco.
As alterações são mapeadas em memória otimizando a performance da aplicação.
$this->entityManager->persist($medico);
$this->entityManager->flush();
Caso a entidade seja obtida por meio do repositório, não há necessidade de usar o método persist pois o doctrine já 'observa' automaticamente essa entidade.
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
public $id;
No código acima é informado que o atributo id é um campo do tipo identificador com valor gerado automaticamente e no formato inteiro.
Para informar um formato texto (char, varchar, text) basta fazer da seguinte forma:
@ORM\Column(type="string")
Gerar migration verificando se há diferenças entre o banco e o que foi mapeado para atualizar o banco
php bin\console doctrine:migrations:diff
php bin\console doctrine:migrations:migrate
Mais informações/resumos sobre doctrine
Para utilizar repositórios é preciso fazer a classe de controller estender a classe AbstractController que é do próprio Symfony.
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
class MedicosController extends AbstractController
{
...
$repositorioDeMedicos = $this->getDoctrine()->getRepository(Medico::class);
$medicoList = $repositorioDeMedicos->findAll();
É possivel receber como parâmetro do método parâmetros definidos na rota desde que tenham o mesmo nome.
/**
* @Route("/medicos/{id}", methods={"PUT"})
*/
public function atualiza(int $id, Request $request) : Response
{
$this->entityManager->remove($medico);
$this->entityManager->flush();
Ao deletar ou atualizar não é preciso retornar todos os dados pelo repositório. Para melhor performance use:
$medico = $this->entityManager->getReference(Medico::class, $id);
composer require maker
Listar tudo o que pode ser feito com maker
php bin\console list make
php bin\console make:entity
Após esse comando, o componente irá perguntar pelo nome, tipo e tamanho dos atributos dessa entidade e irá gerar automaticamente os códigos.
Para finalizar, crie e rode a migration.
php bin/console make:migration
php bin\console doctrine:migrations:migrate
Caso a entidade já exista, ao utilizar esse comando de criação de entidade, novos atributos podem ser adicionados.
php bin/console doctrine:database:drop
php bin\console make:controller
class Especialidade implements JsonSerializable
{
public function jsonSerialize()
{
return [
'id' => $this->getId(),
'descricao' => $this->getDescricao()
];
}
}
Ao implementar a classe JsonSerializable e o método jsonSerialize na classe de entidade Especialidade, o método será chamado ao chamar o metodo json_encode().
Recebe como primeiro parâmetro um filtro no formato array:
[
"crm" => "123456",
"nome" => "João"
]
O segundo parâmetro é um array para ordenação:
[
"crm" => "ASC",
"nome" => "DESC"
]
O terceiro e o quarto são para páginação e correspondem a quantidade de itens por página e o offset que determina a partir de qual item deve trazer.
$EntityList = $this->repository->findBy(
$informacoesDeFiltro,
$informacoesDeOrdenacao,
$itensPorPagina,
($paginaAtual - 1) * $itensPorPagina
);
composer require security
composer require firebase/php-jwt
php bin\console make:user
Criar uma migration
php bin\console make:migration
Rodar a migration
php bin\console doctrine:migrations:migrate
composer require orm-fixtures
php bin\console make:fixtures
php bin\console security:encode-pasword
php bin\console doctrine:fixtures:load
Define um método e o nome do método bate com o evento que é lançado.
Sabe que evento está ouvindo porque é definido.
Classe utilizada
use Psr\Cache\CacheItemPoolInterface;
$cacheItem = $this->cache->getItem(
$this->cachePrefix() . $entity->getId()
);
$cacheItem->set($entity);
$this->cache->save($cacheItem);
$this->cache->deleteItem($this->cachePrefix() . $id);
$cacheItem = $this->cache->getItem(
$this->cachePrefix() . $id
);
$cacheItem->set($entityExistente);
$this->cache->save($cacheItem);
Metodologia de criação de software: https://12factor.net
Monolog é uma biblioteca de logs muito usada.
Normalmente este tipo de operação de infraestrutura (gravar logs, enviar e-mails, etc) é feita de forma assíncrona, ou seja: Quando um evento do domínio acontecer, o mesmo é salvo em algum sistema de filas. Alguma rotina agendada (cron job) ou serviço (deamon) poderá acessar esta fila e realizar as operações depois que a requisição finalizar e o usuário já tiver recebido a resposta.
composer require --dev symfony/test-pack
php bin\phpunit
php bin\console -e test doctrine:database:create
php bin\console -e test doctrine:migrations:migrate
php bin\console -e test doctrine:schema:create
php bin\console -e test doctrine:fixtures:load
composer require --dev dama/doctrine-test-bundle
<!-- phpunit.xml.dist -->
<phpunit>
<!-- ... -->
<extensions>
<extension class="DAMA\DoctrineTestBundle\PHPUnit\PHPUnitExtension"/>
</extensions>
</phpunit>
Mais sobre testes usando banco de dados: https://symfony.com/doc/current/testing/database.html
composer require symfony/css-selector
Mais sobre testes com seletores: https://symfony.com/doc/2.8/testing.html#the-crawler
Twig é uma template engine para PHP
composer require twig
em config>packages>security.yaml trocar login do firewalls > main > pattern pra (especialidades_html|login)