Escriba menos c贸digo duplicado usando carpetas en Laravel

imagen

Buen tiempo, damas y caballeros.

No hace mucho tiempo, me encontr茅 con el fen贸meno del c贸digo duplicado y duplicado al revisar un solo proyecto en Laravel.

La conclusi贸n es: el sistema tiene una estructura de la API interna para las solicitudes de AJAX, esencialmente devolviendo una colecci贸n de algo de la base de datos (pedidos, usuarios, cuotas, etc.). El objetivo de esta estructura es devolver JSON con los resultados, no m谩s. En la revisi贸n del c贸digo, cont茅 5 o 6 clases usando el mismo c贸digo, la 煤nica diferencia estaba en la inyecci贸n de dependencia de ResourceCollection, JsonResource y el modelo en s铆. Este enfoque me pareci贸 fundamentalmente incorrecto, y decid铆 hacer los cambios correctos en este c贸digo, seg煤n creo, utilizando el poderoso DI que nos proporciona el Marco Laravel.

Entonces, 驴c贸mo llegu茅 a lo que hablar茅 m谩s tarde?

Ya tengo aproximadamente un a帽o y medio de experiencia en desarrollo para Magento 2, y cuando encontr茅 este CMS por primera vez, me sorprendi贸 su DI. Para aquellos que no saben: en Magento 2, no una peque帽a parte del sistema se basa en los llamados "tipos virtuales". Es decir, refiri茅ndose a una clase particular, no siempre recurrimos a la clase "real". Nos estamos refiriendo a un tipo virtual que se ha "ensamblado" en funci贸n de una clase "real" espec铆fica (por ejemplo, Colecci贸n para la grilla de administraci贸n, ensamblado a trav茅s de DI). Es decir, podemos construir cualquier clase para usar con nuestras dependencias simplemente escribiendo algo similar en 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> 

Ahora, al solicitar la clase Vendor \ Module \ Model \ ResourceModel \ MyData \ Grid \ Collection, obtenemos una instancia de Magento \ Framework \ View \ Element \ UiComponent \ DataProvider \ SearchResult, pero con las dependencias mainTable configuradas en "vendor_table" y resourceModel - " Proveedor \ M贸dulo \ Modelo \ ResourceModel \ MyData ".

Al principio, este enfoque no me parec铆a del todo claro, ni del todo "apropiado" ni del todo normal, pero despu茅s de un a帽o de desarrollo para este bal贸n , por el contrario, me convert铆 en un seguidor de este enfoque y, adem谩s, encontr茅 aplicaci贸n en mis proyectos. .

De vuelta a Laravel.

DI Laravel se basa en un "contenedor de servicios", una entidad que administra carpetas y dependencias en el sistema. Por lo tanto, podemos, por ejemplo, indicar a la interfaz DummyDataProviderInterface la implementaci贸n de esta interfaz DummyDataProvider.

 app()->bind(DummyDataProviderInterface::class, DummyDataProvider::class); 

Luego, cuando solicitamos DummyDataProviderInterface en el contenedor del servicio (por ejemplo, a trav茅s del constructor de la clase), obtenemos una instancia de la clase DummyDataProvider.

Muchos (por alguna raz贸n) terminan este conocimiento en el contenedor de servicios de Laravel y van a hacer sus propias cosas, mucho m谩s interesantes, pero en vano .

Laravel puede "enlazar" no solo entidades reales, como una interfaz determinada, sino tambi茅n crear los llamados "tipos virtuales" (tambi茅n conocidos como alias). E, incluso en este caso, Laravel no tiene que aprobar una clase que implemente su tipo. El m茅todo bind () puede tomar una funci贸n an贸nima como segundo argumento, con el par谩metro $ app pasado all铆, una instancia de la clase de aplicaci贸n. En general, ahora vamos m谩s al enlace contextual, donde lo que pasamos a la clase que implementa el "tipo virtual" depende de la situaci贸n actual.

Te advierto que no todos est谩n de acuerdo con este enfoque para construir arquitectura de aplicaciones, as铆 que si eres fan谩tico de cientos de las mismas clases, omite este material.

Entonces, para empezar, decidiremos qu茅 actuar谩 como una clase "real". Usando el ejemplo de un proyecto que se me ocurri贸 en una revisi贸n de c贸digo, tomemos la misma situaci贸n con las solicitudes de recursos (de hecho, CRUD, pero un poco reducido).

Veamos la implementaci贸n de un controlador Crud com煤n:

 <?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 { /** * @var Model */ protected $model; /** * @var \Illuminate\Http\Resources\Json\ResourceCollection|null */ protected $resourceCollection; /** * @var \Illuminate\Http\Resources\Json\JsonResource|null */ protected $jsonResource; /** * BaseController constructor. * @param Model $model * @param \Illuminate\Http\Resources\Json\ResourceCollection|null $resourceCollection * @param \Illuminate\Http\Resources\Json\JsonResource|null $jsonResource */ public function __construct( $model, $resourceCollection = null, $jsonResource = null ) { $this->model = $model; $this->resourceCollection = $resourceCollection; $this->jsonResource = $jsonResource; } /** * Display a listing of the resource. * * @param Request $request * @return \Illuminate\Http\Resources\Json\ResourceCollection */ public function index(Request $request) { return $this->resourceCollection::make($this->model->get()); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Resources\Json\JsonResource */ public function show($id) { return $this->jsonResource::make($this->model->find($id)); } } 

No me molest茅 mucho con la implementaci贸n, porque el proyecto est谩 en la etapa de planificaci贸n, de hecho.

Tenemos dos m茅todos que deber铆an devolvernos algo: index, que devuelve una colecci贸n de entidades de la base de datos, y show, que devuelve el recurso json de una entidad espec铆fica.

Si us谩ramos clases reales, cada vez crear铆amos una clase que contuviera 1-2 setters que definir铆an clases para modelos, recursos y colecciones. Imagine docenas de archivos, de los cuales la implementaci贸n verdaderamente compleja es solo 1-2. Podemos evitar tales "clones" usando DI Laravel.

Entonces, la arquitectura de este sistema ser谩 simple, pero confiable como un reloj suizo.
Hay un archivo json que contiene una matriz de "tipos virtuales" con referencia directa a las clases que se utilizar谩n como colecciones, modelos, recursos, etc.

Por ejemplo, esto:

 { "Wolf\\Http\\Controllers\\Backend\\Crud\\OrdersResourceController": { "model": "Wolf\\Model\\Backend\\Order", "resourceCollection": "Wolf\\Http\\Resources\\OrdersCollection", "jsonResource": "Wolf\\Http\\Resources\\OrderResource" } } 

Adem谩s, utilizando el enlace Laravel, configuraremos Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController para nuestro tipo virtual Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController como la clase de implementaci贸n (tenga en cuenta que la clase no debe ser abstracto, porque al solicitar Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController deber铆amos obtener una instancia de Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController, no una clase abstracta).

En CrudServiceProvider, en el m茅todo boot (), ingrese el siguiente 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) { /** @var Application $app */ $bindingData = [ 'model' => $app->make($data['model']), 'resourceCollection' => $data['resourceCollection'], 'jsonResource' => $data['jsonResource'] ]; return $app->makeWith(self::BASE_CRUD_CONTROLLER, $bindingData); }); } } 

La constante BASE_CRUD_CONTROLLER contiene el nombre de la clase que implementa la l贸gica del controlador CRUD.

Lejos de ser ideal, pero funciona :)

Aqu铆 vamos a trav茅s de una matriz con tipos virtuales y establecemos las carpetas. Tenga en cuenta que solo obtenemos la instancia del modelo del contenedor de servicios, y ResourceCollection y JsonResource son solo nombres de clase. Por qu茅 El modelo no tiene que tomar los atributos para llenar, puede prescindir de ellos. Pero las colecciones deben incluir alg煤n tipo de recurso del que obtendr谩n datos y entidades. Por lo tanto, en BaseController usamos los m茅todos est谩ticos collection () y make () respectivamente (en principio, podemos agregar captadores din谩micos que pondr谩n algo en el recurso y nos devolver谩n una instancia, pero se lo dejar茅 a usted) que nos devolver谩 instancias de estos mismas colecciones, pero con los datos transferidos a ellas.

De hecho, puede, en principio, llevar todo el enlace de Laravel a dicho estado.

En total, al solicitar Wolf \ Http \ Controllers \ Backend \ Crud \ OrdersResourceController obtenemos una instancia del controlador Wolf \ Http \ Controllers \ Backend \ Crud \ BaseController, pero con dependencias integradas de nuestro modelo, recurso y colecci贸n. Solo queda crear una ResourceCollection y JsonResource y puede controlar los datos devueltos.

Source: https://habr.com/ru/post/451462/


All Articles