dakujem/nette-wires

Wire in Nette with genie powers.

1.1 2020-11-22 10:42 UTC

This package is auto-updated.

Last update: 2024-03-22 18:05:39 UTC


README

💿 composer require dakujem/nette-wires

SK sk_SK / CS cs_CZ

Wire Genie umoznuje redukovat "boilerplate" kod suvisiaci s drotovanim sluzieb cez prezentery (napriklad vytvaranie komponent a niektorych jednorazovych sluzieb typu builder alebo factory).

Ale bacha! 🤚

V zavislosti od konkretneho pouzitia moze dojst k poruseniu IoC principov a degradacii dependency injection principov na service locator. Pouzivajte na vlastnu zdpovednost, ak viete, co robite.

Na druhej strane, ak vytiahujete sluzby z kontajneru, mozete uz radsej pouzit Wire Genie.

Instalacia

💿 composer require dakujem/nette-wires

Tento metapackage instaluje Wire Genie a navod nizsie odporuca instalaciu Contributte/PSR-11-kontajner, ale mozete pouzit lubovolny iny PSR-11 wrapper Nette DI kontajneru.

Pokial si nainstalujete contributte/psr11-container-interface, mozete vo svojom bazovom prezenteri pouzit WireGenieTrait, ktory prida do prezenteru metodu wire.

composer require contributte/psr11-container-interface

namespace App\Presenters;

use Dakujem\WireGenieTrait;
use Nette;

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    use WireGenieTrait;
}

💡 Nepouzivajte traity, pokial nerozumiete, co s nimi nie je v poriadku.

Implementacia wire metody je inak vo vasich rukach.
Namiesto nej mozete tiez priamo volat $wireGenie->provide( ... )->invoke( ... ).

Pouzitie

Metodu wire je mozne pouzit napriklad v createComponent* metodach:

    protected function createComponentFoobarForm()
    {
        $factory = function (InputFactory $inputs, TextRepository $textRepo) {
            $form = new Form();
            $form->addComponent(
                $inputs->create('stuff', $textRepo->getAllUnread()),
                'unread_stuff'
            );
            // ...
            return $form;
        };

        // explicitne vyziadanie zavislosti
        return $this->wire(InputFactory::class, 'model.repository.text')->invoke($factory);
        
        // alebo automaticke nadrotovanie zavislosti (autowiring)
        return $this->wire()->invoke($factory);
    }

Lepsie je zabalit kod do tovarne alebo populatoru (moznost testovat):

    protected function createComponentFoobarForm()
    {
        return $this->wire(InputFactory::class, 'model.repository.text')
            ->invoke([new FoobarFormFactory, 'create']);
    }

Lokalne zavislosti z prezenteru je mozne pribalit cez anonymne funkcie:

    protected function createComponentFoobarForm()
    {
        return $this->wire(InputFactory::class, 'model.repository.text')
            ->invoke(function (...$deps) {
                return (new FoobarFormFactory)->create(
                    $this->localDependency,
                    $this->getParameter('id'),
                    ...$deps
                );
            });
    }

Tento postup umoznuje vyhnut sa injektovaniu mnozstva zavislosti do prezenterov, pokial nie su vzdy pouzivane (prezetner moze mat viacero akcii, pouzije sa len jedna; komponenty detto).

Taketo pouzitie Wire Genie riesi okrajovy pripad, kedy vznika boilerplate.
Pred pouzitim skuste pouvazovat, ci sa vas pripad neda vyriesit cistejsie.
Nette 3 podporuje injektovanie skupin sluzieb, SearchExtension umoznuje hromadne registrovat sluzby do kontajneru, atd.

Porovnajte vyhody a nevyhody:

  • ❔ zachovanie IoC je otazne (zalezi na uhle pohladu)
  • ➕ vyhodou je mala pracnost riesenia
  • ➕ prehladne prepojenie zavislosti, jednoduche na pochopenie
  • ➕ autowiring, vid detaily v balicku Wire Genie
  • ➕ moznost konfiguracie drotovania zavislosti existuje
  • ➕ testovatelnost je jednoduchsia ako v priapde tovarni vygenerovanych DI
  • ➕ prezenter neriesi, odkial zavislosti tecu, ale deklaruje, ake sluzby sa maju nadrotovat
  • ➕ lazy loading v momente realneho pouzitia
  • ➖ „maskovany“ service lokator (❔)
  • ➖ kontajner pri kompilacii nezisti problemy s chybajucimi alebo konfliktnymi sluzbami

Alternativne mozete skusit iny kompromis, napr. Kdyby/Autowired.

Osobne odporucam tieto techniky pouzivat len vo faze prototypovania.

EN en_GB

Allows to fetch multiple dependencies from a DI container and provide them as arguments to a callable.
Metapackage.

Disclaimer 🤚

Depending on actual use, this might be breaking IoC and degrade your dependency injection container to a service locator, so use it with caution.

But then again, if you can get a service from your container, you can use wire genie.

Installation

💿 composer require dakujem/nette-wires

Then either install Contributte/PSR-11-kontajner or any other PSR-11 wrapper for Nette DI container.

If you install contributte/psr11-container-interface, you can use WireGenieTrait, in your presenter(s), that will add wire method.

composer require contributte/psr11-container-interface

namespace App\Presenters;

use Dakujem\WireGenieTrait;
use Nette;

abstract class BasePresenter extends Nette\Application\UI\Presenter
{
    use WireGenieTrait;
}

💡 Do not use traits unless you understand what's wrong with them.

Otherwise, implementation of wire method is in your hands.
You can of course call $wireGenie->provide( ... )->invoke( ... ) directly as well.

Then you can wire dependencies without first labourously injecting them to your presenters, creating factories and accessors in the meantime.

    protected function createComponentFoobarForm()
    {
        $factory = function (InputFactory $inputs, TextRepository $textRepo) {
            $form = new Form();
            $form->addComponent(
                $inputs->create('stuff', $textRepo->getAllUnread()),
                'unread_stuff'
            );
            // ...
            return $form;
        };

        // with explicit dependencies
        return $this->wire(InputFactory::class, 'model.repository.text')->invoke($factory);
        
        // with automatic dependency resolution (autowiring)
        return $this->wire()->invoke($factory);
    }

Local dependencies can naturally be passed to the closures:

    protected function createComponentFoobarForm()
    {
        return $this->wire(InputFactory::class, 'model.repository.text')
            ->invoke(function (...$deps) {
                return (new FoobarFormFactory)->create(
                    $this->localDependency,
                    $this->getParameter('id'),
                    ...$deps
                );
            });
    }

Please understand that this approach has its advantages and disadvantages. It might actually degrade your aplication if misused.
First try to think if your case can not be solved in a cleaner way.\

I would recommend only using this and similar approaches during prototyping phase.