Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Disable Doctrine foreign key constraint

I have a relationship on one of my models:

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

And when I delete the parent page, I get this error:

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

Basically my models are a page, and page revision. 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).

How can I do this with Doctrine?

like image 359
Petah Avatar asked Sep 06 '16 22:09

Petah


People also ask

Can we disable foreign key constraint?

You can disable a foreign key constraint during INSERT and UPDATE transactions in SQL Server by using SQL Server Management Studio or Transact-SQL. Use this option if you know that new data will not violate the existing constraint or if the constraint applies only to the data already in the database.

Can we disable foreign key constraint in MySQL?

You can disable foreign key check in MySQL by setting the system variable foreign_key_checks to 0. However, please note, after you enable foreign key checks, MySQL will not re-validate your existing data that you added after disabling foreign key check. It will only check any new additions/updates to your database.


3 Answers

You can disable the exporting of foreign keys for specific models:

User:
  attributes:
    export: tables
  columns:

Now it will only export the table definition and none of the foreign keys. You can use: none, tables, constraints, plugins, or all.

like image 33
Ross Keddy Avatar answered Sep 22 '22 20:09

Ross Keddy


By definition you cannot delete the record that the foreign key is pointing at without setting the key to null (onDelete="SET NULL") or cascading the delete operation (There are two options - ORM Level: cascade={"remove"} | database level: onDelete="CASCADE").
There is the alternative of setting a default value of a still existing record, but you have to do that manually, I don't think Doctrine supports this "out-of-the-box" (please correct me if I am wrong, but in this case setting a default value is not desired anyway).

This strictness is reflecting the concept of having foreign key constraints; like @Théo said:

a FK is to ensure data consistency.

Soft delete (already mentioned) is one solution, but what you could also do is add an additional removed_page_id column that you sync with the page_id just before you delete it in a preRemove event handler (life cycle callback). Whether such information has any value I wonder but I guess you have some use for it, otherwise you wouldn't ask this question.

I am definitely not claiming this is good practice, but it is at least something that you can use for your edge case. So something in the line of:

In your 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;

And then in your 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();
}

Alternatively you could of course already set the correct $removedPageId value during construction of your Revision, then you don't even need to execute a life cycle callback on remove.

like image 68
Wilt Avatar answered Sep 23 '22 20:09

Wilt


I solved this by overriding one doctrine class in symfony 4.3, it looks like this for me:

enter image description here

enter image description here

enter image description here

<?php declare(strict_types=1);

namespace App\DBAL;

use Doctrine\DBAL\Platforms\MySQLPlatform;

/**
 * Class MySQLPlatformService
 * @package App\DBAL
 */
class MySQLPlatformService extends MySQLPlatform
{
    /**
     * Disabling the creation of foreign keys in the database (partitioning is used)
     * @return false
     */
    public function supportsForeignKeyConstraints(): bool
    {
        return false;
    }

    /**
     * Disabling the creation of foreign keys in the database (partitioning is used)
     * @return false
     */
    public function supportsForeignKeyOnUpdate(): bool
    {
        return false;
    }
}
like image 21
Сергей Шевченко Avatar answered Sep 21 '22 20:09

Сергей Шевченко