Uma pessoa esta a procura de emprego e dentre as várias vagas que existem no mercado (disponibilizadas nesse JSON) e ela quer encontrar vagas que estejam de acordo com o que ela saiba fazer, seja direto pelo cargo ou atribuições que podem ser encontradas na descrição das vagas. Para atender essa necessidade precisamos:
- uma API simples p/ procurar vagas (um GET p/ procurar as vagas no .json disponibilizado);
- deve ser possível procurar vagas por texto (no atributos title e description);
- deve ser possível procurar vagas por uma cidade;
- deve ser possível ordenar o resultado pelo salário (asc e desc);
O teste deve ser feito utilizando PHP (com ou sem framework, a escolha é sua). Esperamos como retorno, fora o GET da API funcionando, os seguintes itens:
- uma explicação do que é necessário para fazer seu projeto funcionar;
- como executar os testes, se forem testes de unidade melhor ainda;
- comentários nos códigos para nos explicar o que está sendo feito.
Lembre-se que na hora da avaliação olharemos para:
- organização de código;
- desempenho;
- manutenabilidade.
git clone https://github.com/edugalb/backend-test.git
cd backend-test
composer install
php -S localhost:8000 -t web
phpunit
localhost:8000/api/v1/vagas
localhost:8000/api/v1/vagas?title=Filtro no titulo
localhost:8000/api/v1/vagas?description=Descricao
localhost:8000/api/v1/vagas?cidade=Cidade Escolhida
localhost:8000/api/v1/vagas?title=Filtro no titulo&cidade=Cidade Escolhida&description=Descricao
localhost:8000/api/v1/vagas?field=salario&order=asc
localhost:8000/api/v1/vagas?field=salario&order=desc
localhost:8000/api/v1/vagas?title=recepcionista&field=salario&order=desc
'title' => 'max_len,255',
'description' => 'max_len,255',
'cidade' => 'max_len,50',
'field' => 'max_len,10',
'order' => 'contains,asc desc',
http-code: 200
content-type: application/json
body:
{
status: 1,
alerta: "Listagem realizada com sucesso"
data: [Json com elementos listados]
}
http-code: 400
content-type: application/json
body:
{
status: 0,
alerta: "Erro ao validar o campo order"
data: []
}
$app->get( 'api/v1/vagas', function (Request $request) use ($app) {
$ctrl = new App\Vagas\Controller\VagasController($app);
return $ctrl->listVagas($request);
});
Sua única responsabilidade é acionar o controller correspondente a rota especificada, o parametro Request $request
é injetado automáticamente pelo silex e corresponde a requisição HTTP que foi realizada
public function __construct(Application $app)
{
$this->app = $app;
}
O controller é construído passando o Container do Silex Application $app
. Esta abordagem torna as classes mais flexíveis em caso de alteração de framework, portanto ao invés de Extender o Controller do silex e utilizar o método Connect eu preferi injetar direto o application
public function listVagas(Request $request)
{
// Construindo o Response
$response = $this->app['ResponseFactory']->create();
// Delegando a responsabilidade para o service
try {
$data = $this->app['vagas.service']->loadData($request->query->all());
} catch (\Exception $e) {
return $response->makeWithException($e);
}
// Retornando o Response
return $response->makeWithSuccess($data, 'Listagem realizada com sucesso');
}
No caso acima eu preferi manter o controller "Magro" sem regras de negócio, sua única responsabilidade é trabalhar com o Request $request
e Response $response
. Sendo assim ele utiliza uma factory para criar o response (Irei falar mais adiante sobre factories) e delega o processamento para o VagasService
que irá conter toda regra de negócio. Esta abordagem ajuda na criação dos testes unitários para o controller
public function loadData(array $query)
{
// Fabricando o DataMapper
$dm = $this->app['DataMapperFactory']->create('vagas');
// Validando e Filtrando os dados de input
$validatedQuery = VagasValidator::validate($query);
// Acionando o DataMapper passando os filtros e a ordenação
return $dm->loadWithParams(
VagasValidator::getFiltersFromQuery($validatedQuery),
VagasValidator::getOrderFromQuery($validatedQuery)
);
}
O service utiliza a factory para criar o VagasDataMapper
, realiza a validação dos dados e por fim traduz os parametros que foram passados para o modelo que o DataMapper precisa para realizar a consulta
Responsável por carregar os dados baseado em filtros, ordená-los e retornar um array
com os elementos
A utilização de factories auxilia na manutenção futura, utilizando o princípio "Open Closed" do SOLID nós podemos criar novas classes e instancia-las sem edição do código já existente
Baseado no arquivo config.php
que contém as configurações nós podemos instanciar novas classes. Por padrão utilizamos o json
$app['response'] = [
'type' => 'json',
];
Sendo assim a classe JsonResponseService
será construída para trabalhar com o response. caso queira alterar a resposta, basta criar uma classe que implemente a interface ResponseInterface
. Ex:
class XmlResponseService implements ResponseInterface
{
public function makeWithException(\Exception $e)
{
...
}
public function makeWithSuccess($data, $alerta)
{
...
}
}
E depois colocar o prefixo da classe no config.php
$app['response'] = [
'type' => 'xml',
];
Essa abordagem facilita muito na escalabidade do sistema, o Response está desacoplado da regra de negócio
Como a camada de persistência pode mudar e é comum mudar ao longo do tempo, eu preferi utilizar uma factory também, isso faz com que caso nós queiramos mudar a fonte de dados do arquivo vagas.json
para um Banco de dados, só precisamos fazer o seguinte:
- Criar uma classe que implemente o
DataMapperInterface
class VagasDatabaseDataMapper implements DataMapperInterface
{
public function loadWithParams(array $filters, array $order)
{
....
}
}
- Alterar o
config.php
para carregar esta classe
$app['database'] = [
'name' => 'Database',
];