简体   繁体   中英

Doctrine entity association not working

I have two entities configured called User and ApiKey. They have a one-to-one relationship mapping below.

User:

/**
 * @var integer
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
protected $id;

/**
 * @ORM\Column(name="username", type="string", length=225, unique=true)
 * @Assert\NotBlank(message="Please enter a username.")
 * @Assert\Length(
 *     min = 2,
 *     max = 180,
 *     minMessage = "Your username must be at least {{ limit }} characters",
 *     maxMessage = "Your username cannot be longer than {{ limit }} characters"
 * )
 */
protected $username;

/**
 * @ORM\Column(name="email", type="string", length=225)
 * @Assert\NotBlank(message="Please enter a email.")
 * @Assert\Email(message = "The email '{{ value }}' is not a valid email.")
 */
protected $email;

/**
 * @ORM\Column(name="salt", type="text")
 */
protected $salt;

/**
 * @ORM\Column(name="password", type="string", length=225)
 * @Assert\NotBlank(message="Please enter a password.")
 * @Assert\Length(
 *     min = 7,
 *     minMessage = "Your password must be at least {{ limit }} characters long."
 * )
 */
protected $password;

/**
 * @Assert\NotBlank(message="Please enter a confirmation password.")
 */
protected $confirmationPassword;

/**
 * @ORM\Column(name="roles", type="json_array")
 */
protected $roles;

/**
 * @ORM\OneToOne(targetEntity="ApiKey", mappedBy="user")
 * @ORM\JoinColumn(name="id", referencedColumnName="user_id")
 */
protected $apiKey;

ApiKey:

/**
 * @var integer
 * @ORM\Column(name="user_id", type="integer")
 * @ORM\Id
 */
protected $user_id;

/**
 * @var string
 * @ORM\Column(name="api_key", unique=true, type="string", length=225)
 * @Assert\NotBlank(message="Please enter a username.")
 */
protected $api_key;

/**
 * @var string
 * @ORM\Column(name="expires", type="datetime")
 * @Assert\NotBlank(message="Please enter a username.")
 */
protected $expires;

/**
 * @ORM\OneToOne(targetEntity="User", inversedBy="apiKey")
 * @ORM\JoinColumn(name="user_id", referencedColumnName="id")
 */
protected $user;

However, when I run the following code:

$user = new User();
    $user->setUsername($request->get('username'));
    $user->setSalt(uniqid(mt_rand(), true));
    $user->setPassword(hash('sha256', $user->getSalt() . $request->get('password')));
    $user->setRoles(['ROLE_API']);

    $apiKey = new ApiKey();
    $apiKey->setUser($user);

    $this->getDoctrine()->getManager()->persist($user);
    $this->getDoctrine()->getManager()->flush();

I get this error, what am I doing wrong?

An exception occurred while executing 'INSERT INTO users (username, email, salt, password, roles, id) VALUES (?, ?, ?, ?, ?, ?)' with params ["bobsmith", "bob@live.com", "147423531058bd98d9ac9af4.48409486", "6196012972b1294e7f3fbf9c140bc26f0bf3b354ea37d00753bc1ecba0a72866", "[\\"ROLE_API\\"]", null]:SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails ( app . users , CONSTRAINT FK_1483A5E9BF396750 FOREIGN KEY ( id ) REFERENCES api_key ( user_id ))

Change:

    $user = new User();
    $user->setUsername($request->get('username'));
    $user->setSalt(uniqid(mt_rand(), true));
    $user->setPassword(hash('sha256', $user->getSalt() . $request->get('password')));
    $user->setRoles(['ROLE_API']);

    $apiKey = new ApiKey();
    $this->getDoctrine()->getManager()->persist($apiKey); 
    $apiKey->setUser($user);

    $this->getDoctrine()->getManager()->persist($user);
    $this->getDoctrine()->getManager()->flush();

Entity of type AppBundle\\Entity\\ApiKey is missing an assigned ID for field 'user_id'. The identifier generation strategy for this entity requires the ID field to be populated before EntityManager#persist() is called. If you want automatically generated identifiers instead you need to adjust the metadata mapping accordingly.

You seem to have a cascading issue here. You are saving a User in the database, but that user is linked to an ApiKey that is NOT yet saved in the database. To tell the player that it needs to persist it's ApiKey along with it you need to add a cascade on persist between User and ApiKey

/**
 * @ORM\OneToOne(targetEntity="ApiKey", mappedBy="user", cascade="persist")
 */
protected $api_key;

If you also want to delete the ApiKey along with the User you can use cascade={"persist", "remove"} You can find all the Doctrine documentation about associations here

EDIT: Removed the line // @ORM\\JoinColumn(name="id", referencedColumnName="user_id") that shouldn't exist on the owner side, as commented by Chausser

This is 2 fold, what Cyril said, but its also because you have extra mapping in your User class:

/**
 * @ORM\OneToOne(targetEntity="ApiKey", mappedBy="user")
 * @ORM\JoinColumn(name="id", referencedColumnName="user_id") // Remove this line
 */
protected $apiKey;

Should just be:

/**
 * @ORM\OneToOne(targetEntity="ApiKey", mappedBy="user")
 */
protected $apiKey;

Only the mapping that actually doing the mapping needs the join column. By Using mappedBy="user" you are telling doctrine to look at the ApiKey class user property for how to map this association. Having a join column in this is class is causing problems.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM