Guia de Desenvolvimento Laravel — Pt. 1

Guia de Desenvolvimento Laravel — Pt. 1GABRIEL MOURÃO DE MELOBlockedUnblockFollowFollowingDec 14O PHP sempre teve diversos frameworks open-source, como o Zend Framework, o Codeigniter e Symfony que estão no mercado desde 2005 e 2006, e mais muitos outros que rodeiam os desenvolvedores PHP, diferente dos três frameworks nomeados acima, o Laravel teve sua primeira versão beta lançada a pouco tempo, em 2011, mas mesmo sendo recente, o Laravel sempre está no topo, ou quase no topo, de todas listas de sites e blogs especializados como ‘Framework PHP mais popular’.Um dos principais fatores para isso é a versatilidade de uso e a cobertura de abstrações para seu código, que abrange praticamente todas as necessidades básicas de desenvolvimento, e o que não for abrangido pela versão base, devido a comunidade bastante ativa, existem dezenas de componentes e pacotes de terceiros que podem suprir sua necessidade.A intenção desse grupo de artigos é aprofundar a cima de todos os recursos e componentes do Framework Laravel, assim como fornecer exemplos práticos e aplicados em todos os módulos de cada artigo, tomaremos como guia de tópicos a lista de tópicos para a certificação oficial Laravel (https://certification.laravel.com/prepare/), por esse motivo consideraremos que o leitor já tenha todos os conhecimentos necessários na linguagem PHP, consideremos também que já possua o conhecimento necessário sobre a ferramenta de gerenciamento de pacotes Composer e que ao menos já tenha uma base com Framework Laravel.1 — ArquiteturaO Laravel é um framework que pode ser dito como ‘focado em serviços’, ou seja, existe um container, e esse container é responsável por armazenar os recursos de sua aplicação e fornece-los como serviço as diversas partes do código, esse container também é responsável por armazenar dados como objetos, dados comuns, closures, enfim, quaisquer tipo de dados que um objeto de classe armazene, esse container nativo do Laravel é a instância de IlluminateFoundationApplication, veremos no modulo relacionado. O modelo padrão de arquitetura utilizado pelo framework é o MVC, as Models do Laravel são extensões de IlluminateDatabaseEloquentModel, o Controller é uma extensão de IlluminateRoutingController, e a View é uma extensão de IlluminateViewView, cada uma de suas responsabilidades ficam bem dispostas de fácil acesso dentro do posicionamento das pastas, veremos detalhadamente cada uma em das classes citadas acima em seus respectivos módulos.Além do que foi citado nos dois parágrafos anteriores ainda existe alguns outros fatores importantes sobre a arquitetura do framework, como o funcionamento do roteamento, dos Middlewares, entre alguns outros, porém também serão tratados em seus respectivos módulos.1.1 — Request LifecycleO ciclo de vida de uma requisição para a sua aplicação tem início no arquivo index.php, presente dentro da pasta ‘public’ de seu projeto, esse arquivo será responsável por incluir os arquivos que iniciam a aplicação, o autoloader para os arquivos importados via Composer e o arquivo responsável por fazer o start da aplicação (bootstrap/app.php), aqui é onde é injetado em forma de singleton o Kernel da parte responsável pelas requisições Http feitas para a aplicação (AppHttpKernel), na sequência o Kernel do console é que é injetado, o Kernel do Console é responsável pelas chamadas via terminal/console para a sua aplicação, o melhor exemplo acerca do uso dessa injeção é o próprio artisan, sem a injeção dos serviços de Console seria impossível utilizar os recursos do artisan na sua aplicação, e por fim é injetado no método singleton o Exception Handler ( AppExceptionsHandler ).Após as injeções primárias, de volta ao arquivo index.php, a instância de IlluminateFoundationApplication é usada para construir uma nova instância separada do Kernel Http, logo após isso a requisição é capturada e tratada, e após passar pelo código da aplicação estamos prontos para retornar ao Client.Durante o ciclo de vida da requisição, após a injeção dos Kernels, é feito a injeção dos serviços declarados como Service Providers da sua aplicação dentro da instância de IlluminateFoundationApplication, aqui chamada de Service Container, os serviços serão injetados na ordem de iteração da array onde os mesmos são declarados, esse é um ótimo recurso para injeção de dependências dentro da classe, veja abaixo um exemplo de um classe tendo um model sendo injetado através do método construtor válido dentro do ciclo de processo da aplicação ( após a injeção dos serviços) :<?phpnamespace AppHttpControllers;use AppUser;use AppLojasLojasModel;use AppHttpControllersController;class LojasController extends Controller{ /** * Implementação de um model de Lojas através do construtor * * @var LojasModel */ protected $lojas;/** * Criação de uma nova instância do controller de lojas LojasController. * * @param LojasModel $lojas * @return void */ public function __construct(LojasModel $lojas) { $this->lojas = $lojas; }/** * Exibe a loja localizada através do model injetado * * @param int $id * @return Response */ public function show($id) { $loja = $this->loja->find($id);return response()->json($loja); }}No exemplo acima, podemos ver que a classe controller LojasController possui definido em seu construtor, que o atributo público lojas irá receber uma nova instância limpa de LojasModel, instância a qual ficará disponível em qualquer local da classe, vemos isso na chamada do método index, onde o atributo lojas é utilizado para se localizar a loja através do primaryKey.Porém, essa injeção só possível se o serviço a ser colocado no container possuir um provedor ativo, o provedor de serviços do Laravel é chamado de Service Provider, e nativamente já possui uma série de serviços registrados no projeto inicial, no caso do exemplo, a injeção da classe só foi efetuada pois a classe DatabaseServiceProvider, do pacote Illuminate/Database, está definida como um dos serviços iniciais do Laravel, e esta classe é um FactoryBuilder para os serviços de conexão com banco de dados e para o Eloquent (O Eloquent é um QueryBuilder que prove construção de querys semânticas, das mais complexas as mais simples, detalharemos no artigo correspondente ao mesmo), sendo esta instância construída através do FactoryBuilder do Illuminate Eloquent, onde todas as dependências de conexão e métodos do QueryBuilder são injetados junto com o Model.1.2 — Service Container Binding and ResolutionÉ possível, injetar classes dentro do Service Container de diversas formas, o que vimos no primeiro exemplo, foi um exemplo de injeção via typehint, o ato de efetuar um Bind de um serviço ao Service Container, é a ligação de serviços de acessibilidade global dentro o seu Container principal, um bind simples é feito da seguinte forma:$this->app->bind(‘LocationValidatorValidator’, function ($app) { return new LocationValidatorValidator($app->make(‘HttpClient’));});Sabendo que, esse trecho de código está localizado dentro de uma herança da classe IlluminateSupportServiceProviders, ela irá adicionar esse serviço dentro da instância principal Application.Porém, em certos momentos, podemos precisar de serviços que não serão necessários em outras etapas da aplicação, a forma dinâmica de resolução de serviços fornecida pelo IlluminateSupportServiceProvider permite que serviços sejam ligados a aplicação em qualquer tempo da execução do código, a sintaxe permanece praticamente a mesma, podendo ser feita com a função auxiliar global app() ou a instância de Applicationapp()->bind(‘LocationValidatorValidator’, function ($app) { return new LocationValidatorValidator($app->make(‘HttpClient’));});A caso de semântica, caso deseje ligar um serviço declaradamente único a sua instância de aplicação, podes utilizar o método singleton, assim gerando um resolução de acesso único em qualquer chamada feita dentro do controle do Container.Outra possibilidade dentro da resolução de serviços é possibilidade de definir um objeto já instanciado como um serviço da sua aplicação:$locationValidator = new LocationValidatorValidator();app()->instance($locationValidator);A possibilidade de definir uma instância como serviço em tempo de execução lógica pode vir a ser útil quando um serviço precise ser tratado de acordo o perfil do usuário por exemplo, uma parte da lógica pode ser executada durante a execução do serviço de autenticação, e apenas após isso ligar a instância do serviço de análise do perfil.Também dentro da ligação de serviços (Binding), é possível atrelar valores a suas classes através do que o Framework chama de Binding Primitives:$this->app->when(‘LocationValidatorValidator’)->needs(‘$local’)->give(request()->ip());Como visto no exemplo acima, é possível definir um valor em tempo de chamada para a classe que será ligada, podendo assim definir um serviço específico de acordo com o valor, como novamente por exemplo, direcionar um grupo de ips de usuários para uma parte em testes de sua aplicação, diminuindo o possível impacto de um erro.Ainda dentro do Container Binding, um outro recurso é a possibilidade de implementar classes com interfaces, assim tornando-as acessíveis via typehint dentro do controle do Container$this->app->bind( ‘LocationValidatorContractsLocationValidatorContract’, ‘LocationValidatorValidator’);public function __construct(LocationValidatorValidator $local){ $this->local = $local;…}Ainda dentro do Container Binding, um desenvolvedor pode acabar caindo em uma problema, ‘duas classes com a mesma interface’, pensando na solução desse problema, o Laravel fornece um recurso dentro do Container Binding chamado de Contextual Binding, numa tradução literal ‘Ligação Contextual’, que nada mais é que um condicionamento semântico para definir qual será instanciado em sua aplicaçãouse AppServerServer;use AppServerContractsServerContract;use AppServerEuropeServer;use AppServerAmericasServer;$this->app->when(AmericasServer::class) ->needs(ServerContract::class) ->give(function () { return Server::run(‘Americas’); });$this->app->when([EuropeServer::class]) ->needs(ServerContract::class) ->give(function () { return Server::run(‘Europe’); });Em alguns casos podemos precisar acessar vários serviços de uma única vez, isso é possível através do tagging de seus serviços, o tagging pode ser feito de forma simples, apenas informando suas instâncias e as nomeando:$this->app->bind(‘AccessLogs’, function () { … });$this->app->bind(‘CPULogs’, function () { …});$this->app->tag([‘AccessLogs’, ‘CPULogs’], ‘logs’);$this->app->bind(‘LogHandler’, function ($app) { return new LogHandler($app->tagged(‘logs’));});Como é demonstrado no exemplo acima, as duas instâncias ‘tageadas’ como ‘logs’ dentro do Container de serviços foram passadas como parâmetro para o LogHandler com uma única chamada.Após instanciar todos os seus serviços ou durante a construção do Service Container, você pode acessar seus serviços através do métodos de resolução, App::make, resolve ou App::makeWith, o ato de acessar o serviço, previne a necessidade de criar novas instâncias em tempo de execução da lógica de sua aplicação, podendo por exemplo, utilizar o serviço de autenticação para prover a política de permissão do usuário autenticado (Auth Service Provider) fora do container.$policies = app()->make(‘AppAuthPolicies’);$policies = resolve(‘AppAuthPolicies’);Ou caso sua injeção de classe do serviço precise de parâmetros, você pode fornecê-los através de uma array associativa pelo método de Application, resolveWith:$policies = app()->makeWith(‘AppAuthPolicies’, [‘ignoreUploadRule’ => true]);Um outro recurso muito útil para o desenvolvimento de aplicação, a para o desenvolvimento de semânticas, é o typehint das dependências e injeção de valores dentro dessas dependências diretamente através da semântica dos parâmetros dos métodos, como é utilizado pelo Controller para receber um parâmetro de URL através do router,namespace AppHttpControllers;class companiesController extends Controller{public $local;public function __construct(companiesModel $companies) { $this->companies = $companies;}public function show($id) {}…}O exemplo acima, quando consumido, irá retornar a empresa com primaryKey correspondente ao id passado como parâmetro, citamos no exemplo que podemos consumir esse recurso através do router, porém ele pode ser consumido em qualquer parte da execução de sua aplicação.E por último e não menos importante, quando estamos tratando do Container, temos o recurso de eventos, o evento disponível é o resolving, e está disponível de duas formas, de maneira global, onde é disparado para qualquer resolução de serviço, ou declarado para um serviço específico.$this->app->resolving(function ($object, $app) { // Será disparado sempre que um objeto de qualquer tipo for chamado});$this->app->resolving(AppAuthPolicies::class, function ($api, $app) { // Será disparado sempre que for um objeto do tipo AppAuthPolicies a ser resolvido});No parágrafo anterior o recurso de eventos foi chamado de último recurso, porém este está disponível apenas nas versões 5.5 e posteriores, como estamos tratando desde a versão 5.1 até a atual (no momento da escrita 5.7), um recurso bem específico que pela a implementação da interface padrão de container aplicada pelo padrão de PSR-11, (https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-11-container.md) a especificação recomendada para implementação de uma interface de container para serviços, é possível recuperar a instancia do Laravel Container atráves da interface de container padrão:use PsrContainerContainerInterface;Route::get(‘/policies’, function (ContainerInterface $container) { $service = $container->get(‘policies’);});Nesse primeiro artigo da série, tratamos a base do service container, do ciclo de vida de uma requisição, e fizemos um overview sobre a ordem e funcionamento da arquitetura de uso de uma aplicação desenvolvida com o Framework Laravel, intenção dessa série é servir como guia de desenvolvimento e referência de estudos para certificação então aguarda as próximas publicações.See Ya. More details

Leave a Reply