I have a class that extends fos_user class from FOSUserBundle.
Every user have some roles.
If user has role "BASIC" he has a OneToMany relation with entity Entry (one basic user has many entries).
If user has role "MASTER" he has a ManyToMany relation with entity House (many master users have many houses).
I done this:
/**
* #ORM\Entity
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="House", inversedBy="users")
*/
protected $houses;
/**
* #ORM\OneToMany(targetEntity="Entry", mappedBy="user")
*/
protected $entries;
}
But it doesn't sound fine.
Is there a design pattern for this scenario?
As #lxg wrote a possible pattern is single table inheritance.
You can look an example here.
Related
Simple example:
I've got two users Admin and Client (both implements UserInterface) and Cart - three entity classes at a. Admin and Client can have his own carts. How to configure/resolve Cart entity relation to have method 'getUser()' which returns Admin or Client user?
Maybe I can have column user_id and second column with user entity name in Cart (something similar as DiscriminatorMapping can do)?
class Admin implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Cart", mappedBy="???")
*/
private $carts;
....
class Client implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Cart", mappedBy="???")
*/
private $carts;
....
class Cart
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var UserInterface
* #ORM\ManyToOne(targetEntity="UserInterface", ???)
*/
private $user;
....
I tried this Doctrine feature, also tried DisciminatorMapping and composite keys (join by multiple columns) option with no luck.
Any help?
I think you were almost there. DiscriminatorMapping is probably the way to go. However, you need to bind Client and Admin to a Parent class. So consider this hierarchy:
User (parent class)
Admin (extends User)
Client (extends User)
Then in your Cart entity you bind the relation to the User entity.
I downloaded FOUSERBUNDLE but I have a problem when I add an instance entity.
I have a entity 'Person', it inherits User of FOUSERBUNDLE :
class Person extends BaseUser{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
*
* #ORM\Column(name="firstname", type="string", length=255)
*/
private $firstname;
...
}
And I have two others entities and these entities inherits Person ! :
class Person1 extends Person{
...
/* Same attributes that Person but differents associations */
...
}
class Person2 extends Person{
...
/* Same attributes that Person but differents associations */
...
}
But the authentication with FOUSERBUNDLE use online one entity (Person).
How can I add instance of Person1 or Person2 and login with this instance ?
See https://github.com/PUGX/PUGXMultiUserBundle
The PUGXMultiUserBundle extends FOSUserBundle adding the feature to
handle users of different types. For now only supports the ORM db
driver and is based on the doctrine2 inheritance.
I have two roles in my application, for example ROLE_USER and ROLE_SUPERUSER. Users are stored in the database using Doctrine. Users with the ROLE_USER role are based on a simple User class. (Getters and setters have been removed for readability.)
Acme\MyBundle\Entity\User.php
namespace Acme\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #ORM\Entity(repositoryClass="Acme\MyBundle\Entity\UserRepository")
* #ORM\Table("users")
* #UniqueEntity(
* fields={"email"},
* message="email already used"
* )
*/
class User implements UserInterface, \Serializable
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
protected $email;
/**
* #ORM\Column(type="string", length=32)
*/
private $salt;
/**
* #ORM\Column(type="string", length=4096)
*/
private $password;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
public function __construct()
{
$this->isActive = true;
$this->salt = md5(uniqid(null, true));
}
/**
* #inheritDoc
*/
public function getRoles()
{
return array('ROLE_USER','ROLE_SUPERUSER');
}
}
I have a SuperUser class that extends User to provides more fields that only users with ROLE_SUPERUSER would need.
Acme\MyBundle\Entity\SuperUser.php
namespace Acme\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="super_users")
*/
class SuperUser extends User
{
/**
* #var integer
*/
protected $id;
/**
* #var string
*/
protected $email;
/**
* #ORM\Column(type="string", length=32)
*/
proteced $avatar;
/**
* #ORM\Column(type="string", length=50, unique=true)
*/
proteced $username;
}
In my controllers I'm using $user = $this -> getUser(); to get the current user, but this returns an instance of the User class and I cannot access the SuperUser properties or methods, even if the user has the role ROLE_SUPERUSER.
For example, I would like to be able to use the following code.
if ($this->get('security.context')->isGranted('ROLE_SUPERUSER')) {
$avatar = $this->getUser()-> avatar;
}
Is there anyway to be able to do that? I would say there's something to do with Doctrine relationships, but I don't really know what to change.
By the way, as you can see I don't have an username field in my standard User class, it's only present in SuperUser. Does this may cause any problems since the authentication is based on username?
I think my problem hasn't been clearly exposed, but this might be due to my current code, which is wrong.
I don't have two user tables. I want only one User class (with one users table). The authentication is operated only on this class with email and password.
I have another class SuperUser```that provides extra fields to users that have the ROLE_SUPERUSER`` role, but the superusers are users and have an entry in the users table. I just want to create a left join on the concerned rows, that's why I used inheritance. (Maybe there's a better way to do it.)
If I want to get all the emails, I can query the users table. If I want to get all the usernames, since only superusers have one, I can query the superusers table.
I would go for two entities with a OneToOne relationship (No extends)
/** #Entity **/
class SuperUser
{
// ...
/**
* #ORM\OneToOne(targetEntity="User", mappedBy="superUser")
**/
private $user;
// ...
}
/** #Entity **/
class User
{
// ...
/**
* #ORM\OneToOne(targetEntity="SuperUser", inversedBy="user")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
**/
private $superUser;
// ...
}
Otherwise take a look at Inheritance Mapping
You should have two user providers then, and use one master provider:
security:
providers:
master_provider:
chain:
providers: [user, super_user]
user:
entity: { class: Acme\MyBundle\Entity\User, property: username }
super_user:
entity: { class: Acme\MyBundle\Entity\SuperUser, property: username }
firewalls:
main:
provider: master_provider
http://symfony.com/doc/current/cookbook/security/multiple_user_providers.html
I have installed the FOS UserBundle.
The problem is: the "id" field of my User-Table is called userID.
I can't change it bc of other Tables and Programming that is dependent on the userID field.
If i try to login, i get an error:
Unrecognized field: id
The problem seems to lie in the call for the id field:
UserManager ->findUserBy (array('id' => 1))
Can I somehow override FOS UserBundle so that the findby() method transfers id to userID?
Or do i get it totally wrong and have to do it another way?
Field names in the entity don't have to be identical to the field names in your sql table. So you can use "id" as field in your doctrine entity and map it to a "userID" field in your sql table.
If you are using Doctrine annotations for your user entity like in the FOSUserBundle documentation, this could do the trick:
/**
* #ORM\Id
* #ORM\Column(name="userID", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
I haven't tested it and there is a chance that it doesn't work, because the id field is always a bit "special", but it may be worth a try.
Generate your own UseBundle and add in define the entity something like this.
<?php
namespace Demo\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity(repositoryClass="Sciviz\UserBundle\Entity\UserRepository")
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #ORM\Column(name="first_name", type="string", length=255, nullable=true) */
protected $first_name;
..................
Also see this tutorial
http://knpuniversity.com/screencast/fosuserbundle-ftw
your column name "USER_ID" can be case sensitive.
/**
* #ORM\Entity
* #ORM\Table(name="user-table")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer", name="USER_ID")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
I have several bundles in my app and I would like to have relations between tables.
One is my User(StoreOwner) which is in UserBundle, and the second is Store in StoreBundle.
The relation between them is OneToMany (User -> is owner of -> Store).
Store
/**
* Description of Store
*
* #ORM\Table(name="Store")
* #ORM\Entity(repositoryClass="Traffic\StoreBundle\Repository\StoreRepository")
* #author bart
*/
class Store extends StoreModel {
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string $name
*
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(
* message="Please provide your shop name"
* )
*/
protected $name;
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\StoreOwner", inversedBy="stores")
*
*/
protected $owner;
}
StoreOwner
/**
* #ORM\Entity
*
*/
class StoreOwner extends User implements StoreOwnerInterface {
/**
* #var type ArrayCollection()
*
* #ORM\OneToMany(targetEntity="Traffic\StoreBundle\Entity\Store", mappedBy="owner", cascade={"persist"})
*/
protected $stores;
}
My question is:
Is there any solution to avoid dependency between StoreBundle and UserBundle and keep relations between Entities in Doctrine?
This is a valid concern in my opinion. Two-way dependencies between bundles are a smell.
One way of solving the dependency issue is moving your entities out of the bundles into a more general namespace. This way both bundles will depend on the same "library" but won't depend on each other directly.
I recently wrote a blog post on how to do it: How to store Doctrine entities outside of a Symfony bundle?