IPub\DoctrineDynamicDiscriminatorMap – dynamická discriminator mapa
- akadlec
- Člen | 1326
Ř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
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.
- Tomáš Votruba
- Moderator | 1114
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
@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.
- Tomáš Votruba
- Moderator | 1114
@akadlec Boží, skvělá práce! Časem si to možná vypůjčím do Symfony :)