Problem Symfony/Doctrine : One-To-Many - Self-referencing on a primary key - symfony

I would like to have a "post" with an identifier. This one could be classified in another "post" by storing the identifier of his parent.
I tried to do like this:
class Post {
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
* #ORM\OneToMany(targetEntity="Post", mappedBy="Id_Post_Parent")
*/
private $Id_Post;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="Id_Post")
* #ORM\JoinColumn(name="Id_Post", referencedColumnName="Id_Post", nullable=true)
*/
private $Id_Post_Parent;
...
}
but I have this error when i'm checking with doctrine:schema:validate :
[FAIL] The entity-class App\Entity\Post mapping is invalid:
The association App\Entity\Post#Id_Post_Parent refers to the inverse side field App\Entity\Post#Id_Post which is not defined as association.
The association App\Entity\Post#Id_Post_Parent refers to the inverse side field App\Entity\Post#Id_Post which does not exist.
The referenced column name 'Id_Post' has to be a primary key column on the target entity class 'App\Entity\Post'.
Can someone help me to fix this ?

There is small logical error with your structure - your ID_Post variable tries to be both the primary key (the ID) and the collection association side. I didn't check this syntax in too much details (you can find an example of this association along with most of the other associations from doctrine documentation: https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/association-mapping.html#one-to-many-self-referencing), but basically you need to add the children association separately to your entity like this:
class Post
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="postChildren")
* #ORM\JoinColumn(name="id_parent_post", referencedColumnName="id", nullable=true)
*/
private $postParent;
/**
* #ORM\OneToMany(targetEntity="Post", mappedBy="postParent")
*/
private $postChildren;
public function __construct() {
$this->postChildren = new \Doctrine\Common\Collections\ArrayCollection();
}
}

Related

Symfony 6.1 Doctrine gives error 'Could not resolve type of column "id" of class "App\Entity\Officecurrencymax"'

I am getting the error Could not resolve type of column "id" of class "App\Entity\Officecurrencymax" from my installation. Have checked similar questions but I can't seem to get the Doctrine annotations right.
I have 2 entities with a ManyToOne relationship, Office and OfficeCurrencyMax. One Office can have many OfficeCurrencyMax's.
/**
* Office
*
* #ORM\Table(name="Office")
* #ORM\Entity(repositoryClass="App\Repository\OfficeRepository")
*/
class Office
{
// ...
/**
* #ORM\ManyToOne(targetEntity="Officecurrencymax", inversedBy="offices")
*/
private $officeCurrencyMaxes;
// ...
public function getOfficeCurrencyMaxes(): ?Officecurrencymax
{
return $this->officeCurrencyMaxes;
}
public function setOfficeCurrencyMaxes(?Officecurrencymax $officeCurrencyMaxes): self
{
$this->officeCurrencyMaxes = $officeCurrencyMaxes;
return $this;
}
}
Then there is the Officecurrencymax entity:
/**
* Officecurrencymax
*
* #ORM\Table(name="OfficeCurrencyMax", indexes={#ORM\Index(name="IDX_6F39111B73FD6E34", columns={"Office"})})
* #ORM\Entity(repositoryClass="App\Repository\OfficeCurrencyMaxRepository")
*/
class Officecurrencymax
{
// ...
/**
* #var integer
*
* #ORM\Column(name="Id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \Office
*
* #ORM\ManyToOne(targetEntity="Office", inversedBy="offices")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="Office", referencedColumnName="OfficeId")
* })
*/
private $office;
// ...
public function getId(): ?int
{
return $this->id;
}
// ...
}
I had to cut down the code a lot since StackOverflow wouldn't let me post since it looks like your post is mainly code, please add some more details.
In your case, you want to have 1-Office have Many-Officecurrencymax
So Office should have a OneToMany property and Officecurrencymax a ManyToOne.
There are a few errors, for example your Office entity says that the property is inversed by: inversedBy="offices" whereas $offices does not exist in the Officecurrencymax entity.
Your OneToMany in Office should look like:
/**
* #ORM\OneToMany(targetEntity=Officecurrencymax, mappedBy="office")
*/
private $officeCurrencyMaxes;
And in Officecurrencymax we have the opposite side of the relation:
/**
* #ORM\ManyToOne(targetEntity=Office, inversedBy="officeCurrencyMaxes")
* #ORM\JoinColumn(name="Office", referencedColumnName="OfficeId")
*/
private $office;
Do not forget to update the database schema.
based on these docs I would assume the default column name for your mapping should be id (note the case). You have capitalized your column name for some reason
#ORM\Column(name="Id" ...
I would suggest simply changing that to
#ORM\Column(name="id" ...

ORM Doctrine - JoinTable and JoinColumn "column not found" or "could not resolve type of the column"

I am a begginer in Doctrine and i am stuck in kind of a loop trying to establish a join table between 2 entities.
I have a relation OneToMany between a "train" and "containers" and ManyToOne between "containers" and "train" obviously.
I placed annotations on the OneToMany relation like this :
#ORM\Entity/Train
#ORM\OneToMany(targetEntity=Container::class, mappedBy="Train")
#ORM\JoinTable(name="train_container",
#ORM\joinColumns={#JoinColumn(name="id_tm", referencedColumnName="id_tm", nullable=false)},
#ORM\inverseJoinColumns={#JoinColumn(name="id_container", referencedColumnName="id_container", nullable=false)})
)
private $Container;
and on the ManyToOne i did like this :
#ORM\Entity/Container
#ORM\ManyToOne(targetEntity=Train::class, inversedBy="Containers")
#ORM\JoinColumn(name="id_tm", referencedColumnName="id_tm")
private $Train;
Now, if i let it like that, i have an error saying :
Annotation #ORM\joinColumns is not allowed to be declared on property App\Entity\Train::$container. You may only use this annotation on these code elements: PROPERTY.
If i remove the joinColumn and just put this :
#ORM\ManyToOne(targetEntity=Train::class, inversedBy="Container")
private $Train;
I have this error :
Could not resolve type of column "id" of class "App\Entity\Train"
I have no idea why its displaying an "id" column because the name of the column and the property on "Train" is always "id_tm" everywhere.
Could anyone tell me what i am doing wrong please ?
thanks a lot in advance
M
edit to add the code on the id's properties :
#ORM\Id
#ORM\GeneratedValue
#ORM\Column(type="integer", name="id_container")
private $id_container;
#ORM\Id
#ORM\GeneratedValue
#ORM\Column(type="integer", name="id_train")
private $id_train;
On the Train entity, you need to use the ManyToMany annotation with the unique parameter to get a OneToMany association as shown in the doctine documentation.
Here is the code that worked for me, try this:
Train Entity
/**
* Class Train
* #package App\Entity
* #ORM\Entity()
* #ORM\Table(name="train", options={"comment":"Train"})
*/
class Train
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_train", type="integer", unique=true)
*/
private int $id_train;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Container", mappedBy="Train", cascade={"persist"})
* #ORM\JoinTable(name="train_container",
* joinColumns={#ORM\JoinColumn(name="train_id", referencedColumnName="id_train")},
* inverseJoinColumns={#ORM\JoinColumn(name="container_id", referencedColumnName="id_container", unique=true)}
* )
*/
private $Container;
public function __construct() {
$this->Container = new ArrayCollection();
}
//....
Also, if you want to have a bidirectional relationship between Container and Train entities, then you need to specify the following in the Container entity.
Container Entity
/**
* Class Container
* #package App\Entity
* #ORM\Entity()
* #ORM\Table(name="container", options={"comment":"Container"})
*/
class Container
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_container", type="integer", unique=true)
*/
private int $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Train", inversedBy="Container")
* #ORM\JoinColumn(name="train_id", referencedColumnName="id_train")
*/
private $Train;
Don't forget to generate getters and setters after
php bin/console make:entity --regenerate
And update schema
php bin/console d:s:u -f

How to override the id's annotation in a Doctrine inheritnce hiearchy

I'm working with Symfony and MySQL and I'm trying to follow some convention across all my table, one of them is to keep each id's colmun name in the format id_tablename (see diagram). So i kept the id name generated by Symfony in the classes, but I want to replace each field in the database by id_product, id_tire, etc, ...
For that i'm using the Column annotation, e.g:
abstract class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
private $id;
// ...
}
And for each child class, I use AttributeOverride annotation as explained in the doc, like bellow
/**
* #ORM\Entity(repositoryClass=TireRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(name="id_tire")
* )
* )
*/
class Tire extends Product
{
// ...
}
But when attempting a php bin/console make:migration I got the error The column type of attribute 'id' on class 'App\Entity\Tire' could not be changed.
Did I miss something ?
Edit: I tried to override another attribute ($name) with the following code that work:
/**
* #ORM\Entity(repositoryClass=RimRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "name",
* column=#ORM\Column(name="name_rim")
* )
* )
*/
class Rim extends Product
{
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
// ...
}
But even by doing the same thing with $id attribute, I still have the same error message.
Seem like Doctrine have difficult to work with renamed fields too, when you have relations betweens classes. So for now I keep the default id name for each table in database, to continue working.
Please check correct example below.
Looks like you just missing type="integer" in AttributeOverride
use Doctrine\ORM\Mapping as ORM;
/**
* #MappedSuperclass
*/
abstract class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $name;
// ...
}
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(name="id_tire", type="integer")
* )
* )
*/
class Tire extends Product
{
// ...
}
As result migration SQL will be similar to following
$this->addSql('CREATE TABLE tire (id_tire INT NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id_tire))');
It seem like it's a problem with how Doctrine works. As my system require many relations between entities, I didn't noticed it, but without relations, evrything works fine if they are correctly mapped. For exemple with:
Parent class
/**
* #ORM\Entity(repositoryClass=ProductRepository::class)
*/
class Product
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer", name="id_product")
*/
private $id;
// ...
}
Child classes
/**
* #ORM\Entity(repositoryClass=RimRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(type="integer", name="id_rim")
* )
* )
*/
class Rim extends Product
{
/**
* #ORM\Column(type="integer")
*/
private $id;
// ...
}
/**
* #ORM\Entity(repositoryClass=TIreRepository::class)
* #ORM\AttributeOverrides(
* #ORM\AttributeOverride(
* name = "id",
* column=#ORM\Column(type="integer", name="id_tire")
* )
* )
*/
class Tire extends Product
{
/**
* #ORM\Column(type="integer")
*/
private $id;
// ...
}
Will generate those tables in database. But with relations at any level of the hiearchy like in this case, Doctrine will fail to retrive renamed column with annotations. Si in my case I had to keep the default id name across all tables in order to let Doctrine find what he excpect when making relations between tables.
I tried to remove all relation from child class and keep those from parent class, also the opposite, but Doctrine alwas still searching column id while looking for relation/contraints:
$this->addSql('ALTER TABLE picture ADD CONSTRAINT FK_16DB4F894584665A FOREIGN KEY (product_id) REFERENCES product (id)');
It's seem like impossible to do right now, with complex database structure.

Join-Table with metadata, composite key and one to many relationship

i am building a cart that can take items with specified versions.
I am using Symfony 2.4.3 and Doctrine 2
I have following code for three entities, Cart, CartItem and CartItemVersion.
Cart.php
// ----
/**
* #ORM\OneToMany(targetEntity="CartItem", mappedBy="cart")
*/
private $cartItems;
// ----
CartItem.php
// ----
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Cart")
*/
private $cart;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="Item")
*/
private $item;
/**
* #ORM\OneToMany(targetEntity="CartItemVersion", mappedBy="cartItem")
*/
private $cartItemVersions;
// ----
CartItemVersion.php
// ----
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="CartItem")
*/
private $cartItem;
/**
* #ORM\Id
* #ORM\ManyToOne(targetEntity="ItemVersion")
*/
private $itemVersion;
// ----
While updating schema, i got this error.
[Doctrine\ORM\ORMException]
Column name `id` referenced for relation from CartItemVersion towards CartItem does not exist.
Then i gave name to the fields like cartItem_id in CartItemVersion.php and others.
Then updating schema returns,
[Doctrine\DBAL\DBALException]
An exception occurred while executing 'ALTER TABLE cart_item_version ADD CONSTRAINT FK_4D3EA2E02EA80FC1 FOREIGN KEY (cartItem_id) REFERENCES cart_item (cartItemVersion_id)':
SQLSTATE[HY000]: General error: 1005 Can't create table 'symfony.#sql-3d8_12a' (errno: 150)
I have referred Doctrine 2's documentation and followed Use Cases for OrderItem but it seems that this is something because of composite primary keys, but still giving proper names couldn't solve this issue.
Can anyone help?
In your mapping you have a bunch of issues which are corrected as below. You need define the referenced Column name and its own column name; furthermore, for those field which has mappedBy you need to define inversedBy, too.
Cart.php
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="CartItem", mappedBy="cart")
*/
private $cartItems;
CartItem.php
/**
* #ORM\ManyToOne(targetEntity="Cart", inversedBy="cartItems")
* #ORM\JoinColumn(name="cart_id", referencedColumnName="id")
*/
private $cart;
/**
* #ORM\ManyToOne(targetEntity="Item")
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
*/
private $item;
/**
* #var ArrayCollection
* #ORM\OneToMany(targetEntity="CartItemVersion", mappedBy="cartItem")
*/
private $cartItemVersions;
CartItemVersion.php
/**
* #ORM\ManyToOne(targetEntity="CartItem", inversedBy="cartItemVersions")
* #ORM\JoinColumn(name="cart_item_id", referencedColumnName="id")
*/
private $cartItem;
/**
* #ORM\ManyToOne(targetEntity="ItemVersion")
* #ORM\JoinColumn(name="item_version_id", referencedColumnName="id")
*/
private $itemVersion;
To get more info check Relationship Mapping Metadata Documentation
I think you have to make your entities slowly, step by step.
Begin with unidirectional and then add bidirectional when it is necessary...
By using association mapping documentation.
This answer could be a comment but can't post a comment because of my reputation...

Entity containing other entities without having a table

I have an Entity ( Invoice ) which is purely for calculation purposes and has no table, that associates with two other entities having relations by tables. (Although there are so many other entities involved ).
class Row{
/**
* #var integer
*
* #ORM\Column(name="row_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="File")
* #ORM\JoinColumn(name="file_id", referencedColumnName="file_id")
*/
protected $file;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="date")
*/
private $date;
}
class File
{
/**
* #var integer
*
* #ORM\Column(name="file_id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
}
class Invoice
{
/**
* #ORM\Id
* #ORM\Column(name="invoice_id", type="integer")
* #ORM\GeneratedValue
*/
protected $id = null;
/**
* #ORM\OneToMany(targetEntity="Row", mappedBy="row_id")
*/
protected $row;
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="file_id")
*/
protected $file;
}
I want to be able to query for Invoices :
$sDate = //Some date
$this->getEntityManager()
->createQuery("SELECT Invoice, Row, File
FROM
ReportsEntitiesBundle:Invoice Invoice
LEFT JOIN
Row.row Row
LEFT JOIN
Row.file File
WHERE date=:date"
)
->setParaMeter(':date', $sDate)
->setFirstResult($iPage*$iLimit)
->setMaxResults($iLimit)
->getResult();
The questions :
# Doctrine tries to query the database, how can I prevent that and have it find the relevant entities?
# How can I relate the date ( which is in Row entity and cannot be in Invoice ) to the query?
Later this Invoice will become a part of another big entity for calculating/search purposes.
Thank you
Short Answer: You can't
Long Answer : You can't because an entity with #ORM annotations means its persisted to a database - querying that entity relates to querying a database table. Why not just create the table ?!?!?
You need somewhere to persist the association between file and row - a database table is a perfect place !!!!
Update
Just to clarify ... an Entity is just a standard class - it has properties and methods ... just like any other class - When you issue doctrine based commands it uses the annotations within the entities to configure the tables / columns / relationships etc if remove those you can use it however you like ... but you will need to populate the values to use it and you wont be able to use it in a Doctrine query and it obviously wont be persisted !
You can use a read-only entity. It's contents are backed by a view which you create manually in SQL.
PHP:
/** #ORM\Entity(readOnly =true) */
class InvoiceView
{ ...
SQL:
CREATE VIEW invoice_view AS (
SELECT ...

Resources