I use symfony 3.1. I want to create a notification system in my application in symfony. I have created an entity named notification to save notifications. So when a user creates, edits or removes a record in the database, I want to save this action in a notification table. I used HasLifecycleCallbacks() annotation method and it forced me to create a controller object in my entity but nothing has worked. How can i do it? Is there another solution?
/**
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="CM\UserBundle\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User extends BaseUser {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", unique=true, length=255, nullable=true)
* #Assert\NotBlank()
*/
protected $nom;
/**
* #var int
*
* #ORM\Column(name="numero", type="integer", unique=true, nullable=true)
* #Assert\NotBlank()
*/
protected $numero;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set nom
*
* #param string $nom
*
* #return User
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
/**
* Set numero
*
* #param integer $numero
*
* #return User
*/
public function setNumero($numero)
{
$this->numero = $numero;
return $this;
}
/**
* Get numero
*
* #return int
*/
public function getNumero()
{
return $this->numero;
}
/**
* #ORM\PreRemove
*/
public function notify(){
$controlleur = new RemoveController();
$em = $controlleur->getDoctrine()->getManager();
$notif = new Notification();
$notif->setOperation('recording');
$notif->setUser('William');
$em->persist($notif);
$em->flush();
}
}
I've resolved my problem. I had not read very well the documentation. And then i did some more research. So here is a solution that works for me in symfony 3.1.
I used dependency injection. I have injected ManagerRegistry to have the entity manager service and TokenStorage for tokenStorage service to know the current user connected.
I have created a folder name NotificationDB. And then i have also created a class name NotificationDB.php which here is the code. For this example, i suppose that i have a form to register a foo entity.
namespace CM\GestionBundle\NotificationBD;
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use \CM\GestionBundle\Entity\Notification;
class NotificationBD {
private $managerRegistry;
/**
* #var TokenStorage
*/
private $tokenStorage;
public function __construct(ManagerRegistry $managerRegistry, TokenStorage $tokenStorage)
{
$this->managerRegistry = $managerRegistry;
$this->tokenStorage = $tokenStorage;
}
public function notifyEvent(){
$entityManager = $this->managerRegistry->getManager();
$user = $this->tokenStorage->getToken()->getUser();
$notif = new Notification();
$notif->setDateNotif(new \Datetime());
$notif->setUser($user);
$notif->setActionNotif('recording');
$notif->setObjetNotif('foo');
$entityManager->persist($notifEdition);
$entityManager->flush();
}
}
In CM\GestionBundle\Resources\config**, i have configure as service in **services.yml file which here is the content :
CM_gestion.notification:
class: CM\GestionBundle\NotificationBD\NotificationBD
arguments: ['#doctrine', '#security.token_storage']
And then i have created a listener for which will call this service. For this example, i will create a foo entity listener which will listen register event and use the service to persist the notification entity. Here is my foo listener.
namespace CM\GestionBundle\DoctrineListener;
use CM\GestionBundle\NotificationBD\NotificationBD;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use \CM\GestionBundle\Entity\Foo;
class FooListener {
/**
* #var NotificationBD
*/
private $notificationBD;
public function __construct(NotificationBD $notificationBD) {
$this->notificationBD = $notificationBD;
}
public function postPersist(LifecycleEventArgs $args) {
$entity = $args->getObject();
if (!$entity instanceof Foo) {
return;
}
$this->notificationBD->notifyEvent();
}
}
In CM\GestionBundle\Resources\config
The last thing to do is to add this listener in services.yml file. Here is the content
CM_gestion.doctrine_listener.foo:
class: CM\GestionBundle\DoctrineListener\FooListener
arguments:
- "#CM_gestion.notification"
tags:
- {name: doctrine.event_listener, event: postPersist}
That all. this solution work for me in symfony 3.1. This problem can be marked as resolved. Thank
Related
Currently I'm trying to modify my classes and looking for idea to save dynamic relations between users and roles.
I want to create associations when loading fixtures and also to have such a functionality in controller when I need to create an user with relation, example:
...
$user = new User();
$user->setName($_POST['name']);
$user->setPassword($_POST['password']);
...
$user->setRole('ROLE_USER');//Role for everyone
...
$role = new Role();
$role->setName('ROLE_' . strtoupper($_POST['name']) );//Role for personal use
...
//Here need to implement user+role association (I'm looking for recommendations)
...
$entityManager->persist($user);
$entityManager->persist($role);
//Persist role+user assosiacion
$entityManager->flush();
$entityManager->clear();
My User.php :
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* User
*
* #ORM\Table(name="user", uniqueConstraints={#ORM\UniqueConstraint(name="user_name", columns={"user_name"}), #ORM\UniqueConstraint(name="email", columns={"email"})})
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Cache(usage="NONSTRICT_READ_WRITE", region="fast_cache")
* #UniqueEntity(fields="email", message="Email already taken")
* #UniqueEntity(fields="username", message="Username already taken")
*/
class User implements UserInterface, \Serializable
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"remove"})
* #ORM\JoinTable(name="users_roles",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")}
* )
*/
protected $roles;
/**
* #var int
*
* #ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="user_name", type="string", length=255, nullable=false)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255, nullable=false)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, nullable=false)
*/
private $password;
/**
* #var bool
*
* #ORM\Column(name="is_enabled", type="boolean", nullable=false)
*/
private $isEnabled;
/**
* #var bool
*
* #ORM\Column(name="is_verified", type="boolean", nullable=false)
*/
private $isVerified;
/**
* #var DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* #var DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #return string
*/
public function getEmail(): string
{
return $this->email;
}
/**
* #param string $email
*/
public function setEmail(string $email): void
{
$this->email = $email;
}
/**
* #return bool
*/
public function isEnabled(): bool
{
return $this->isEnabled;
}
/**
* #param bool $isEnabled
*/
public function setIsEnabled(bool $isEnabled): void
{
$this->isEnabled = $isEnabled;
}
/**
* #return bool
*/
public function isVerified(): bool
{
return $this->isVerified;
}
/**
* #param bool $isVerified
*/
public function setIsVerified(bool $isVerified): void
{
$this->isVerified = $isVerified;
}
/**
* #return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* #param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* #return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* #param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
/**
* String representation of object
* #link http://php.net/manual/en/serializable.serialize.php
* #return string the string representation of the object or null
* #since 5.1.0
* NOTE: SYNFONY BUG 3.4 -> 4.1; https://github.com/symfony/symfony-docs/pull/9914
*/
public function serialize(): string
{
// add $this->salt too if you don't use Bcrypt or Argon2i
return serialize([$this->id, $this->username, $this->password]);
}
/**
* Constructs the object
* #link http://php.net/manual/en/serializable.unserialize.php
* #param string $serialized <p>
* The string representation of the object.
* </p>
* #return void
* #since 5.1.0
*/
public function unserialize($serialized): void
{
// add $this->salt too if you don't use Bcrypt or Argon2i
[$this->id, $this->username, $this->password] = unserialize($serialized, ['allowed_classes' => false]);
}
/**
* Returns the roles granted to the user.
*
* <code>
* public function getRoles()
* {
* return array('ROLE_USER');
* }
* </code>
*
* Alternatively, the roles might be stored on a ``roles`` property,
* and populated in any number of different ways when the user object
* is created.
*
* #return array The user roles
*/
public function getRoles(): array
{
$roles = [];
foreach ($this->roles->toArray() AS $role) {
$roles[] = $role->getName();
}
return $roles;
}
/**
* Returns the password used to authenticate the user.
*
* This should be the encoded password. On authentication, a plain-text
* password will be salted, encoded, and then compared to this value.
*
* #return string The password
*/
public function getPassword(): string
{
return $this->password;
}
/**
* #param string $password
*/
public function setPassword(string $password): void
{
$this->password = $password;
}
/**
* Returns the salt that was originally used to encode the password.
*
* This can return null if the password was not encoded using a salt.
*
* #return string|null The salt
*/
public function getSalt()
{
// See "Do you need to use a Salt?" at https://symfony.com/doc/current/cookbook/security/entity_provider.html
// we're using bcrypt in security.yml to encode the password, so
// the salt value is built-in and you don't have to generate one
return null;
}
/**
* Returns the username used to authenticate the user.
*
* #return string The username
*/
public function getUsername()
{
return $this->username;
}
/**
* #param string $username
*/
public function setUsername(string $username): void
{
$this->username = $username;
}
/**
* Removes sensitive data from the user.
*
* This is important if, at any given point, sensitive information like
* the plain-text password is stored on this object.
*/
public function eraseCredentials()
{
// if you had a plainPassword property, you'd nullify it here
$this->plainPassword = null;
}
}
Role.php file:
<?php
namespace App\Entity;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* #ORM\Table(name="role", uniqueConstraints={#ORM\UniqueConstraint(name="name", columns={"name"})})
* #ORM\Entity(repositoryClass="App\Repository\RoleRepository")
*/
class Role
{
/**
* #var ArrayCollection
*
* #ORM\ManyToMany(targetEntity="App\Entity\User", mappedBy="roles", cascade={"remove"})
* #ORM\JoinTable(name="users_roles",
* joinColumns={#ORM\JoinColumn(name="role_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*/
protected $users;
/**
* #var int
*
* #ORM\Column(name="id", type="smallint", nullable=false, options={"unsigned"=true})
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=false)
*/
private $name;
/**
* #var DateTime
*
* #ORM\Column(name="created_at", type="datetime", nullable=false)
*/
private $createdAt;
/**
* #var DateTime
*
* #ORM\Column(name="updated_at", type="datetime", nullable=false)
*/
private $updatedAt;
/**
* Role constructor.
*/
public function __construct()
{
$this->users = new ArrayCollection();
}
/**
* #return array
*/
public function getUsers(): array
{
return $this->users->toArray();
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getName(): string
{
return $this->name;
}
/**
* #param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* #return DateTime
*/
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
/**
* #param DateTime $createdAt
*/
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
/**
* #return DateTime
*/
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
/**
* #param DateTime $updatedAt
*/
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
}
My data fixtures AppFixtures.php:
<?php
namespace App\DataFixtures;
use App\Entity\Role;
use App\Entity\User;
use DateTime;
use Doctrine\Bundle\FixturesBundle\Fixture;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
/**
* Class AppFixtures
* #package App\DataFixtures
*/
class AppFixtures extends Fixture
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* AppFixtures constructor.
* #param UserPasswordEncoderInterface $userPasswordEncoder
* #param EntityManagerInterface $entityManager
*/
public function __construct(UserPasswordEncoderInterface $userPasswordEncoder, EntityManagerInterface $entityManager)
{
$this->encoder = $userPasswordEncoder;
$this->entityManager = $entityManager;
}
/**
* #param ObjectManager $manager
*/
public function load(ObjectManager $manager)
{
//Creating default roles
$role = new Role();
$role->setName('ROLE_USER');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_MODERATOR');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$role = new Role();
$role->setName('ROLE_ADMIN');
$role->setCreatedAt(new DateTime());
$role->setUpdatedAt(new DateTime());
$manager->persist($role);
$manager->flush();
$manager->clear();
//Creating users
$user = new User();
$user->setUserName('john');
$user->setEmail('john#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JOHN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('tom');
$user->setEmail('tom#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_TOM', 'ROLE_MODERATOR']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$user = new User();
$user->setUserName('jimmy');
$user->setEmail('jimmy#localhost');
//$user->setRoles(['ROLE_USER', 'ROLE_JIMMY', 'ROLE_ADMIN']);
//$roleAssociation = null;
$user->setPassword($this->encoder->encodePassword($user, 'test'));
$user->setCreatedAt(new DateTime());
$user->setUpdatedAt(new DateTime());
$user->setIsVerified(true);
$user->setIsEnabled(true);
//$manager->persist($roleAssociation);
$manager->persist($user);
$manager->flush();
$manager->clear();
}
}
I'm looking for advises for:
User entity is cached in annotation, because symfony on each request loads it. config part:
orm:
metadata_cache_driver:
type: redis
result_cache_driver:
type: redis
query_cache_driver:
type: redis
I'm caching all the data for 1min in redis. Any better solution?
DB schema creates Foreign Keys (FK) and extra indexes on users_roles
table and I would love to not to have FK, because IMHO it's "heavy" thing. I would prefer to have primary key on (user_id,role_id) only. Any recommendations of this?
The solution for having flexible add/remove for user + role + roles_users
Big thanks!
I'll try to answer this but as I mentioned in my comment, this question is a bit large so some of this may be generalized.
User entity is cached in annotation, because symfony on each request
loads it. config part:
orm:
metadata_cache_driver:
type: redis
result_cache_driver:
type: redis
query_cache_driver:
type: redis
I'm caching all the data for 1min in redis. Any better solution?
When you say "better" here, it's subjective. Each application is different. Caches, and the length at which caches stay alive, is specific to each application's requirements. I don't know if there's anything inherently wrong with this, but it's largely dependent on your app's requirements.
DB schema creates Foreign Keys (FK) and extra indexes on users_roles table and I would love to not to have FK, because IMHO
it's "heavy" thing. I would prefer to have primary key on
(user_id,role_id) only. Any recommendations of this?
First, FKs are important, and they're intended to keep integrity in your data. They ensure that if someone attempted to delete a user or role that a user_roles row linked to, the operation would fail. You usually want this behavior so that you don't lose data or create orphaned data.
Secondly, I'm not sure what version of Doctrine you're using but my similar ManyToMany tables do create a PK and unique index on (user_id, role_id).
The solution for having flexible add/remove for user + role + roles_users
You do this by using Doctrine's ArrayCollections (click Collections in the menu to make sure the anchor link worked, they're broken sometimes).
There is one caveat when doing this with the the default Symfony User entity though. It implements the Symfony\Component\Security\Core\User\UserInterface interface which defines a getRoles() method that is meant to return an array of roles as strings. This is so that certain Symfony security features work as expected. This means that if you have a private $roles property that you will have to rename its standard Doctrine getter to something else so that you can leave getRoles() functioning as expected.
So, for the User entity, I typically just rename my getter, setter, adder, and remover to something like getUserRoles(), setUserRoles(), addUserRole(), and removeUserRole() and then I leave getRoles() to implement the expected interface.
Here is an incomplete example of a user class with no Role class example.
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection/*Interface*/;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use \InvalidArgumentException;
use function array_unique;
class User implements UserInterface
{
/* ... */
/**
* #var Role[]|array User's roles
*
* Note that the getter, setter, adder, and remover for this method are renamed so that the getRoles() method that
* we implement from Symfony\Component\Security\Core\User\UserInterface can function as expected.
*
* #see UserInterface
* #see User::getRoles()
* #see User::getUserRoles()
* #see User::setUserRoles()
* #see User::addUserRole()
* #see User::removeUserRole()
*
* #ORM\ManyToMany(targetEntity="App\Entity\Role", inversedBy="users", cascade={"persist"})
*/
private $roles;
/* ... */
/**
* Constructor
*/
public function __construct()
{
$this->roles = new ArrayCollection();
}
/* ... */
/**
* #return Collection|Role[]
*/
public function getUserRoles(): Collection
{
return $this->roles;
}
/**
* #param Collection|Role[] $roles
*
* #return self
*/
public function setUserRoles(Collection/*Interface*/ $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #param Role $role
*
* #return self
*/
public function addUserRole(Role $role): self
{
$this->roles->add($role);
return $this;
}
/**
* #param Role $role
*
* #return self
*/
public function removeUserRole(Role $role): self
{
$this->roles->removeElement($role);
return $this;
}
/**
* Get array of roles as strings
*
* This method is an implementation of UserInterface::getRoles(). The getter for self::$roles is
* self::getUserRoles().
*
* #return string[]|array
*
* #see UserInterface
*/
public function getRoles()
{
$roleStrings = [];
foreach ($this->roles as $role) {
$roleStrings[] = $role->getName();
}
// guarantee every user at least has ROLE_USER
$roleStrings[] = 'ROLE_USER';
return array_unique($roleStrings);
}
/* ... */
}
You can also do the reverse for the Role object if you wanted to, but this depends on if you wanted to add users to roles that way and it's often best to choose the owning side of the relation.
Here is an example of how this can be used anywhere where you're working with entities, fixtures or otherwise.
<?php
use Doctrine\Common\Collections\ArrayCollection;
use App\Entity\User;
use App\Entity\Role;
$entityManager = (get entity manager);
$user = new User();
// You can also add the name as an argument to Role::__construct()
// then call from setName() from that if you wanted to simplify this.
$roleFoo = (new Role())->setName('ROLE_FOO');
$roleBar = (new Role())->setName('ROLE_BAR');
// set roles using ArrayCollection of roles.
$user->setUserRoles(new ArrayCollection($roleFoo, $roleBar));
// add new role, see User::addUserRole() for
$user->addUserRole((new Role()->setName('ROLE_WIN'));
// remove ROLE_BAR
// You can also do this with entities that you find with Doctrine
// if you want to remove them from a persisted collection.
$user->removeUserRole($roleBar);
// get roles as a collection of Role objects.
// This will return either ArrayCollection or PersistentCollection
// depending on context. These are objects that act like arrays
// and each element will be a Role object.
$roles = $user->getUserRoles();
// get roles as strings... mostly used by Symfony's security system
// but could be used by your app too.
$roleStrings = $user->getRoles();
[SETTINGS]
Symfony 3.4
Logged user (FosUserBundle)
Projet Entity (below)
src/AppBundle/Entity/Projet.php
class Projet {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=50)
*/
private $titre;
/**
* #var \DateTime
*
* #ORM\Column(name="creation", type="datetime")
*/
private $creation;
/**
* #var \DateTime
*
* #ORM\Column(name="modification", type="datetime")
*/
private $modification;
/**
* #var
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="projet")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function updatedTimestamps() {
$this->setModification(new \DateTime());
if($this->getCreation() == null) {
$this->setCreation(new \DateTime());
$this->setSupprime(false);
}
}
}
[PROBLEM]
As is, I'm setting creation and modification on PrePersit/PreUpdate.
I would like to also set the id of the current logged user, how can I do it?
To do this, you need to have access to the security.token_storage service and call $tokenStorage->getToken()->getUser() to retrieve the current user. Since it is not really recommended to inject a service into an entity, you should create an entity listener as described in this section of the doctrine documentation.
Then, by declaring your entity listener as a service and adding the security.token_storage to its constructor arguments like this (in your services.yml):
listener.projet:
class: AppBundle\Listener\ProjetListener
arguments: [ "#security.token_storage" ]
tags:
- { name: doctrine.orm.entity_listener, lazy: true }
You would be able to access the currently logged in user inside your prePersist and preUpdate methods.
Edit: Here is how your listener should look like
AppBundle/Listener/ProjetListener.php
namespace AppBundle\Listener;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;
use Doctrine\ORM\Event\LifecycleEventArgs;
use Doctrine\ORM\Event\PreUpdateEventArgs;
use AppBundle\Entity\Projet;
class ProjetListener
{
private $tokenStorage;
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function prePersist(Projet $projet, LifecycleEventArgs $args)
{
// Assigning the current user to the Projet instance being persisted
$projet->setUser($this->tokenStorage->getToken()->getUser());
/* .. other actions of prePersist .. */
}
public function preUpdate(Projet $projet, PreUpdateEventArgs $args)
{
/* .. actions of preUpdate .. */
}
}
I think you have to do this in a subscriber, listening on prepersist and preupdate doctrine event.
You will be able to get the currentn user by injecting the security.token_storage service
in my symfony app, i'm using embedded forms. In my case, an object "CompetenceGroupe" can have multiple objects "CompetenceItem", but an object "CompetenceItem" belongs to only one object "CompetenceGroupe", so the relation is manyToOne.
The form work perfectly, and I have two tables (one for each entity), and it's well saved in the database.
But when I select an CompetenceGroupe object with doctrine in my controller, I have all informations of the object, and he's got an empty "competenceItems" property, so I can't retrieve the childs object (CompetenceItem).
My "CompetenceGroupe" entity :
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="competences_groupes")
*/
class CompetenceGroupe
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id_competence_groupe;
/**
* #var User $user
*
* #ORM\ManyToOne(targetEntity="User", cascade={"persist", "merge"})
* #ORM\JoinColumn(name="id_user", referencedColumnName="id_user", nullable=false)
*/
private $user;
/**
* #ORM\Column(type="string", length=60, nullable=true)
*/
protected $titre;
protected $competence_items;
public function __construct()
{
$this->competence_items = new ArrayCollection();
}
public function getCompetenceItems()
{
return $this->competence_items;
}
/**
* Get idCompetenceGroupe
*
* #return integer
*/
public function getIdCompetenceGroupe()
{
return $this->id_competence_groupe;
}
/**
* Set titre
*
* #param string $titre
*
* #return CompetenceGroupe
*/
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre()
{
return $this->titre;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return CompetenceGroupe
*/
public function setUser(\AppBundle\Entity\User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
public function addItem(CompetenceItem $item)
{
$this->competence_items->add($item);
}
public function removeItem(CompetenceItem $item)
{
// ...
}
/**
* Set competenceItems
*
* #param \AppBundle\Entity\CompetenceItem $competenceItems
*
* #return CompetenceGroupe
*/
public function setCompetenceItems(\AppBundle\Entity\CompetenceItem $competenceItems = null)
{
$this->competence_items = $competenceItems;
return $this;
}
}
And my "CompetenceItem" entity :
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="competences_items")
*/
class CompetenceItem
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id_competence_item;
/**
* #ORM\Column(type="string", length=60, nullable=false)
*/
protected $libelle;
/**
* #var CompetenceNiveau $niveau
*
* #ORM\ManyToOne(targetEntity="CompetenceNiveau", cascade={"persist", "merge"})
* #ORM\JoinColumn(name="id_competence_niveau", referencedColumnName="id_competence_niveau", nullable=true)
*/
private $niveau;
/**
* #var CompetenceGroupe $competence_groupe
*
* #ORM\ManyToOne(targetEntity="CompetenceGroupe", cascade={"persist", "merge"})
* #ORM\JoinColumn(name="id_competence_groupe", referencedColumnName="id_competence_groupe", nullable=false)
*/
private $competence_groupe;
/**
* Get idCompetenceItem
*
* #return integer
*/
public function getIdCompetenceItem()
{
return $this->id_competence_item;
}
/**
* Set libelle
*
* #param string $libelle
*
* #return CompetenceItem
*/
public function setLibelle($libelle)
{
$this->libelle = $libelle;
return $this;
}
/**
* Get libelle
*
* #return string
*/
public function getLibelle()
{
return $this->libelle;
}
/**
* Set niveau
*
* #param \AppBundle\Entity\CompetenceNiveau $niveau
*
* #return CompetenceItem
*/
public function setNiveau(\AppBundle\Entity\CompetenceNiveau $niveau = null)
{
$this->niveau = $niveau;
return $this;
}
/**
* Get niveau
*
* #return \AppBundle\Entity\CompetenceNiveau
*/
public function getNiveau()
{
return $this->niveau;
}
/**
* Set competenceGroupe
*
* #param \AppBundle\Entity\CompetenceGroupe $competenceGroupe
*
* #return CompetenceItem
*/
public function setCompetenceGroupe(\AppBundle\Entity\CompetenceGroupe $competenceGroupe)
{
$this->competence_groupe = $competenceGroupe;
return $this;
}
/**
* Get competenceGroupe
*
* #return \AppBundle\Entity\CompetenceGroupe
*/
public function getCompetenceGroupe()
{
return $this->competence_groupe;
}
}
I think I have a missing annotation of the "competence_items" property in the CompetenceGroupe entity, but i'm really not sure ...
Thanks for your help !
A good practice may be to have a competence form, which would be call inside your competence group form
You may add a CollectionType as parrent and include query to search which competence already exist
There are some good example with post form type in symfony demo blog
Or you can use form events (onSubmit, preSubmit, etc...) to charge your entity with your required competence. This example show a message form which allow to choose friend from preset data, this is a good example.
You have tow choice , even to create a Many-To-One, Unidirectional , in this case , you need clean some code , take a look:
In CompetenceGroupe class :
class CompetenceGroupe
{
/**
* Many competence have One Group.
* #ManyToOne(targetEntity="CompetenceItem")
* #JoinColumn(name="id_competence_item", referencedColumnName="id_competence_item")
*/
protected $competence_items;
public function __construct()
{
// $this->competence_items = new ArrayCollection();
//delete that line
}
In CompetenceItem class :
class CompetenceItem
{
You need to delete private $competence_groupe; attribute with his annotation :
By this way, when you dump a CompetenceGroupe object you gonna find the competence items.
Also, you can do it with One-To-Many, Bidirectional ,if you want to get the data from the inverse side and from the owning side .
EDIT: If one competenceGroupe can have many competenceItems, then that is a OneToMany relationship; this is the inverse side of the relationship as defined by doctrine, but that is ok. Your question asked how to pull a competenceGroupe and retrieve all related competenceItems. You can do this by making the competenceItems an ArrayCollection in your CompetenceGroupe entity, just as you have done. You do have to define that further in the annotation, see (updated) code below.
For an ArrayCollection, you can remove your method setCompetenceItems and instead define a method addCompetenceItem in your CompetenceGroupe entity.
class CompetenceGroupe
{
/**
* #ORM\OneToMany(targetEntity="CompetenceItem", mappedBy="competence_groupe")
*/
protected $competenceItems;
public function __construct()
{
$this->competenceItems= new ArrayCollection();
}
/**
* Add competenceItem
*
* #param CompetenceItem $competenceItem
* #return CompetenceGroupe
*/
public function addCompetenceItem(CompetenceItem $competenceItem)
{
$this->competence_items->add($competenceItem);
return $this;
}
}
You'll also need to define the owning side to make all this work.
I am new in symfony2, I would like to register some user in my profile_tbl.
here is my code in the controller.
<?php
namespace Ai\QABlogBundle\Controller;
use Ai\QABlogBundle\Entity\Profile_tbl;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class QABlogController extends Controller
{
/**
*#Route("/" , name= "home")
*/
public function homeAction()
{
return $this->render('AiQABlogBundle:QABlog:primaries/index.html.twig');
}
/**
*#Route("/register/" ,name= "register")
*/
public function registerAction(Request $request)
{
$profile = new Profile_tbl();
$form = $this -> createFormBuilder($profile)
-> add ('fname' , 'text')
-> add ('lname' , 'text')
-> add ('gender' , 'text')
-> add ('email' , 'text')
-> add ('register' , 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($profile);
$em->flush();
return new Response('News added successfuly');
}
$build['form'] = $form->createView();
return $this->render('AiQABlogBundle:QABlog:primaries/registration.html.twig', $build);
}
}
When I try to run it in my browser it gives an error
"Attempted to load class "Profile_tbl" from namespace "Ai\QABlogBundle\Entity".
Did you forget a "use" statement for another namespace?"
I don't know what is wrong .. Can you help me?
my Profile_tbl entity
<?php
namespace Ai\QABlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Profile_tbl
*
* #ORM\Table()
* #ORM\Entity
*/
class Profile_tbl
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="fname", type="string", length=255)
*/
private $fname;
/**
* #var string
*
* #ORM\Column(name="lname", type="string", length=255)
*/
private $lname;
/**
* #var string
*
* #ORM\Column(name="gender", type="string", length=255)
*/
private $gender;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=255)
*/
private $email;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set fname
*
* #param string $fname
* #return Profile_tbl
*/
public function setFname($fname)
{
$this->fname = $fname;
return $this;
}
/**
* Get fname
*
* #return string
*/
public function getFname()
{
return $this->fname;
}
/**
* Set lname
*
* #param string $lname
* #return Profile_tbl
*/
public function setLname($lname)
{
$this->lname = $lname;
return $this;
}
/**
* Get lname
*
* #return string
*/
public function getLname()
{
return $this->lname;
}
/**
* Set gender
*
* #param string $gender
* #return Profile_tbl
*/
public function setGender($gender)
{
$this->gender = $gender;
return $this;
}
/**
* Get gender
*
* #return string
*/
public function getGender()
{
return $this->gender;
}
/**
* Set email
*
* #param string $email
* #return Profile_tbl
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
}
I try to rename or make another entity ..
steps that I tried to generate my new entity.
Creating Database:
php app/console doctrine:database:create
Generate Entity:
php app/console doctrine:generate:entity
Generate the Getters and Setters:
php app/console doctrine:generate:entities AiQABlogBundle
after this I run it in my browser.
and gives me an error ,
Attempted to load class "Profile" from namespace "Ai\QABlogBundle\Controller".
Did you forget a "use" statement for e.g. "Twig_Profiler_Profile" or "Symfony\Component\HttpKernel\Profiler\Profile"?
This happened to me a few times. It usually happens when I copy-paste an old class file to create a new class. Check your classname and the filename of the file containing your class; they should be the same. In your case they should be Profile_tbl and Profile_tbl .php respectively.
Update
Profile_tbl is converted to Profile/tbl.php as per the PSR-0 autoload convention which Composer uses by default to load classes inside the src/ directory. You'll have to rename the class to something that does not use an underscore e.g. ProfileTbl or simply Profile.
// This should be in the file: src/Ai/QABlogBundle/Entity/Profile.php
namespace Ai\QABlogBundle\Entity;
/**
* Profile
*
* #ORM\Table()
* #ORM\Entity
*/
class Profile
{
// ...
}
Credits to Pazi for pointing this out.
I'm trying to add 4 new fields in Sonata MediaBundle for the GalleryHasMedia.
I correctly override the GalleryHasMediaAdmin :
To override it i added in services.yml this line :
parameters:
sonata.media.admin.gallery_has_media.class: Application\Sonata\MediaBundle\Admin\GalleryHasMediaAdmin
I had to create the methods manually (getName and else) since php app/console doctrine:generate:entities ApplicationSonataMediaBundle:GalleryHasMedia
apparently not caring about my new fields set in my custom entity Application\Sonata\MediaBundle\Entity\GalleryHasMedia.
As well --dump-sql return "Nothing to update". But the methods (getName and else) are correctly recognize in the Sonata admin, so why not the new fields?
here my custom entity :
<?php
namespace Application\Sonata\MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sonata\MediaBundle\Entity\BaseGalleryHasMedia as BaseGalleryHasMedia;
/**
* #ORM\Entity
* #ORM\Table(name="media__gallery_media")
*/
class GalleryHasMedia extends BaseGalleryHasMedia
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=64, nullable=false, name="name")
**/
private $name;
/**
* #ORM\Column(type="string", length=64, nullable=false, name="activity")
**/
private $activity;
/**
* #ORM\Column(type="text", nullable=false, name="description")
*/
private $description;
/**
* #ORM\Column(type="string", length=255, nullable=false, name="code")
**/
private $link;
/**
* Get id
*
* #return integer $id
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return GalleryHasMedia
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set activity
*
* #param string $activity
* #return GalleryHasMedia
*/
public function setActivity($activity)
{
$this->activity = $activity;
return $this;
}
/**
* Get activity
*
* #return string
*/
public function getActivity()
{
return $this->activity;
}
/**
* Set description
*
* #param string $description
* #return GalleryHasMedia
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set link
*
* #param string $link
* #return GalleryHasMedia
*/
public function setLink($link)
{
$this->link = $link;
return $this;
}
/**
* Get link
*
* #return string
*/
public function getLink()
{
return $this->link;
}
}
And i correctly set as said in their Documentation :
sonata_media:
# if you don't use default namespace configuration
class:
media: Application\Sonata\MediaBundle\Entity\Media
gallery: Application\Sonata\MediaBundle\Entity\Gallery
gallery_has_media: Application\Sonata\MediaBundle\Entity\GalleryHasMedia
I'm using auto mapping so my custom entity is correctly mapped :
[OK] Application\Sonata\MediaBundle\Entity\GalleryHasMedia
here the actual table (sonata's default table) :
So any ideas why i can't add any new fields to the gallery_has_media table?
UPDATE :
I'm guessing it is because i'm using annotations. How can i keep using annotations and makes it sync with my database?
This guy encountered a similar problem Issue
Okay, i found the answer correctly explained here.
Deleting Application/Sonata/MediaBundle/Resources/config/doctrine allowed me to use annotations inside my custom entity.