I have 2 entities: User and Metadata. They have a bi-directional one-to-many relationship (a user can have many metadata records).
In a context decorator I'm using for flagception, I query the user and some of the user's relationships that may be needed using DQL:
$dql = "
SELECT user
FROM
\meQ\Entity\User user
JOIN user.profile profile
JOIN profile.client client
JOIN user.metadata metadata
WHERE user.uid = :user_id
";
$this->user = $em->createQuery($dql)
->setParameter('user_id', $session_user->getId())
->useQueryCache(true)
->getOneOrNullResult();
// for debug
dump($this->user);
exit();
This query returns the user, complete with the user->profile and user->profile->client fields populated correctly. However, it does not have its metadata field populated.
The user in question has 18 metadata records, but performing dump($this->user) shows me an empty ArrayCollection:
Here is my Doctrine mapping for this relationship:
AppBundle\Entity\User:
# ...
oneToMany:
metadata:
fetch: EXTRA_LAZY
targetEntity: AppBundle\Entity\UserMetadata
mappedBy: user
cascade: [all]
orphanRemoval: true
indexBy: name
and the other side of the relationship:
AppBundle\Entity\UserMetadata:
# ...
manyToOne:
user:
targetEntity: AppBundle\Entity\User
inversedBy: metadata
joinColumn:
name: user_id
referencedColumnName: uid
Does this not work because User isn't the owning side?
I had to explicitly list metadata in my SELECT:
$dql = "
- SELECT user
+ SELECT user, metadata
FROM
\meQ\Entity\User user
JOIN user.profile profile
JOIN profile.client client
JOIN user.metadata metadata
WHERE user.uid = :user_id
";
Related
I have a relationship like the one in doctrine's docs so i'll use it as an example:
Product:
type: entity
oneToOne:
shipping:
targetEntity: Shipping
joinColumn:
name: shipping_id
referencedColumnName: id
I'm attempting to delete a Shipping entity but am getting a foreign key constraint exception because the Product's row holds a reference to it. What's the proper way of handling this? Is there something in the yaml that i can add to take care of this? Or do i need to do something like below:
$product->setShipping(null);
$entityManager->persist($product);
$entityManager->remove($shipping);
$entityManager->flush();
You should set the onDelete option to CASCADE if you want the Shipping to be removed too, or to SET NULL if you want to delete just the Product.
Product:
type: entity
oneToOne:
shipping:
targetEntity: Shipping
joinColumn:
name: shipping_id
referencedColumnName: id
onDelete: "SET NULL"
You can read more about this on the Doctrine docs.
I have two entities, invoices and sales tax. Recently my team has decided to upgrade to Symfony 2.3 from 2.1. We had to rewrite a lot of the ways we were doing queries because of this, and some entities that didn't have relationships needed to have them.
Before the update, my invoices and sales tax records were created by getting the transaction id date('U'); and setting up both with the same transaction id (both tables have a primary index id as well).
So you can imagine it looks like this:
Sales Tax: id, transaction_id, amount
Invoice: id, transaction_id, amount
So when I queried for them I just joined on the transaction id. Now joins aren't working without relationships, so I'm having to update this to have a relationship. But when I go to create an invoice or sales tax record, I get this error: Notice: Undefined index: transactionId in C:\folders\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\BasicEntityPersister.php line 539.
In the code the sales tax record is being created just fine, but when it goes to create the invoice it fails:
public function insertSalesTax($transactionId, $amount)
{
$tax = new SalesTax();
$tax->setAmount($amount);
$tax->setTransactionId($transactionId);
$this->entityManager->persist($tax);
$this->entityManager->flush();
return $tax;
}
That inserts, but then I take that tax record and try to create the invoice:
$invoice = new Invoice();
$em = $this->entityManager;
$invoice //omitted other invoice data
->setSalesTax($salesData['salesTax']);
$em->persist($invoice);
$em->flush();
Here's the relevant portions of my mapping in yml:
Bundle\Entity\Invoice:
type: entity
table: invoices
indexes:
transactionId:
columns: [ transaction_id ]
id:
id:
type: integer
generator: { strategy: AUTO }
manyToOne:
salesTax:
targetEntity: SalesTax
inversedBy: invoice
joinColumn:
name: transaction_id
referencedColumnName: transaction_id
And SalesTax:
Bundle\Entity\SalesTax:
type: entity
table: sales_taxes
indexes:
transactionId:
columns: [ transaction_id ]
id:
id:
type: integer
generator: { strategy: AUTO }
oneToMany:
invoice:
targetEntity: Invoice
mappedBy: salesTax
If you're wondering why oneToMany, that's because invoices are stored as individual line items. There may be many invoices with the same transaction ID. One transaction ID in the invoices table represents one order, each row only represents a line item. So this invoice entity will probably need a self-referencing relationship at some point.
I have a Stats entity that is related to a Journey entity through a ManyToOne association.
id:
index:
type: integer
fields:
idJourney:
type: string
// data fields...
manyToOne:
journey:
targetEntity: Journey
joinColumn:
name: idJourney
referencedColumnName: idJourney
The Journey is related to the Station entity through two ManyToOne association: one for the first Station of the Journey, one for the last.
id:
idjourney:
type: string
fields:
idFirstStation:
type: string
idLastStation:
type: string
// other info fields
manyToOne:
firstStation:
targetEntity: Station
joinColumn:
name: idFirstStation
referencedColumnName: idStation
lastStation:
targetEntity: Station
joinColumn:
name: idLastStation
referencedColumnName: idStation
Finally, the Station entity :
id:
idStation:
type: string
fields:
name:
type: string
// other station info
I retrieve a collection of Stats objects with all related sub-objects via a custom Repository method which works fine.
$statCollection = $statsRepository->getStatsByDateAndArea($date, $area);
//This retrieves the expected data
$statCollection[0]->getJourney()->getFirstStation()->getName();
However, iterating through the collection with a foreach loop doesn't work:
foreach($statCollection as $stat) {
$journey = $stat->getJourney(); // works fine
$firstStationId = $journey->getFirstStationId(); // works too
$firstStation = $journey->getFirstStation(); // still works, but returns a Proxies\AcmeAppPathWhateverBundleEntityStationProxy object instead of a AcmeAppPathWhateverBundleEntityStation, but this should be transparent (as per Doctrine documentation)
$firstStationName = $firstStation->getName(); // throws an EntityNotFoundException
}
Any idea what's going on ? Should I force Doctrine to fetch all sub entities ?
EDIT
The error message is fairly laconic :
EntityNotFoundException: Entity was not found.
Not very helpful...
I ended up querying explicitly for the full set of sub-entities in my custom repository method...
I changed this query :
->select('stats')
->leftJoin('stats.journey', 'j')
->leftJoin('j.firstStation', 'fs')
->leftJoin('j.lastStation', 'ls')
to :
->select('stats, j, fs, ls')
->leftJoin('stats.journey', 'j')
->leftJoin('j.firstStation', 'fs')
->leftJoin('j.lastStation', 'ls')
I guess Doctrine's use of Proxy objects are not all that transparent...
I am using QueryBuilder to get 10 biggest cities with states.
$query = $em->createQueryBuilder()
->select('c','s')
->from('CitiesBundle:Cities', 'c')
->innerJoin('c.state', 's','WITH', 'c.state = s')
->orderBy('c.population', 'DESC')
->setMaxResults(10)
->getQuery();
generated SQL:
SELECT c0_.id AS id0, c0_.name AS name1, c0_.landarea AS landarea2,
c0_.density AS density3, c0_.population AS population4, s1_.id AS id5,
s1_.name AS name6, c0_.state_id AS state_id7 FROM cities c0_ INNER
JOIN states s1_ ON c0_.state_id = s1_.id AND (c0_.state_id = s1_.id)
ORDER BY c0_.population DESC LIMIT 10
When I testiing that query in PHPMyAdmin every city has own state, but in app all cities in the array has association with state of first city.
Could somebody explain for me Doctrine2 behaviour in this case?
[EDIT]
Schema:
XYZ\Bundle\CitiesBundle\Entity\Cities:
type: entity
table: cities
fields:
#fields
oneToMany:
state:
targetEntity: States
cascade: { }
mappedBy: null
inversedBy: null
joinColumns:
state_id:
referencedColumnName: id
orphanRemoval: false
lifecycleCallbacks: { }
XYZ\Bundle\CitiesBundle\Entity\States:
type: entity
table: states
fields:
id:
id: true
type: boolean
nullable: false
generator:
strategy: IDENTITY
name:
type: string
length: 50
fixed: false
nullable: false
lifecycleCallbacks: { }
I try different schema option (ManyToOne etc) but with no luck
Screenshoot from PHPMyAdmin, mazowieckie etc are names of states. In my app all 10 cities has state->name mazowieckie.
Screenshoots from app look:
I outputting state name in loop: {{ city.state.name }}
SchemaTool is generating unique index for associations that are OneToOne. I believe this is incorrect.
Section 6.6 of the Associations manual page at Doctrine shows an example of a OneToOne for a Product has one Shipping. This is shown to generate the Product table:
CREATE TABLE Product (
id INT AUTO_INCREMENT NOT NULL,
shipping_id INT DEFAULT NULL,
PRIMARY KEY(id)
) ENGINE = InnoDB;
However, with the same code for my entity User has one Organisation, my User table SQL is generated as
CREATE TABLE User (
id INT AUTO_INCREMENT NOT NULL,
organisation_id INT DEFAULT NULL,
UNIQ_3B978F9FA7F43455 (organisation_id),
PRIMARY KEY(id)
) ENGINE = InnoDB;
This prevents me adding 2 users with the same Organisation. Not correct.
I additinally tried to be verbose with the unique JoinColumn annotation param.
#JoinColumn(name="organisation_id", referencedColumnName="id", unique="false")
Any ideas? I can't seem to find anything at all about this.
Thanks
If One organisation has Many Users and Many Users have One and only one organisation then it's not a One-to-One association.
One-to-One associations have to be unique.
Your association is ManyToOne on the User side and OneToMany on the organisation side.
User.php
/**
* #ManyToOne(targetEntity="Organisation")
*/
private $organisation;
Organisation.php
use Doctrine\Common\Collections\ArrayCollection;
/**
* #OneToMany(targetEntity="User", mappedBy="organisation")
*/
private $users;
function __construct() {
$this->users = new ArrayCollection();
}
Maybe a YML version could help someone else starting with doctrine/symfony. Here is my version of it:
User.orm.yml
manyToOne:
organisation:
targetEntity: Organisation
joinColumn:
name: organisation_id
referencedColumnName: id
Organisation.orm.yml
oneToMany:
users:
targetEntity: User
mappedBy: organisation
joinColumn:
name: organisation_id
referencedColumn: id
Hope it helps.