2012-09-26 11 views
14

W jaki sposób zmienisz rekord dla "Osoby" na "Pracownik"?Doctrine: Zaktualizuj dyskryminatora dla SINGLE_TABLE Dziedziczenie

/** 
* @Entity 
* @InheritanceType("SINGLE_TABLE") 
* @DiscriminatorColumn(name="discr", type="string") 
* @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) 
*/ 
class Person 
{ 
    // ... 
} 

/** 
* @Entity 
*/ 
class Employee extends Person 
{ 
    // ... 
} 

Próbowałem zmienić wartość kolumny dyskryminacyjnej, ale nie mam do niej dostępu. Próbowałem także utworzyć instancję "Pracownik" i ręcznie skopiować dane, ale to nie działa z automatycznymi inkrementującymi identyfikatorami. Zostaje on dodany jako nowy rekord zamiast aktualizacji istniejącego.

Czy muszę napisać niestandardowe zapytanie sql lub czy robię coś innego, co jest zasadniczo złe?

Odpowiedz

32

To nie jest dobry znak, gdy typ obiektu musi się zmienić z czasem. Nie mówię tutaj o downcastingu/upcastingu, ale o potrzebie zmiany prawdziwego typu obiektu.

Przede wszystkim, pozwól mi powiedzieć, dlaczego jest to zły pomysł:

  1. Podklasa może zdefiniować więcej atrybutów i zrobić jakieś źródło dodatkowego pracę w jego konstruktora. Czy powinniśmy ponownie uruchomić nowy konstruktor? Co , jeśli nadpisuje niektóre z atrybutów naszego starego obiektu?
  2. Co by było, gdybyś pracował nad instancją tej Osoby w jakiejś części swojego kodu, a potem nagle przekształca się w Pracownika (który może mieć jakieś przedefiniowane zachowanie, którego byś się nie spodziewał) ?!

Jest to jeden z powodów, dla których większość języków nie pozwala na zmianę prawdziwego typu klasy obiektu podczas wykonywania (i oczywiście pamięć, ale nie chcę wchodzić w szczegóły). Niektórzy pozwalają ci to robić (czasami w poskręcany sposób, np. JVM), ale to naprawdę nie jest dobra praktyka!

Często zdarza się, że trzeba to zrobić w złych obiektowych decyzjach projektowych.

Z tych powodów Doctrine nie zezwoli na zmianę typu obiektu obiektu. Oczywiście, możesz napisać zwykły kod SQL (na końcu tego posta - ale proszę przeczytać!), Aby to zmienić, ale tutaj są dwie "czyste" opcje, które proponuję:

Mam świadomość, że masz już powiedziałem, że pierwsza opcja nie była opcją, ale poświęciłem chwilę na napisanie tego posta, więc mam wrażenie, że powinienem zrobić to jak najpełniej, jak tylko się da, aby móc się z nim zapoznać w przyszłości.

  1. Gdy trzeba „zmienić typ” od Person do Employee, utworzyć nową instancję Pracownika i skopiować dane, które chcesz skopiować ze starego obiektu Person do obiektu pracownika. Nie zapomnij usunąć starego obiektu i utrzymać go.
  2. Użyj składu zamiast dziedziczenia (szczegóły na ten temat można znaleźć w artykule wiki article oraz w linkach do innych artykułów). EDYTOWANIE: Do diabła, here's a part of a nice conversation with Erich Gamma o "Składzie na dziedziczenie"!

Zobacz powiązane dyskusje here i here.


Teraz tutaj jest zwykły sposób SQL mówiłem o wcześniej - Mam nadzieję, że nie będzie trzeba go używać!

Upewnij się, że twoje zapytanie zostało oczyszczone (ponieważ zapytanie zostanie wykonane bez żadnej weryfikacji).

$query = "UPDATE TABLE_NAME_HERE SET discr = 'employee' WHERE id = ".$entity->getId(); 
$entity_manager->getConnection()->exec($query); 

Oto dokumentacja i kod na exec metody, która jest w klasie DBAL\Connection (dla twojej informacji):

/** 
* Execute an SQL statement and return the number of affected rows. 
* 
* @param string $statement 
* @return integer The number of affected rows. 
*/ 
public function exec($statement) 
{ 
    $this->connect(); 
    return $this->_conn->exec($statement); 
} 
+1

Dziękuję bardzo. Spokojnie; Wezmę twoją radę i uniknę zwykłego SQL. – Nate

+0

Natknąłem się na to dwa razy i naprawdę mi pomogło. Dzięki! – Strategist