Doctrine 2:使用to-many关系插入:未插入引用的行

时间:2015-12-07 16:26:56

标签: php mysql doctrine-orm

我有两个实体A和B,其中A有很多B(从A到B的单向多对多)。 在创建新A并为其分配B时,对连接表的约束失败,因为doctrine正在尝试为新A插入ID为0的关联。这使我相信新A的INSERT具有那时候没有被执行。

/**
 * @Entity
 * @Table(name="a")
 */
class A {
    /**
     * @var int
     * @Id @Column(type="integer") @GeneratedValue
     */
    protected $id;
    /**
     * @var B[]
     * @ManyToMany(targetEntity="B", fetch="LAZY")
     * @JoinTable(name="jointable",
     *     joinColumns={@JoinColumn(name="a_id", referencedColumnName="id")},
     *     inverseJoinColumns={@JoinColumn(name="b_id", referencedColumnName="id")}
     * )
     */
    protected $bs;

    public function getBs() { return $this->bs; }
}

// I omit B here, because it is not important

// controller code:
$a = new A();
$a->getBs()->add($em->find(B::class, 8));
$em->persist($a);
$em->flush();

这是我得到的错误:

An exception occurred while executing 'INSERT INTO jointable (a_id, b_id) VALUES (?, ?)' with params [0, 8]:

SQLSTATE[23000]: Integrity constraint violation...

查看数据库(和查询日志),永远不会创建A.结果,与B#8的关联失败。

我怎样才能使学说得到这个权利?

1 个答案:

答案 0 :(得分:0)

原来,学说确实一切正确。问题是我报告的这个PHP Bug:https://bugs.php.net/bug.php?id=71059

要使用Doctrine进行补偿,您有两种选择:

覆盖布尔类型

如果数据库中的所有布尔字段都是TINYINT(就像我一样),你可以简单地覆盖布尔类型:

Type::overrideType(Type::BOOLEAN,    \Doctrine\DBAL\Types\TinyintBooleanType::class);

为TINYINT布尔值添加自定义类型

如果数据库中的所有布尔字段都不是TININT,则必须向doctrine添加新类型:

Type::addType(
    \Doctrine\DBAL\Types\TinyintBooleanType::NAME,
    \Doctrine\DBAL\Types\TinyintBooleanType::class
);

$entityManager->getConnection()->getDatabasePlatform()->registerDoctrineTypeMapping(
    Type::SMALLINT, \Doctrine\DBAL\Types\TinyintBooleanType::NAME
);  

然后您可以在实体中使用此类型:

@Column(type="tinyintasbool")

TinyintBooleanType

的来源
<?php
namespace Doctrine\DBAL\Types;

use Doctrine\DBAL\Platforms\AbstractPlatform;

/**
 * This type is appropriate when storing boolean values in TINYINT columns.
 * @author Tobias Marstaller
 */
class TinyintBooleanType extends Type
{
    const NAME = "tinyintasbool";

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if ($value === null)
        {
            return null;
        }
        else return $value? 1 : 0;
    }

    public function convertToPHPValue($value, AbstractPlatform $patform)
    {
        if ($value === null)
        {
            return null;
        }
        else return ((int) $value) === 0? false : true;
    }

    /**
     * Gets the SQL declaration snippet for a field of this type.
     *
     * @param array $fieldDeclaration The field declaration.
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform The currently used database platform.
     *
     * @return string
     */
    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return $platform->getSmallIntTypeDeclarationSQL($fieldDeclaration);
    }

    /**
     * Gets the name of this type.
     *
     * @return string
     *
     * @todo Needed?
     */
    public function getName()
    {
        return static::NAME;
    }

    public function getBindingType()
    {
        return \PDO::PARAM_INT;
    }
}