FOUserundle-Synfony2.8 : 2 differents entities - symfony

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.

Related

ORM Doctrine - JoinTable and JoinColumn "column not found" or "could not resolve type of the column"

I am a begginer in Doctrine and i am stuck in kind of a loop trying to establish a join table between 2 entities.
I have a relation OneToMany between a "train" and "containers" and ManyToOne between "containers" and "train" obviously.
I placed annotations on the OneToMany relation like this :
#ORM\Entity/Train
#ORM\OneToMany(targetEntity=Container::class, mappedBy="Train")
#ORM\JoinTable(name="train_container",
#ORM\joinColumns={#JoinColumn(name="id_tm", referencedColumnName="id_tm", nullable=false)},
#ORM\inverseJoinColumns={#JoinColumn(name="id_container", referencedColumnName="id_container", nullable=false)})
)
private $Container;
and on the ManyToOne i did like this :
#ORM\Entity/Container
#ORM\ManyToOne(targetEntity=Train::class, inversedBy="Containers")
#ORM\JoinColumn(name="id_tm", referencedColumnName="id_tm")
private $Train;
Now, if i let it like that, i have an error saying :
Annotation #ORM\joinColumns is not allowed to be declared on property App\Entity\Train::$container. You may only use this annotation on these code elements: PROPERTY.
If i remove the joinColumn and just put this :
#ORM\ManyToOne(targetEntity=Train::class, inversedBy="Container")
private $Train;
I have this error :
Could not resolve type of column "id" of class "App\Entity\Train"
I have no idea why its displaying an "id" column because the name of the column and the property on "Train" is always "id_tm" everywhere.
Could anyone tell me what i am doing wrong please ?
thanks a lot in advance
M
edit to add the code on the id's properties :
#ORM\Id
#ORM\GeneratedValue
#ORM\Column(type="integer", name="id_container")
private $id_container;
#ORM\Id
#ORM\GeneratedValue
#ORM\Column(type="integer", name="id_train")
private $id_train;
On the Train entity, you need to use the ManyToMany annotation with the unique parameter to get a OneToMany association as shown in the doctine documentation.
Here is the code that worked for me, try this:
Train Entity
/**
* Class Train
* #package App\Entity
* #ORM\Entity()
* #ORM\Table(name="train", options={"comment":"Train"})
*/
class Train
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_train", type="integer", unique=true)
*/
private int $id_train;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Container", mappedBy="Train", cascade={"persist"})
* #ORM\JoinTable(name="train_container",
* joinColumns={#ORM\JoinColumn(name="train_id", referencedColumnName="id_train")},
* inverseJoinColumns={#ORM\JoinColumn(name="container_id", referencedColumnName="id_container", unique=true)}
* )
*/
private $Container;
public function __construct() {
$this->Container = new ArrayCollection();
}
//....
Also, if you want to have a bidirectional relationship between Container and Train entities, then you need to specify the following in the Container entity.
Container Entity
/**
* Class Container
* #package App\Entity
* #ORM\Entity()
* #ORM\Table(name="container", options={"comment":"Container"})
*/
class Container
{
/**
* #var int
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(name="id_container", type="integer", unique=true)
*/
private int $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Train", inversedBy="Container")
* #ORM\JoinColumn(name="train_id", referencedColumnName="id_train")
*/
private $Train;
Don't forget to generate getters and setters after
php bin/console make:entity --regenerate
And update schema
php bin/console d:s:u -f

Doctrine|ORM|Symfony: Is possible relation to Interface or multiple entities

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.

Cascading simple derived identity in Doctrine

How can I cascade a joined model with a OneToOne relationship where by only the User table has an auto increment strategy and the joined Profile model must have an id that matches the User id.
My models look like this:
Company\Model\User:
class User
{
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
* #var int
*/
private $id;
/**
* #OneToOne(targetEntity="Profile", inversedBy="user", cascade={"persist"})
* #var Profile
*/
private $profile;
Company\Model\Profile:
class Profile
{
/**
* #Id
* #OneToOne(targetEntity="User", mappedBy="profile")
* #JoinColumn(name="id")
* #var User
*/
private $user;
When persisting an instance of the User model, it causes the following error to be output:
Entity of type Company\Model\Profile is missing an assigned ID for field 'profile'. The identifier generation strategy for this entity requires the ID field to be populated before EntityManager#persist() is called. If you want automatically generated identifiers instead you need to adjust the metadata mapping accordingly.
The doctrine documentation calls this a simple derived identity, but does not explain how to cascade it.
https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/tutorials/composite-primary-keys.html#use-case-2-simple-derived-identity
It turns out that the answer is actually quite simple.
First the mappedBy and inversedBy need to be swapped around.
Secondly, when setting the Profile on the User you must set the user on the profile in turn.
Company\Model\User:
class User
{
/**
* #Id
* #GeneratedValue
* #Column(type="integer")
* #var int
*/
private $id;
/**
* #OneToOne(targetEntity="Profile", mappedBy="user", cascade={"persist"})
* #var Profile
*/
private $profile;
public function setProfile(Profile $profile): void
{
$this->profile = $profile;
$this->profile->setUser($this);
}
Company\Model\Profile:
class Profile
{
/**
* #Id
* #OneToOne(targetEntity="User", inversedBy="profile")
* #JoinColumn(name="id")
* #var User
*/
private $user;

Symfony: Handling file entity

What is the best way to handle a File entity where you have multiple ManyToOne relationships.
Let's say I have 5 entities that has a OneToMany relationship with the File entity.
File.php
/**
* #ORM\ManyToOne(targetEntity="Entity1", inversedBy="files")
* #ORM\JoinColumn(name="entity1_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $entity1;
/**
* #ORM\ManyToOne(targetEntity="Entity2", inversedBy="files")
* #ORM\JoinColumn(name="entity2_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
private $entity2;
and so one....
Entity1.php
/**
* #ORM\OneToMany(targetEntity="File", mappedBy="entity1" , cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $images;
The great thing about the above is the getter and setter are set, I can persist and save to the database automatically. The relationship is set and I can load the files by just calling $entity1->getFiles().
What I don't like is every time I want to add another entity that has a OneToMany with File it creates a new column in the database so potential I could have 10 columns referencing Ids from other entities.
What I would like to achieve is saving the class of the entity in the class field and saving the id of the record in an id field but also somehow still allowing the persist and collection saving to work.
entity_id | class
------------------------------------------
2 | ProjectBundle/Entity/Entity1
3 | ProjectBundle/Entity/Entity2
You don't need the class field at all.
Use Doctrine's inheritance mapping by creating a base class for all entities you want to refer from File:
/**
* #ORM\Entity()
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="entityType", type="string")
* #ORM\DiscriminatorMap({
* "entity1" = "Entity1",
* "entity2" = "Entity2"
* })
*/
abstract class BaseEntity
{
/**
* #ORM\ManyToMany(targetEntity="File", mappedBy="entities" , cascade={"persist", "remove"}, orphanRemoval=true)
*/
protected $images;
}
/**
* #ORM\Entity
*/
class Entity1 extends BaseEntity
{
...
}
/**
* #ORM\Entity
*/
class Entity2 extends BaseEntity
{
...
}
This way you can refer to both Entity1 and Entity2 by their base class from File. When calling getEntities, Doctrine creates instances of the proper class "automatically", based on the discriminator value of each entity.
File
/**
* #ORM\ManyToMany(targetEntity="Entity", inversedBy="images")
* #ORM\JoinColumn(name="entity_id", referencedColumnName="id", nullable=true, onDelete="CASCADE")
*/
protected $entities;
OneToMany, ManyToOne become ManyToMany because now the file may have many entities.

FOSUserBundle Entity with ManyToOne relation

I have AbstractUser super class that extends FOSUser, then I have entities that extends AbstractUser with fields related to that class. For instance Customer has ManyToOne relation to City entity.
Now when I try to log-in with FOSUser login form I'm getting error:
.... SQLSTATE[42S22]: Column not found: 1054 Unknown column 't0.city' in 'field list'
Of course there is no city field in the users table because it's relation column named city_id. Can anybody shed some light for me why doctrine builds query like this? Am I missing something ?
Thanks in advance.
Here is related code parts.
AbstractUser:
/**
* #ORM\Table(name="users")
* #ORM\Entity
* #ORM\MappedSuperclass
* #ORM\InheritanceType("SINGLE_TABLE")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"admin"="Admin", "customer"="Customer", "seller"="Seller"})
*/
abstract class AbstractUser extends FOSUser
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
}
}
Customer:
/**
* #ORM\Entity
*/
class Customer extends AbstractUser {
....
/**
* #var City $city
* #ORM\ManyToOne(targetEntity="City", inversedBy="customers")
*/
private $city;
....
}
City:
/**
* #ORM\Table(name="city")
* #ORM\Entity
*/
class City
{
....
/**
* #var ArrayCollection $customers
* #ORM\OneToMany(targetEntity="Customer", mappedBy="city")
*/
private $customers;
....
}
I believe the visibility on $city and $customers has to be protected, not private.
I'm sorry for disturbance. This is my fault that I turned on doctrine cache (and impressed with results btw).
Almost scratched the hole in my head while trying to understand why this is happening. Luckily I ran into another error something woith doctrine and apc. Voila! That gave me a clue where to look.

Resources