Návrhový vzor pro entity, které se navzájem rozšiřují

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
Jan Mikeš
Člen | 771
+
0
-

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
+
0
-

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.

akadlec
Člen | 1326
+
0
-

To co popisuješ je hýr ale jak píše enumag, s DQL je to horší.

Jan Mikeš
Člen | 771
+
0
-

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 :-)

akadlec
Člen | 1326
+
0
-

Teoreticky by to mohlo fungovat, jen teda pokud chceš využívat table inheritance tak u těch překladu bych to pak nedělal. Nevím jak u knp, ale tuším že se to dotahuje jako entita ve vazbě onetomany?

Jan Mikeš
Člen | 771
+
0
-

@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?