使用Doctrine中的类表继承从子表中删除行

时间:2014-05-31 20:18:54

标签: php mysql doctrine-orm single-table-inheritance

我用两个模型做了一个非常基本的例子。

  

"歌手"延伸自" Person"

我正在使用class table Inheritance这两个模型:

<?php
namespace models;

/**
 * @Table(name="persons")
 * @entity
 * @InheritanceType("JOINED")
 * @DiscriminatorColumn(name="type", type="string")
 * @DiscriminatorMap({"singer" = "\models\Singer"})
 */
abstract class Person {

    /**
     * @Id
     * @Column(type="integer", nullable=false, name="id_person")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** @column(type="string", nullable=false) */
    protected $name;

歌手模特看起来像:

namespace models;

/**
 * @Table(name="singers")
 * @entity
 */
class Singer extends Person{

    /** @column(type="string", nullable=false) */
    protected $gender;
}

工作流

考虑这种情况。

  1. 在db中我有这些行:

    人员表:

    id_person | name | type
    -----------------------
    1           john   singer
    

    歌手表:

    id_person | gender
    ------------------
    1           pop  
    
  2. 我继续删除这位歌手:

    $singer = $em->find('models\Singer', 1);
    $em->remove($singer);
    $em->flush();
    
  3. 执行上面的代码后,我再次检查数据库,我发现了这个:

    人员表:

    id_person | name | type
    -----------------------
    (empty)
    

    歌手表:

    id_person | gender
    ------------------
    1           pop  
    

    如您所知,未按预期删除子表中的行。

  4. 因此,在搜索了学说文档之后,它说:

      

    如果不使用SchemaTool生成所需的SQL,则应该知道删除类表继承会在所有数据库实现中使用外键属性ON DELETE CASCADE。如果未能自行实现,将导致数据库中出现死行。

    所以,我继续改变人员表如下:

    ALTER TABLE persons 
    ADD CONSTRAINT fk_persons_1
    FOREIGN KEY (id_person)
    REFERENCES singers (id_person)
    ON DELETE CASCADE
    ON UPDATE NO ACTION;
    
  5. 现在,问题变得复杂了:

    • 当我删除一名歌手时,信息仍然存在,甚至人员表也被更改,以便从歌手表中删除。
    • 当我尝试插入像

      这样的新歌手时
      $singer = new \models\Singer('Zara', 'rock');
      $em->persist($singer);
      $em->flush();
      

      它引发了异常:

      Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`practica`.`persons`, CONSTRAINT `fk_persons_1` FOREIGN KEY (`id_person`) REFERENCES `singers` (`id_person`) ON DELETE CASCADE ON UPDATE NO ACTION)' in /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php:138 Stack trace: #0 /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/Statement.php(138): PDOStatement->execute(NULL) #1 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/JoinedSubclassPersister.php(165): Doctrine\DBAL\Statement->execute() #2 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(929): Doctrine\ORM\Persisters\JoinedSubclassPersister->executeInserts() #3 /var/www/html/doctrine/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php(318): Doctrine\ORM\UnitOfWork->executeInserts(Object(Doctrine\ORM\Mapping\ClassMetadata)) #4 /var/www/html/doctrine/vendor/doct in /var/www/html/doctrine/vendor/doctrine/dbal/lib/Doctrine/DBAL/DBALException.php on line 47
      

    所以,基本上,我需要的是在MySQL db中删除子表中的信息。但是我没理解。

2 个答案:

答案 0 :(得分:1)

尝试撤消FOREIGN KEY,即将其从人员表中移除并将其添加到您的歌手表中

ALTER TABLE singers 
ADD CONSTRAINT fk_singers_1
FOREIGN KEY (id_person)
REFERENCES persons (id_person)
ON DELETE CASCADE
ON UPDATE NO ACTION;

答案 1 :(得分:0)

我想你可能不得不问自己是否真的需要一个与多个表有关的字段。

在任何情况下,最好通过关系而不是使用继承和鉴别器映射来实现,这对使用相同表的对象很有效,但因为在你的情况下它们是不同的表,所以最好使用它相反的关系:

<?php
namespace models;

/**
 * @Table(name="persons")
 * @entity
 */

class Person {

    /**
     * @Id
     * @Column(type="integer", nullable=false, name="id_person")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** @column(type="string", nullable=false) */
    protected $name;

    /**
     * @OneToOne(targetEntity="Singer")
     * @JoinColumn(name="id_person", referencedColumnName="id_person")
     */
    private $singer;


namespace models;

/**
 * @Table(name="singers")
 * @entity
 */
class Singer{

    /**
     * @Id
     * @Column(type="integer", nullable=false, name="id_person")
     * @GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /** @column(type="string", nullable=false) */
    protected $gender;

    /**
     * @OneToOne(targetEntity="Person", cascade={"persist", "remove", "merge"}, orphanRemoval=true)
     * @JoinColumn(name="id_person", referencedColumnName="id_person")
     */
    private $person;
}

通过尝试将设计附加到现实世界,您将使实现变得复杂,这将使您的应用程序难以保留。 我没有尝试代码(可能需要调整)。