Bom momento, senhoras e senhores.
Há pouco tempo, encontrei o fenômeno de código duplicado e duplicado ao revisar um único projeto no Laravel.
A linha inferior é: o sistema possui alguma estrutura da API interna para solicitações AJAX, retornando essencialmente uma coleção de algo do banco de dados (pedidos, usuários, cotas, etc ...). O ponto principal dessa estrutura é retornar JSON com os resultados, nada mais. Na revisão de código, contei 5 ou 6 classes usando o mesmo código, a única diferença estava na injeção de dependência de ResourceCollection, JsonResource e no próprio modelo. Essa abordagem me pareceu fundamentalmente errada, e eu decidi fazer as minhas próprias, como acredito, as mudanças corretas nesse código, usando o poderoso DI que o Laravel Framework nos fornece.
Então, como cheguei ao que falarei mais tarde.
Eu já tenho cerca de um ano e meio de experiência em desenvolvimento para o Magento 2 e, quando encontrei esse CMS pela primeira vez, fiquei chocado com o DI. Para quem não sabe: no Magento 2, uma pequena parte do sistema não se baseia nos chamados "tipos virtuais". Ou seja, referindo-se a uma classe específica, nem sempre nos voltamos para a classe "real". Estamos nos referindo a um tipo virtual que foi "montado" com base em uma classe "real" específica (por exemplo, Coleção para a grade de administração, montada através do DI). Ou seja, podemos realmente criar qualquer classe para uso com nossas dependências simplesmente escrevendo algo semelhante no DI:
<virtualType name="Vendor\Module\Model\ResourceModel\MyData\Grid\Collection" type="Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult"> <arguments> <argument name="mainTable" xsi:type="string">vendor_table</argument> <argument name="resourceModel" xsi:type="string">Vendor\Module\Model\ResourceModel\MyData </argument> </arguments> </virtualType>
Agora, solicitando a classe Vendor \ Module \ Model \ ResourceModel \ MyData \ Grid \ Collection, obtemos uma instância da Magento \ Framework \ View \ Element \ UiComponent \ DataProvider \ SearchResult, mas com as dependências mainTable definidas como "vendor_table" e resourceModel - " Fornecedor \ Módulo \ Modelo \ Modelo de Recursos \ Meus Dados. "
No começo, essa abordagem não me parecia totalmente clara, nem inteiramente "apropriada" e nem totalmente normal, mas depois de um ano de desenvolvimento
para essa bola , eu, pelo contrário, tornei-me seguidora dessa abordagem e, além disso, encontrei aplicação em meus projetos .
De volta ao Laravel.
O DI Laravel é construído sobre um “contêiner de serviço” - uma entidade que gerencia ligantes e dependências no sistema. Assim, podemos, por exemplo, indicar à interface DummyDataProviderInterface a implementação dessa interface DummyDataProvider.
app()->bind(DummyDataProviderInterface::class, DummyDataProvider::class);
Então, quando solicitamos DummyDataProviderInterface no contêiner de serviço (por exemplo, através do construtor de classe), obtemos uma instância da classe DummyDataProvider.
Muitos
(por algum motivo) encerram esse conhecimento no contêiner de serviço do Laravel e vão fazer suas próprias coisas muito mais interessantes,
mas em vão .
O Laravel pode "ligar" não apenas entidades reais, como uma determinada interface, mas também criar os chamados "tipos virtuais" (também conhecidos como aliases). E, mesmo nesse caso, o Laravel não precisa passar por uma classe que implementa seu tipo. O método bind () pode assumir uma função anônima como o segundo argumento, com o parâmetro $ app passado para lá - uma instância da classe de aplicativo. Em geral, agora estamos mais entrando em ligação contextual, onde o que passamos para a classe que implementa o "tipo virtual" depende da situação atual.
Eu aviso que nem todos concordam com essa abordagem de construção da arquitetura de aplicativos; portanto, se você é fã de centenas das mesmas classes, pule esse material.
Portanto, para começar, decidiremos o que atuará como uma classe "real". Usando o exemplo de um projeto que me veio em uma revisão de código, vamos considerar a mesma situação com solicitações de recursos (na verdade, CRUD, mas um pouco reduzidas).
Vejamos a implementação de um controlador Crud comum:
<?php namespace Wolf\Http\Controllers\Backend\Crud; use Illuminate\Database\Eloquent\Model; use Illuminate\Http\Request; use Wolf\Http\Controllers\Controller; class BaseController extends Controller { protected $model; protected $resourceCollection; protected $jsonResource; public function __construct( $model, $resourceCollection = null, $jsonResource = null ) { $this->model = $model; $this->resourceCollection = $resourceCollection; $this->jsonResource = $jsonResource; } public function index(Request $request) { return $this->resourceCollection::make($this->model->get()); } public function show($id) { return $this->jsonResource::make($this->model->find($id)); } }
Não me preocupei muito com a implementação, porque o projeto está na fase de planejamento, de fato.
Temos dois métodos que devem retornar algo para nós: index, que retorna uma coleção de entidades do banco de dados, e show, que retorna o recurso json de uma entidade específica.
Se usássemos classes reais, cada vez que criaríamos uma classe contendo 1-2 setters que definiriam classes para modelos, recursos e coleções. Imagine dezenas de arquivos, dos quais a implementação verdadeiramente complexa é de apenas 1-2. Podemos evitar esses “clones” usando o DI Laravel.
Portanto, a arquitetura deste sistema será simples, mas confiável como um relógio suíço.
Existe um arquivo json que contém uma matriz de "tipos virtuais" com referência direta às classes que serão usadas como coleções, modelos, recursos, etc ...
Por exemplo, isto:
{ "Wolf\\Http\\Controllers\\Backend\\Crud\\OrdersResourceController": { "model": "Wolf\\Model\\Backend\\Order", "resourceCollection": "Wolf\\Http\\Resources\\OrdersCollection", "jsonResource": "Wolf\\Http\\Resources\\OrderResource" } }
Além disso, usando a ligação do Laravel, definiremos Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController para o nosso tipo virtual Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController como a classe de implementação (observe que a classe não deve ser abstrato, porque ao solicitar Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController, devemos obter uma instância de Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController, não uma classe abstrata).
No CrudServiceProvider, no método boot (), coloque o seguinte código:
$path = app_path('etc/crud.json'); if ($this->filesystem->isFile($path)) { $virtualTypes = json_decode($this->filesystem->get($path), true); foreach ($virtualTypes as $virtualType => $data) { $this->app->bind($virtualType, function ($app) use ($data) { $bindingData = [ 'model' => $app->make($data['model']), 'resourceCollection' => $data['resourceCollection'], 'jsonResource' => $data['jsonResource'] ]; return $app->makeWith(self::BASE_CRUD_CONTROLLER, $bindingData); }); } }
A constante BASE_CRUD_CONTROLLER contém o nome da classe que implementa a lógica do controlador CRUD.
Longe do ideal, mas funciona :)
Aqui, examinamos uma matriz com tipos virtuais e configuramos os ligantes. Observe que apenas obtemos a instância do modelo do contêiner de serviço, e ResourceCollection e JsonResource são apenas nomes de classe. Porque O modelo não precisa receber os atributos para preencher, ele pode ficar sem eles. Mas as coleções devem receber algum tipo de recurso do qual obterão dados e entidades. Portanto, em BaseController, usamos os métodos estáticos collection () e make (), respectivamente (em princípio, podemos adicionar getters dinâmicos que colocarão algo no recurso e retornarão uma instância para nós, mas deixarei isso para você) que nos retornará instâncias desses mesmas coleções, mas com os dados transferidos para eles.
De fato, você pode, em princípio, trazer toda a ligação do Laravel a esse estado.
No total, solicitando Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController, obtemos uma instância do controlador Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController, mas com dependências internas do nosso modelo, recurso e coleção. Resta apenas criar um ResourceCollection e JsonResource e você pode controlar os dados retornados.