diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..f8d382f9 --- /dev/null +++ b/.env.example @@ -0,0 +1,30 @@ +# Environment: LOCAL | DEVELOP | STAGING | PRODUCTION +ENVIRONMENT= + +# Environment Local +LOCAL_HOST= +LOCAL_USER= +LOCAL_PASSWORD= +LOCAL_DATABASE= +LOCAL_DEBUG= + +# Environment Develop +DEVELOP_HOST= +DEVELOP_USER= +DEVELOP_PASSWORD= +DEVELOP_DATABASE= +DEVELOP_DEBUG= + +# Environment Staging +STAGING_HOST= +STAGING_USER= +STAGING_PASSWORD= +STAGING_DATABASE= +STAGING_DEBUG= + +# Environment Production +PRODUCTION_HOST= +PRODUCTION_USER= +PRODUCTION_PASSWORD= +PRODUCTION_DATABASE= +PRODUCTION_DEBUG= \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..451fb35e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +.idea \ No newline at end of file diff --git a/README.md b/README.md index 9d4dc0fb..9cbe4fc1 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,276 @@ -# A tarefa -Sua tarefa consiste em desenvolver uma API RESTful para manipular um determinado recurso. +### Tecnologias utilizadas: +- Composer +- Silex +- Doctrine +- MySQL +- Servidor Web: Nginx + PHP-FPM +- PHP 7.0 -# Requisitos -A escolha do recurso deverá ser feita pelo desenvolvedor, atendendo apenas os requisitos mínimos abaixo: +### Arquivos inclusos: +- **data/Test.postman_collection.json** - Arquivo do Postman com as rotas criadas e a autenticação configurada para Basic Auth. +- **data/db.sql** - Arquivo com o Dump do Banco de Dados -* Deverá conter um ID -* Deverá conter pelo menos cinco propriedades (exemplos: nome, email, etc.) -* Deverá conter campos que armazenem as datas de criação e alteração do recurso +### Como configurar o projeto: +1. Rodar o comando "php composer.phar install" -A API deverá atender às seguintes exigências: +2. Criar o banco de dados -* Listagem de todos os recursos -* Busca de um recurso pelo ID -* Criação de um novo recurso -* Alteração de um recurso existente -* Exclusão de um recurso -* Aceitar e retornar apenas JSON -* Deverá possuir algum método de autenticação para utilização de seus endpoints +3. Configurar o arquivo .env. Arquivo fica na raíz do projeto (.env.example) deve-ser criar um arquivo .env na raíz do projeto e configurar as seguintes informações: +* ENVIRONMENT = Ambiente que está rodando (Produção, desenvolvimento, etc.) +* HOST = host do banco de dados +* USER = usuário do banco de dados +* PASSWORD = senha do usuário do banco de dados +* DATABASE = nome do banco de dados +* DEBUG = modo debug | true ou false -# Ferramentas -* PHP -* Banco de dados SQLite -* Frameworks à escolha do desenvolvedor +4. Adicionar a pasta data/Proxy com permissão para escrita -# Fluxo de desenvolvimento -1. Faça um fork deste repositório -2. Crie uma nova branch e nomeie-a com seu usuário do Github -3. Quando o desenvolvimento estiver concluído, faça um pull request +5. Rodar o comando **bin/doctrine orm:schema-tool:update --dump-sql -f** para criar as entidades no banco de dados (Também pode importar o db.sql caso preferir) -# Padrões de nomenclatura -1. Código fonte, nome do banco de dados, tabelas e campos devem estar em inglês +### Configurações Nginx que foram utilizadas: +``` +server { + listen 80; + server_name local.capgemini; + + root /var/www/local.capgemini/public; + index index.php index.html index.htm; + access_log /var/log/nginx/local.capgemini-access; + error_log /var/log/nginx/local.capgemini-error; -**Inclua no seu commit todos os arquivos necessários para que possamos testar o código.** + location / { + try_files $uri $uri/ /index.php?$args; + fastcgi_read_timeout 5m; + index index.html index.htm index.php; + } + + error_page 404 /404.html; + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + location ~ \.php$ { + try_files $uri =404; + fastcgi_pass unix:/run/php/php7.0-fpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + #fastcgi_param ENVIRONMENT staging; + include fastcgi_params; + } +} +``` + +## Documentação da API + +### Modo de Autenticação: +- Utilizado Basic Auth +- Usuário: teste +- Senha: teste123 + +### Rota: +**Método e Recurso:** GET - /person + +**Descrição:** Lista todas as pessoas + +**Json retorno:** +``` +{ + "code": 200, + "message": "Requisição efetuada com sucesso.", + "data": [ + { + "first_name": "André", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "is_active": true, + "id": 2, + "created_at": { + "date": "2017-03-08 06:53:35.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": null, + "deleted_at": null + }, + { + "first_name": "João", + "last_name": "Almeida", + "email": "joao@email.com", + "gender": "M", + "is_active": true, + "id": 3, + "created_at": { + "date": "2017-03-08 06:54:45.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": null, + "deleted_at": null + }, + { + "first_name": "Rafaela", + "last_name": "Garcia", + "email": "rafaela@email.com", + "gender": "F", + "is_active": true, + "id": 4, + "created_at": { + "date": "2017-03-08 06:54:59.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": null, + "deleted_at": null + } + ] +} +``` + +### Rota: +**Método e Recurso:** GET - /person/{id} + +**Descrição:** Busca apenas uma pessoa por ID + +**Json retorno:** +``` +{ + "code": 200, + "message": "Requisição efetuada com sucesso.", + "data": { + "first_name": "André", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "is_active": true, + "id": 1, + "created_at": { + "date": "2017-03-08 06:53:35.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": null, + "deleted_at": null + } +} +``` + +### Rota: +**Método e Recurso:** POST - /person + +**Descrição:** Cria uma pessoa + +**Json envio:** +``` +{ + "first_name": "André", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "active": true +} +``` + +**Json retorno:** +``` +{ + "code": 200, + "message": "Requisição efetuada com sucesso.", + "data": { + "first_name": "André", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "is_active": true, + "id": 5, + "created_at": { + "date": "2017-03-08 06:58:03.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": null, + "deleted_at": null + } +} +``` + +### Rota: +**Método e Recurso:** PUT - /person + +**Descrição:** Atualiza uma pessoa com base no seu ID + +**Json envio:** +``` +{ + "id": 1, + "first_name": "André alterado", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "active": false +} +``` + +**Json retorno:** +``` +{ + "code": 200, + "message": "Requisição efetuada com sucesso.", + "data": { + "first_name": "André alterado", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "is_active": false, + "id": 1, + "created_at": { + "date": "2017-03-08 06:53:35.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": { + "date": "2017-03-08 06:59:32.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "deleted_at": null + } +} +``` + +### Rota: +**Método e Recurso:** DELETE - /person/{id} + +**Descrição:** Deleta uma pessoa com base em seu ID + +**Json retorno:** +``` +{ + "code": 200, + "message": "Requisição efetuada com sucesso.", + "data": { + "first_name": "André alterado", + "last_name": "Felipe", + "email": "email@email.com", + "gender": "M", + "is_active": false, + "id": 1, + "created_at": { + "date": "2017-03-08 06:53:35.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "updated_at": { + "date": "2017-03-08 06:59:32.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + }, + "deleted_at": { + "date": "2017-03-08 07:00:23.000000", + "timezone_type": 3, + "timezone": "America/Sao_Paulo" + } + } +} +``` \ No newline at end of file diff --git a/composer.json b/composer.json new file mode 100644 index 00000000..c4aa0b77 --- /dev/null +++ b/composer.json @@ -0,0 +1,30 @@ +{ + "type": "library", + "authors": [ + { + "name": "André Felipe de Souza", + "email": "andre.felipe3@gmail.com.br", + "role": "PHP Developer" + } + ], + "require": { + "php": ">=7.0.0", + "silex/silex": "2.0.*", + "doctrine/dbal": "2.*", + "doctrine/orm": "2.*", + "symfony/yaml": "^2.7", + "vlucas/phpdotenv": "2.*", + "zendframework/zend-hydrator": "^2.0", + "zendframework/zend-filter": "^2.6", + "zendframework/zend-servicemanager": "^2.5.1" + }, + "require-dev": { + "squizlabs/php_codesniffer": "2.*" + }, + "autoload": { + "psr-4": { "": "src/API/" } + }, + "config": { + "bin-dir": "bin/" + } +} \ No newline at end of file diff --git a/composer.phar b/composer.phar new file mode 100755 index 00000000..1987803b Binary files /dev/null and b/composer.phar differ diff --git a/config/cli-config.php b/config/cli-config.php new file mode 100644 index 00000000..d7724b14 --- /dev/null +++ b/config/cli-config.php @@ -0,0 +1,28 @@ + new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($entityManager->getConnection()), + 'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($entityManager), + 'dialog' => new \Symfony\Component\Console\Helper\QuestionHelper() +); + +$cli = new \Symfony\Component\Console\Application('Doctrine Command Line Interface', Doctrine\Common\Version::VERSION); +$cli->setCatchExceptions(true); + +$helperSet = $cli->getHelperSet(); + +foreach ($helpers as $name => $helper) { + $helperSet->set($helper, $name); +} + +$cli->addCommands(array( + new \Doctrine\ORM\Tools\Console\Command\SchemaTool\UpdateCommand() +)); + +$cli->run(); diff --git a/config/database.php b/config/database.php new file mode 100644 index 00000000..15fad149 --- /dev/null +++ b/config/database.php @@ -0,0 +1,36 @@ +setProxyDir(__DIR__ . '/../data/Proxy'); +$defaultConfig->setProxyNamespace('Proxy'); + +$driver = new SimplifiedYamlDriver([ + __DIR__ . '/../src/API/Person/Entity/Mapping' => 'Person\Entity', +]); + +$defaultConfig->setMetadataDriverImpl($driver); + +$entityManager = EntityManager::create( + [ + 'driver' => 'pdo_mysql', + 'host' => getenv(getenv('ENVIRONMENT') . '_HOST'), + 'port' => '3306', + 'user' => getenv(getenv('ENVIRONMENT') . '_USER'), + 'password' => getenv(getenv('ENVIRONMENT') . '_PASSWORD'), + 'dbname' => getenv(getenv('ENVIRONMENT') . '_DATABASE'), + 'charset' => 'utf8', + ], + $defaultConfig +); + +return $entityManager; diff --git a/config/env.php b/config/env.php new file mode 100644 index 00000000..4f2050c5 --- /dev/null +++ b/config/env.php @@ -0,0 +1,33 @@ +overload(); + +$environment = getenv('ENVIRONMENT'); + +$environments = ['LOCAL', 'DEVELOP', 'STAGING', 'PRODUCTION']; + +if (!in_array($environment, $environments)) { + throw new \Exception('You need to configure ENVIRONMENT in the .env file'); +} + +$envRequired[] = $environment . '_HOST'; +$envRequired[] = $environment . '_USER'; +$envRequired[] = $environment . '_PASSWORD'; +$envRequired[] = $environment . '_DATABASE'; +$envRequired[] = $environment . '_DEBUG'; + +$dotenv->required($envRequired); + +if (getenv($environment . '_DEBUG') === 'true') { + error_reporting(E_ALL); + ini_set('display_errors', 1); +} diff --git a/data/Test.postman_collection.json b/data/Test.postman_collection.json new file mode 100644 index 00000000..1c648615 --- /dev/null +++ b/data/Test.postman_collection.json @@ -0,0 +1,111 @@ +{ + "id": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "name": "Test", + "description": "", + "order": [ + "34ad05ad-0e8c-dcf8-f6ff-f963cbbe8d2d", + "b85a7fc3-0e85-7bc7-6e77-7f5f97a4daab", + "a8cc2398-224a-58e4-2dcd-01bf853560ec", + "65054035-da2a-24b2-16ee-1a73146282d5", + "bb8120cd-3902-e8cc-a215-0dd98ce36276" + ], + "folders": [], + "timestamp": 1488957828240, + "owner": "1223946", + "public": false, + "requests": [ + { + "id": "34ad05ad-0e8c-dcf8-f6ff-f963cbbe8d2d", + "headers": "Authorization: Basic dGVzdGU6dGVzdGUxMjM=\n", + "url": "http://{{host}}/person", + "preRequestScript": null, + "pathVariables": {}, + "method": "GET", + "data": null, + "dataMode": "params", + "version": 2, + "tests": null, + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1488962030974, + "name": "/person", + "description": "", + "collectionId": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "responses": [] + }, + { + "id": "65054035-da2a-24b2-16ee-1a73146282d5", + "headers": "Content-Type: application/json\nAuthorization: Basic dGVzdGU6dGVzdGUxMjM=\n", + "url": "http://{{host}}/person", + "preRequestScript": null, + "pathVariables": {}, + "method": "PUT", + "data": [], + "dataMode": "raw", + "tests": null, + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1488962065265, + "name": "/person", + "description": "", + "collectionId": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "responses": [], + "rawModeData": "{\n\t\"id\": 1,\n\t\"first_name\": \"André alterado\",\n\t\"last_name\": \"Felipe\",\n\t\"email\": \"email@email.com\",\n\t\"gender\": \"M\",\n\t\"active\": false\n}" + }, + { + "id": "a8cc2398-224a-58e4-2dcd-01bf853560ec", + "headers": "Content-Type: application/json\nAuthorization: Basic dGVzdGU6dGVzdGUxMjM=\n", + "url": "http://{{host}}/person", + "preRequestScript": null, + "pathVariables": {}, + "method": "POST", + "data": [], + "dataMode": "raw", + "tests": null, + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1488962057240, + "name": "/person", + "description": "", + "collectionId": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "responses": [], + "rawModeData": "{\n\t\"first_name\": \"André 2\",\n\t\"last_name\": \"Felipe\",\n\t\"email\": \"email@email.com\",\n\t\"gender\": \"M\",\n\t\"active\": true\n}" + }, + { + "id": "b85a7fc3-0e85-7bc7-6e77-7f5f97a4daab", + "headers": "Authorization: Basic dGVzdGU6dGVzdGUxMjM=\n", + "url": "http://{{host}}/person/1", + "preRequestScript": null, + "pathVariables": {}, + "method": "GET", + "data": null, + "dataMode": "params", + "tests": null, + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1488962047032, + "name": "/person/{id}", + "description": "", + "collectionId": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "responses": [] + }, + { + "id": "bb8120cd-3902-e8cc-a215-0dd98ce36276", + "headers": "Authorization: Basic dGVzdGU6dGVzdGUxMjM=\n", + "url": "http://{{host}}/person/5", + "preRequestScript": null, + "pathVariables": {}, + "method": "DELETE", + "data": null, + "dataMode": "params", + "tests": null, + "currentHelper": "normal", + "helperAttributes": {}, + "time": 1488962079241, + "name": "/person/1", + "description": "", + "collectionId": "ba038806-d9a1-68ad-22e5-59c6008cd5dc", + "responses": [] + } + ] +} \ No newline at end of file diff --git a/data/db.sql b/data/db.sql new file mode 100644 index 00000000..5d71bda4 --- /dev/null +++ b/data/db.sql @@ -0,0 +1,36 @@ +-- -------------------------------------------------------- +-- Host: 127.0.0.1 +-- Server version: 10.1.21-MariaDB-1~xenial - mariadb.org binary distribution +-- Server OS: debian-linux-gnu +-- HeidiSQL Version: 9.4.0.5125 +-- -------------------------------------------------------- + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET NAMES utf8 */; +/*!50503 SET NAMES utf8mb4 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; + + +-- Dumping database structure for test +CREATE DATABASE IF NOT EXISTS `test` /*!40100 DEFAULT CHARACTER SET utf8 */; +USE `test`; + +-- Dumping structure for table test.Person +CREATE TABLE IF NOT EXISTS `Person` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `created_at` datetime NOT NULL, + `deleted_at` datetime DEFAULT NULL, + `updated_at` datetime DEFAULT NULL, + `first_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, + `last_name` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `gender` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, + `active` tinyint(1) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; + +-- Data exporting was unselected. +/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */; +/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; diff --git a/public/index.php b/public/index.php new file mode 100644 index 00000000..39fd055d --- /dev/null +++ b/public/index.php @@ -0,0 +1,15 @@ +run(); diff --git a/src/API/Common/Entity/Abstraction/AbstractEntity.php b/src/API/Common/Entity/Abstraction/AbstractEntity.php new file mode 100644 index 00000000..a5383721 --- /dev/null +++ b/src/API/Common/Entity/Abstraction/AbstractEntity.php @@ -0,0 +1,34 @@ +hydrate($data); + } + } +} diff --git a/src/API/Common/Entity/Traits/DateTrait.php b/src/API/Common/Entity/Traits/DateTrait.php new file mode 100644 index 00000000..0bd23d90 --- /dev/null +++ b/src/API/Common/Entity/Traits/DateTrait.php @@ -0,0 +1,84 @@ +createdAt; + } + + /** + * @param mixed $createdAt + */ + public function setCreatedAt($createdAt) + { + if ($createdAt instanceof \DateTime) { + $this->createdAt = $createdAt; + } + } + + /** + * @return mixed + */ + public function getUpdatedAt() + { + return $this->updatedAt; + } + + /** + * @param mixed $updatedAt + */ + public function setUpdatedAt($updatedAt) + { + if ($updatedAt instanceof \DateTime) { + $this->updatedAt = $updatedAt; + } + } + + /** + * @return mixed + */ + public function getDeletedAt() + { + return $this->deletedAt; + } + + /** + * @param mixed $deletedAt + */ + public function setDeletedAt($deletedAt) + { + if ($deletedAt instanceof \DateTime || is_null($deletedAt)) { + $this->deletedAt = $deletedAt; + } + } +} diff --git a/src/API/Common/Entity/Traits/HydratorTrait.php b/src/API/Common/Entity/Traits/HydratorTrait.php new file mode 100644 index 00000000..893aef24 --- /dev/null +++ b/src/API/Common/Entity/Traits/HydratorTrait.php @@ -0,0 +1,26 @@ +hydrate($data, $this); + } + + public function toArray() + { + return (new ClassMethods)->extract($this); + } +} diff --git a/src/API/Common/Entity/Traits/IdTrait.php b/src/API/Common/Entity/Traits/IdTrait.php new file mode 100644 index 00000000..9e607660 --- /dev/null +++ b/src/API/Common/Entity/Traits/IdTrait.php @@ -0,0 +1,35 @@ +id; + } + + /** + * @param $id + */ + public function setId($id) + { + $this->id = $id; + } +} diff --git a/src/API/Common/Services/Controller/AbstractController.php b/src/API/Common/Services/Controller/AbstractController.php new file mode 100644 index 00000000..19eba975 --- /dev/null +++ b/src/API/Common/Services/Controller/AbstractController.php @@ -0,0 +1,46 @@ +getContent()); + + if (is_null($content)) { + throw new \Exception('Formato do JSON inválido.'); + } + + return $content; + } + + /** + * @param array $data + * @param int $code + * @param string $message + * @return JsonResponse + */ + protected function createResponse($data = [], $code = 200, $message = 'Requisição efetuada com sucesso.') + { + $response = ['code' => $code, 'message' => $message, 'data' => $data]; + return new JsonResponse($response); + } +} diff --git a/src/API/Common/Services/ExceptionHandler/ExceptionHandler.php b/src/API/Common/Services/ExceptionHandler/ExceptionHandler.php new file mode 100644 index 00000000..c33007ed --- /dev/null +++ b/src/API/Common/Services/ExceptionHandler/ExceptionHandler.php @@ -0,0 +1,27 @@ + $exception->getMessage(), 'status' => 'error']; + return new JsonResponse($response, $code, ['Content-Type' => 'application/json']); + } +} diff --git a/src/API/Common/Services/Persist/AbstractPersist.php b/src/API/Common/Services/Persist/AbstractPersist.php new file mode 100644 index 00000000..6621819b --- /dev/null +++ b/src/API/Common/Services/Persist/AbstractPersist.php @@ -0,0 +1,78 @@ +entityManager = $entityManager; + } + + /** + * @param AbstractEntity $entity + * @return AbstractEntity + */ + protected function create(AbstractEntity $entity) + { + if (empty($entity->getCreatedAt())) { + $entity->setCreatedAt(new \DateTime()); + } + + $this->entityManager->persist($entity); + return $entity; + } + + /** + * @param AbstractEntity $entity + * @return AbstractEntity + */ + protected function update(AbstractEntity $entity) + { + $entity->setUpdatedAt(new \DateTime()); + $this->entityManager->persist($entity); + return $entity; + } + + /** + * @param AbstractEntity $entity + * @return AbstractEntity + */ + protected function delete(AbstractEntity $entity) + { + $entity->setDeletedAt(new \DateTime()); + $this->entityManager->persist($entity); + return $entity; + } + + /** + * @param AbstractEntity $entity + * @return AbstractEntity + * @throws \Exception + */ + protected function remove(AbstractEntity $entity) + { + $this->entityManager->remove($entity); + return $entity; + } +} diff --git a/src/API/Common/Services/Retrieve/AbstractRetrieve.php b/src/API/Common/Services/Retrieve/AbstractRetrieve.php new file mode 100644 index 00000000..cbb354a3 --- /dev/null +++ b/src/API/Common/Services/Retrieve/AbstractRetrieve.php @@ -0,0 +1,54 @@ +entityRepository = $entityRepository; + } + + /** + * @return EntityRepository + */ + abstract protected function getEntityRepository(); + + /** + * @return ArrayCollection + */ + public function retrieveAll() + { + return new ArrayCollection($this->entityRepository->findBy(['deletedAt' => null])); + } + + /** + * @param $id + * @return null|object + */ + public function retrieveById($id) + { + $entity = $this->entityRepository->findOneBy(['id' => $id, 'deletedAt' => null]); + return $entity; + } +} diff --git a/src/API/Person/Entity/Mapping/Person.orm.yml b/src/API/Person/Entity/Mapping/Person.orm.yml new file mode 100755 index 00000000..84d49c81 --- /dev/null +++ b/src/API/Person/Entity/Mapping/Person.orm.yml @@ -0,0 +1,34 @@ +Person\Entity\Person: + type: entity + id: + id: + type: integer + generator: { strategy: AUTO } + fields: + createdAt: + column: created_at + type: datetime + deletedAt: + column: deleted_at + type: datetime + nullable: true + updatedAt: + column: updated_at + type: datetime + nullable: true + firstName: + column: first_name + type: string + lastName: + column: last_name + type: string + nullable: true + email: + type: string + nullable: true + gender: + type: string + nullable: true + active: + type: boolean + nullable: true \ No newline at end of file diff --git a/src/API/Person/Entity/Person.php b/src/API/Person/Entity/Person.php new file mode 100644 index 00000000..089eef89 --- /dev/null +++ b/src/API/Person/Entity/Person.php @@ -0,0 +1,122 @@ +firstName; + } + + /** + * @param string $firstName + */ + public function setFirstName($firstName) + { + $this->firstName = $firstName; + } + + /** + * @return string + */ + public function getLastName() + { + return $this->lastName; + } + + /** + * @param string $lastName + */ + public function setLastName($lastName) + { + $this->lastName = $lastName; + } + + /** + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * @param string $email + */ + public function setEmail($email) + { + $this->email = $email; + } + + /** + * @return string + */ + public function getGender() + { + return $this->gender; + } + + /** + * @param string $gender + */ + public function setGender($gender) + { + $this->gender = $gender; + } + + /** + * @return boolean + */ + public function isActive() + { + return $this->active; + } + + /** + * @param boolean $active + */ + public function setActive($active) + { + $this->active = $active; + } +} diff --git a/src/API/Person/Resources/resources.php b/src/API/Person/Resources/resources.php new file mode 100644 index 00000000..b57bbce5 --- /dev/null +++ b/src/API/Person/Resources/resources.php @@ -0,0 +1,13 @@ +get('/person', function (Request $request) use ($api) { + /** @var Person\Services\Controller\PersonController $PersonController */ + $PersonController = $api[\Person\Services\Controller\PersonController::class]; + + return $PersonController->get($request); +}); + +$api->get('/person/{id}', function (Request $request) use ($api) { + /** @var Person\Services\Controller\PersonController $PersonController */ + $PersonController = $api[\Person\Services\Controller\PersonController::class]; + + return $PersonController->getById($request); +}); + +$api->post('/person', function (Request $request) use ($api) { + /** @var Person\Services\Controller\PersonController $PersonController */ + $PersonController = $api[\Person\Services\Controller\PersonController::class]; + + return $PersonController->post($request); +}); + +$api->put('/person', function (Request $request) use ($api) { + /** @var Person\Services\Controller\PersonController $PersonController */ + $PersonController = $api[\Person\Services\Controller\PersonController::class]; + + return $PersonController->put($request); +}); + +$api->delete('/person/{id}', function (Request $request) use ($api) { + /** @var Person\Services\Controller\PersonController $PersonController */ + $PersonController = $api[\Person\Services\Controller\PersonController::class]; + + return $PersonController->delete($request); +}); diff --git a/src/API/Person/Resources/services/person.php b/src/API/Person/Resources/services/person.php new file mode 100644 index 00000000..c467aaf4 --- /dev/null +++ b/src/API/Person/Resources/services/person.php @@ -0,0 +1,30 @@ +getRepository(Person\Entity\Person::class) + ); +}; + +$api[Person\Services\Persist\PersonPersist::class] = function ($api) { + /** @var \Doctrine\ORM\EntityManager $entityManager */ + $entityManager = $api[\Doctrine\ORM\EntityManager::class]; + + return new Person\Services\Persist\PersonPersist( + $entityManager + ); +}; diff --git a/src/API/Person/Services/Controller/PersonController.php b/src/API/Person/Services/Controller/PersonController.php new file mode 100644 index 00000000..a8a857bd --- /dev/null +++ b/src/API/Person/Services/Controller/PersonController.php @@ -0,0 +1,127 @@ +personRetrieve = $personRetrieve; + $this->personPersist = $personPersist; + } + + /** + * @return JsonResponse + */ + public function get() + { + /** @var ArrayCollection|Person $personFromDB */ + $personFromDB = $this->personRetrieve->retrieveAll(); + + $return = []; + + foreach ($personFromDB as $person) { + $return[] = $person->toArray(); + } + + return $this->createResponse($return); + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \Exception + */ + public function getById(Request $request) + { + return $this->createResponse(($this->getPersonById($request->get('id')))->toArray()); + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function post(Request $request) + { + /** @var \stdClass $data */ + $data = $this->getJsonParameters($request); + return $this->createResponse(($this->personPersist->processCreate($data))->toArray()); + } + + /** + * @param Request $request + * @return JsonResponse + * @throws \Exception + */ + public function put(Request $request) + { + /** @var \stdClass $data */ + $data = $this->getJsonParameters($request); + + return $this->createResponse( + ($this->personPersist->processUpdate($data, $this->getPersonById($data->id ?? null)))->toArray() + ); + } + + /** + * @param Request $request + * @return JsonResponse + */ + public function delete(Request $request) + { + return $this->createResponse( + ($this->personPersist->processDelete($this->getPersonById($request->get('id'))))->toArray() + ); + } + + /** + * @param string|null $id + * @return null|Person + * @throws \Exception + */ + private function getPersonById(string $id = null) + { + /** @var Person|null $personFromDB */ + $personFromDB = $this->personRetrieve->retrieveById($id); + + if (!$personFromDB instanceof Person) { + throw new \Exception('Person not found with id: ' . $id); + } + + return $personFromDB; + } +} diff --git a/src/API/Person/Services/Persist/PersonPersist.php b/src/API/Person/Services/Persist/PersonPersist.php new file mode 100644 index 00000000..0424ce9d --- /dev/null +++ b/src/API/Person/Services/Persist/PersonPersist.php @@ -0,0 +1,63 @@ +entityManager->transactional(function () use ($data, $person) { + $this->create($person); + }); + + return $person; + } + + /** + * @param \stdClass $data + * @param Person $person + * @return Person + */ + public function processUpdate(\stdClass $data, Person $person) + { + $person->hydrate((array) $data); + + $this->entityManager->transactional(function () use ($data, $person) { + $this->update($person); + }); + + return $person; + } + + /** + * @param Person $person + * @return Person + */ + public function processDelete(Person $person) + { + $this->entityManager->transactional(function () use ($person) { + $this->delete($person); + }); + + return $person; + } +} diff --git a/src/API/Person/Services/Retrieve/PersonRetrieve.php b/src/API/Person/Services/Retrieve/PersonRetrieve.php new file mode 100644 index 00000000..08139de5 --- /dev/null +++ b/src/API/Person/Services/Retrieve/PersonRetrieve.php @@ -0,0 +1,26 @@ +entityRepository; + } +} diff --git a/src/api.php b/src/api.php new file mode 100644 index 00000000..3a895310 --- /dev/null +++ b/src/api.php @@ -0,0 +1,38 @@ +error(function (\Exception $exception, $request) use ($api) { + $exceptionHandler = new \Common\Services\ExceptionHandler\ExceptionHandler(); + return $exceptionHandler->createErrorResponse($exception, \Symfony\Component\HttpFoundation\Response::HTTP_OK); +}, 666); + +$api->before(function (Request $request, Application $api) { + if ('teste' !== $request->server->get('PHP_AUTH_USER') || 'teste123' !== $request->server->get('PHP_AUTH_PW')) { + header('WWW-Authenticate: Basic realm="Person"'); + header('HTTP/1.0 401 Unauthorized'); + header('Content-type: application/json'); + die; + } + + if (('POST' === $request->getMethod() || 'PUT' === $request->getMethod()) && + 'json' !== $request->getContentType() + ) { + throw new \Exception('Content-Type invalid for method. Only application/json is acceptable.'); + } + + $api[\Doctrine\ORM\EntityManager::class] = require_once __DIR__ . '/../config/database.php'; +}, Application::EARLY_EVENT); + +foreach (glob(__DIR__ . "/API/*/Resources/resources.php") as $filename) { + require_once $filename; +} + +return $api;