Why my methid setUserId not work?
It's my Post entity:
<?php
// src/Acme/UserBundle/Entity/User.php
namespace Acme\PostBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity
* #ORM\Table(name="posts")
*/
class Post
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #ORM\Column(type="integer", nullable=false)
*/
public $user_id;
/**
* #ORM\ManyToOne(targetEntity="Acme\PostBundle\Entity\User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank(message="Введите текст")
* )
*/
protected $text;
/**
* #ORM\Column(type="string", length=255)
*/
protected $address;
/**
*
* #ORM\Column(type="datetime")
*/
protected $date;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set user_id
*
* #param integer $userId
* #return Post
*/
public function setUserId($userId)
{
$this->user_id = $userId;
return $this;
}
/**
* Get user_id
*
* #return integer
*/
public function getUserId()
{
return $this->user_id;
}
public function getText()
{
return $this->text;
}
public function __construct() {
$this->date = new \DateTime();
}
//...
}
And my User entity:
<?php
// src/Acme/UserBundle/Entity/User.php
namespace Acme\PostBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
*
* #ORM\Column(type="string")
*/
protected $path;
/**
*
* #ORM\Column(type="string")
*/
protected $username;
/**
* #ORM\OneToMany(targetEntity="Acme\PostBundle\Entity\Post", mappedBy="users")
*/
protected $posts;
public function __construct() {
$this->posts = new ArrayCollection();
}
}
I'm saving in the database via my controller:
public function createAction(Request $request)
{
$post = new Post();
$form = $this->createFormBuilder($post)
->add('text')
->add('address')
->getForm();
$post->setUserId($this->getUser()->getId());
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
}
return $this->redirect($this->generateUrl('home'));
}
And i throw this error:
Integrity constraint violation: 1048 Column 'user_id' cannot be null
Why? My $this->getUser()->getId() is not null, i tried return new Response($this->getUser()->getId()) and get my id
You dont need user_id field because you have user relation on field:
/**
* #ORM\ManyToOne(targetEntity="Acme\PostBundle\Entity\User", inversedBy="posts")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
Look in you database, doctrine already created user_id for you.
As the others stated its not possible to set $userId on its own if you have also defined an $user property as entity using the same database column.
Instead change your setters and keep everything else as it is:
class Post
{
// ...
/**
* Set user_id
*
* #param integer $userId
* #throws \Exception
*/
public function setUserId($userId)
{
throw new \Exception('Post->userId can not be set directly');
}
/**
* Set user
*
* #param User $user
* #return Post
*/
public function setUser($user)
{
$this->user = $user;
$this->user_id = $user->getId();
return $this;
}
// ...
}
Now the $userId is automatically updated when you use setUser(...).
Throwing an exception in setUserId helps you preventing bugs in your code. Of course you could also just delete the setter, but it then would be recreated everytime you run another doctrine:generate:entities for your post entity.
Related
Thank you all for your answers from now. Thatś the question. I have a symfony 2 app with two entities (Tasks and Products). When i tried to find (findBy,findOneBy,findAll) a product it returns an empty array.
Tasks Entity
<?php
namespace pablo\UserBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Task
*
* #ORM\Table(name="tasks")
* #ORM\Entity(repositoryClass="pablo\UserBundle\Repository\TaskRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Task
{
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="tasks")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $user;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="task")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id", onDelete="CASCADE")
*/
protected $product;
public function __construct()
{
$this->tasks = new ArrayCollection();
}
/**
* Set product
*
* #param \pablo\UserBundle\Entity\Product $product
*
* #return Task
*/
public function setProduct(\pablo\UserBundle\Entity\Product $product = null)
{
$this->product = $product;
return $this;
}
/**
* Get product
*
* #return \pablo\UserBundle\Entity\Product
*/
public function getProduct()
{
return $this->product;
}
/**
* #return ArrayCollection
*/
public function getTasks()
{
return $this->tasks;
}
/**
* #param ArrayCollection $tasks
*/
public function setTasks($tasks)
{
$this->tasks = $tasks;
}
And Products Entity
<?php
namespace pablo\UserBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Products
*
* #ORM\Table(name="products")
* #ORM\Entity(repositoryClass="pablo\UserBundle\Repository\ProductsRepository")
* #UniqueEntity("productsName")
*/
class Product
{
/**
* #ORM\OneToMany(targetEntity="Task", mappedBy="product")
*/
protected $task;
/**
* #ORM\OneToMany(targetEntity="Publicaciones", mappedBy="product")
*/
protected $publicaciones;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="products_name", type="string", length=255)
* #Assert\NotBlank()
*/
private $productsName;
public function __construct()
{
$this->task = new ArrayCollection();
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set productsName
*
* #param string $productsName
*
* #return Product
*/
public function setProductsName($productsName)
{
$this->productsName = $productsName;
return $this;
}
/**
* Get productsName
*
* #return string
*/
public function getProductsName()
{
return $this->productsName;
}
public function __toString() {
return $this->productsName;
}
/**
* Get task
*
* #return \Doctrine\Common\Collections\ArrayCollection
*/
public function getTask()
{
return $this->task;
}
/**
* Set task
*
* #param \Doctrine\Common\Collections\ArrayCollection $typeSponsor
*
* #return Task
*/
public function setTask($task)
{
$this->task = $task;
}
/**
* #return mixed
*/
public function getPublicaciones()
{
return $this->publicaciones;
}
/**
* #param mixed $publicaciones
*/
public function setPublicaciones($publicaciones)
{
$this->publicaciones = $publicaciones;
}
}
Now, when i tried to find a product from controller it returns an empty array ({}). I can't see what is wrong with this.
$productId = '18';
$product = $this->get('doctrine.orm.default_entity_manager')->getRepository('pabloUserBundle:Product')->find($productId);
Actually you have a result, it just is an empty object because you have not defined which of the properties should be printed.
The best solution is for your entity to implement JsonSerializable.
class Product implements \JsonSerializable
{
...
public function jsonSerialize()
{
return [
"id"=> $this->getId(),
"name" => $this->getProductsName()
];
}
Now it knows what it should print when converting the class to json object.
If you want the task collection also, implement JsonSerializable for the Task Entity and add in Product Entity:
public function jsonSerialize()
{
return [
"id"=> $this->getId(),
"name" => $this->getProductsName(),
"task" => $this->getTask()->toArray()
];
}
The JsonSerializable interface
I use Symfony 3.2. I have two related entities:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
* #ORM\Table(name="user", options={"collate"="utf8mb4_unicode_ci", "charset"="utf8mb4"}, indexes={ #Index(name="idx_email", columns={"email"}) })
* #UniqueEntity(fields={"email"}, message="Пользователь с данным email'ом существует.", groups={"Registration"})
* #ORM\HasLifecycleCallbacks()
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\VisitedPage", mappedBy="user")
*/
private $visitedPages;
public function __construct()
{
$this->visitedPages = new ArrayCollection();
}
public function addVisitedPage(VisitedPage $visitedPage)
{
$this->visitedPages[] = $visitedPage;
return $this;
}
/**
* Remove visitedPage
*
* #param \AppBundle\Entity\VisitedPage $visitedPage
*/
public function removeVisitedPage(\AppBundle\Entity\VisitedPage $visitedPage)
{
$this->visitedPages->removeElement($visitedPage);
}
/**
* Get visitedPages
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getVisitedPages()
{
return $this->visitedPages;
}
}
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* VisitedPage
*
* #ORM\Table(name="visited_page")
* #ORM\Entity(repositoryClass="AppBundle\Repository\VisitedPageRepository")
*/
class VisitedPage
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var User
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="visitedPages")
*/
private $user;
/**
* #var string
* #ORM\Column(name="page", type="text")
*/
private $page;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set user
*
* #param \AppBundle\Entity\User $user
*
* #return VisitedPage
*/
public function setUser(\AppBundle\Entity\User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \AppBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set page
*
* #param string $page
*
* #return VisitedPage
*/
public function setPage($page)
{
$this->page = $page;
return $this;
}
/**
* Get page
*
* #return string
*/
public function getPage()
{
return $this->page;
}
}
When i try call VisitedPage::getUser() (i mean $visitedPage->getUser()) i get object type of user.
But when i call method User::getVisitedPages() i get null.
Method User::addVisitedPage($page) works and relations are saved.
How can i decide this problem?
//Some text to pass validation
When you add Visited Page from user try this:
class User
{
//......
public function addVisitedPage(VisitedPage $visitedPage)
{
$visitedPage->setUser($this);
$this->visitedPages->add($visitedPage);
return $this;
}
}
also I think you will need to add cascade={"persist"} to your private $visitedPages; inside of User object:
/**
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\VisitedPage", mappedBy="user", cascade={"persist"})
*/
private $visitedPages;
I fixed the issue by running this command - bin/console doctrine:cache:clear-metadata
You should run
php app/console cache:clear --env=prod
or
php app/console cache:clear --env=dev
depending upon env you are working
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'm trying to setup a one-to-many database, using Doctrine. One User, can have multiple Roles, and when I get the User, I want to get the associated Roles. I have a raw MySQL query that does that.
SELECT
u.*,
GROUP_CONCAT(r.role) as roles
FROM
users u
INNER JOIN user_roles ur ON ur.user_id = u.id
INNER JOIN roles r ON r.id = ur.role_id
GROUP BY id;
+----+----------+------------------------+--------------------------------------------------------------+-----------+----------------------+
| id | username | email | password | is_active | GROUP_CONCAT(r.role) |
+----+----------+------------------------+--------------------------------------------------------------+-----------+----------------------+
| 1 | admin | my#email.address | $2a$08$jHZj/wJfcVKlIwr5AvR78euJxYK7Ku5kURNhNx.7.CSIJ3Pq6LEPC | 1 | ROLE_USER,ROLE_ADMIN |
+----+----------+------------------------+--------------------------------------------------------------+-----------+----------------------+
Is this something I can actually automatically replicate in Doctrine? And if so, where abouts am I going wrong? Here are my current (broken) classes (apologies for the code dump).
The error this is currently logging is:
[2016-12-20 12:01:35] security.INFO: Authentication request failed. {"exception":"[object] (Symfony\\Component\\Security\\Core\\Exception\\Auth
enticationServiceException(code: 0): Notice: Undefined index: user at /var/www/vendor/symfony/symfony/src/Symfony/Component/Security/Core/Authe
ntication/Provider/DaoAuthenticationProvider.php:94, Symfony\\Component\\Debug\\Exception\\ContextErrorException(code: 0): Notice: Undefined in
dex: user at /var/www/vendor/doctrine/orm/lib/Doctrine/ORM/Persisters/Entity/BasicEntityPersister.php:1768)"} []
User.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface, \Serializable
{
...
/**
* #var \Doctrine\Common\Collections\ArrayCollection
*
* #ORM\OneToMany(targetEntity="UserRoles", mappedBy="user", fetch="EAGER")
*/
private $roles;
public function __construct()
{
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
$this->roles = new ArrayCollection();
}
...
public function getRoles()
{
return $this->roles;
}
/**
* Add role
*
* #param \AppBundle\Entity\UserRoles $role
*
* #return User
*/
public function addRole(\AppBundle\Entity\UserRoles $role)
{
$this->roles[] = $role;
return $this;
}
/**
* Remove role
*
* #param \AppBundle\Entity\UserRoles $role
*/
public function removeRole(\AppBundle\Entity\UserRoles $role)
{
$this->roles->removeElement($role);
}
}
Role.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Role
*
* #ORM\Table(name="roles")
* #ORM\Entity(repositoryClass="AppBundle\Repository\RoleRepository")
*/
class Role
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="role", type="string", length=255, unique=true)
*/
private $role;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set role
*
* #param string $role
*
* #return Role
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return string
*/
public function getRole()
{
return $this->role;
}
}
UserRoles.php
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* UserRoles
*
* #ORM\Table(name="user_roles")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRolesRepository")
*/
class UserRoles
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="userroles")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $userId;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Role")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id")
*/
private $roleId;
/**
* Set userId
*
* #param integer $userId
*
* #return UserRoles
*/
public function setUserId($userId)
{
$this->userId = $userId;
return $this;
}
/**
* Get userId
*
* #return int
*/
public function getUserId()
{
return $this->userId;
}
/**
* Set roleId
*
* #param integer $roleId
*
* #return UserRoles
*/
public function setRoleId($roleId)
{
$this->roleId = $roleId;
return $this;
}
/**
* Get roleId
*
* #return int
*/
public function getRoleId()
{
return $this->roleId;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
In the $roles association inside your User entity you set the mappedBy attribute to user, but your UserRoles entity does not have such a field. Instead it has a field called userId.
Doctrine tries to find the association using your mapping. The user value from your #mappedBy attribute cannot be found in the database, hence the error message:
Undefined index...
Since you are mapping objects you should reconsider the naming convention inside your UserRoles entity. So don't use $userId and $roleId but simply use User and Role and also set dependency injection and return values inside your setters and getters to match User and Role classes.
Since UserRoles is an entity (and not a collection) I would also suggest renaming it to UserRole (singular). It will help you understand what you have.
Start with changing your UserRoles like this:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use AppBundle\Entity\User;
use AppBundle\Entity\Role;
/**
* UserRole
*
* #ORM\Table(name="user_roles")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRoleRepository")
*/
class UserRole
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var User
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\User", inversedBy="userRoles")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private $user;
/**
* #var Role
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Role")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id")
*/
private $role;
/**
* Set user
*
* #param User $user
* #return UserRole
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return User
*/
public function getUser()
{
return $this->user;
}
/**
* Set role
*
* #param Role $role
* #return UserRole
*/
public function setRole(Role $role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return Role
*/
public function getRole()
{
return $this->role;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
}
And change your User entity to match these changes accordingly:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\UserRole;
use Serializable;
/**
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface, Serializable
{
...
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="UserRole", mappedBy="user", fetch="EAGER")
*/
private $userRoles;
public function __construct()
{
$this->isActive = true;
// may not be needed, see section on salt below
// $this->salt = md5(uniqid(null, true));
$this->userRoles = new ArrayCollection();
}
...
/**
* #return Collection
*/
public function getUserRoles()
{
return $this->userRoles;
}
/**
* Add user role
*
* #param UserRole $userRole
*
* #return User
*/
public function addUserRole(UserRole $userRole)
{
$this->userRoles[] = $userRole;
return $this;
}
/**
* Remove role
*
* #param UserRole $userRole
*/
public function removeUserRole(UserRole $userRole)
{
$this->userRoles->removeElement($userRole);
}
}
Since your the repository for your User class is called UserRepository I also renamed UserRolesRepository to UserRoleRepository (singular).
I'm having an issue with something I think is probably fairly basic but the cause is escaping me.
I have a two entities, an Organisation and a Subscription. An organisation is linked with one subscription.
I want to be able to edit this via a form (creating via form already works). When I do the following:
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->findOneBy(array(
'id' => $id
));
$form = $this->createForm(new OrganisationType(), $organisation, array(
'method' => 'POST'
));
$form->submit($paramFetcher->all());
I get an exception because the subscription property of the organisation entity is not set (despite passing one which has a subscription into the form defaults). The exception is as follows:
Expected argument of type "AppBundle\Entity\Subscription", "NULL" given
This is obviously being thrown by the setSubscription method on the Organisation entity but I'm not sure why as the form should be converting the int value passed to a Subscription entity automatically.
Am I doing something stupid here? I have attached the relevant code below. Thank you!
The simplest controller action that replicates this issue
public function testAction(Request $request)
{
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->findOneBy(array('id' => 7));
$form = $this->createForm(new OrganisationType(), $organisation);
$form->submit($request->request->all());
return $this->render('test.html.twig', array(
'testform' => $form->createView()
));
}
Organisation.php
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Organisation
*
* #ORM\Table(name="organisation")
* #ORM\Entity(repositoryClass="AppBundle\Repository\OrganisationRepository")
*/
class Organisation
{
const ENABLED = 1;
const DISABLED = 2;
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"default", "list-organisation"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #JMS\Groups({"default", "list-organisation"})
*/
private $name;
/**
* #var Subscription|null The subscription this organisation is currently linked to
* #ORM\ManyToOne(targetEntity="Subscription", inversedBy="organisations")
* #ORM\JoinColumn(name="subscription_id", referencedColumnName="id", onDelete="set null")
* #JMS\Groups({"default", "list-organisation"})
*/
private $subscription;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="User", mappedBy="organisation")
* #JMS\Groups({})
*/
private $users;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Subgroup", mappedBy="organisation")
* #JMS\Groups({"default"})
*/
private $subgroups;
/**
* #var string $enabled
*
* #ORM\Column(type="string")
*/
private $enabled = self::ENABLED;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default", "list-organisation"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default", "list-organisation"})
*/
private $updated;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Organisation
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Sets subscription
*
* #param Subscription $subscription
* #return $this
*/
public function setSubscription(Subscription $subscription)
{
$this->subscription = $subscription;
return $this;
}
/**
* Gets subscription
*
* #return Subscription|null
*/
public function getSubscription()
{
return $this->subscription;
}
/**
* Sets enabled
*
* #param $enabled
*/
public function setEnabled($enabled)
{
if (!in_array($enabled, array(self::ENABLED, self::DISABLED))) {
throw new \InvalidArgumentException('Invalid enabled status');
}
$this->enabled = $enabled;
}
/**
* Gets enabled
*
* #return string
*/
public function getEnabled()
{
return $this->enabled;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns Users belonging to this organisation
*
* #return Collection
*/
public function getUsers()
{
return $this->users;
}
public function __toString()
{
return $this->getName();
}
}
Subscription.php
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Organisation
*
* #ORM\Table(name="subscription")
* #ORM\Entity(repositoryClass="AppBundle\Repository\SubscriptionRepository")
*/
class Subscription
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #JMS\Groups({"default", "list-organisation"})
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
* #JMS\Groups({"default", "list-organisation"})
*/
private $name;
/**
* #var Collection
* #ORM\OneToMany(targetEntity="Organisation", mappedBy="subscription")
* #JMS\Groups({"default"})
*/
private $organisations;
/**
* #var datetime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default"})
*/
private $created;
/**
* #var datetime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
* #JMS\Groups({"default"})
*/
private $updated;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
* #return Subscription
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Returns Organisations with this subscription type
*
* #return Collection
*/
public function getOrganisations()
{
return $this->organisations;
}
/**
* #return string
*/
public function __toString()
{
return $this->getName();
}
}
OrganisationType.php
<?php
namespace AppBundle\Form;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class OrganisationType extends AbstractType
{
private $manager;
public function __construct(ObjectManager $objectManager)
{
$this->manager = $objectManager;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('subscription', EntityType::class, array(
'class' => 'AppBundle\Entity\Subscription'
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Organisation',
'csrf_protection' => false
));
}
}
On a fresh Symfony 3.1, this works like a charm (and this is the recommended way to handle form submission, as stated in a previous comment) :
public function testAction(Request $request)
{
$organisation = $this->getDoctrine()->getRepository('AppBundle\Entity\Organisation')
->find(1);
$form = $this->createForm(OrganisationType::class, $organisation);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->persist($organisation);
$this->getDoctrine()->getManager()->flush();
die('saved into database.');
}
return $this->render('test.html.twig', array(
'testform' => $form->createView()
));
}