IPub\DoctrineDynamicDiscriminatorMap – dynamická discriminator mapa

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
akadlec
Člen | 1326
+
+1
-

Řešil jsem problém jak osamostatnit moduly kde řeším identity a protože entitu identity mám jako single table inheritance, tak v ní mám definovnaé jednotlivé typy identit: system, facebook, github atd. a protože identity ze sociálních konektorů jsem potřeboval mít v jiném modulu, řešil jsem jak ty moduly oddělit. A tak jsem udělal jednoduchý listener pro doctrine, který tu mapu discriminatorů rozšíří podle potřeb.

Vše je to teď k dispozici jako extension pro nette a využívá kdyby/events.

Tomáš Votruba
Moderator | 1114
+
0
-

Awesome, to jsem nedávno taky dělal, ale už jsem se nedostal ke Githubu.

Nešlo by to ještě jednodušeji, bez nutnosti registrovat do configu? Tím vlastně neodpadá žádná práce, jen se přesouvá od Entity do configu, tedy nová závislost.

akadlec
Člen | 1326
+
0
-

Teď nevím zda tě chápu. Chceš aby si to samo načetlo entitu o kterou se má mapa rozšířit?

Tomáš Votruba
Moderator | 1114
+
0
-

Přesně tak to myslím. Dělal jsem to v rychlosti na projektu, kde už nedělám, nicméně cílem bylo přesunout zodpovědnost z parent entity na children.

Dalším krokem by mohlo zbavit se interface a použít discriminator jako entity::class => entity::class (kdyby to šlo).

Toto byl můj mezikrok:

	/**
	 * @param string $class
	 * @return string[]
	 */
	private function detectFromChildren($parentClass)
	{
		$this->discriminators = [];
		foreach ($this->driver->getAllClassNames() as $class) {
			$childrenClassReflection = new ReflectionClass($class);
			if ( ! $parentClassReflection = $childrenClassReflection->getParentClass()) {
				continue;
			}

			$childParent = $parentClassReflection->getName();
			if ($childParent !== $parentClass) {
				continue;
			}

			if ($discriminator = $this->detectDiscriminatorForClass($class)) {
				$this->discriminators[$discriminator] = $class;
			}
		}

		return $this->discriminators;
	}


	/**
	 * @param string $class
	 * @return bool
	 */
	private function detectDiscriminatorForClass($class)
	{
		$classReflection = new ReflectionClass($class);
		if ($classReflection->isAbstract()) {
			return FALSE;
		}

		if ( ! $classReflection->implementsInterface(DiscriminatorProviderInterface::class)) {
			return FALSE;
		}

		/** @var DiscriminatorProviderInterface $object */
		$object = $classReflection->newInstanceWithoutConstructor();
		$discriminator = $object->getDiscriminatorName();
		if ( ! $discriminator) {
			return FALSE;
		}

		$this->ensureDiscriminatorIsUnique($discriminator, $class);

		return $discriminator;
	}


	/**
	 * @param string $discriminator
	 * @param string $class
	 * @throws DuplicatedDiscriminatorException
	 */
	private function ensureDiscriminatorIsUnique($discriminator, $class)
	{
		if (in_array($discriminator, $this->discriminators)) {
			throw new DuplicatedDiscriminatorException(
				sprintf(
					'Found duplicate discriminator map entry "%s" in "%s".',
					$discriminator,
					$class
				)
			);
		}
	}

Editoval Tomáš Votruba (8. 12. 2015 17:25)

akadlec
Člen | 1326
+
+1
-

No toto by asi šlo a ulehčilo by to implementaci. Díky za tip. Zkusím to tam zahrnout.

akadlec
Člen | 1326
+
+1
-

@TomášVotruba tak jsem si s tím trochu pohrál a je to tam :D zbavil sem se závislosti na konfiguraci. Takže stačí implemenovat interface a metodu která bude vracet název do diskriminační mapy a je to, vše se namapuje automaticky.

enumag
Člen | 2118
+
0
-

@akadlec Pls co přesně to rozšíření dělá? DiscriminatorMap si Doctrine umí generovat sama, není třeba ji uvádět v anotaci. Uniká mi co tohle rozšíření přináší navíc.

Editoval enumag (8. 1. 2016 1:42)

akadlec
Člen | 1326
+
+1
-

@enumag no tak to teda nevím zda umí sama, pokud entitu neanotuju např. takto:

/**
 * @ORM\Entity
 * @ORM\Table(name="person")
 * @ORM\InheritanceType("SINGLE_TABLE")
 * @ORM\DiscriminatorColumn(name="type", type="string")
 * @ORM\DiscriminatorMap({
 *      "person" = "Person"
 *      "student" = "Student"
 *      "teacher" = "Teacher"
 * })
 */
class Person
{
    // ...
}

Tak to nebude fungovat. Jak by vůbec doctrine odhadovala názvy pro jednotlivé entity pro které tvoří hodnotu v daném sloupci?

A tahle extenension dělá to, že nemusíš mít vypsány všechny prvky v ORM\DiscriminatorMap což pro mě znamená že zruším závislost mezi dvěma moduly.

F.Vesely
Člen | 369
+
0
-

If no discriminator map is provided, then the map is generated automatically. The automatically generated discriminator map contains the lowercase short name of each class as key.

A pozna to jednoduse, protoze od Person dedis.

akadlec
Člen | 1326
+
+1
-

Nemyslel jsem jak pozná že je to prvek, ale to jméno, no dělá to nad čím sem uvažoval, že lowercasne jméno třídy.

Každopádně tohle umí tu předem definovanou mapu doplňovat a to je cíle této extension.

Tomáš Votruba
Moderator | 1114
+
0
-

@akadlec Boží, skvělá práce! Časem si to možná vypůjčím do Symfony :)