I have 3 tables:
users, users_profile and extra table users_addresses.
User class:
class User
{
/**
* #ORM\Id()
* #ORM\Column(name="user_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $userId;
/**
* #ORM\OneToOne(targetEntity="Entity\UserProfile", cascade={"persist", "remove"}, mappedBy="user", fetch="LAZY")
* #var Entity\UserProfile
*/
private $profile;
/**
* #param Entity\UserProfile $profile
*/
public function setProfile(UserProfile $profile) {
$this->profile = $profile;
$profile->setUser($this);
return $this;
}
(...)
}
User profile class
class UserProfile
{
/**
* #ORM\Id()
* #ORM\Column(name="profile_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $profileId;
/**
* #ORM\OneToOne(targetEntity="Entity\User", inversedBy="profile")
* #ORM\JoinColumn(name="user_id", referencedColumnName="user_id")
* #var Entity\User
*/
private $user;
/**
* #ORM\ManyToMany(targetEntity="Entity\Address", cascade={"persist"})
* #ORM\JoinTable(name="users_addresses",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="user_id")},
* inverseJoinColumns={#ORM\JoinColumn(name="address_id", referencedColumnName="address_id")}
* )
**/
private $addresses;
/**
* #param Address $address
*/
public function addAddress(Address $address) {
if (!$this->addresses->contains($address)) {
$this->addresses->add($address);
}
return $this;
}
(...)
}
Address class
class Address
{
/**
* #ORM\Id()
* #ORM\Column(name="address_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
* #var int
*/
private $addressId;
/**
* #ORM\Column(type="string", length=128, nullable=false)
* #var string
*/
private $city;
(...)
}
Add user with profile data and address
$userAddress = new \Entity\Address();
$userAddress->setStreet($street);
$userAddress->setCity($city);
$userAddress->setCountry($country);
$userAddress->setState($state);
$userAddress->setZipCode($zipCode);
$userProfile = new \Entity\UserProfile();
$userProfile->addAddress($userAddress);
$user = new \Entity\User();
$user->setProfile($userProfile);
$em->persist($user);
$em->flush();
The problem i am having at the moment is that the entities persist ok, but the 'user_id' in the join table ('users_addresses') is not correct ('user_id' = 0, 'address_id' = correct). Table 'addresses' must not contain extra field like 'user_id'. What am I doing wrong?
I need a structure like this:
Your modeling shows that tables users and users_profile share the same primary key, namely user_id, and thus that column is also a foreign key toward users in users_profile.
Hence, you should replace profile_id in the Php annotations by user_id, remove the key auto generation directive, and update (or delete and recreate) the schema.
class UserProfile {
/**
* #ORM\Id
* #ORM\Column(name="user_id", type="integer")
* #var int
*/
private $profileId;
/* ... */
}
Then
$ doctrine orm:info --verbose
Found 3 mapped entities:
[OK] Address
[OK] User
[OK] UserProfile
$ doctrine orm:schema-tool:create
ATTENTION: This operation should not be executed in a production environmnent.
Creating database schema...
Database schema created successfully!
Or use the update command if you already have a schema.
You could also rename the php class attribute ($profileId), for "consistency" with the DB model, but I don't think it matters much, unless you have code which relies on a specific name.
Related
I am getting the following exception whilst trying to persist a entity and associative entity to the database. Not sure what i'm doing wrong here:
Exception:
The given entity of type 'AppBundle\Entity\User'
(AppBundle\Entity\User#0000000065c3019f00000000123d7c75) has no
identity/no id values set. It cannot be added to the identity map.
Entities:
/**
* #ORM\Entity
*/
class User
{
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="Address", inversedBy="user")
* #ORM\JoinColumn(name="address_id", referencedColumnName="id")
* #ORM\Id
*/
protected $address;
}
/**
* #ORM\Entity
*/
class Address
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
*/
private $id
/**
* #ORM\OneToOne(targetEntity="User", mappedBy="address", cascade={"all"}, fetch="LAZY")
*/
private $user;
}
Entity creation:
$user = new User();
$address = new Address();
$address->setUser($user->setAddress($address));
$this->getDoctrine()->getManager()->persist($user);
$this->getDoctrine()->getManager()->persist($address);
$this->getDoctrine()->getManager()->flush();
Please see: Doctrine OneToOne identity through foreign entity exception on flush which helped to resolve this issue.
The actual object address needs to be saved by the EntityManager first. Just giving the class as reference to the other class does not make the entityManager aware of the fact both classes exists. With this in mind, the following code allows me to persist these objects to the database. I belive this is because doctrine needs to persist the address object first, so that it can retrieve the id for the primary & foreign key relationship in the User object.
$user = new User();
$address = new Address();
$this->getDoctrine()->getManager()->persist($address);
$this->getDoctrine()->getManager()->flush();
$address->setUser($user->setAddress($address));
$this->getDoctrine()->getManager()->persist($user);
$this->getDoctrine()->getManager()->flush();
You MUST specify an ID for doctrine entity
like this:
class User
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="string")
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="Address", inversedBy="user")
* #ORM\JoinColumn(name="address_id", referencedColumnName="id")
* #ORM\Id
*/
protected $address;
}
I have two tables:
Posts
id | title | content | user
Users
id | name
and I want to have user name in Posts collection. What am I doing wrong here? Do I have to write custom query to do this?
Right now I'm getting user's id in posts collection.
class Posts
{
...
/**
* #ORM\Column(name="user", type="text", length=250)
* #ORM\ManyToOne(targetEntity="Users", inversedBy="posts")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
protected $user;
...
User class:
class Users
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="text", length=250)
*/
public $name;
/**
* #ORM\OneToMany(targetEntity="Posts", mappedBy="user")
*/
protected $posts;
...
you have an error in your $user relation annotation, you can't make a column be a regular #ORM\Column AND a #ORM\JoinColumn, try:
/**
* #ORM\ManyToOne(targetEntity="Users", inversedBy="posts")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
protected $user;
this will give you the entity(referencedColumnName must be primary key), then you can do $user->getName();
I have made a queryBuilder inside an entity repository to delete a link between two table.
I have this two entities
Domain :
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(type="string", length=64)
* #Assert\NotBlank
* #Assert\Length(max="64")
* #AppAssert\DomainName
*/
private $name;
// Some other fields
/**
* #var SshKey[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\SshKey", inversedBy="domains")
* #ORM\JoinTable(name="domain_sshkey",
* joinColumns={#ORM\JoinColumn(referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="key_id", referencedColumnName="id")}
* )
*/
private $sshKeys;
And SshKeys :
/**
* #var int
*
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $createdAt;
// Other fields
/**
* #var Domain[]|ArrayCollection
*
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Domain", mappedBy="sshKeys")
*/
private $domains;
I am trying to delete links between this two tables when SshKeys id is in sshKeys field inside domain table.
So I made this query builder in my DomainRepository
public function deleteSshkeyDomainLink($invalidSshkey)
{
$qb = $this->createQueryBuilder('d');
$qb->delete()
->where($qb->expr()->in('ssh.id', ':ssh_keys_id'))
->setParameter('ssh_keys_id', $invalidSshkey)
->join('d.sshKeys', 'ssh')
;
return $qb->getQuery()->execute();
}
But this QB return this error
[Doctrine\ORM\Query\QueryException]
[Semantical Error] line 0, col 39 near 'ssh.id IN(:s': Error: 'ssh' is not defined.
[Doctrine\ORM\Query\QueryException]
DELETE AppBundle\Entity\Domain d WHERE ssh.id IN(:ssh_keys_id)
I don't understand why this is returning ssh is not defined because I have made a join with this alias.
This query builder should work ? I really don't know how too fix this.
Thanks for your help.
why do you want to delete Domain when you just need to delete sshKey from Domain (link between them)?
In Domain entity you can define method removeSshKey like this for example
public function removeSshKey(SshKey $key)
{
$this->sshKeys->removeElement($key);
return $this;
}
Then in controller where you want to delete the link between entities you should call it something like this
$domain = $this->getDoctrine()->getRepository('Domain')->find($domainId);
foreach ($domain->getSshKeys() as $sshKey)
{
if ($sshKey->getId() == $invalidSshKeyId)
{
$domain->removeSshKey($sshKey);
}
}
$em = $this->getDoctrine()->getManager();
$em->flush();
this should delete the link
I working on the following Models:
User
Category
One User can only get one Category. Categories are "standalone". So I can update, create and delete (okay, maybe with cascading) Categories whenever I want.
When I create a new User, I want do reference one Category to the User.
How can I do so? I want to avoid bad practices.
User entity:
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="XXX\Repository\UserRepository")
*/
class User
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="user")
* #ORM\JoinColumn(name="category", referencedColumnName="id")
*/
private $category;
//...
}
Category entity:
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="XXX\Repository\CategoryRepository")
*/
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="User", mappedBy="category")
*/
private $user;
//...
}
You write a User can have one Category, but it seems from your description that a Category can be used by several users. If this is true then your category definition should be changed a bit:
/**
* Category
*
* #ORM\Table(name="category")
* #ORM\Entity(repositoryClass="XXX\Repository\CategoryRepository")
*/
class Category
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="User", mappedBy="category")
*/
private $users;
public function __construct()
{
// It is important to initialize your collection
$this->users = new ArrayCollection();
}
// add methods for adding/removing and getting users:
// addUser, removeUser, addUsers, removeUsers and getUsers method
}
Read more on initializing collections in the doctrine documentation chapter 27.7. Initialize collections in the constructor.
Then in your User class you will need methods for setting and getting category.
public function setCategory(Category $category)
{
$category->addUser($user);
$this->category = $category;
}
public function getCategory()
{
return $this->category;
}
Now you can do:
$category = // get the category you want to set from entity manager
$user = new User();
$user->setCategory($category);
$entityManager->persist($user);
$entityManager->flush();
Now both your user and category are correctly set.
This is just a basic example that will help you get on your way.
I always get an exception with my entities. I tried to play with them. I checked on google which relation is the most appropriate for my entities... But I couldn't find the best configuration.
I'm making a website where you can create some albums.
A user can have multiple albums.So I have an entity Album and I have inside a user property :
/**
* Album
*
* #ORM\Table(name="album")
* #ORM\Entity(repositoryClass="Moodress\Bundle\AlbumBundle\Entity\AlbumRepository")
*/
class Album
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
** #ORM\ManyToOne(targetEntity="Moodress\Bundle\UserBundle\Entity\User")
** #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var \DateTime
*
* #ORM\Column(name="creationDate", type="datetime")
*/
private $creationDate;
/**
* #var \DateTime
*
* #ORM\Column(name="modificationDate", type="datetime")
*/
private $modificationDate;
/**
* #ORM\OneToMany(targetEntity="Moodress\Bundle\AlbumBundle\Entity\Picture", cascade={"persist"}, mappedBy="album")
*/
private $pictures;
/**
* Constructor
*/
public function __construct()
{
$this->creationDate = new \Datetime();
$this->modificationDate = new \Datetime();
}
// Get and set
}
However, When a user subscribe on the website, I create a default album called Upload that I want to keep in the user class.
This is what I tried to do :
/**
* User
*
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Moodress\Bundle\AlbumBundle\Entity\Album", cascade={"persist"})
*/
protected $albumUpload;
// Get and set
}
I have this error :
Undefined index: album in /Users/Sandro/sites/moodress-website/Symfony/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1608. This error appears directly when I serialize any entity that has a user object...
The error is caused by the association annotation of the $albumUpload property in the User class.
Doctrine assumes that you have a $album property in your Album entity when you use a #ORM\OneToOne(targetEntity="Moodress\Bundle\AlbumBundle\Entity\Album") association in your User entity.
Try
/**
** #ORM\ManyToOne(targetEntity="Moodress\Bundle\UserBundle\Entity\User", inversedBy="albums")
** #ORM\JoinColumn(nullable=false)
*/
private $user;
in your Album entity and
/**
* #ORM\OneToMany(targetEntity="Moodress\Bundle\AlbumBundle\Entity\Album", mappedBy="user", cascade={"persist"})
*/
protected $albums;
in your User entity. In addition please add $this->albums = new ArrayCollection() to User::__construct().
Now if you want to add and keep a default album for users you should implement this somewhere in your business logic. You could figure out a way to identify the default album in your collection of albums and prevent deletion especially for this item.
Hope this helps.
My error:
'Undefined index: album'
results from this annotation in the Album class.
/**
* #ORM\OneToMany(
* targetEntity="Moodress\Bundle\AlbumBundle\Entity\Picture",
* cascade={"persist"},
* mappedBy="album"
* )
*/
I have set mappedBy to album but there is no $album property in my Picture class.
I did add this to my Picture class and clear my cache.
/**
* #ORM\ManyToOne(
* targetEntity="Moodress\Bundle\AlbumBundle\Entity\Album",
* inversedBy="pictures"
* )
*/
protected $album;
It solved my problem