Návrhový vzor pro entity, které se navzájem rozšiřují
- Jan Mikeš
- Člen | 771
Ahoj, abych co nejvíce nastínil situaci, mám něco jako nástěnku, jsou
na ní příspěvky a ty příspěvky jsou různého typu, např. video, alert
(100 znaků, něco jako tweet), textový příspěvek. Momentálně to mám
vyřešeno dost nevkusně a konečně se dostalo k refactoringu této části,
ale nevím úplně jakou cestou se vydat a jaká cesta by byla nejlepší
s ohledem na rozšíření do budoucna, protože typy příspěvků budou
příbývat. Používám doctrine a přemýšlím, jak to celé navrhnout, aby
se s daty pracovalo co nejpohodlněji.
Každý z typů příspěvků má společné informace (nadpis, datum), zbytek
je různý, podle typu (url videa, tweet, text).
Napadlo mě mít parent entitu Post
a pak @OneToOne
vazby, s tím, že použitá by byla pouze jedna podle typu.
/** @ORM\Entity */
class Post
{
/** @ORM\OneToOne(targetEntity="Video", mappedBy="post") */
private $video;
/** @ORM\OneToOne(targetEntity="Alert", mappedBy="post") */
private $alert;
public function getContent()
{
if ($this->video) {
return $this->video;
}
if ($this->alert) {
return $this->alert;
}
throw new Exception("Invalid type of post");
}
}
Pokud by se přidal nový typ příspěvku, založím novou entitu, přidám
vazbu OneToOne a doplním metodu getContent()
.
Dále mě napadlo mít abstraktního předka Post
, který by
měl společné údaje (nadpis, datum) a jednotlivé typy by od něj dědily.
U tohoto přístupu ale nevím, jak bych vypsal všechny typy příspěvků na
nástěnce najednou (u 1. možnosti je to jednoduché, findAll() na
entitu Post).
Taktéž se snažím přijít na ideální způsob výpisu příspěvků. Pro každý typ příspěvku mám control, jednotlivé komponenty zastřešuje ještě jedna mateřská komponenta, vypadá zhruba takto:
// PostControl.php
protected function createComponentPost() {
return new Multiplier(function ($postId) use ($articleFactory, $alertFactory, $videoFactory) {
$post = $this->myModel->find($postId);
if ($post) {
if ($post->type == PostManager::TYPE_ARTICLE) {
$control = $articleFactory->create($post);
}
if ($post->type == PostManager::TYPE_ALERT) {
$control = $alertFactory->create($post);
}
if ($post->type == PostManager::TYPE_VIDEO) {
$control = $videoFactory->create($post);
}
return $control;
}
});
}
Ani u tohoto přístupu si nejsem jist, jestli je to čisté řešení a zvažuju refactoring, ačkoliv mě momentálně lepší řešení nenapadá.
Nepotřebuji konkrétní řešení, pouze o názory, co je čistější, nebo co byste v podobné situaci použili vy, případně jestli máte své vlastní řešení.
- enumag
- Člen | 2118
Teoreticky čistší je imho entitu Post vždy podědit. FindAll by mělo normálně fungovat – Doctrine si přidá sloupec podle kterého zjistí typ dané instance Post a vždy si data dotahá aby mohla vytvořit instanci správné třídy. V praxi s tím ale jsou spojené jiné problémy. Např. DQL nad poděděnými entitami je slabé. Pokud vytváříš custom DQL dotaz nad tou parent entitou Post, nemůžeš si v select klauzuli říct které sloupce z child entit chceš vybrat.
- Jan Mikeš
- Člen | 771
Bude mi stacit findAll a jednoduche where podminky (autor, datum), takze pokud to ma pouze tyto limitace, tak verim ze mi to bude dostacovat.
Situaci jeste trosku zkomplikuju tim, ze vyuzivam Zenify\Translatable. Nedokazu absolutne odhadnout vysledne chovani, takze to nakonec stejne asi budu muset vyzkouset.
Budu si muset vytvorit entitu PostTranslation
, kterou pote budou
dedit entity VideoTranslation
, AlertTranslation
atd..
? Zde asi nezbyde nic jineho nez vyzkouset a pohrat si s tim :-)
- Jan Mikeš
- Člen | 771
@akadlec máš pravdu, pro překlady je to zde: https://github.com/…bscriber.php#L203
což by asi při dědění nemuselo vadit, jestli chápu správně, tak to, že
zdědím PostTranslation, neznamená, že ta relace bude na Post, díky tomu,
že target entity se určije
skrze $classMetadata->getReflectionClass()->getMethod('getTranslatableEntityClass')
Nebo jsi měl namysli nějaký jiný zádrhel?