Oznámení

Fórum bylo uzavřeno. Využijte prosím služeb serverů JakPsatWeb, Builder nebo Webtrh.

#1 před 2 lety

vrtak-cz
Nette Evangelist
Registrovaný: 8. 3. 2008
Příspěvky: 2120

Změna typu entity u SINGLE_TABLE inheritance

Už dlouhou dobu řeším problém u SINGLE_TABLE inheritance. Potřebuji totiž změnit typ ale Doctrine zdá se nejdříve ukládá a pak teprve maže. Protože narážím na problém s unikátností klíče. Z ukázky snad bude vše patrné.

/**
 * @entity
 */
class Test
{
        /**
         * @id @column(type="integer")
         * @generatedValue(strategy="AUTO")
         */
        private $id;

        /**
         * @OneToOne(targetEntity="Base", mappedBy="test")
         */
        private $item;
}

/**
 * @Entity
 * @InheritanceType("SINGLE_TABLE")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"foo" = "Foo", "bar" = "Bar"})
 */
class Base
{
        /**
         * @id @column(type="integer")
         * @generatedValue(strategy="AUTO")
         */
        private $id;

        /**
         * @OneToOne(targetEntity="Test", inversedBy="item")
         * @JoinColumn(name="test_id", referencedColumnName="id")
         */
        private $test;

        public function __construct(Test $test)
        {
                $this->test = $test;
        }
}

/**
 * @entity
 */
class Foo extends Base { }

/**
 * @entity
 */
class Bar extends Base { }

Problém nastane když mám v DB uložené:

Test(2) {
   "id" private => 1
   "item" private => Foo(2) {
      "id" private => 1
      "test" private => Test(2) { *RECURSION* }
   }
}

a potřebuji Foo zaměnit za Bar. První co mě napadne je:

$em->remove($test->item);
$item = new Bar($test);
$em->persist($item);
$em->flush();

Jenomže je tu problém pokud to takhle udělám vyběhne na mě PDOException že v tabulce Base už existuje řádek s test = 1. (Protože Doctrine evidentně nejdříve ukládá (insert/update) a pak teprve maže (delete)). Jediné funkční a zároveň ošklivé řešení které mě napadlo je hodit za $em->remove($test->item); ještě jeden flush.

Napadá někoho elegantnější řešení?


Sorry my bad english.
Vrtak-CZ | Nella Project | Osobní Blog | Twitter | LinkedIn | GitHub
„Nastala chyba která neměla nastat“ aneb „Když se chce všechno jde.“

 

#2 před 2 lety

jantichy
Člen
Registrovaný: 29. 1. 2010
Příspěvky: 29

Re: Změna typu entity u SINGLE_TABLE inheritance

Protože Doctrine evidentně nejdříve ukládá (insert/update) a pak teprve maže (delete)

Je to přesně tak, takto je v Doctrine 2 udělaný postup zpracování všech změn v UnitOfWork.

Jediné funkční a zároveň ošklivé řešení které mě napadlo je hodit za $em->remove($test->item); ještě jeden flush.

Osobně mi to přijde taky trošku ošklivé, každopádně toto je best-practice pro řešení podobných problémů se špatným pořadím vykonávaných operací. Takže ano, přesně takhle – udělat to ve více postupných transakcích.

 

#3 před 2 lety

paranoiq
Moderator
Registrovaný: 14. 11. 2006
Příspěvky: 381

Re: Změna typu entity u SINGLE_TABLE inheritance

je opravdu nutné starý objekt nahradit úplně novým?

nestačilo by změnit data objektu tak, aby se po příštím načtení podle podmínek dědičnosti entity manager rozhodl, že jej má vytvořit jako objekt jiné třídy? (např když povýším usera na admina, nebudu ho přece mazat)

$item->setType('whatever');
$em->flush();
$em->detach($item);
$item = $em->getOne(...);

 

#4 před 2 lety

vrtak-cz
Nette Evangelist
Registrovaný: 8. 3. 2008
Příspěvky: 2120

Re: Změna typu entity u SINGLE_TABLE inheritance

@paranoiq: to není dost dobře možné

/**
 * @entity
 */
class Foo extends Base
{
        /** @column */
        private $foo;
}

/**
 * @entity
 */
class Bar extends Base
{
        /** @column */
        private $bar;
}

v objektu instance Foo nemám přístup k vlastnosti $bar z Bar.


Sorry my bad english.
Vrtak-CZ | Nella Project | Osobní Blog | Twitter | LinkedIn | GitHub
„Nastala chyba která neměla nastat“ aneb „Když se chce všechno jde.“

 

#5 před 2 lety

paranoiq
Moderator
Registrovaný: 14. 11. 2006
Příspěvky: 381

Re: Změna typu entity u SINGLE_TABLE inheritance

DiscriminatorColumn skutečně nemusí být součástí rodičovského objektu? protože právě na něm se definuje.
UPDATE: teď mi došlo, že o DiscriminatorColumn se nebavíme

podle mě je správné řešení to co navrhuji + data specifická pro druhou třídu nastavit až poté. to že je nelze předat konstruktorem je bohužel důsledek toho jak se chová databáze vespod a nic s tím dělat nejde

postup remove … flush … insert je naprosto špatně:

  1. pokud aplikace lehne před insertem přijdete o data. také proto je pořadí akcí v UnitOfWork takové jaké je
  2. co pokud jsou na ‚vyměňovaném‘ objektu další závislosti přes cizí klíče?

@jantichy: ptám se tě jako doctrine-guru :] umí Doctrine takovouhle změnu třídy (Foo → Bar) objektu detekovat a z dříve připojené tabulky (třeba test_foo) odmazat nepotřebný řádek?

Editoval paranoiq (14. 10. 2010 12:56)

 

#6 před 2 lety

vrtak-cz
Nette Evangelist
Registrovaný: 8. 3. 2008
Příspěvky: 2120

Re: Změna typu entity u SINGLE_TABLE inheritance

Nejsem si jist zda se dá do DiscriminatorColumn zasahovat „zvenku“ protože se jedná o interní věc Doctrine kterou si řeší sama. (Získat aktuální hodnotu umím ale změnit nikoli).

Tebou navrhované řešení je super a taky mě napadlo nicméně uvedení do praxe je problém.

Přesně z těchto důvodů se mě to nelíbí.

Pokud vím detekovat to neumí. Proto tam mám to remove. Zkoušel jsem to totiž tak že jsem „naklonoval“ společná data (v zásadě jde hlavně o hodnotu PK) z instance Foo, kterou jsem detachnul. Naplnil data do instance Bar udělal merge a flushnul. (Ale nešlo to už nevím proč).


Sorry my bad english.
Vrtak-CZ | Nella Project | Osobní Blog | Twitter | LinkedIn | GitHub
„Nastala chyba která neměla nastat“ aneb „Když se chce všechno jde.“

 

#7 před 2 lety

paranoiq
Moderator
Registrovaný: 14. 11. 2006
Příspěvky: 381

Re: Změna typu entity u SINGLE_TABLE inheritance

ad merge() – to asi záleží na implementaci obsluhy mergovaných entit. pokud se ukládají komplet, tak by to mělo projít. pokud se při uložení natáhne z db původní verze pro porovnání (aby se ukládal jen rozdíl), tak to dopadne tak jako by jsi ji předtím nedetachoval – nebude mít stejnou připojenou tabulku a neprojde to

poznámka pro sebe:
po dlouhém přemýšlení, dvoutýdenní přestávce a dalším dlouhém přemýšlení mi došlo následující: Doctrine to jednoduše řešit nemůže, protože se nechává svázat pravidly jazyka – tak jako neumí přetypování z třídy na třídu PHP samotné (btw: který jazyk tohle umí?), neumí to ani Doctrine. podpora přetypování v ORM tedy musí vycházet z podpory přetypování objektů v jazyce samotném (nebo frameworku)…

 

#8 před 2 lety

vrtak-cz
Nette Evangelist
Registrovaný: 8. 3. 2008
Příspěvky: 2120

Re: Změna typu entity u SINGLE_TABLE inheritance

jsem rád že jsem došel k tomu samému… :-)


Sorry my bad english.
Vrtak-CZ | Nella Project | Osobní Blog | Twitter | LinkedIn | GitHub
„Nastala chyba která neměla nastat“ aneb „Když se chce všechno jde.“

 

#9 před rokem

paranoiq
Moderator
Registrovaný: 14. 11. 2006
Příspěvky: 381

Re: Změna typu entity u SINGLE_TABLE inheritance

došli jsme sice k tomu samému, ale já se nehodlám vzdát.

inspirovalo mě to k tomuhle: přetypování objektů v PHP

 

#10 před rokem

HosipLan
Člen
Registrovaný: 1. 6. 2009
Příspěvky: 2635

Re: Změna typu entity u SINGLE_TABLE inheritance

docela bych se podíval na použití, asi tam bude potřeba doplnit do životního cyklu nahrazení atd, že?


Neptej se, jestli se můžeš ptát | Blog | Twitter | GitHubCMS Kdyby

Nette Jabber Room – nette@conf.netlab.cz , všichni jste vítáni

 

#11 před rokem

paranoiq
Moderator
Registrovaný: 14. 11. 2006
Příspěvky: 381

Re: Změna typu entity u SINGLE_TABLE inheritance

ve třídě Cast je háček, na který by se měla navěsit UnitOfWork. ta by pak měla implementovat nový druh updatu (jak říkáš „nahrazení“), při kterém si vytvoří/zruší záznamy v rozšiřujících tabulkách a hlavní záznam pouze updatuje (tedy žádné mazání starého a zakládání nového záznamu ve dvou transakcích, jak se to musí dělat v Doctrině)

tak daleko zatím ještě nejsem. jde jen o koncept

btw: s Doctrinou to integrovat nehodlám

 

Zápatí