DDC-1799: Doctrine's Reverse Engineering 1-n (one to many) association misunderstood as 1-1 (one to one) #2264

Open
opened 2026-01-22 13:46:42 +01:00 by admin · 0 comments
Owner

Originally created by @doctrinebot on GitHub (Apr 27, 2012).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user linuxatico:

I found an odd behaviour of Doctrine's reverse engineering process, just create two simple tables tied by a simple 1-n relationship, take a look at the snap of the folowing SQL code:

    SET @OLD*UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE*CHECKS=0;
    SET @OLD*FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY*CHECKS=0;
    SET @OLD*SQL_MODE=@@SQL_MODE, SQL*MODE='TRADITIONAL';

    DROP SCHEMA IF EXISTS `ACME` ;
    CREATE SCHEMA IF NOT EXISTS `ACME` DEFAULT CHARACTER SET latin1 COLLATE latin1*swedish*ci ;
    USE `ACME` ;

    -- -----------------------------------------------------
    -- Table `ACME`.`task`
    -- -----------------------------------------------------
    DROP TABLE IF EXISTS `ACME`.`task` ;

    CREATE  TABLE IF NOT EXISTS `ACME`.`task` (
      `id*task` INT UNSIGNED NOT NULL AUTO*INCREMENT ,
      `description` VARCHAR(45) NULL ,
      PRIMARY KEY (`id_task`) )
    ENGINE = InnoDB;


    -- -----------------------------------------------------
    -- Table `ACME`.`tag`
    -- -----------------------------------------------------
    DROP TABLE IF EXISTS `ACME`.`tag` ;

    CREATE  TABLE IF NOT EXISTS `ACME`.`tag` (
      `id*tag` INT UNSIGNED NOT NULL AUTO*INCREMENT ,
      `name` VARCHAR(50) NULL ,
      `task_id` INT UNSIGNED NOT NULL ,
      PRIMARY KEY (`id_tag`) ,
      INDEX `fk*tag_task` (`task*id` ASC) ,
      CONSTRAINT `fk*tag*task`
        FOREIGN KEY (`task_id` )
        REFERENCES `ACME`.`task` (`id_task` )
        ON DELETE NO ACTION
        ON UPDATE NO ACTION)
    ENGINE = InnoDB;



    SET SQL*MODE=@OLD_SQL*MODE;
    SET FOREIGN*KEY_CHECKS=@OLD_FOREIGN_KEY*CHECKS;
    SET UNIQUE*CHECKS=@OLD_UNIQUE*CHECKS;

I have a Symfony2 netbeans project at

/Applications/MAMP/htdocs/Acme

and from that location, according to

http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html

in a terminal I did:

    $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:mapping:convert yml ./src/Acme/TaskBundle/Resources/config/doctrine/ --from-database --force
    Processing entity "Tag"
    Processing entity "Task"

    Exporting "yml" mapping information to "/Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine"

    $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:mapping:import Acme\TaskBundle yml
    Importing mapping information from "default" entity manager
      > writing /Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine/Tag.orm.yml
      > writing /Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine/Task.orm.yml

    $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:generate:entities Acme\TaskBundle
    Generating entities for bundle "AcmeTaskBundle"
      > backing up Tag.php to Tag.php<sub>
      > generating Acme\TaskBundle\Entity\Tag
      > backing up Task.php to Task.php</sub>
      > generating Acme\TaskBundle\Entity\Task

The fact is that it only seems ok, because if you take a look at "Tag.orm.yml":

    Acme\TaskBundle\Entity\Tag:
      type: entity
      table: tag
      fields:
        idTag:
          id: true
          type: integer
          unsigned: false
          nullable: false
          column: id_tag
          generator:
            strategy: IDENTITY
        name:
          type: string
          length: 50
          fixed: false
          nullable: true
      oneToOne:
        task:
          targetEntity: Task
          cascade: {  }
          mappedBy: null
          inversedBy: null
          joinColumns:
            task_id:
              referencedColumnName: id_task
          orphanRemoval: false
      lifecycleCallbacks: {  }

It created a **oneToOne* relationship and not a oneToMany* !

If you need any more confirmation, here are _Task.php_ and _Tag.php_:

_Task.php_

    <?php

    namespace Acme\TaskBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;

    /****
     * Acme\TaskBundle\Entity\Task
     */
    class Task
    {
        /****
         * @var integer $idTask
         */
        private $idTask;

        /****
         * @var string $description
         */
        private $description;


        /****
         * Get idTask
         *
         * @return integer 
         */
        public function getIdTask()
        {
            return $this->idTask;
        }

        /****
         * Set description
         *
         * @param string $description
         */
        public function setDescription($description)
        {
            $this->description = $description;
        }

        /****
         * Get description
         *
         * @return string 
         */
        public function getDescription()
        {
            return $this->description;
        }
    }
****Tag.php****

    <?php

    namespace Acme\TaskBundle\Entity;

    use Doctrine\ORM\Mapping as ORM;

    /****
     * Acme\TaskBundle\Entity\Tag
     */
    class Tag
    {
        /****
         * @var integer $idTag
         */
        private $idTag;

        /****
         * @var string $name
         */
        private $name;

        /****
         * @var Acme\TaskBundle\Entity\Task
         */
        private $task;


        /****
         * Get idTag
         *
         * @return integer 
         */
        public function getIdTag()
        {
            return $this->idTag;
        }

        /****
         * Set name
         *
         * @param string $name
         */
        public function setName($name)
        {
            $this->name = $name;
        }

        /****
         * Get name
         *
         * @return string 
         */
        public function getName()
        {
            return $this->name;
        }

        /****
         * Set task
         *
         * @param Acme\TaskBundle\Entity\Task $task
         */
        public function setTask(\Acme\TaskBundle\Entity\Task $task)
        {
            $this->task = $task;
        }

        /****
         * Get task
         *
         * @return Acme\TaskBundle\Entity\Task 
         */
        public function getTask()
        {
            return $this->task;
        }
    }

linuxatico

Originally created by @doctrinebot on GitHub (Apr 27, 2012). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user linuxatico: I found an odd behaviour of Doctrine's reverse engineering process, just create two simple tables tied by a simple 1-n relationship, take a look at the snap of the folowing SQL code: ``` SET @OLD*UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE*CHECKS=0; SET @OLD*FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY*CHECKS=0; SET @OLD*SQL_MODE=@@SQL_MODE, SQL*MODE='TRADITIONAL'; DROP SCHEMA IF EXISTS `ACME` ; CREATE SCHEMA IF NOT EXISTS `ACME` DEFAULT CHARACTER SET latin1 COLLATE latin1*swedish*ci ; USE `ACME` ; -- ----------------------------------------------------- -- Table `ACME`.`task` -- ----------------------------------------------------- DROP TABLE IF EXISTS `ACME`.`task` ; CREATE TABLE IF NOT EXISTS `ACME`.`task` ( `id*task` INT UNSIGNED NOT NULL AUTO*INCREMENT , `description` VARCHAR(45) NULL , PRIMARY KEY (`id_task`) ) ENGINE = InnoDB; -- ----------------------------------------------------- -- Table `ACME`.`tag` -- ----------------------------------------------------- DROP TABLE IF EXISTS `ACME`.`tag` ; CREATE TABLE IF NOT EXISTS `ACME`.`tag` ( `id*tag` INT UNSIGNED NOT NULL AUTO*INCREMENT , `name` VARCHAR(50) NULL , `task_id` INT UNSIGNED NOT NULL , PRIMARY KEY (`id_tag`) , INDEX `fk*tag_task` (`task*id` ASC) , CONSTRAINT `fk*tag*task` FOREIGN KEY (`task_id` ) REFERENCES `ACME`.`task` (`id_task` ) ON DELETE NO ACTION ON UPDATE NO ACTION) ENGINE = InnoDB; SET SQL*MODE=@OLD_SQL*MODE; SET FOREIGN*KEY_CHECKS=@OLD_FOREIGN_KEY*CHECKS; SET UNIQUE*CHECKS=@OLD_UNIQUE*CHECKS; ``` I have a Symfony2 netbeans project at > /Applications/MAMP/htdocs/Acme and from that location, according to > http://symfony.com/doc/current/cookbook/doctrine/reverse_engineering.html in a terminal I did: ``` $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:mapping:convert yml ./src/Acme/TaskBundle/Resources/config/doctrine/ --from-database --force Processing entity "Tag" Processing entity "Task" Exporting "yml" mapping information to "/Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine" $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:mapping:import Acme\TaskBundle yml Importing mapping information from "default" entity manager > writing /Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine/Tag.orm.yml > writing /Applications/MAMP/htdocs/Acme/src/Acme/TaskBundle/Resources/config/doctrine/Task.orm.yml $ ./../../bin/php/php5.3.6/bin/php app/console doctrine:generate:entities Acme\TaskBundle Generating entities for bundle "AcmeTaskBundle" > backing up Tag.php to Tag.php<sub> > generating Acme\TaskBundle\Entity\Tag > backing up Task.php to Task.php</sub> > generating Acme\TaskBundle\Entity\Task ``` The fact is that it only seems ok, because if you take a look at "Tag.orm.yml": ``` Acme\TaskBundle\Entity\Tag: type: entity table: tag fields: idTag: id: true type: integer unsigned: false nullable: false column: id_tag generator: strategy: IDENTITY name: type: string length: 50 fixed: false nullable: true oneToOne: task: targetEntity: Task cascade: { } mappedBy: null inversedBy: null joinColumns: task_id: referencedColumnName: id_task orphanRemoval: false lifecycleCallbacks: { } ``` It created a _**oneToOne_\* relationship and not a **oneToMany**\* ! If you need any more confirmation, here are ***_Task.php**_ and **_Tag.php**_*: ***_Task.php**_* ``` <?php namespace Acme\TaskBundle\Entity; use Doctrine\ORM\Mapping as ORM; /**** * Acme\TaskBundle\Entity\Task */ class Task { /**** * @var integer $idTask */ private $idTask; /**** * @var string $description */ private $description; /**** * Get idTask * * @return integer */ public function getIdTask() { return $this->idTask; } /**** * Set description * * @param string $description */ public function setDescription($description) { $this->description = $description; } /**** * Get description * * @return string */ public function getDescription() { return $this->description; } } ``` ``` ****Tag.php**** <?php namespace Acme\TaskBundle\Entity; use Doctrine\ORM\Mapping as ORM; /**** * Acme\TaskBundle\Entity\Tag */ class Tag { /**** * @var integer $idTag */ private $idTag; /**** * @var string $name */ private $name; /**** * @var Acme\TaskBundle\Entity\Task */ private $task; /**** * Get idTag * * @return integer */ public function getIdTag() { return $this->idTag; } /**** * Set name * * @param string $name */ public function setName($name) { $this->name = $name; } /**** * Get name * * @return string */ public function getName() { return $this->name; } /**** * Set task * * @param Acme\TaskBundle\Entity\Task $task */ public function setTask(\Acme\TaskBundle\Entity\Task $task) { $this->task = $task; } /**** * Get task * * @return Acme\TaskBundle\Entity\Task */ public function getTask() { return $this->task; } } ``` linuxatico
admin added the Bug label 2026-01-22 13:46:42 +01:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#2264