Doctrine - ManyToMany with custom primary key - mysql

I've a unidirectional ManyToMany relation:
class Account {
/* other attributes ... */
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Item")
* #ORM\JoinTable("account_items",
* joinColumns={#ORM\JoinColumn(name="account_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="item_id", referencedColumnName="vnum")}
* )
*/
private $items;
}
What I want to do:
I would like to make the account possible to have one or more identical Item (the same identifier.). For example:
`account_id` 1, `item_id` 1
`account_id` 1, `item_id` 1
should've been allowed.
It's not possible when doctrine generates DDL query with two primary keys (account_id, item_id) and if I try to do it I got mysql duplciation entry error.
I customized migration that creates id as primary key (as only one), but I think it's not proper solution.
`id`: 1, account_id` 1, `item_id` 1
`id` : 2, account_id` 1, `item_id` 1
Do you have any?

Make a bi-directionnal OneToMany <=> ManyToOne relation between 3 entities.
src/AppBundle/Entity/Account.php
class Account {
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\AccountItem", mappedBy="accounts")
*/
private $accountItems;
}
src/AppBundle/Entity/AccountItem.php
class AccountItem {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Account", inversedBy="accountItems")
*/
private $accounts;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Item", inversedBy="itemAccounts")
*/
private $items;
}
src/AppBundle/Entity/Item.php
class Item {
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\AccountItem", mappedBy="items")
*/
private $itemAccounts;
}
It's in fact a ManyToMany relation, but you can now add id, which will be unique, en still have duplicate entries.
Side now, you're referencing item.vnum in your annotation. Know that Symfony hate it when you don't use id.

Related

Foreign Key constraint when inserting rows in table

I have an error while trying to insert some rows in a table. It's an association problem between my entities but I don't know how to solve it.
banque_statistiquecolle table is empty and I can't insert any row, banque_colle contains some rows. Rows I'm trying to insert are linked to these banque_colle rows.
Query :
INSERT INTO banque_statistiquecolle (id, colle_id, effectif, moyenne, mediane, note100, major, minor)
SELECT sc.id, sc.colle_id, sc.effectif, sc.moyenne, sc.mediane, sc.note100, sc.major, sc.minor
FROM statistiquecolle_groupe scg
LEFT JOIN statistiquecolle sc ON sc.id = scg.statistiquecolle_id
WHERE scg.groupe_id = 1
AND sc.id NOT IN (SELECT sc1.id
FROM statistiquecolle_groupe scg1
LEFT JOIN statistiquecolle sc1 ON sc1.id = scg1.statistiquecolle_id
WHERE scg1.groupe_id != 1)
The error :
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row:
a foreign key constraint fails ('tutotour_tharmo18052017test'.'banque_statistiquecolle',
CONSTRAINT 'FK_5C1250C5F0B40A43' FOREIGN KEY ('colle_id') REFERENCES 'banque_colle' ('id'))
BanqueStatistiqueColle Entity :
/**
* BanqueStatistiqueColle
*
* #ORM\Table(name="banque_statistiquecolle")
* #ORM\Entity
*/
class BanqueStatistiqueColle
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="\PACES\BanqueBundle\Entity\BanqueColle")
* #ORM\JoinColumn(name="colle_id", referencedColumnName="id")
*/
private $colle;
{Other properties}
/**
* #ORM\OneToMany(targetEntity="\PACES\BanqueBundle\Entity\BanqueRepartitionColle", mappedBy="idStatColle",
* cascade={"all"})
*/
private $repartitionsColle;
BanqueColle Entity :
/**
* Colle
*
* #ORM\Table(name="banque_colle")
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"colle"="BanqueColle", "colleQC"="BanqueColleQC"})
*/
class BanqueColle
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
{Other properties}
/**
* #ORM\OneToMany(targetEntity="PACES\BanqueBundle\Entity\BanqueStatistiqueColle", mappedBy="colle", cascade={"all"})
*/
protected $statsColle;
The error is self explanatory
Integrity constraint violation: 1452 Cannot add or update a child row:
a foreign key constraint fails
When two table are binded with foreign key relationship, in that case you can only insert those values in child table which are already present in parent table.
Your foreign key relation is:
FOREIGN KEY ('colle_id') REFERENCES 'banque_colle' ('id'))
so you can insert only those value in colle_id which are already their in 'banque_colle' ('id')) column.

Doctrine one-to-many association: Issues with composite primary key

I am trying to populate the below one-to-many doctrine association however I am hitting a problem because every Customer (primary key: id) has their visits (primary key: customer_id & visitday) captured in the Visit table (I am deriving visitday as the number of days since 1st Jan 2000 before persisting to the database (since datetime objects can not be in the primary key)):
Entities
class Customer
{
/**
* #ORM\Column(type="integer", options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Visit", mappedBy="visitday")
*/
protected $visits;
public function __construct()
{
$this->visits = new ArrayCollection();
}
/* -- */
}
Class Visit
{
/**
* #ORM\Column(name="customer_id", type="integer", options={"unsigned"=true})
* #ORM\JoinColumn(name="customer_id", referencedColumnName="id")
* #ORM\Id
*/
private $customer;
/**
* #ORM\Column(type="smallint")
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="visits")
* #ORM\JoinColumn(name="visitday", referencedColumnName="id")
* #ORM\Id
*/
protected $visitday;
/* -- */
}
My problem is that my Customer objects are not getting populated with the customer's corresponding visits. I assume this is because doctrine can't see that it should also include its own customer ID in the lookup. Is there a way to fix this?
I would recommend you to change $visitday attribute to DateTime. It will be your visit date timestamp. Then customer attribute should be inversed with visits.
/**
* #ORM\Column(name="customer_id", type="integer", options={"unsigned"=true})
* #ORM\ManyToOne(targetEntity="Customer", inversedBy="visits")
* #ORM\Id
*/
private $customer;
And as an option you could change relation Customer to Visits as ManyToMany. So you wont have duplicate visit dates.

Wrong ID given in Doctrine2 OneToOne mapping

I am seriously stack right now with the problem I have occurred with OneToOne mapping.
So let me show what I currently have:
OrderItem entity
/**
* OrderItem
*
* #ORM\Table(name="order_item")
* #ORM\Entity
*/
class OrderItem
{
/**
* #var integer
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ... //
/**
* #ORM\OneToOne(targetEntity="UserPricingOption", mappedBy="orderItem")
*/
private $userPricingOption;
/**
* #ORM\ManyToOne(targetEntity="Order", inversedBy="items")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $order;
// ... //
UserPricingOption entity
/**
* UserPricingOption
*
* #ORM\Table(name="user_pricing_option")
* #ORM\Entity
* #ExclusionPolicy("all")
*/
class UserPricingOption
{
/**
* #var integer
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
// ... //
/**
* #ORM\OneToOne(targetEntity="OrderItem", inversedBy="userPricingOption")
* #ORM\JoinColumn(referencedColumnName="id")
*/
private $orderItem;
// ... //
so generated tables look like this:
order_item table
* `id` 5
user_pricing_option table
* `id` 12
* `order_item_id` 5
So now I am trying to do the following:
echo $orderItem->getId(); // gives 5, `GOOD`
echo $orderItem->getUserPricingOption()->getId(); // gives 5 `BAD` (completely different user_pricing_option row), it should return 12.
I seriously have no idea why is that, please find the raw Doctrine query:
Keep in mind that query contains way more info than the showed examples above
SELECT t0.id AS id_1, t0.guid AS guid_2, t0.created_at AS created_at_3, t0.modified_at AS modified_at_4, t5.id AS id_6, t5.guid AS guid_7, t5.created_at AS created_at_8, t5.modified_at AS modified_at_9, t5.user_id AS user_id_10, t5.order_item_id AS order_item_id_11, t5.pricing_option_id AS pricing_option_id_12, t13.id AS id_14, t13.guid AS guid_15, t13.created_at AS created_at_16, t13.modified_at AS modified_at_17, t13.user_id AS user_id_18, t13.order_item_id AS order_item_id_19, t13.product_variant_id AS product_variant_id_20, t0.order_invoice_id AS order_invoice_id_21, t0.order_id AS order_id_22 FROM order_item t0 LEFT JOIN user_pricing_option t5 ON t5.order_item_id = t0.id LEFT JOIN user_product_variant t13 ON t13.order_item_id = t0.id WHERE t0.order_id = ? [131]
So basically $orderItem->getUserPricingOption()->getId() always returns the same ID as $orderItem->getId();
Anyone possibly see what is going on?
OneToOne bidireccional?
Try to define in joinColumn the name with column to reference id.
/**
* #ORM\OneToOne(targetEntity="OrderItem", inversedBy="userPricingOption")
* #ORM\JoinColumn(name="order_item_id", referencedColumnName="id")
*/
private $orderItem;
http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-one-bidirectional

Building OneToMany and ManyToOne associations beetwen more than three tables via additional table Doctrine 2

Building associations beetwen more than three tables
via additional [EntityTypes] table Doctrine2
I have next schema of database:
//==== some Tables ====
articles (
id
entity_type_id
)
posts (
id
entity_type_id
)
other... (
id
entity_type_id
)
//==== additional table of Types ====
entity_types (
id
)
//==== Comments ====
comments (
id
entity_type_id
entity_id
)
I use Symfony2 with Doctrine2 thereby I've been written next assotiations:
class Article
{
/**
* #ORM\ManyToOne(targetEntity="EntityType", inversedBy="articles", cascade={"persist"})
* #ORM\JoinColumn(name="entity_type_id", referencedColumnName="id")
*/
protected $entityType;
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="article")
*/
protected $comments;
}
class Post
{
/**
* #ORM\ManyToOne(targetEntity="EntityType", inversedBy="posts", cascade={"persist"})
* #ORM\JoinColumn(name="entity_type_id", referencedColumnName="id")
*/
protected $entityType;
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="post")
*/
protected $comments;
}
...
class EntityType
{
/**
* #ORM\OneToMany(targetEntity="Article", mappedBy="entityType")
*/
protected $articles;
/**
* #ORM\OneToMany(targetEntity="Post", mappedBy="entityType")
*/
protected $posts;
/**
* #ORM\OneToMany(targetEntity="Comment", mappedBy="entityType")
*/
protected $comments;
}
class Comment
{
/**
* #ORM\ManyToOne(targetEntity="EntityType", inversedBy="comments", cascade={"persist"})
* #ORM\JoinColumn(name="entity_type_id", referencedColumnName="id")
*/
protected $entityType;
/**
* #ORM\ManyToOne(targetEntity="Article", inversedBy="comments", cascade={"persist"})
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id")
*/
protected $article;
/**
* #ORM\ManyToOne(targetEntity="Post", inversedBy="comments", cascade={"persist"})
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id")
*/
protected $post;
}
There are no any mistakes in mapping and all operations are available
including commenting articles
but an exception appeared when I'm trying to comment post
need someone to help...

Doctrine2 Query where Subquery would be needed

Let me first introduce the Entities used in this example:
Order (_order in mysql)
$id (primary key, auto increment)
OrderStatus (order_status in mysql)
$id (primary key, auto increment)
$order (storing the order object it is related to, named order_id in mysql)
$statuscode (Storing the integer code)
$created_at (Storing the datetime of creation)
The relationship is Order n:1 OrderStatus. For every status change, I create a new OrderStatus with the new statuscode. So for one Order there can be many OrderStatus. The actual OrderStatus can be figured out by looking at the OrderStatus with the latest created_at.
I now want to get all objects which have the status 0 right now. In SQL, my query would look like this:
SELECT o.id,os.statuscode,os.created_at
FROM `_order` o
LEFT JOIN `order_status` os ON o.id = os.order_id
WHERE os.created_at = (SELECT MAX(created_at)
FROM order_status
WHERE order_id = os.order_id);
Can I do such a query in DQL or do I have to work with objects? If so, would I need to read all OrderStatus objects and manually figure out which is the most current one or can I somehow preselect?
I figured out a way which is not quite what I was searching for, but it doesn't require any changes in my application, so it's ok as long as I don't find a better solution.
What I'll do is I'll store the actual status directly within the Order. Through LifecycleCallbacks I'll update the order as soon as a OrderStatus related to it is created.
So my Order-Object looks like this:
/**
* #ORM\Entity
* #ORM\Table(name="_order")
*/
class Order{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
//..
/**
* #ORM\Column(type="smallint", nullable=false)
*/
protected $status = -1;
// Getter and Setter!
}
My Order-Status will look like this:
/**
* #ORM\Entity
* #ORM\Table(name="order_status")
* #ORM\HasLifecycleCallbacks
*/
class OrderStatus{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Order", inversedBy="order_status", cascade={"persist"})
* #ORM\JoinColumn(name="order_id", referencedColumnName="id", nullable=false)
*/
protected $order;
/**
* #ORM\Column(type="smallint", nullable=false)
*/
protected $statuscode;
// ..
/**
* #ORM\prePersist
*/
public function prePersist(){
$this->order->setStatus($this->statuscode);
}
// Getter and Setter!
}
Mind the , cascade={"persist"} within the OrderStatus so the Order will also be persisted when the OrderStatus is persisted, otherwise the status will be set, but not stored!
On the plus side of this solution is that I now get the OrderStatus by calling $order->getStatus() instead of having to query the database for the status and that I'm able to do $repository->findByStatus(0). On the other hand, the moment the status field gets changed due to some error, the data will be inconsistent.
As said, if you got a solution where I don't need an extra field to store the status, post it! I'm very interested in a better solution!