Číselníky v komponentě a přístup přes Facade
- Danny
- Člen | 146
Zdravím,
řeším takový problém, začal jsem používat architekturu kde jsou
oddělené vrstvy s použití ORM
- Entita
- Repository
- Service
- Facade
- Controller
Například tedy v komponentě kde je nějaký formulář mám UserFacade která řeší přidání uživatele a jeho sportu
$userFacade->addUser($values);
Chci se tedy odprostit od toho abych v komponentách injectoval Repository a sahal pouze do Facade. Jak ale řešit číselníky pokud jsou v databázi? Mám číselník/entitu Sport kde jsou jako hodnoty různé typy sportů a lze to editovat v administraci.
Abych naplnil selectbox formuláře bych musel udělat nějakou SportFacade ta by si injectovala SportRepository a mělo methodu getSports(), je to správné řešení? Přijde mi to hodně zbytečného psaní, ve chvíli kdy bych si mohl injectnout jen SportRepository do komponenty udělal bych
$form->addSelect('sport', 'Sport', $this->sportRepository()->fetchPairs('id','name'));
tak mám hotovo.
Díky za tipy
- pavelmlejnek
- Člen | 16
Ahoj, nevím jestli je to správné řešení (to nechám na posouzení jiných), ale u opakujících se věcí používám vlastní formulářový prvek, který extenduje SelectBox. Celé to vypadá nějak takto:
<?php declare(strict_types=1);
use Nette\Forms\Container;
use Nette\Forms\Controls\SelectBox;
final class SportControl extends SelectBox
{
/** @var array<Sport> */
private array $sportsIndexedById;
public function __construct(SportRepository $sportRepository, ?string $label = null)
{
$items = [];
$sportsIndexedById = [];
foreach ($sportRepository->findAll() as $sport) {
assert($sport instanceof Sport);
$items[$sport->id] = $sport->name;
$sportsIndexedById[$sport->id] = $sport;
}
$this->sportsIndexedById = $sportsIndexedById;
parent::__construct($label, $items);
}
public function setValue($value)
{
if ($value instanceof Sport) {
$value = $value->id;
}
return parent::setValue($value);
}
public function getValue()
{
$sportId = parent::getValue();
if ($sportId === null) {
return null;
}
return $this->sportsIndexedById[(int) $sportId] ?? null;
}
public static function register(SportRepository $sportRepository, string $method = 'addSport'): void
{
Container::extensionMethod($method, static function (Container $container, string $name, ?string $label = null) use ($sportRepository) {
$container[$name] = new SportControl($sportRepository, $label);
return $container[$name];
});
}
}
Poté registrace v DI extension:
<?php declare(strict_types=1);
use Nette\DI\CompilerExtension;
use Nette\PhpGenerator\ClassType;
class AppExtension extends CompilerExtension
{
public function afterCompile(ClassType $class)
{
parent::afterCompile($class);
$containerBuilder = $this->getContainerBuilder();
$initializeMethod = $class->getMethod('initialize');
$initializeMethod->addBody(sprintf('%s::register($this->getService(?));', SportControl::class), [
$containerBuilder->getByType(SportRepository::class),
]);
}
}
Došel jsem k tomuto řešení hlavně kvůli podpoře při mapování formulářových hodnot na datové třídy.
- jiri.pudil
- Nette Blogger | 1032
Nevidím nic špatného na tom sáhnout si z formuláře přímo do repository, obzvlášť pokud potřebuješ jenom vytáhnout seznam sportů. Ukládat a poskytovat uložené položky mi zní přesně jako zodpovědnost repository.
Balit to do další vrstvy má v takovém případě asi jen jednu přidanou
hodnotu: hezčí API navenek (getSports()
vs
fetchPairs('id', 'name')
– posuď sám, jestli je tenhle
přínos pro tebe a případně ostatní v týmu natolik velký, aby vyvážil
psaní více kódu. No a pak je tu ještě element času: třeba se ti ten kód
bude časem hodit i na jiném místě a najednou bude dávat větší smysl ho
obalit do fasády s jednoduchou metodou :)