Symfony2 OneToMany bidirectional referencing not id column - php

I depends on an external api to populate my tables, and with this relationships i can handle some required functions on the app with less queries.
Instead of the errors and warnings the tables and FK are created after use the command, but i dont know if i can just bypass this errors or it will generate problems in the future.
Obviously i can just references the id and the problem will dissapear, but need to now if this is possible.
I have two tables:
RARITIES
id
name
...
ITEMS
id
item_rarity
...
And the entities looks like:
class Rarity
{
...
/**
* #ORM\OneToMany(targetEntity="Item", mappedBy="rarity")
*/
private $items;
public function __construct() {
$this->items = new ArrayCollection();
}
...
}
class Item
{
...
/**
* #ORM\ManyToOne(targetEntity="Rarity", inversedBy="items")
* #ORM\JoinColumn(name="item_rarity", referencedColumnName="name")
**/
private $rarity;
...
}
When i create or update the schema, it outputs some errors and warnings:
[Doctrine\ORM\Tools\ToolsException]
Schema-Tool failed with Error 'An exception occurred while executing 'ALTER TABLE items ADD CONSTRAINT FK_E11EE94D3139CB89 FOREIGN KEY (item_rarity) REFERENCES rarities (name)':
SQLSTATE[HY000]: General error: 1005 Can't create table 'd2a.#sql-4fa_7c' (errno: 150)' while executing DDL: ALTER TABLE items ADD CONSTRAINT FK_E11EE94D3139CB89 FOREIGN KEY (item_rarity) REFERENCES rarities (name)
[Doctrine\DBAL\DBALException]
An exception occurred while executing 'ALTER TABLE items ADD CONSTRAINT K_E11EE94D3139CB89 FOREIGN KEY (item_rarity) REFERENCES rarities (name)':
SQLSTATE[HY000]: General error: 1005 Can't create table 'd2a.#sql-4fa_7c' (errno: 150)
[PDOException]
SQLSTATE[HY000]: General error: 1005 Can't create table 'd2a.#sql-4fa_7c' (errno: 150
Thanks in advice!

I suspect this has something to do with trying to link the two via 'name', which Doctrine does not believe to be a unique Identifier. There could be two Rarities with the same name as far as Doctrine is concerned. The reason 'id' works is because it is declared as the Primary key in the annotation using #Id, and therefore assumed to be unique:
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
Without looking into it, I would assume that this relationship requires that Items references back to a Primary key.

First of all, thanks to Alex, he gives me the idea of whats going on.
Like he said "There could be two Rarities with the same name as far as Doctrine is concerned" so, lets make rarities.name column as unique:
/**
* Rarity
*
* #ORM\Table(name="rarities",
* uniqueConstraints={#ORM\UniqueConstraint(name="unique_name", columns={"name"})})
* #ORM\Entity
*/
class Rarity
{
And it works, no errors, no warnings, column items.item_rarity referencing rarities.name, and rarities.name is not a primary key in the table.
I hope it helps others, it took me time!

Related

How can I set my entity field to "nullable=false"?

I am connecting my entity "data" with my entity "documents"
/**
* #ORM\ManyToOne(targetEntity="Documents")
* #ORM\JoinColumn(name="document", referencedColumnName="id")
*/
private $document;
If the data is not connected to any document, then my database shows NULL.
But I want it to be empty, when it is not connected. This is my approach:
/**
* #ORM\ManyToOne(targetEntity="Documents")
* #ORM\JoinColumn(name="document", referencedColumnName="id", nullable=false)
*/
private $document;
But I get an error message:
Migration 20190405143905 failed during Execution. Error An exception
occurred while executing 'ALTER TABLE data CHANGE document document
INT NOT NULL':
An exception occurred while executing 'ALTER TABLE data CHANGE
document document INT NOT NULL':
SQLSTATE[HY000]: General error: 1832 Cannot change column
'document': used in a foreign key constraint 'FK_ADF3F363D8698A76'
You need to remove the document(s) entries linked to your data, otherwise you are breaking the foreign key constraint. Then you can set the document ID to null inside the data occurrence
If you can't delete the documents entries because you need them, then I recommend you migrate them to a temporary table, do the required changes on your database model, and then put the documents entries back into the original table

Doctrine's "options" property in #UniqueConstraint not working

Context
I'm using Doctrine version 2.5.0 and I have two entities: Group and Item. I'm trying to create an unique constraint so that Items cannot have the same position in a Group:
/**
* #Entity
* #Table(uniqueConstraints={
* #UniqueConstraint(name="position", columns={"group_id", "position"})
* )
*/
class Item {
...
}
This works well.
Problem
I added an active field to the Item entity, so instead of deleting an Item, I 'inactivate' it. But now the unique constraint doesn't work anymore, since the Item stays in the database with his Group reference and his position.
Attempts
Looking at the Doctrine docs, I've discovered that I can use the options property with a where clause in the #UniqueConstraint:
/**
* #Entity
* #Table(uniqueConstraints={
* #UniqueConstraint(name="position", columns={"group_id", "position"},
* options={"where":"(active = 1)"})}
* )
*/
class Item {
...
}
But the I get the same error as before:
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry
'1-1' for key 'position'
Here is my deletion code:
$item->setActive(false);
$this->_em->persist($item);
$this->_em->flush();
foreach ($item->getNextItems() as $nextItem) {
$nextItem->setPosition($nextItem->getPosition() - 1);
$this->_em->persist($nextItem);
}
$this->_em->flush();
Any idea why the options property is not working?
Update
I realised a strange behaviour. Every time I run the command ./doctrine orm:schema-tool:update --force it recreates the index:
DROP INDEX position ON Item;
CREATE UNIQUE INDEX position ON Item (group_id, position);
But once I remove the options property and run the command, I get:
Nothing to update - your database is already in sync with the current
entity metadata.
It seems I read over this from the Doctrine docs:
SQL WHERE condition to be used for partial indexes. It will only have effect on supported platforms.
After research:
MySQL does not support partial indexes of this nature
My solution is to check if the Item is really unique in a LifeCycleEvent inside the entity:
/**
* #PostPersist #PostUpdate
*/
public function checkUnicity(LifecycleEventArgs $eventArgs)
{
$entity = $eventArgs->getEntity();
$em = $eventArgs->getEntityManager();
// checking unicity here
// throw exception if not
}
if you have data already in database and then after you are adding unique constraint first please check in database that you have unique values or not.

Cannot delete a records (OneToMany) with Symfony

I have 2 entities linked together PICTURE <-(OneToMany)-> NOTE (see class details below). When I tried to delete a picture record I got the following message from Symfony
An exception occurred while executing 'DELETE FROM Picture WHERE id =
?' with params [118]: SQLSTATE[23000]: Integrity constraint violation:
1451 Cannot delete or update a parent row: a foreign key constraint
fails (symfony.note, CONSTRAINT FK_6F8F552A8671F084 FOREIGN KEY
(picture_id) REFERENCES Picture (id))
I find it strange because if the note.author_id is the same as the current user, then I can delete the picture (and the note associated to the picture) without any problem.
here some details of my class picture and note:
Class Picture
{
/**
* #ORM\OneToMany(targetEntity="XXX\XXXBundle\Entity\Note", mappedBy="picture", cascade={"persist", "remove"})
* #ORM\JoinColumn(name="note_id", referencedColumnName="picture_id", onDelete="CASCADE")
**/
private $notes;
}
Class Note
{
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="XXX\XXXBundle\Entity\Picture", inversedBy="notes")
**/
private $picture;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Sdz\UserBundle\Entity\User", cascade={"persist"})
*/
private $user;
}
Here my controller to delete the picture. FYI picture is a collection in my form. I set the option allow_delete to true
foreach($pictures as $picture) // $pictures is the initial list of picture
{
if(false === $data_form->getPictures()->contains($picture))
{
$em->remove($picture);
}
}
You have constraints on database that prevents you from cascaded deleting. I would suggest to use phpMyAdmin tool (mysql command line would be horrible to use for checking constraints): open both tables structure tab, go into relational view, and here check how constraints are set.
You may have different constraints set inside doctrine mappings and inside database, and it may work fine, until you try to perform an action on database, that collides with database constraints, like here.

Doctrine's JoinColumns not working properly

I'm trying to make a ManyToOne association to work in doctrine, but no luck, here's the code of my relationship :
// the entity : Product
/**
* #Id
* #ManyToOne(targetEntity="ProductsModule\Entity\Configuration", inversedBy="product")
* #JoinColumns={#JoinColumn(name="id_type", referencedColumnName="id"),
* #JoinColumn(name="id_site", referencedColumnName="id")}
*/
private $configurations;
/**
* #Id
* #ManyToOne(targetEntity="ProductsModule\Entity\Picture", inversedBy="product")
* #JoinColumn(name="id_picture", referencedColumnName="id")
*/
private $picture;
and the inverse of the association is :
// the entity : Configuration
/**
* #OneToMany(targetEntity="ProductsModule\Entity\Product", mappedBy="configurations")
*/
private $product;
// the code in the controller
$product = new Product;
$product->setConfiguration($configuration); // the configuration object is retrieved from DB
$product->setPicture($picture);
$this->em->persist($product);
try
{
$this->em->flush();
}
catch (\Exception $e)
{
var_dump($e->getMessage());die();
}
The problem is that Doctrine tries to set the first column of JoinColumn (in the example above : id_type) but it ignores the second column, which throws the following error :
An exception occurred while executing 'INSERT INTO tableX (id_picture, id_type) VALUES (?, ?)' with params [1, 1]:SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`db`.`tableX`, CONSTRAINT `fk2` FOREIGN KEY (`id_site`) REFERENCES `sites` (`id`))
the product entity in itself is an entity that was born out of a ManyToMany relationship (that's why it has two primary keys), I just changed the names of the entities to keep the code private, because I'm not allowed to share it.
thank you.
It seems that you have with a foreign key related to site property try to update your schema with :
app/console doctrine:schema:update --force
If you got the same error try to drop the table or the database if this will not create any problem and make sure to export the sql file of the database and remove all foreign key , import your data then re-excute the commande :
app/console doctrine:schema:update --force

Symfony/Doctrine 2 ManyToOne relationship, ORM does not retain set value for column, error ensues

I'm creating a ManyToOne relationship in table B, to one entry in table A.
I am using a particular key_id parameter, which is set in the controller, before being flushed to the db. My problem is that the SQL created by the ORM nulls the value for this key, for some reason. Before the ManyToOne relationship the same controller had no issues setting and persisting the key_id value.
Here is my annotation on Table B
/**
* #ORM\Column(type="string", length=63)
* #Filter\Trim()
* #Filter\StripNewlines()
*/
private table_b_id
/**
* #ORM\ManyToOne(targetEntity="TableA", inversedBy="table_b_entries", fetch="EAGER")
* #ORM\JoinColumn(name="table_b_id", referencedColumnName="table_a_id")
*/
private $reference_to_table_a;
And Table A
/**
* Constructor
*/
function __construct()
{
$this->table_b_entries = new ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="TableB", mappedBy="table_b_id")
*/
private $table_b_entries;
The error that I get is:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'table_b_id' cannot be null
Although I DO set it in the controller before flushing to db, and I have verified the object that I flush that it contains the value, however the SQL does NOT contain this value anymore... I'm not sure where or why it gets lost...
Update
The error described here is because I was not setting the reference object, but trying to set the reference column id manually on Table B. I Should have first fetched the Table A, and set that object on Table B, through the reference setter...
The initial question was answered by: Doctrine 2 category_id always NULL when set ManyToOne relationship
Now I'm dealing with another error:
ContextErrorException: Notice: Undefined index: table_a_id in
/vagrant/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php
line 628
You don't need two fields for ManyToOne relation. This is sufficient:
class B
{
/**
* #ORM\ManyToOne(targetEntity="A", inversedBy="entitiesB")
* #ORM\JoinColumn(name="name_of_column_on_table_b", referencedColumnName="column_from_table_a_to_be_referenced")
*/
private $entityA;
// ...
}
class A
{
/**
* #ORM\OneToMany(targetEntity="B", mappedBy="entityA")
*/
private $entitiesB;
// ...
}
Doctrine will automatically create field called entityA_id on your B table, index it and create foreign key between the two.
When you have instance of entity B, you can retrieve it's A record relation Id by just calling:
$b->getEntityA()->getId()
People commonly think that this will trigger another query, but it will not, it will just read entityA_id from B record, for exaplanation see here

Categories