【发布时间】:2015-10-14 04:05:32
【问题描述】:
Symfony 2.7.2。教义 ORM 2.4.7。 MySQL 5.6.12。 PHP 5.5.0.
我有一个具有自定义 ID 生成器策略的实体。它完美无瑕。
在某些情况下,我必须用“手工” ID 覆盖此策略。它在没有关联的情况下刷新主要实体时起作用。但它不适用于关联。抛出此示例错误:
使用参数 ["a004r0", 4] 执行 'INSERT INTO article_tags (article_id, tag_id) VALUES (?, ?)' 时发生异常:
SQLSTATE[23000]:完整性约束违规:1452 无法添加或更新子行:外键约束失败(
sf-test1.articles_tags,CONSTRAINTFK_354053617294869CFOREIGN KEY(article_id)参考article(id) 删除级联)
复制方法如下:
- Install and create a Symfony2 application。
- 使用您的数据库参数编辑
app/config/parameters.yml。 -
使用示例
AppBundle命名空间,在src/AppBundle/Entity目录中创建Article和Tag实体。<?php // src/AppBundle/Entity/Article.php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity * @ORM\Table(name="article") */ class Article { /** * @ORM\Column(type="string") * @ORM\Id * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class="AppBundle\Doctrine\ArticleNumberGenerator") */ protected $id; /** * @ORM\Column(type="string", length=255) */ protected $title; /** * @ORM\ManyToMany(targetEntity="Tag", inversedBy="articles" ,cascade={"all"}) * @ORM\JoinTable(name="articles_tags") **/ private $tags; public function setId($id) { $this->id = $id; } }<?php // src/AppBundle/Entity/Tag.php namespace AppBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; /** * @ORM\Entity * @ORM\Table(name="tag") */ class Tag { /** * @ORM\Column(type="integer") * @ORM\Id * @ORM\GeneratedValue */ protected $id; /** * @ORM\Column(type="string", length=255) */ protected $name; /** * @ORM\ManyToMany(targetEntity="Article", mappedBy="tags") **/ private $articles; } -
为上述实体生成getter和setter:
php app/console doctrine:generate:entities AppBundle -
在
src/AppBundle/Doctrine中创建ArticleNumberGenerator类:<?php // src/AppBundle/Doctrine/ArticleNumberGenerator.php namespace AppBundle\Doctrine; use Doctrine\ORM\Id\AbstractIdGenerator; use Doctrine\ORM\Query\ResultSetMapping; class ArticleNumberGenerator extends AbstractIdGenerator { public function generate(\Doctrine\ORM\EntityManager $em, $entity) { $rsm = new ResultSetMapping(); $rsm->addScalarResult('id', 'article', 'string'); $query = $em->createNativeQuery('select max(`id`) as id from `article` where `id` like :id_pattern', $rsm); $query->setParameter('id_pattern', 'a___r_'); $idMax = (int) substr($query->getSingleScalarResult(), 1, 3); $idMax++; return 'a' . str_pad($idMax, 3, '0', STR_PAD_LEFT) . 'r0'; } } 创建数据库:
php app/console doctrine:database:create。- 创建表:
php app/console doctrine:schema:create。 -
编辑位于
src\AppBundle\Controller中的示例AppBundleDefaultController。将内容替换为:<?php // src/AppBundle/Controller/DefaultController.php namespace AppBundle\Controller; use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use AppBundle\Entity\Article; use AppBundle\Entity\Tag; class DefaultController extends Controller { /** * @Route("/create-default") */ public function createDefaultAction() { $tag = new Tag(); $tag->setName('Tag ' . rand(1, 99)); $article = new Article(); $article->setTitle('Test article ' . rand(1, 999)); $article->getTags()->add($tag); $em = $this->getDoctrine()->getManager(); $em->getConnection()->beginTransaction(); $em->persist($article); try { $em->flush(); $em->getConnection()->commit(); } catch (\RuntimeException $e) { $em->getConnection()->rollBack(); throw $e; } return new Response('Created article id ' . $article->getId() . '.'); } /** * @Route("/create-handmade/{handmade}") */ public function createHandmadeAction($handmade) { $tag = new Tag(); $tag->setName('Tag ' . rand(1, 99)); $article = new Article(); $article->setTitle('Test article ' . rand(1, 999)); $article->getTags()->add($tag); $em = $this->getDoctrine()->getManager(); $em->getConnection()->beginTransaction(); $em->persist($article); $metadata = $em->getClassMetadata(get_class($article)); $metadata->setIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_NONE); $article->setId($handmade); try { $em->flush(); $em->getConnection()->commit(); } catch (\RuntimeException $e) { $em->getConnection()->rollBack(); throw $e; } return new Response('Created article id ' . $article->getId() . '.'); } } 运行服务器:
php app/console server:run。-
导航到http://127.0.0.1:8000/create-default。刷新 2 次即可看到此消息:
已创建文章 ID a003r0。
-
现在,导航到http://127.0.0.1:8000/create-handmade/test。预期结果是:
已创建文章 ID test1。
但是你会得到错误:
使用参数 ["a004r0", 4] 执行 'INSERT INTO article_tags (article_id, tag_id) VALUES (?, ?)' 时发生异常:
SQLSTATE[23000]:完整性约束违规:1452 无法添加或更新子行:外键约束失败(
sf-test1.articles_tags,CONSTRAINTFK_354053617294869CFOREIGN KEY(article_id)参考article(id) 删除级联)显然是因为带有
id“a004r0”的文章不存在。
如果我在 createHandmadeAction 中注释掉 $article->getTags()->add($tag);,它会起作用 - 结果是:
创建文章 ID 测试。
并相应更新数据库:
id | title
-------+----------------
a001r0 | Test article 204
a002r0 | Test article 12
a003r0 | Test article 549
test | Test article 723
但不是在添加关系时。出于某种原因,Doctrine 没有使用手工制作的id 进行关联,而是使用默认的 Id 生成器策略。
这里有什么问题?如何说服实体经理使用我的手工 Id 进行关联?
【问题讨论】:
标签: php symfony oop doctrine-orm associations