2016-09-06 2 views
7

ich auf einer meiner Modelle eine Beziehung haben:deaktivieren Lehre Fremdschlüssel

/** 
* @ORM\ManyToOne(targetEntity="Page", cascade="persist") 
* @ORM\JoinColumn(name="page_id", referencedColumnName="id") 
*/ 
private $parentPage; 

Und wenn ich die übergeordnete Seite löschen, bekomme ich diesen Fehler:

Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails 

Grundsätzlich meine Modelle sind eine Seiten- und Seitenrevision. Wenn ich die Seite lösche, möchte ich die Revisionen nicht löschen. Ich möchte auch die page_id auf den Seitenversionen behalten (d. H. Nicht auf Null setzen).

Wie kann ich das mit Doctrine machen?

+0

Entschuldigung für die falsche enge Abstimmung, ich habe Ihre Frage falsch gelesen. Was Sie tun müssen, ist die tatsächliche Fremdschlüsseleinschränkung zu entfernen. Wenn die Einschränkung über Doctrine erstellt wurde, sollten Sie sie so konfigurieren, dass sie dies nicht mehr tut, oder Sie müssen sie einfach entfernen. – Phil

+0

@Phil Doctine erzeugt das Schema jedoch, und ich weiß nicht, wie ich es sagen soll die Einschränkung nicht hinzufügen. – Petah

+1

Eine Alternative könnte das Hinzufügen einer Eigenschaft (z. B. 'enabled') sein, die die Seite von Suchen ausschließt. Dies bewirkt, dass die Seite gelöscht wird, ohne dass eine Fremdschlüsseleinschränkung verletzt wird. – geoB

Antwort

8

von Definition Sie können den Datensatz löschen, dass der Fremdschlüssel an zeigt, ohne den Schlüssel zu Null-Einstellung (onDelete="SET NULL") oder Kaskadierung des Löschvorganges (There are two options - ORM Level: cascade={"remove"} | Datenbank-Ebene: onDelete="CASCADE").
Es gibt die Alternative setting a default value of a still existing record, aber Sie müssen das manuell tun, ich glaube nicht, dass Doctrine dies unterstützt out-of-the-box (bitte korrigieren Sie mich, wenn ich falsch liege, aber in diesem Fall ist die Einstellung eines Standardwertes sowieso nicht erwünscht).

Diese Strenge spiegelt das Konzept von Fremdschlüsseleinschränkungen wider; wie @ Théo sagte:

a FK is to ensure data consistency.

Soft-delete (bereits erwähnt) ist eine Lösung, aber was Sie auch eine zusätzliche removed_page_id Spalte tun können, ist hinzufügen, die Sie mit dem page_id nur synchronisieren, bevor Sie es in einer preRemove Event-Handler löschen (Lebenszyklus Rückruf). Ob solche Informationen irgendeinen Wert haben, frage ich mich, aber ich denke, Sie haben etwas dafür, sonst würden Sie diese Frage nicht stellen.

Ich bin auf jeden Fall nicht behaupten, das ist gute Praxis, aber es ist zumindest etwas, das Sie für Ihren Rand Fall verwenden können. So etwas in der Linie:

In Ihrem Revision:

/** 
* @ORM\ManyToOne(targetEntity="Page", cascade="persist") 
* @ORM\JoinColumn(name="page_id", referencedColumnName="id", onDelete="SET NULL") 
*/ 
private $parentPage; 

/** 
* @var int 
* @ORM\Column(type="integer", name="removed_page_id", nullable=true) 
*/ 
protected $removedPageId; 

Und dann in Ihrem Page:

/** 
* @ORM\PreRemove 
*/ 
public function preRemovePageHandler(LifecycleEventArgs $args) 
{ 
    $entityManager = $args->getEntityManager(); 
    $page = $args->getEntity(); 
    $revisions = $page->getRevisions(); 
    foreach($revisions as $revision){ 
     $revision->setRemovedPageId($page->getId()); 
     $entityManager->persist($revision); 
    } 
    $entityManager->flush(); 
} 

Alternativ können Sie natürlich bereits den richtigen $removedPageId Wert während der Bauphase einstellen könnten Ihre Revision, dann müssen Sie beim Entfernen nicht einmal einen Lebenszyklus-Callback ausführen.

1

Sie können den Export von Fremdschlüssel für bestimmte Modelle deaktivieren:

User: 
    attributes: 
    export: tables 
    columns: 

Jetzt wird es nur die Tabellendefinition und keine der Fremdschlüssel exportieren. Sie können verwenden: keine, Tabellen, Einschränkungen, Plugins oder alle.

2

Sie fragen explizit nach Dateninkonsistenz, aber ich bin mir ziemlich sicher, dass Sie das wirklich nicht wollen. Ich kann mir keine Situation vorstellen, in der dies vertretbar wäre. Es ist eine schlechte Übung und wird definitiv Probleme verursachen. Zum Beispiel: Was ist das erwartete Ergebnis von $revision->getPage()?

Es gibt eine sehr einfache und elegante Lösung: softdeletable. Es fügt Ihrer Entität im Grunde ein Attribut hinzu (mit anderen Worten: fügt der Tabelle eine Spalte hinzu) mit dem Namen deletedAt, um zu speichern, ob (oder besser: wann) diese Entität gelöscht wird. Wenn das Attribut null lautet, wird die Entität nicht gelöscht.

Das einzige, was Sie tun müssen, ist hinzuzufügen this bundle, fügen Sie ein Merkmal zu Ihrer Entität (Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity) und aktualisieren Sie Ihre Datenbank. Es ist sehr einfach zu implementieren: Dieses Paket wird die Arbeit für Sie erledigen. Lesen Sie the documentation, um diese Erweiterung zu verstehen.

Alternativ können Sie ein 'aktiviert' boolesches Attribut oder ein Statusfeld hinzufügen (zum Beispiel 'veröffentlicht', 'Entwurf', 'gelöscht').

0

When I delete the page I don't want to delete the revisions. I also want to keep the page_id on the page revisions (i.e. not set it to null).

Ich glaube, du hast schon deine Antwort: Lehre, dass nicht tun, einfach weil es auf den Begriff des Fremdschlüssels fremd ist. Das Prinzip eines FK ist die Sicherstellung der Datenkonsistenz. Wenn Sie also einen FK haben, muss dieser auf eine bestehende ID verweisen. Beim Löschen können Sie mit einer DB-Engine wie InnoDB für MySQL einen FK auf null setzen (vorausgesetzt, Sie haben die FK-Spalte auf Null gesetzt). Aber auf eine nicht vorhandene ID zu verweisen, ist nicht machbar, oder es ist kein FK.

Wenn Sie es wirklich tun möchten, verwenden Sie Doctrine nicht für diesen speziellen Fall, es hindert Sie nicht, Doctrine an anderer Stelle in Ihrer Codebasis zu verwenden. Eine andere Lösung besteht darin, die FK-Bedingung einfach manuell zu löschen oder vor der Abfrage eine DB-Anweisung zu verwenden, um die FK-Prüfungen zu überspringen.

+0

MySQL ist keine Datenbank-Engine, es ist ein DBMS. Beliebte Datenbank-Engines für MySQL (InnoDB und MyISAM) behandeln unterschiedlich mit Fremdschlüsseln. Wenn Sie mehr darüber wissen möchten, lesen Sie diese Frage: http://stackoverflow.com/questions/12614541/whats-the-difference-between-myisam-and-innodb –

+1

True, bearbeitet, um auf das –

+0

widerspiegeln Und wie Sie lassen die FK-Bedingung manuell fallen? – ether