Automatically updating created_by in the model - symfony

I wanted to have a created_by field for my model, say Product, that is automatically updated and I am using FOSUserBundle and Doctrine2. What is the recommended way of inputting the User id into Product?
Can I do it in the Product model? I am not sure how to do so and any help would be wonderful. Thanks!
I want to do something like this in the model, but I don't know how to get the user id.
/**
* Set updatedBy
*
* #ORM\PrePersist
* #ORM\PreUpdate
* #param integer $updatedBy
*/
public function setUpdatedBy($updatedBy=null)
{
if (is_null($updatedBy)) {
$updatedBy = $user->id;
}
$this->updatedBy = $updatedBy;
}

To relate the user to the product you want to associate the two entities:
http://symfony.com/doc/current/book/doctrine.html#entity-relationships-associations
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="products")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* You may need to use the full namespace above instead of just User if the
* User entity is not in the same bundle e.g FOS\UserBundle\Entity\User
* the example is just a guess of the top of my head for the fos namespace though
*/
protected $user;
and for the automatic update field you may be after lifecyclecallbacks:
http://symfony.com/doc/current/book/doctrine.html#lifecycle-callbacks
/**
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Product
{
/**
* #ORM\PreUpdate
*/
public function setCreatedValue()
{
$this->created = new \DateTime();
}
}
EDIT
This discussion talks about getting the container in the entity in which case you could then get the security.context and find the user id from that if you mean to associate the current user to the product they edited:
https://groups.google.com/forum/?fromgroups#!topic/symfony2/6scSB0Kgds0
//once you have the container you can get the session
$user= $this->container->get('security.context')->getToken()->getUser();
$updated_at = $user->getId();
Maybe that is what you are after, not sure it is a good idea to have the container in the entity though, could you not just set the user on the product in the update action in your product controller:
public function updateAction(){
//....
$user= $this->get('security.context')->getToken()->getUser();
$product->setUser($user)
}

Related

How can I have two User that share the same table?

I have an entity User with lots of feature built for it.
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email", message="Email already in use")
* #ORM\HasLifecycleCallbacks
* #Table(name="users")
*/
class User implements UserInterface
{
/* variables + getter & setter */
}
This entity is good as is for most of my User.
However, a few of them will have a special ROLE, ROLE_TEACHER.
With this role, I need to store a lot of new variables specially for them.
If I create a new entity Teacher, doctrine creates a new table with every User's data + the Teacher's data.
/**
* #ORM\Entity(repositoryClass="App\Repository\TeacherRepository")
* #Table(name="teachers")
*/
class Teacher extends User
{
/**
* #ORM\Column(type="string", length=64, nullable=true)
*/
protected $test;
public function __construct()
{
parent::__construct();
}
}
What I want, is for Teacher & User to share the users table and have the teachers table only store the extra data. How could I achieve that ?
This is more of system design problem than implementation problem. as #Gary suggested you can make use of Inheritance Mapping which can have Performance issues, I'd rather suggest re think your schema and make use of database normalization techniques to break up your data into more manageable entities.
You can have User entity :
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email", message="Email already in use")
* #ORM\HasLifecycleCallbacks
* #Table(name="users")
*/
class User implements UserInterface
{
/* variables + getter & setter */
/**
* One user has many attibute data. This is the inverse side.
* #OneToMany(targetEntity="UserData", mappedBy="data")
*/
private $data;
}
With other UserData Entity with OneToMany relationship :
/**
* #ORM\Entity(repositoryClass="App\Repository\UserDataRepository")
* #Table(name="user_data")
*/
class UserData
{
/* variables + getter & setter */
#ORM\Id()
private $id;
/**
* Many features have one product. This is the owning side.
* #ManyToOne(targetEntity="User", inversedBy="data")
* #JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #ORM\Column(type="string")
*/
private $attribute;
/*
* #ORM\Column(name="value", type="object")
*/
private $value;
}
Now you can have list of user attributes without requiring specific structure to each role. It's scalable and arbitrary.
You can also define same Relation with TeacherData, StudentData or UserProfile Entities with foreign keys and branch your application logic according to the roles. Key is to break data into their separate domains and keep common data in one table. Load related data by querying related entity, this increases readability and makes it easy to break complex structure into manageable codebase.

Doctrine OneToMany relation does not fetch related entities

I am stuck at this case, I reproduced it in an example from symfony documentation, here it how it looks:
FIXTURES
/**
* #ORM\Entity
*/
class Category
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Product", mappedBy="category", fetch="EAGER")
*/
private $products;
public function __construct()
{
$this->products = new ArrayCollection();
}
public function products(): Collection
{
return $this->products;
}
public function id()
{
return $this->id;
}
}
and related Product class
/**
* #ORM\Entity
*/
class Product
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="products")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
public function __construct($category)
{
$this->category = $category;
}
public function id()
{
return $this->id;
}
public function category()
{
return $this->category;
}
}
TEST
Now I have this snippet of test code where I want to fetch Category and be able to get its Products:
$cat = new Category();
$prod = new Product($cat);
$this->entityManager->persist($prod);
$this->entityManager->persist($cat);
$this->entityManager->flush();
$crepo = $this->getEntityManager()->getRepository(Category::class);
$c = $crepo->findAll()[0];
var_dump(get_class($c->products()), $c->products()->count())
What I am getting is products of class PersistentCollection which is expected, but the count is 0 while there should be 1 product.
I can see that in the database I have proper category and product records with proper foreign key set.
WORKAROUND
I am debugging PersistentCollection for products and can see that its flag is set to initialized = true. With this I am able to force this to work by calling
$c->products()->setInitialized(false);
$c->products()->initialize();
But afaik this is not how it should work, should it ?
I managed to found an answer. It basically works but not when run in the same process. If I split the script in two - first one persists, second retrieves the data then the products collection will contain products related to category.
This is because when it is done in single process doctrine does not know that the category in question has products, and since it retrieves the same object it just saved and that was created few lines above, the entity manager won't populate the collection using database but will use the one from the category object. And the category object does not have products in products collection, since there is no call like $this->products()->add($category) neither in Product constructor or anywhere else. Only forcing to reinitialize the collection works since then it really retrieves products from database

symfony2 entity relationship

I try to make a social network with symfony2. I am not sure of how to modelize a user and his friends in the User entity.
I mean a friend is also a user so I have a User entity linked to another User entity.
How in the annotation of the User entity can I express that kind of relationship?
For the moment I have something like :
the User entity :
...
/**
* #ORM\ManyToMany(targetEntity="User")
*
*/
private $friends;
...
with $friends as an arraycollection. Is it correct?
That is a self-referencing ManyToMany relationship. It's actually the example used in the documentation.
Basically, you would so something like this:
<?php
/** #Entity **/
class User
{
// ...
/**
* #ManyToMany(targetEntity="User", mappedBy="myFriends")
**/
private $friendsWithMe;
/**
* #ManyToMany(targetEntity="User", inversedBy="friendsWithMe")
* #JoinTable(name="friends",
* joinColumns={#JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#JoinColumn(name="friend_user_id", referencedColumnName="id")}
* )
**/
private $myFriends;
public function __construct() {
$this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection();
$this->myFriends = new \Doctrine\Common\Collections\ArrayCollection();
}
// ...
}
Note that it's set as a bidirectional relation. That is, you can get the list of friends and the list of people that are friends with a User.

ManyToMany relationship with extra fields in symfony2 orm doctrine

Hi i have that same question as here: Many-to-many self relation with extra fields? but i cant find an answer :/ I tried first ManyToOne and at the other site OneToMany ... but then i could not use something like
public function hasFriend(User $user)
{
return $this->myFriends->contains($user);
}
because there was some this problem:
This function is called, taking a User type $user variable and you then use the contains() function on $this->myFriends.
$this->myFriends is an ArrayCollection of Requests (so different type than User) and from the doctrine documentation about contains():
The comparison of two elements is strict, that means not only the value but also the type must match.
So what is the best way to solve this ManyToMany relationship with extra fields? Or if i would go back and set the onetomany and manytoone relationship how can i modify the hasFriend method? To example check if ID is in array collection of ID's.
EDIT: i have this table... and what i need is:
1. select my friends... and my followers ...check if i am friend with him or not. (because he can be friend with me and i dont have to be with him... like on twitter). I could make manytomany but i need extra fields like: "viewed" "time when he subscribe me" as you can see at my table.
And make query like this and then be able in twig check if (app.user.hasFriend(follower) or something like that)
$qb = $this->createQueryBuilder('r')
->select('u')
->innerJoin('UserBundle:User', 'u')
->Where('r.friend_id=:id')
->setParameter('id', $id)
->orderBy('r.time', 'DESC')
->setMaxResults(50);
return $qb->getQuery()
->getResult();
I was trying to have a many to many relationship with extra fields, and couldn't make it work either... The thing I read in a forum (can't remember where) was:
If you add data to a relationship, then it's not a relationship anymore. It's a new entity.
And it's the right thing to do. Create a new entity with the new fields, and if you need it, create a custom repository to add the methods you need.
A <--- Many to many with field ---> B
would become
A --One to many--> C (with new fields) <-- One to many--B
and of course, C has ManyToOne relationships with both A and B.
I searched everywhere on how to do this, but in the end, it's the right thing to do, if you add data, it's no longer a relationship.
You can also copy what contains usually do, or try to overwrite it in a custom repository, to do whatever you need it to do.
I hope this helps.
I'm adding another answer since it has nothing to do with my original answer. Using the new info you posted, I'm calling the table/entity you posted "Follower". The original entity, "User".
What happens if you create the following associations:
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\UserBundle\Entity\User
*
* #ORM\Table()
* #ORM\Entity
*/
class User
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="Acme\FollowerBundle\Entity\Follower", mappedBy="followeduser")
*/
protected $followers;
/**
* #ORM\OneToMany(targetEntity="Acme\FollowerBundle\Entity\Follower", mappedBy="followeeuser")
*/
protected $followees;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function __construct()
{
$this->followers = new \Doctrine\Common\Collections\ArrayCollection();
$this->followees = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add followers
*
* #param Acme\FollowerBundle\Entity\Follower $follower
*/
public function addFollower(\Acme\FollowerBundle\Entity\Follower $follower)
{
$this->followers[] = $follower;
}
/**
* Add followees
*
* #param Acme\FollowerBundle\Entity\Follower $followee
*/
public function addFollowee(\Acme\FollowerBundle\Entity\Follower $followee)
{
$this->followees[] = $followee;
}
/**
* Get followers
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFollowers()
{
return $this->followers;
}
/**
* Get followees
*
* #return Doctrine\Common\Collections\Collection
*/
public function getFollowees()
{
return $this->followees;
}
}
namespace Acme\FollowerBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Acme\FollowerBundle\Entity\Follower
*
* #ORM\Table()
* #ORM\Entity
*/
class Follower
{
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $followeduser;
/**
* #ORM\ManyToOne(targetEntity="Acme\UserBundle\Entity\User", inversedBy="followees")
* #ORM\JoinColumn(name="followee_id", referencedColumnName="id")
*/
protected $followeeuser;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set followeduser
*
* #param Acme\UserBundle\Entity\User $followeduser
*/
public function setFolloweduser(\Acme\UserBundle\Entity\User $followeduser)
{
$this->followeduser = $followeduser;
}
/**
* Get followeduser
*
* #return Acme\UserBundle\Entity\User
*/
public function getFolloweduser()
{
return $this->followeduser;
}
/**
* Set followeeuser
*
* #param Acme\UserBundle\Entity\User $followeeuser
*/
public function setFolloweeuser(\Acme\UserBundle\Entity\User $followeeuser)
{
$this->followeeuser = $followeeuser;
}
/**
* Get followeeuser
*
* #return Acme\UserBundle\Entity\User
*/
public function getFolloweeuser()
{
return $this->followeeuser;
}
}
I'm not sure if this would do the trick, I really don't have much time to test it, but if it doesn't, I thnk that it's on it's way. I'm using two relations, because you don't need a many to many. You need to reference that a user can have a lot of followers, and a follower can follow a lot of users, but since the "user" table is the same one, I did two relations, they have nothing to do with eachother, they just reference the same entity but for different things.
Try that and experiment what happens. You should be able to do things like:
$user->getFollowers();
$follower->getFollowedUser();
and you could then check if a user is being followed by a follower whose user_id equals $userThatIwantToCheck
and you could search in Followers for a Follower whose user = $user and followeduser=$possibleFriend

Symfony2: Setting entity value from parent

I am using FoSUserBundle. I Have a User entity. I need to set code property with the username during registration. How can i do this.
protected $code = parent::username;
Is this correct? I don't know much of symfony.
One way would be creating #PrePersist callback if I understand your problem correctly. E.g
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class User extends BaseUser
{
/**
* #ORM\PrePersist
*/
public function setCodeValue()
{
$this->code = $this->username;
}
}
For more info check here.

Resources