I am having an issue with the primary key of a mapped entity not being detected by doctrine.
I have 2 tables, users and operators. There is a unidirectional many to one relationship between users and operators.
User entity is defined as
XXX\SecurityBundle\Entity\User:
type: entity
repositoryClass: XXX\SecurityBundle\Repository\UserRepository
table: XXX_USER
id:
id:
type: integer
unsigned: false
nullable: false
column: ID
generator:
strategy: AUTO
fields:
username:
type: string
length: 32
nullable: false
column: USERNAME
...
manyToOne:
operator:
targetEntity: XXX\CoreDataBundle\Entity\Operator
joinColumn:
name: operator_id
referencedColumnName: id
The operators entity defined as
XXX\CoreDataBundle\Entity\Operator:
type: entity
respositoryClass: XXX\CoreDataBundle\Repository\OperatorRepository
table: OPERATORS
id:
id:
type: integer
unsigned: false
nullable: false
column: ID
generator:
strategy: AUTO
fields:
operName:
type: string
length: 32
nullable: false
column: OPERATOR_NAME
When validating the schema, I receive the following error :
The referenced column name 'id' has to be a primary key column on the target entity class 'XXX\CoreDataBundle\Entity\Operator'.
When I try to assign an operator to a user, I receive the following error :
[ErrorException]
Notice: Undefined index: id in vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line
607
As far as I can see, 'id' is set as the primary key of the operator entity. The database behind doctrine is oracle, in is in sync with the entity definitions and the primary key is set correctly on the database.
Anyone able to spot my error in defining the mapping ?
I do not think the fact the entities are in different bundles is an issue as I am getting similar issues with entities in the same bundle.
Thank you.
Oracle's columns and table names are always uppercase. So if using them, you need to put the referencedColumnName in uppercase even if in the PHP side it is lowercase.
Related
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
I have the following ORM:
AppBundle\Domain\ValueObject\Date\Birthday:
type: embeddable
fields:
birthday:
column: fecha_nacimiento
type: datetime
And I want to use this value object in two tables, the first is "User" table:
AppBundle\Domain\Entity\User\User:
type: entity
table: usuarios_web
repositoryClass: AppBundle\Infrastructure\Persistence\Doctrine\Repository\User\UserDoctrineRepository
embedded:
birthday:
class: AppBundle\Domain\ValueObject\Date\Birthday
columnPrefix: false
But then I want to use the same birthday on another table who's birthday field is not "fecha_nacimiento", is different.
It's possible to map the DB field in the emmbedded instead of the embeddable?
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
There is a User class which has two types of users such as patient and doctor. So, I created another attribute that is used to describe the relation between patient and doctor, a doctor can have many patients. So, If the user is patent, he should have doctorId, if not, the doctorId is NULL.
Here is my User.orm.yml:
Acme\Bundle\DemoBundle\Entity\User:
type: entity
table: User
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
name:
type: string
length: 30
lastName:
type: string
length: 30
email:
type: string
length: 60
unique: true
username:
type: string
length: 10
unique: true
password:
type: string
length: 100
unique: false
doctorId:
type: integer
unique: false
nullable: true
dateCreated:
type: datetime
manyToMany:
roles:
targetEntity: Role
mappedBy: users
How can I map the doctorId as a foreign key that refers to the id?
You can find instructions for self-referencing One-To-Many relations in the Doctrine documentation:
User:
type: entity
oneToMany:
patients
targetEntity: User
mappedBy: doctor
manyToOne:
doctor:
targetEntity: User
inversedBy: patients
http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#one-to-many-self-referencing
With doctrine you might want to take a look at association mapping. http://docs.doctrine-project.org/en/latest/reference/association-mapping.html. In your case you are after a one to many self referencing relationship http://docs.doctrine-project.org/en/latest/reference/association-mapping.html#one-to-many-self-referencing.
A doctor can have many patients but can a patient have many doctors? If so you have a many to many relationship which you could map out using a cross Reference table as the joining table. The best way to model this I feel is to have a userTypeId in your users Table where a value of 1 could be doctor, 2 a patient etc. Then a doctor can have many patients but also a patient can see many doctors. This would also be extensible if you added new user types like nurses etc.
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...