Symfony2 - EntityNotFoundException - symfony

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...

Related

Doctrine - Unable to persist entity with composite key

I'm using Doctrine with YML mappings. I have two entities. One Group entity and one User entity.
I'm trying to set it up so Users have unique names within a group.
I can create a User, assign it a Group, and save it to the DB. But when I try to create a User with the same name and a different Group, then I get an error saying the unique constraint on name is violated.
Why can't I persist that User?
Their mappings look like this:
Entity\Group:
type: entity
table: groups
id:
id:
type: guid
nullable: false
id: true
generator:
strategy: AUTO
fields:
name:
type: text
nullable: true
Entity\User:
type: entity
table: users
id:
group:
associationKey: true
nullable: false
name:
type: string
manyToOne:
Group:
targetEntity: Entity\Group
joinColumn:
name: group
referencedColumnName: id
I eventually figured this out. I was confusing Doctrine.
The non-standard thing was that I'm using a capitalized parameter name to represent an object and changing that to lowercase for the table column. I did this in the manyToOne: section using name:.
The documentation doesn't really do this, so I didn't know that I still had to reference the capitalized property name in the id: section and separately define the column name there.
So, I changed the User mapping to this:
Entity\User:
type: entity
table: users
id:
Group:
column: group
associationKey: true
nullable: false
name:
type: string
manyToOne:
Group:
targetEntity: Entity\Group
joinColumn:
name: group
referencedColumnName: id

Weird Doctrine Entity Behaviour

I have two entities, Invoice and InvoiceItem definined as follows in yml.
Invoice.orm.yml
AppBundle\Entity\Invoice:
type: entity
table: invoices
repositoryClass: AppBundle\Repository\InvoiceRepository
fields:
id:
id: true
type: integer
generator:
strategy: AUTO
......
oneToMany:
items:
targetEntity: InvoiceItem
mappedBy: invoice
deductions:
targetEntity: InvoiceDeduction
mappedBy: invoice
payments:
targetEntity: InvoicePayment
mappedBy: invoice
InvoiceItem.orm.yml
AppBundle\Entity\InvoiceItem:
type: entity
table: invoice_items
repositoryClass: AppBundle\Repository\InvoiceItemRepository
fields:
id:
id: true
type: integer
generator:
strategy: AUTO
....
manyToOne:
invoice:
targetEntity: Invoice
inversedBy: items
joinColumn:
invoice_id:
referencedColumnName: id
onDelete: CASCADE
My controller receives invoice details and saves the elements as illustrated below
public function createInvoiceAction(Request $request)
{
....
$invoice = new Invoice();
$invoice->setReference($ref);
$invoice->setInvoiceDate(\DateTime::createFromFormat('d/m/Y',$request->query->get('invoiceDate')));
$invoice->setDescription($request->query->get('summary'));
$invoice->setAttn($request->query->get('att'));
$invoice->setIsVatable($request->query->get('vattable') == 'true' ? 1 : 0);
$invoice->setAdditionalInstructions($request->query->get('additional'));
$invoice->setJob($job);
$invoice->setCurrency($request->query->get('currency'));
$invoice->setGeneratedBy($createdBy);
$invoice->setFile($filename);
$job->setStatus('Invoiced');
$em->persist($invoice);
$em->persist($job);
$em->flush();
$items = $request->query->get('items');
for($i=0;$i<count($items);$i++)
{
if($items[$i]['description']!= '' && $items[$i]['amount'] != '')
{
$item = new InvoiceItem();
$item->setDescription($items[$i]['description']);
$item->setAmount($items[$i]['amount']);
$item->setInvoice($invoice);
$em->persist($item);
$em->flush();
}
}
$deductions = $request->query->get('deductions');
for($i=0;$i<count($deductions);$i++)
{
if($deductions[$i]['description'] != '' && $deductions[$i]['value'] != '')
{
$deduction = new InvoiceDeduction();
$deduction->setDescription($deductions[$i]['description']);
$deduction->setValue($deductions[$i]['value']);
$deduction->setIsPercentage($deductions[$i]['isPercentage'] == 'true' ? 1 : 0);
$deduction->setInvoice($invoice);
$em->persist($deduction);
$em->flush();
}
}
$html = $this->renderView('AppBundle:Default:invoice.html.twig', array('invoice' => $invoice));
return new Response(
$html
);
}
invoice.html.twig which renders the invoice details including items and deductions.
.....
{% for item in invoice.items %}
<tr>
<td>{{ loop.index }}</td>
<td>{{ item.description }}</td>
<td>{{ item.amount|number_format(0, '', ',') }}</td>
</tr>
{% endfor %}
.....
The challenge I'm having is that the $invoice object sent to the template is has no items or deductions which are supposed to be collections. I've checked in the database and can confirm that the items and deductions are persisted into the respective tables so I don't understand why there are no invoice.items or invoice.deductions in the tables.
Out of desperation I flushed after every InvoiceItem object creation thinking that maybe the template was rendered before the persist transaction was completed but this didn't help either.
UPDATE:
Atleast one other error I've spotted is the onDelete option being defined under joinColumn. It should be:
manyToOne:
invoice:
targetEntity: Invoice
inversedBy: items
joinColumn:
invoice_id:
referencedColumnName: id
onDelete: CASCADE
--------
If you are having this problem only when the view is rendered during createInvoiceAction, then the problem might be that since you don't actually add the InvoiceItem(s) to the Invoice object via a method called something like addItem() (the naming depends on how you name the relation in the invoice entity), those items might only be present in the Invoice entity the next time you query the invoice, because even though you have set the invoice of each item, it might be that if those entities were created just now, doctrine might not be able to retrieve them using the proxy classes. So the solution would be simple: use the addItem method on invoice entity to add the items to the invoice and then don't forget to add the logic that sets the invoice for the given item inside that method
public function addItem(InvoiceItem $item)
{
$item->setInvoice($this);
$this->items[] = $item;
return $this;
}
However if this problem is still present the next time you query for the already persisted invoice and its items, I would suggest you check whether the object actually contains the getter methods and you aren't simply trying to access the items via a public property. In other words inside the invoice class the property named items should be defined as private, and you should have a getItems() method, which retrieves the items.
Objects used with doctrine must have these methods, since it overrides the logic inside those methods using its proxy classes in order to add code that would also send out the query to retrieve the, in your case, InvoiceItem(s) from the database and place them inside of the private property of the object and only then run the getter code inside of your class. Without these methods private properties would stay empty, since there is no other way for doctrine to retrieve them form the database for you (unless maybe fetch: "EAGER").

destroy a one-to-one relationship in doctrine

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.

How to diff and intersect Many-To-Many Self-referencing ArrayCollections

I have Many-To-Many Self-referencing relation in my user entity.
// Acme\DemoBundle\Resources\config\doctrine\User.orm.yml
Acme\DemoBundle\Entity\User:
type: entity
repositoryClass: Acme\DemoBundle\Entity\Repository\UserRepository
table: users
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
username:
type: string
length: 25
unique: true
manyToMany:
friendsWithMe:
targetEntity: User
mappedBy: myFriends
myFriends:
targetEntity: User
inversedBy: friendsWithMe
joinTable:
name: friends
joinColumns:
user_id:
referencedColumnName: id
inverseJoinColumns:
friend_user_id:
referencedColumnName: id
Now i want to get three different user collections:
MyFriends - collection of entities with myFriend == true and friendWithMe == false
FriendWithMe - collection of users with myFriend == false and friendWithMe == true
MutualFriends - collection of users with myFriend == true and friendWithMe == true
Standart getMyFriends an getFriendsWithMe (generated in user entity) returns all MyFriends and FriendWithMe records if friends are mutual =(
I tried to dig into the side of the Criteria but it does not work with many-to-many relations.
I think there is a general problem in your design structure. Self-Referencing relations in Doctrine are equal to Mutual relations. The states, that not both users are friends with each others, sounds more like a FriendsRequest. Maybe you should change this behavior to have on relation for MutualFriends and two different relations for MyFriendRequests and RecievedFriendRequest.
Antother possiblity is to use an Relationship Entity like "Friendship" this could look like
// Acme\DemoBundle\Resources\config\doctrine\Friendship.orm.yml
Acme\DemoBundle\Entity\Friendship:
type: entity
repositoryClass: Acme\DemoBundle\Entity\Repository\FriendshipRepository
table: friendship
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
user_one_accepted:
type: boolean
user_two_accepted:
type: boolean
manyToOne:
user_one:
targetEntity: User
user_two:
targetEntity: User

Query builder join on one to many relationship

I have a LastTrait object that has a many to one relationship with my user object: one user can have many different lastTrait:
YOP\YourOwnPoetBundle\Entity\LastTrait:
type: entity
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
traitCategoryID:
type: integer
verseID:
type: integer
manyToOne:
user:
targetEntity: User
inversedBy: lastTrait
joinColumn:
name: user_id
referencedColumnName: id
I need to retrieve the verseID knowing the userID and traitCategoryID. So I created a Repository for my LastTrait object:
<?php
namespace YOP\YourOwnPoetBundle\Repository;
use Doctrine\ORM\EntityRepository;
class LastTraitRepository extends EntityRepository {
public function findOneByTraitCategoryID($userID, $traitCategoryID)
{
return $this->getEntityManager()
->createQuery('SELECT l
FROM YOPYourOwnPoetBundle:LastTrait l
JOIN l.user u
WHERE l.traitCategoryID = :categoryID AND u.id = :userID')
->setParameters(array(
'categoryID' => $traitCategoryID,
'userID' => $userID,
))
->getResult();
}
}
?>
However, when I call :
$lastTrait = $this->getDoctrine()
->getRepository('YOPYourOwnPoetBundle:LastTrait')
->findOneByTraitCategoryID($user->getID(), $collector->getTraitCategory()->getID());
I always get NULL, even though I should get a LastTrait object back!
What am I doing wrong here?
I think you should be thinking more in entities and less in foreign keys.
Try this in your method.
public function findOneByTraitCategoryID($userID, $traitCategoryID)
{
return $this->findOneBy(
array(
'traitcategoryID' => $traitCategoryID,
'user' => $userID
));
}
You can call ->getVerse()->getId(); after this or return the id directly on the method (too narrow for future uses IMO).
This only works if a user only has one trait per category, otherwise you don't have enough data to get a specific trait.
The issue was that I forgot to register the repository in my entity:
YOP\YourOwnPoetBundle\Entity\LastTrait:
repositoryClass: YOP\YourOwnPoetBundle\Repository\LastTraitRepository
type: entity
table: null
fields:
id:
type: integer
id: true
generator:
strategy: AUTO
traitCategoryID:
type: integer
verseID:
type: integer
manyToOne:
user:
targetEntity: User
inversedBy: lastTrait
joinColumn:
name: user_id
referencedColumnName: id
After adding the repositoryClass line, my custom Repository class is called and I get the desired result.
However, #dhunter's answer made me realize that I didn't even need a custom Repository class. I can get the desired by simply using the regular findOneBy function from my controller:
$lastTrait = $this->getDoctrine()
->getRepository('YOPYourOwnPoetBundle:LastTrait')
->findOneBy(
array('user' => $user->getId(), 'traitCategoryID' => $collector->getTraitCategory()->getID())
);
Hard to tell. Try echoing either DQL or SQL before retreiving results:
$q = $this->getEntityManager()->createQuery(....);
die($q->getSql()); //or getDql();
If, for some reason, you have invalid mapping (which I don't see above) you should see it here.
Also, try wrapping method with try-catch with most general Exception catch and seeing if it catches anything...
Furthermore, what is bugging me is the fact that you get NULL back...

Resources