Symfony 2.7.2. Lehre ORM 2.4.7. MySQL 5.6.12. PHP 5.5.0.
Ich habe eine Entität mit benutzerdefinierten ID-Generator-Strategie. Es funktioniert einwandfrei.
In einigen Fällen muss ich diese Strategie mit einer "handgemachten" ID überschreiben. Es funktioniert, wenn die Haupteinheit ohne Assoziationen gelöscht wird. Aber es funktioniert nicht mit Assoziationen. Dieses Beispiel Fehler ausgelöst:Das Überschreiben der Standard-ID-Generierungsstrategie hat keine Auswirkungen auf die Verknüpfungen
An exception occurred while executing 'INSERT INTO articles_tags (article_id, tag_id) VALUES (?, ?)' with params ["a004r0", 4]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (
sf-test1
.articles_tags
, CONSTRAINTFK_354053617294869C
FOREIGN KEY (article_id
) REFERENCESarticle
(id
) ON DELETE CASCADE)
Hier ist, wie zu reproduzieren:
- Install and create a Symfony2 application.
- Bearbeiten Sie
app/config/parameters.yml
mit Ihren DB-Parametern. Verwenden Sie das Beispiel
AppBundle
Namespace, erstellen SieArticle
undTag
Entities insrc/AppBundle/Entity
Verzeichnis.<?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; }
generieren Getter und Setter für die oben genannten Entitäten:
php app/console doctrine:generate:entities AppBundle
erstellen
ArticleNumberGenerator
Klasse insrc/AppBundle/Doctrine
:<?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'; } }
da erstellen Tabase: .
- Tabellen erstellen:
php app/console doctrine:schema:create
. Bearbeiten Sie das Beispiel AppBundle
DefaultController
befindet sich insrc\AppBundle\Controller
. Ersetzen Sie den Inhalt mit:<?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() . '.'); } }
Run-Server:
php app/console server:run
.Navigieren Sie zu http://127.0.0.1:8000/create-default. Aktualisieren 2 mal diese Meldung zu sehen:
Created article id a003r0.
Nun navigieren Sie zu http://127.0.0.1:8000/create-handmade/test. Das erwartete Ergebnis ist:
Created article id test1.
sondern werden Sie den Fehler:
An exception occurred while executing 'INSERT INTO articles_tags (article_id, tag_id) VALUES (?, ?)' with params ["a004r0", 4]:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (
sf-test1
.articles_tags
, CONSTRAINTFK_354053617294869C
FOREIGN KEY (article_id
) REFERENCESarticle
(id
) ON DELETE CASCADE)offenbar, weil Artikel mit
id
"a004r0" existiert nicht.
Wenn I-Kommentar aus $article->getTags()->add($tag);
in createHandmadeAction
, es funktioniert - das Ergebnis:
Created article id test.
und die Datenbank aktualisiert wird entsprechend:
id | title
-------+----------------
a001r0 | Test article 204
a002r0 | Test article 12
a003r0 | Test article 549
test | Test article 723
aber nicht, wenn eine Beziehung hinzugefügt. Aus einem Grund verwendet Doctrine nicht das handgemachte id
für Assoziationen, stattdessen verwendet es die Standard-Id-Generator-Strategie.
Was ist hier falsch? Wie kann ich den Entity Manager davon überzeugen, meine handgefertigten Ids für Verbände zu verwenden?
Das ist es. Getestet in einer Produktions-App mit allen Arten von Assoziationen - es funktioniert. Vielen Dank! – bostaf