I'm trying to learn Doctrine2, and am having some trouble wrapping my brain around Entities and Mappings, and how to pull data from the db.
If I understand correctly, an Entity would be created for data in my db, like a User or a Product.
That's where I'm getting hung up though. I have a simple database with tables like User and such. Do I need to create an Entity called "User"? And does that entity pull data from the User table, and then I get data from the User entity?
Can someone provide me a code example of how I would do this?
Entities are regular PHP classes... First u have to create ur table, then u create your entity. Properties of class should be private with setters and getters and usually with same names as field in table. When U want to put new record in DB, u must make instance of class u want and set values then persist and flush the entity manager. Example code:
<?php
namespace Entity;
/**
* #Entity
* #Table(name="users")
*/
class User {
/** #Id
* #Column(type="integer")
* */
private $id;
/** #Column(type="string") */
private $username;
/** #Column(type="string") */
private $password;
/** #Column(type="boolean") */
private $active;
public function getId() {
return $this->id;
}
public function setId($id) {
$this->id = $id;
}
public function getUsername() {
return $this->username;
}
public function setUsername($username) {
$this->username = $username;
}
public function getPassword() {
return $this->password;
}
public function setPassword($password) {
$this->password = $password;
}
public function getActive() {
return $this->active;
}
public function setActive($active) {
$this->active = $active;
}
}
and when u want to put a new record:
$user = new Entity\User();
$user->setName('users name');
$user->setPassword('password');
$entityManager->persist($user); // put that entity in queue;
$entityManager->flush(); // execute all pending entities
If u want to get existing record
$found = $entityManager->find('className', $id); // search by id
or $entityManager->getRepository('className')->findOneBy(array('field', 'value'));
Related
My code:
public function create($object): void
{
/** #var CarsEvent $object */
$carsEvent = $object;
$em = $this->getEntityManager($carsEvent );
$carsEvent->addTranslation(new CarsEventTranslation());
$em->persist($sportsEvent);
$em->flush();
}
This creates a new car object and save it into my database, and that works fine, however in this part of the code I'm calling and translation function which needs to add a translation for the NAME field into my translation table for the german language
/**
* {#inheritdoc}
*/
public function addTranslation(Translation $translation)
{
$translation->setLocale('de');
$translation->setField('name');
$translation->setContent('FixedGermanName');
$this->translations[] = $translation;
return $this;
}
This also works but I cannot get the objectId, so my translation table in the database is not connected with the id of object that I created, I have a function
$carsEvent->getId();
but always return NULL
My question is how to get the object id so I can store that value in my database too?
Solution:
/**
* {#inheritdoc}
*/
public function addTranslation(Translation $translation)
{
$translation->setLocale('de');
$translation->setField('name');
$translation->setObject($translation);
$translation->setContent('FixedGermanName');
$this->translations[] = $translation;
return $this;
}
I'm trying to implement a Cart feature in my Symfony app.
The purpose is to allow a User to add some events in a Cart.
So I have created 3 Entities. User, Event and Cart.
A User need to access his Cart to get his events. Like $user->getCart, which will return an ArrayCollection of events.
I have no idea what is the best way to do it with the Doctrine relation.
Everything I have tried does not seems to work.
Here is what I have made so far:
In my User Entity
/**
* #ORM\OneToOne(targetEntity="App\Entity\Cart", mappedBy="user", cascade={"persist", "remove"})
*/
private $cart;
public function getCart(): ?Cart
{
return $this->cart;
}
public function setCart(Cart $cart): self
{
$this->cart = $cart;
// set the owning side of the relation if necessary
if ($this !== $cart->getUser()) {
$cart->setUser($this);
}
return $this;
}
In my User Entity
/**
* #ORM\OneToOne(targetEntity="App\Entity\User", inversedBy="cart", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $user;
I have stopped here, because I feel like I'm not doing the right approach.
May I have your feeling about it?
Although I haven't tested it myself, it will highly likely work.
Relationship: User one-to-many Cart one-to-many Event
Type: Bidirectional Composition
Consider: Cascade "remove" on OneToMany depending on your needs.
Entities:
class User
{
...
/**
* #ORM\OneToMany(targetEntity="Cart", mappedBy="user", cascade={"persist"})
*/
private $carts;
public function __construct()
{
$this->carts = new ArrayCollection();
}
public function addCart(Cart $cart): self
{
$this->carts[] = $cart;
return $this;
}
public function removeCart(Cart $cart): bool
{
return $this->carts->removeElement($cart);
}
public function getCarts(): Collection
{
return $this->carts;
}
}
class Cart
{
...
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="carts", cascade={"persist"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
private $user;
/**
* #ORM\OneToMany(targetEntity="Event", mappedBy="cart", cascade={"persist"})
*/
private $events;
public function __construct()
{
$this->events = new ArrayCollection();
}
public function setUser(User $user): self
{
$this->user = $user;
return $this;
}
public function getUser(): User
{
return $this->user;
}
public function addEvent(Event $event): self
{
$this->events[] = $event;
return $this;
}
public function removeEvent(Event $event): bool
{
return $this->events->removeElement($event);
}
public function getEvents(): Collection
{
return $this->events;
}
}
class Event
{
...
/**
* #ORM\ManyToOne(targetEntity="Cart", inversedBy="events", cascade={"persist"})
* #ORM\JoinColumn(name="cart_id", referencedColumnName="id", nullable=false)
*/
private $cart;
public function setCart(Cart $cart): self
{
$this->cart = $cart;
return $this;
}
public function getCart(): User
{
return $this->cart;
}
}
When doing carts and e-commerces you have to keep in mind a lot of things, and need to ask yourself what kind of information you want to persist. Some people develop Cart modules in a Event Sourced way so they don't loose any data. This talk by Greg Young is great on the subject. That's the approach I would use, but is not the easiest one.
But maybe you don't want to persist all that valuable extra state. In that case, you can use the traditional CRUD approach as you are trying to.
When using this, keep in mind two things.
Your events are mutable. Price can change, location can change, time can change.
When the purchase is complete, you will take that cart and generate a Purchase object from it. That Purchase object CAN NOT have any relationship to Event, because of 1.
You don't want a user to have a single Cart, I guess. A User can have many Carts, so maybe you should make that relationship a ManyToOne on the cart side. Otherwise, Cart can be just a value object that you can store in user.
Given i have 2 classes, User and UserId which look like this:
<?php
class UserId {
/** #var int */
private $value;
public function __construct($value) {
$this->value = (int) $value;
}
}
class User {
/** #var UserId */
private $id;
private $firstName;
private $lastName;
public function __construct(UserId $id, $firstName, $lastName) {
$this->id = $id;
$this->firstName = $firstName;
$this->lastName = $lastName;
}
}
Database:
User:
id : INT PK
firstName: VARCHAR
lastName: VARCHAR
Is it possible to tell doctrine to generate me a User with a UserId when calling "find()"?
Yes, this is possible, I'll explain further on.
The simple way
First I have to say that for this simple use-case, the answer provided by Andresch Serj should suffice.
Set the UserId object using the constructor, get the UserId object by lazily instantiating it if it isn't present yet:
class User
{
/**
* #var UserId
*/
private $userId;
/**
* #var int
*/
private $id;
/**
* #param UserId $userId
* ...
*/
public function __construct(UserId $userId /* ... */)
{
$this->userId = $userId;
$this->id = $userId->getId();
// ...
}
/**
* #return UserId
*/
public function getUserId()
{
if (!$this->userId) {
$this->userId = new UserId($this->id);
}
return $this->userId;
}
// ...
}
The real answer
In order to manipulate an entity when it's loaded (fetched from the database) you could use Doctrine's PostLoad event.
First create a listener that will create a new UserId object and set it on the User entity:
use Doctrine\ORM\Event\LifecycleEventArgs;
class UserListener
{
/**
* #param User $user
* #param LifecycleEventArgs $event
* #return void
*/
public function postLoad(User $user, LifecycleEventArgs $event)
{
$userId = new UserId($user->getId());
$user->setUserId($userId);
}
}
Then refactor the getUserId() method of User to simple return the UserId object. Add a setUserId() method (it's wise to make sure an already present UserId object can't be swapped out). Also note the #EntityListeners annotation, which tells Doctrine there's a listener in place for this entity:
/**
* #EntityListeners({"UserListener"})
*/
class User
{
/**
* #return UserId
*/
public function getUserId()
{
return $this->userId;
}
/**
* #param UserId $userId
* #return void
* #throws RuntimeException
*/
public function setUserId(UserId $userId)
{
if ($this->userId) {
throw new RuntimeException("UsedId already present, you can't swap it!");
}
$this->userId = $userId;
}
// ...
}
Finally, in the configuration/bootstrap phase of your application you should register the listener:
$listener = new UserListener();
$em->getConfiguration()->getEntityListenerResolver()->register($listener);
Now, whenever a User entity is loaded by Doctrine, a UserId object will be inserted into it. This setup is very useful for more advanced use-cases than the one you described.
You can read more about this here and here.
If you just need your User Object to be able to deliver the UserID Object you could add it as a private attribute and add a public getter:
private $userIdObject;
public function __construct(UserId $id, $firstName, $lastName)
{
$this->id = $id; = $id->id;
$this->userIdObject = $id;
$this->firstName = $firstName;
$this->lastName = $lastName;
}
public function getUserId()
{
return $this->userIdObject();
}
It would really help to know why you need this.
What i am saying is: Doctrine is a presentation of your Database. If you want to store the id as an integer, there is no need to have it as an object.
If you need an Object to do certain things with the id (generate it, manipulate it ...) you can have a getter in your user object that generates said object for you.
public function getUserId()
{
return new UserId($this->id);
}
Or you could have all those needed methods for manipulating your UserId in a Helper Class or Service.
I have a doctrine setup where i cant use the many side of collections.
The objects used:
User.php:
class User extends \App\Entity
{
/**
* #Id #Column(type="integer")
* #GeneratedValue
*/
private $id;
/**
* #ManyToOne(targetEntity="Company", inversedBy="users")
*/
private $company;
public function getCompany()
{
return $this->company;
}
public function setCompany($company)
{
$this->company = $company;
}
}
Company.php:
class Company extends \App\Entity
{
/**
* #Id #Column(type="integer", nullable=false)
* #GeneratedValue
*/
private $id;
/**
* #OneToMany(targetEntity="User", mappedBy="company")
*/
private $users;
public function __construct()
{
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getUsers()
{
return $this->users;
}
}
When i create the relation seems to be working. Get no errors. Then i tried the following:
$company = $this->em->getRepository('App\Entity\Company')->findOneByName("Walmart");
$user = $this->em->getRepository('App\Entity\User')->findOneByName("Niek");
$user->setCompany($company);
$company2 = $this->em->getRepository('App\Entity\Company')->findOneByName("Ford");
$user2 = $this->em->getRepository('App\Entity\User')->findOneByName("Henk");
$company2->getUsers()->add($user2);
$this->em->flush();
When i inspect the database for the first user the company is set. Relation is there. The seconds does not persists. and when i do this:
print_r(\Doctrine\Common\Util\Debug::dump($company->getUsers(),$doctrineDepth));
print_r(\Doctrine\Common\Util\Debug::dump($company->getUsers2(),$doctrineDepth));
i get 2 empty arrays.
So it seems that the array isnt connected. It only behaves like this on OneToMany ore ManyToOne relationships. Got one ManyToMany and that one works perfect in the same project
Any ideas?
You still need to set both sides of the relationship with a one-to-many or a many-to-one relationship:
$company2 = $this->em->getRepository('App\Entity\Company')->findOneByName("Ford");
$user2 = $this->em->getRepository('App\Entity\User')->findOneByName("Henk");
$company2->getUsers()->add($user2);
$user2->setCompany($company2);
$this->em->flush();
The reason your many-to-many works is because you don't need to set both sides of the relationship.
I do not understad why with some Entity objects I can set the Id and for others objects I get an error and says me that the Id can't be null and I have to pass an object instead.
e.g.:
$log = new Log();
$log->setTypeId(1);
$log->setUserId(1);
$entityManager->persist($log);
$entityManager->flush();
If I try the code above I get error that says: Integrity constraint violation: 1048 Column 'user_id' cannot be null. And I have to first create the Type Object and de User object and the pass them:
$log->setType($TypeObject)
$log->setUser($UserObject)
But for other entity objects I have no problem assigning the value directly, why is that?
This is my Entity Log:
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $user_id;
/**
*
* #var type
* #Column(type="integer")
*/
protected $type_id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getUserId()
{
return $this->user_id;
}
public function getTypeId()
{
return $this->type_id;
}
public function getCreated()
{
return $this->created;
}
public function setUserId($userId)
{
$this->user_id = $userId;
}
public function setTypeId($typeId)
{
$this->type_id = $typeId;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
?>
The existing answer never did sit well with me. There are many valid scenarios where loading an object just to define the relationship while already having the FK handy just does not make any sense at all.
A better solution is to use Doctrine's EntityManager's getRefrence method.
Reference Proxies...
The method EntityManager#getReference($entityName, $identifier) lets
you obtain a reference to an entity for which the identifier is known,
without loading that entity from the database. This is useful, for
example, as a performance enhancement, when you want to establish an
association to an entity for which you have the identifier. You could
simply do this:
<?php
// $em instanceof EntityManager, $cart instanceof MyProject\Model\Cart
// $itemId comes from somewhere, probably a request parameter
$item = $em->getReference(\MyProject\Model\Item::class, $itemId);
$cart->addItem($item);
Maybe this was not available when this question was first posted - I don't know.
EDIT
I found this statement on the website of Doctrine2. It's a best practice that you might want to follow when coding your models.
Doctrine2 Best Practices
25.9. Don’t map foreign keys to fields in an entity
Foreign keys have no meaning whatsoever in an object model. Foreign keys are how a relational database establishes relationships. Your object model establishes relationships through object references. Thus mapping foreign keys to object fields heavily leaks details of the relational model into the object model, something you really should not do
EDIT
Doctrine does the mapping from your objects to their respective Ids.
What you've done here is a bit redundant.
You've essentially told doctrine the same thing twice.
You've told it that it has a 'user_id' column AND that it also has a User object, which are the same thing. But doctrine can already guess that this relationship will have a user_id column based on the fact that the log class has a user object inside.
You should simply do the following instead
<?php
/**
* #Entity
* #Table(name="log")
* #HasLifecycleCallbacks
*/
class Log
{
/**
* #var type
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
*
* #var type
* #Column(type="datetime")
*/
protected $created;
/**
*
* #var type
* #ManyToOne(targetEntity="User", inversedBy="logs")
*/
protected $user;
/**
*
* #ManyToOne(targetEntity="Type", inversedBy="logs")
*/
protected $type;
public function getId()
{
return $this->id;
}
public function getCreated()
{
return $this->created;
}
public function setCreated($created)
{
$this->created = $created;
}
public function setUser($user)
{
$this->user = $user;
}
public function setType($type)
{
$this->type = $type;
}
/**
* #PrePersist
*/
public function prePersist()
{
$this->setCreated(new DateTime());
}
}
Doctrine will worry about the user_id and type_id on it's own. You don't have to worry about it. This way you get to work with full fledged objects, making it easier to program, instead of having to worry about id's. Doctrine will handle that.
If ALL you have is an id, because that's what you're using on the front end, then just fetch the object associated with that id using the Entitymanager.
$user = $em->getEntity( 'User', $idFromWeb );
$log = new Log();
$log->setUser( $user );