I'm trying to query using entity manager in a entity class file but I'm getting this error:
FatalErrorException: Error: Call to undefined method Acme\MasoudBundle\Entity\User::getDoctrine() in /var/www/test/src/Acme/MasoudBundle/Entity/User.php line 192
my entity class is :
namespace Acme\MasoudBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* Set email
*
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set isActive
*
* #param boolean $isActive
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return boolean
*/
public function getIsActive()
{
return $this->isActive;
}
/**
* #inheritDoc
*/
public function getRoles()
{
$em = $this->getDoctrine()->getManager();
$Permission= $em->getRepository('MasoudBundle:Permission')->find(1);
$this->permissions[]=$Permission->permission;
return $this->permissions;
}
}
I want to have a permission and authentication system like this, can you help me please? there are 5 tables, a user table, a group table, a permission table, and a group_permission and a user_group table. so After user logins, I want to check which user is for which group, and get the groups permission. how can I do that? please help me as much as you have time.
Your entity should not know about other entities and the Entity Manager because of the separation of concerns.
Why don't you simply map your User to the appropriate Role(s) (instances of Permission entity in your case) using Doctrine Entity Relationships/Associations. It will allow you to access the appropriate permissions of a given user from the User instance itself.
In this line:
$em = $this->getDoctrine()->getManager();
$this refers to the current class, the User Entity that does not have a method called getDoctrine(). $this->getDoctrine() works in controllers where you extend the Controller class a subclass of ContainerAware which contains the getDoctrine() method.
In other terms, this method works only on objects of class container or its subclasses, like this: $controller->getDoctrine()->getManager().
Besides, you don't want to have an EntityManager inside your entity classes, that's not a good way of doing things. You would better use listners to do such stuffs
I solved this:
global $kernel;
$em = $kernel->getContainer()->get('doctrine')->getManager();
$role = $em->getRepository('BackendBundle:user_types')->findOneBy(array(
'id' => 10
));
Related
I'm using JMSSerializerBundle in my entities definition, and RestBundle's annotations in controllers.
I have an entity with public and admin-protected attributes, let's say
use JMS\Serializer\Annotation as Serializer;
class UserAddress {
/**
* #Serializer\Expose
* #Serializer\Groups(groups={"address:read"})
*/
private $nonSecretAttribute;
/**
* #Serializer\Expose
* #Serializer\Groups(groups={"address:admin-read"})
*/
private $secretAttribute;
}
and a User like :
class User {
// ...
/**
* #ORM\OneToMany(targetEntity="UserAddress", mappedBy="user")
*/
private $addresses;
and my controller looks like
use FOS\RestBundle\Controller\Annotations as Rest;
class UsersController {
/**
* #Rest\Get("/users/{user}/addresses", requirements={"user"="\d+"})
* #Rest\View(serializerGroups={"address:read"})
* #IsGranted("user_read", subject="user")
*/
public function getUsersAddresses(User $user)
{
return $user->getAddresses();
}
}
but how could I add the address:admin-read to the serializer groups here if the logged in user happens to have the ADMIN_ROLE role ?
Is that possible in the #Rest\View annotation ?
Do I have a way to modify the groups in the controller's method, inside a conditional loop verifying my logged in user's roles ?
You should instantiate the fos-rest View manually and add a Context. On that Context you can set the serialization groups at runtime by evaluating the users roles.
Something like this should work:
use FOS\RestBundle\View\View;
use FOS\RestBundle\Context\Context;
class UsersController
{
public function getUsersAddresses(User $user): View
{
$isAdmin = in_array('ROLE_ADMIN', $user->getRoles());
$context = new Context();
$context->setGroups($isAdmin ? ['address:admin-read'] : ['address:read']);
$view = VVV::create()->setContext($context);
return $view
->setContext($context)
->setData($user->getAddresses());
}
}
I'm trying to understand Doctrine ORM relationship concepts, because I spent lot of time to create, according to me, a very simple use case, but, I didn't manage to do it because I've got a slack of knowledges...
Here is my issue :
I've got 2 entities, UserApp and RoleApp.
I want to have the possibility to manage roles from users threw a database, instead of hardcoding role, in array format, directly into users table. So to do that I've got a classic foreign key role_id into user_app table which references the primary key into role_app table.
I need, for each user, to get his role, and to bring it in the right format expected for Symfony Authentification :
class UserApp implements UserInterface, \Serializable{
.....
/**
* #var UserRole
* #ORM\ManyToOne(targetEntity="App\Entity\UserRole")
* #ORM\JoinColumn(nullable=false)
*
*/
private $role;
public function getUserRole(): array{ // I know it's wrong but I don't know what is the right type to get
return $this->role->getUserRole($this);
}
/**
* Returns the roles or permissions granted to the user for security.
*/
public function getRoles(): array{
//$role = ["ADMIN_USER"];
$role = $this->getUserRole();
dump($role);
return array_unique($role);
class UserRole{
/**
* #ORM\Column(type="json")
*/
private $appRole = [];
public function getUserRole(UserApp $user): ?array
{
$userRoleRepository = new UserRoleRepository();
$userRole[] = $userRoleRepository->find(1);
dump($userRole);
return $userRole;
}
}
But this code doesn't provide the right format for user's role need for Symfony authentification :(
Finaly I managed to do what I want; here is my solution, I hope it can help...
class UserApp implements UserInterface, \Serializable
{
....
/**
* #var UserRole
* #ORM\ManyToOne(targetEntity="App\Entity\UserRole")
* #ORM\JoinColumn(nullable=false)
*
*/
private $role;
public function getUserRole(): UserRole
{
return $this->role->getRoleUser($this);
}
/**
* Returns the roles or permissions granted to the user for security.
* it's for authentification compliance
*/
public function getRoles(): array
{
$arrRole = $this->getUserRole()->getAppRole();
return array_unique($arrRole);
}
......
class UserRole{
.....
/**
* #ORM\Column(type="json")
*/
private $appRole = [];//For authentification compliance
.....
public function getRoleUser(UserApp $user): ?self
{
return $this;
}`enter code here`
I have to persist an entity (let's call it Entity for simplicity) in the database that has to be referenced to a User handled with FOSUserBundle. To make this reference I have a column entity_table.userId.
When the new Entity is created, I have to:
Create the User through the registration procedure of FosUserBundle;
Get the ID of the new created User: [meta code] $userId = $get->newCreatedUserId();
Set this id in Entity: $entity->setUserId($userId);
Persist the Entity to the database.
How can I integrate the registration procedure of FosUserBundle into the controller that persists my Entity?
MORE DETAILS
In the first time I tried to simply copy the code from the method registerAction() of the RegistrationController of FOSUserBundle: a quick and dirty approach that, anyway didn't work as i get an error as the User class i passed was wrong (I passed my custom User entity I use to overwrite the bundle).
This kind of approach has also other drawbacks:
I cannot control the registration procedure (send or decide to not send confirmation e-mails, for example);
I cannot use the builtin checks on passed data;
I cannot be sure that on FOSUserBundles updates my custom method continue to work
Others I cannot imagine at the moment...
So, I'd like to create the user in the cleanest way possible: how can i do this? Which should be a good approach?
A controller forwarding?
Anyway, an "hardcoded" custom method that emulates the registerAction() method?
A custom registration form?
I have read a lot of discussions here at StackOverflow and on Internet, I read the documentation of FOSUserBundle and of Symfony too, but I cannot decide for the good approach, also because I'm not sure I have understood all the pros and cons of each method.
If someone can put me on the right way... :)
SOMETHING MORE ABOUT MY REGISTRATION FLOW
I have a getStarted procedure handled by the controller GetStarteController.
In it I have two methods:
indexAction(), that displays a registration form with only the field "email";
endAction(), that receive the form and creates a Company using the passed e-mail (it gets the domain part only of the email).
HERE IS A WORKING MESSY CODE (inside it for Companies and Stores are called some methods that exists in the source code but are not in the posted classes below, as setBrand() or setUrl(), for example).
// AppBundle/Controller/getStartedController.php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use MyVendor\UserBundle\Entity\User;
use AppBundle\Entity\Companies;
use AppBundle\Entity\Stores;
class GetStartedController extends Controller
{
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
$data = array();
$form = $this->createFormBuilder($data, array(
'action' => $this->generateUrl('getStartedEnd'),
))
->add('email', 'email')
->add('submit', 'submit')
->getForm();
return array(
'form' => $form->createView(),
);
}
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$form = $this->createFormBuilder()
->add('email', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
} else {
/** #todo here we have to raise some sort of exception or error */
echo 'no data submitted (See the todo in the code)';exit;
}
// Pass the email to the template
$return['email'] = $data['email'];
// Get the domain part of the email and pass it to the template
$domain = explode('#', $data['email']);
$return['domain'] = $domain[1];
// 1) Create the new user
$user = new User();
// Get the token generator
$tokenGenerator = $this->container->get('fos_user.util.token_generator');
$user->setEmail($return['email']);
$userRandomUsername = substr($tokenGenerator->generateToken(), 0, 12);
$user->setUsername('random-' . $userRandomUsername);
$plainPassword = substr($tokenGenerator->generateToken(), 0, 12);
$encoder = $this->container->get('security.password_encoder');
$encoded = $encoder->encodePassword($user, $plainPassword);
// Set the password for the user
$user->setPassword($encoded);
/** #var $userManager \FOS\UserBundle\Model\UserManagerInterface */
$userManager = $this->get('fos_user.user_manager');
// Perstist the user in the database
$userManager->updateUser($user);
$userId = $user->getId();
// 2) Create the Company object
$company = new Companies();
$company->setBrand($return['domain'])
->setAdded(new \DateTime())
->setOwnerId($userId);
// 3) Create the Store object
$store = new Stores();
$store->setEmail($return['email'])
->setUrl($return['domain'])
->setAdded(new \DateTime());
// Get the Entity Manager
$em = $this->getDoctrine()->getManager();
// Persist Company and get its ID
$em->persist($company);
$em->flush();
$return['companyId'] = $company->getId();
// Set the property branchOf of the Store object
$store->setBranchOf($return['companyId']);
// Persist the Store object
$em->persist($store);
$em->flush();
$return['storeId'] = $store->getId();
return $return;
}
}
Here the User Entity that ovewrites the one provided by FOSUserBundle
// MyVendor/UserBundle/Entity/User.php
namespace MyVendor\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="prefix_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
Some essential code of Companies.php
// AppBundle/Entity/Companies.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Companies
*
* #ORM\Table(name="companies")
* #ORM\Entity
*/
class Companies
{
/**
* #var integer
*
* #ORM\Column(name="ownerId", type="integer", nullable=false)
*/
private $ownerid;
/**
* Set ownerid
*
* #param integer $ownerid
* #return Companies
*/
public function setOwnerid($ownerid)
{
$this->ownerid = $ownerid;
return $this;
}
/**
* Get ownerid
*
* #return integer
*/
public function getOwnerid()
{
return $this->ownerid;
}
}
Some essential code of Stores.php
// AppBundle/Entity/Stores.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Stores
*
* #ORM\Table(name="stores", uniqueConstraints={#ORM\UniqueConstraint(name="branchOf", columns={"branchOf"})})
* #ORM\Entity
*/
class Stores
{
/**
* #var integer
*
* #ORM\Column(name="branchOf", type="integer", nullable=false)
*/
private $branchof;
/**
* Set branchof
*
* #param integer $branchof
* #return Stores
*/
public function setBranchof($branchof)
{
$this->branchof = $branchof;
return $this;
}
/**
* Get branchof
*
* #return integer
*/
public function getBranchof()
{
return $this->branchof;
}
}
You can use a custom registration form but the best way is clearly to listen to registration event dispatched by FOSUser.
Here is an example :
class RegistrationListener implements EventSubscriberInterface
{
/**
* L'entity manager
*
* #var EntityManager
*/
private $em;
/**
* Constructeur de l'EventListener
*
* #param \Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->em = $entityManager;
}
/**
* {#inheritDoc}
*/
public static function getSubscribedEvents()
{
return array(
FOSUserEvents::REGISTRATION_INITIALIZE => 'onRegistrationInit',
);
}
/**
* Triggered when FOSUserEvents::REGISTRATION_INITIALIZE is caught.
*
* #param \FOS\UserBundle\Event\UserEvent $userEvent
*/
public function onRegistrationInit(UserEvent $userEvent)
{
$user = $userEvent->getUser();
// Define your own logic there
}
}
Don't forget to make this listener a service:
#services.yml
services:
oe_user.registration:
class: OrienteExpress\UserBundle\EventListener\RegistrationListener
# arguments are optional but you still can need them
# so I let the EM as example which is an often used parameter
arguments:
entityManager: "#doctrine.orm.entity_manager"
tags:
- { name: kernel.event_subscriber }
You'll find the complete list of event dispatched by FOSUser here
Moreover, Symfony entities are a model of objects. That said, you need to understand that you don't work with ids within your model, but object.
You should not use thing such as $var->setUserId() within entites. Doctrine is there to manage your relations, so be carefull about this. You might face unexpected problem by not using Symfony & Doctrine the way it has been designed for.
EDIT:
In your company entity, your relation is beetween a Company and a User objects. That means you dont need a User id in your company but just a instance of User.
I think you might go back to the basics before wanting to do advanced stuff.
Your relation beetween the user and the company should not be designed by an integer attribute but a real doctrine relation.
Ex:
class Company {
/**
* #ORM\ManyToOne(targetEntity="Path\To\User")
* #ORM\JoinColumn(nullable=false)
*/
private $owner;
/**
* #param $user User
*/
public function setUser(User $user)
{
$this->user = $user;
}
}
Then when you'll create a new company. You won't need to know the User's id or even insert it to make the link between them. But if you are not aware yet of this, once again, I think you should go back to the basics of Symfony since this is one of the most (maybe the most) important feature to master.
I have a doctrine entity User and a document Address (stored in mongoDB). I want to set an one to many relation between them by userId property. (the user has many addresses)
My User Entity:
namespace BlaBla\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $firstName;
... and so on
My Address document:
namespace BlaBla\UserBundle\Document;
/**
* BlaBla\UserBundle\Document\Address
*/
class Address
{
/**
* #var MongoId $id
*/
protected $id;
/**
* #var string $firstName
*/
protected $firstName;
/**
* #var string $lastName
*/
protected $lastName;
/**
* #var int $userId
*/
protected $userId;
... and so on
My goal is to create the getUser() method for the Address object and the getAddresses() method for the User object.
I've decided to place the method getAddresses() to the doctrine UserRepository class and to inject there the necessary document manager to be able to access to the Address Document. I've overriden the constructor of the userRepository and passed to it the necessary document manager object.
Please, look to the UserRepository class:
<?php
namespace BlaBla\UserBundle\Repository;
use Doctrine\ORM\EntityRepository;
class UserRepository extends EntityRepository
{
/**
* #var \Doctrine\ODM\MongoDB\DocumentManager
*/
private $_dm;
/**
* #param \Doctrine\ORM\EntityManager $dm
*/
public function __construct(\Doctrine\ORM\EntityManager $em, $dm) {
$metaData = new \Doctrine\ORM\Mapping\ClassMetadata('BlaBla\UserBundle\Entity\User');
parent::__construct($em, $metaData);
$this->_dm = $dm;
}
/**
* #param $user_id integer
* #return \BlaBla\UserBundle\Document\Address
*/
public function getAddress($user_id) {
$address = $this->_dm->getRepository('BlaBlaUserBundle:Address');
$rt = $address->findByUserId($user_id);
return $rt;
}
public function getAllUsers()
{
return $this->findAll();
}
}
After this I can access to the repository from my controller via:
$em = $this->getDoctrine()->getManager();
$dm = $this->get('doctrine_mongodb')->getManager();
$t = new \BlaBla\UserBundle\Repository\UserRepository($em, $dm);
var_dump($t->getAddress($id));
var_dump($t->getAllUsers());
Both methods work just fine, but now I can't access to the repository using shortcuts like:
$user = $this->getDoctrine()->getRepository('BlaBlaUserBundle:User');
I thought about making the Repository as service with something like this:
user.repository:
class: BlaBla\UserBundle\Repository\UserRepository
arguments: [#doctrine.orm.entity_manager, #doctrine.odm.mongo_db.document_manager]
in my services.yml file, but this only lets me to access the repository with:
$this->get('user.repository');
the default shortcuts doesn't work still.
Please help to find a correct solution for this problem.
Thanks.
Where did you specified the UserRepository? In your User.php with annotation ? Maybe that is the only thing what is missing.
But if you want to use entity and document repository, I advise you to use Doctrine extensions, specifically Reference.
I have Entity provider (it is just entity repository which searches and gives me a user while authentication) like this entity provider
(MyBundle:Employee implements UserInterface so that`s ok)
class EmployeeRepository extends EntityRepository implements UserProviderInterface
{
public function loadUserByUsername($username)
{
$user = $this->getEntityManager()
->createQuery("SELECT e FROM MyBundle:Employee e ...")
->setParameters(...)->getOneOrNullResult();
if ($user) {
return $user;
}
throw new UsernameNotFoundException();
}
public function refreshUser(UserInterface $user)
{
...
return $this->find($user->getId());
}
}
and I have another entity like
class Task {
...
/**
* #ManyToOne(targetEntity="Employee")
* #JoinColumn()
*/
protected $creator;
... + setters/getters
}
so somewhere in controller i have:
$task = new Task();
$task->setCreator($this->getUser()) // or $this->get('security.context')->getToken()->getUser();
$em->persist($task);
$em->flush();
and I have exception "A new entity was found through the relationship '...\Entity\Task#creator' that was not configured to cascade persist operations for entity: ...\Entity\Employee#0000000066a194ca0000000038e61044. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist"
but how can it be: unpersisted entity was given by entityRepository ???
(if persist getUser() em tries to insert new Employee) How can I set creator of task?
As I understand, you want to be able to join the Employee entity on Task entity in order to know which user has created the Task.
If so, you should probably take a look at the StofDoctrineExtensionBundle that allows you to easily use DoctrineExtension in Symfony2.
DoctrineExtension provides a blameable behavior:
Blameable behavior will automate the update of username or user reference fields on your Entities or Documents. It works through annotations and can update fields on creation, update or even on specific property value change.
namespace Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
*/
class Article
{
// ...
/**
* #var string $createdBy
*
* #Gedmo\Blameable(on="create")
* #ORM\Column(type="string")
*/
private $createdBy;
/**
* #var string $updatedBy
*
* #Gedmo\Blameable(on="update")
* #ORM\Column(type="string")
*/
private $updatedBy;
// ...
public function getCreated()
{
return $this->created;
}
public function getUpdated()
{
return $this->updated;
}
}
Hope this helps
I found the solution.
in this code I used new instance of entity manager like $this->get('doctrine.orm.entity_manager')
$task = new Task();
$task->setCreator($this->getUser())
$em->persist($task);
$em->flush();
, so new entity manager knows nothing about entities such as User
and the solution was to use default entity manager
$em = $this->get('doctrine.orm.default_entity_manager');
...
$em->persist(task);
$em->flush();
works fine.
I can't see al the code, but if you want to insert new task and new user in the same time (using collection type in forms) you need to define it in your entity (cascade annotation):
class Task {
...
/**
* #ManyToOne(targetEntity="Employee", cascade={"persist"})
* #JoinColumn()
*/
protected $creator;
... + setters/getters
}
For more info about cascade operations see Doctrine documentation: http://docs.doctrine-project.org/en/2.0.x/reference/working-with-associations.html#transitive-persistence-cascade-operations