Symfony2/Doctrine2 One-To-Many same object twice - doctrine

My current Client-Entity has an unloading and a loading Area, which are both ClientArea-Entities.
namespace ACME\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sorien\DataGridBundle\Grid\Mapping as GRID;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ACME\DemoBundle\Entity\Client
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="ACME\DemoBundle\Entity\ClientRepository")
*/
class Client
{
/**
* #ORM\OneToMany(targetEntity="ClientArea",mappedBy="client", cascade={"persist", "remove"})
*/
public $unloading_areas;
/**
* #ORM\OneToMany(targetEntity="ClientArea",mappedBy="client", cascade={"persist", "remove"})
*/
public $loading_areas;
}
class ClientArea
{
/**
* #ORM\ManyToOne(targetEntity="Client")
*/
public $client;
}
This does not work because client can only map 1 association.
How can i map the relation properly?

To create entity relations you need to have keys to use when joining tables. Your Client class should have an id key defined and you need to initialize collections, like this:
class Client
{
//....
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="ClientArea", mappedBy="client", cascade={"persist", "remove"})
*/
public $unloading_areas;
/**
* #ORM\OneToMany(targetEntity="ClientArea", mappedBy="client", cascade={"persist", "remove"})
*/
public $loading_areas;
public function __construct() {
// Initialize collections
$this->unloading_areas = new \Doctrine\Common\Collections\ArrayCollection();
$this->loading_areas = new \Doctrine\Common\Collections\ArrayCollection();
}
// ....
}
Your ClientArea class should then look something like this:
class ClientArea
{
// ....
/**
* #ORM\Column(name="client_id", type="int", nullable=false)
*/
private $clientId;
/**
* #ORM\ManyToOne(targetEntity="Client")
* #JoinColumn(name="client_id", referencedColumnName="id")
*/
public $client;
// ....
}
Now, those two entities should be mapped correctly.
To learn more about association mappings in Doctrine, read article here: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html
Hope this helps.

Related

Three level inheritance in doctrine2 not loading second level data

I have a inheritance of three levels using class table inheritance like this:
Class Test
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Test
* #package App\Entities\Test
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"TestA" = "TestA", "TestB" = "TestB"})
* #ORM\Table(name="test")
*/
abstract class Test {
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string")
*/
protected $columnTest;
}
Class TestM extends Test
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class TestM
* #package App\Entities\Test
* #ORM\Entity
*/
abstract class TestM extends Test{
/**
* #var string
* #ORM\Column(type="string")
*/
protected $columnTestM;
}
Class TestA extends TestM
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class TestA
* #package App\Entities\Test
* #ORM\Entity
*/
class TestA extends TestM{
/**
* #var string
* #ORM\Column(type="string")
*/
private $columnTestA;
public function __construct(string $columnTest, string $columnTestM, string $columnTestA) {
$this->columnTest = $columnTest;
$this->columnTestM = $columnTestM;
$this->columnTestA = $columnTestA;
}
/**
* #return string
*/
public function getColumnTest(): string {
return $this->columnTest;
}
/**
* #param string $columnTest
*/
public function setColumnTest(string $columnTest): void {
$this->columnTest = $columnTest;
}
/**
* #return string
*/
public function getColumnTestM(): string {
return $this->columnTestM;
}
/**
* #param string $columnTestM
*/
public function setColumnTestM(string $columnTestM): void {
$this->columnTestM = $columnTestM;
}
/**
* #return string
*/
public function getColumnTestA(): string {
return $this->columnTestA;
}
/**
* #param string $columnTestA
*/
public function setColumnTestA(string $columnTestA): void {
$this->columnTestA = $columnTestA;
}
}
I'm having problem because, when I'm going to retrieve the entity from my DB, it comes with the second level with no data, only the first and last levels comes with all the data. Notice that columnTestM is blank. What am I missing? It's persisting with all three levels with data, the problem is only when I have to get it. As an example I put the column's content with it's own name
>>> print_r(\EntityManager::getRepository('App\Entities\Test\Test')->find(1));
App\Entities\Test\TestA Object
(
[columnTestA:App\Entities\Test\TestA:private] => columnTestA
[columnTestM:protected] =>
[id:protected] => 1
[columnTest:protected] => columnTest
)
EDIT:
Maybe it's a bug and I filed an issue at GitHub about a wrong query generation. I put my MariaDB to log all queries to check the query being generated when I try to retrieve the data back and that's the result:
SELECT
t0.id AS id_3,
t0.column_test AS column_test_4,
t0.discr,
t1.column_test_a AS column_test_a_5,
t2.column_test_b AS column_test_b_6
FROM
test t0
LEFT JOIN
test_as t1 ON t0.id = t1.id
LEFT JOIN
test_bs t2 ON t0.id = t2.id
WHERE
t0.id = 1
It tries to left join with TestB instead of doing the join with TestM
As stated by #LBA the problem is two abstract classes in the chain. It should work even with two abstract classes, but only the first is retrieved, but all the others in the middle stays blank. Issue already reported in doctrine's github project.

Doctrine in laravel, getting all user's roles

I have a simple project in laravel 5.4 with Doctrine 2.0. I have three tables: users, roles, user_roles. Screenshot from tables schemas from HEIDISQL I show below:
users:
roles:
user_roles:
I have three Entities classes for each table of course:
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="users")
*/
class User
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*
*/
private $id;
/**
*
* #ORM\Column(type="string")
*
*/
private $name;
/**
*
* #ORM\Column(type="string")
*
*/
private $email;
/**
* #ORM\OneToMany(targetEntity="Task", mappedBy="user", cascade={"persist"})
* #var ArrayCollection|Task[]
*/
private $tasks;
/**
* #ORM\OneToMany(targetEntity="UserRole", mappedBy="user")
*/
protected $user_roles;
/**
* User constructor
* #param #name
* #param #email
* #param $password
*/
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
$this->tasks = new ArrayCollection();
$this->user_roles = new ArrayCollection();
}
/**
*
* #return mixed
*
*
*/
public function getName()
{
return $this->name;
}
/**
* #return mixed
*/
public function getEmail()
{
return $this->email;
}
/**
* #return mixed
*/
public function getTasks()
{
return $this->tasks;
}
public function addTask(Task $task)
{
if(!$this->tasks->contains($task)) {
$task->setUser($this);
$this->tasks->add($task);
}
}
public function getId(){
return $this->id;
}
public function getUserRoles(){
return $this->user_roles;
}
}
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="roles")
*
*/
class Role{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string")
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="description", type="text")
*/
private $description;
/**
* #ORM\OneToMany(targetEntity="UserRole", mappedBy="role",cascade={"persist"})
*/
protected $user_roles;
public function getId(){
return $this->id;
}
public function getName(){
return $this->name;
}
public function getDescription(){
return $this->description;
}
}
<?php
namespace TodoList\Http\Entities;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="user_roles")
*/
class UserRole
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ORM\ManyToOne(targetEntity="User", inversedBy="user_roles")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*
*/
private $user;
/**
*
* #ORM\ManyToOne(targetEntity="Role", inversedBy="user_roles")
* #ORM\JoinColumn(name="role_id", referencedColumnName="id")
*/
private $role;
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return \NVC\UserBundle\Entity\User
*/
public function getUser()
{
return $this->user;
}
/**
* Set recipe
*
* #param \NVC\RecipeBundle\Entity\Recipe $recipe
* #return UserRecipeAssociation
*/
public function setRole(Role $role)
{
$this->role = $role;
return $this;
}
/**
* Get recipe
*
* #return \NVC\RecipeBundle\Entity\Recipe
*/
public function getRole()
{
return $this->role;
}
}
I have a problem when I'm trying retrieve roles of particular user by doctrine Entity Managar. I'm trying to do that in that way:
public function showUserRoles(EntityManagerInterface $em){
$userRoles = $em->getRepository('\TodoList\Http\Entities\UserRole');
$userRoles->findBy(['user' => 2]);
//count($userRole);
}
There is an error from method findBy
Error message is:
(1/1) FatalErrorException
Doctrine\Common\Proxy\AbstractProxyFactory::getProxyDefinition():
Failed opening required
'C:\xampp\htdocs\Project\storage\proxies__CG__TodoListHttpEntitiesRole.php'
(include_path='C:\xampp\php\PEAR')
Is anything wrong with my Entities? I don't know how could I solve that problem. Could someone help me with that?
I would be very greateful
Best regards ;)
You need to set folder C:\xampp\htdocs\Project\storage\proxies\ as writable and then you need to generate proxies by console command:
doctrine:generate:proxies

How to get all property for a custom entity in FOSUserBundle?

I am using FOSUserBundle
I created a User entity that extents baseuser, and I added a protected property I called $apiKey. The registration form works fine.
then, I created a userController that extends controller, and in a methoid to edit my user I have:
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('AppBundle:User')->find($id);
my $user has the apiKey property but this is empty (of course the field in the DB is not).
any idea?
thanks
UPDATE: user entity
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="AppBundle\Entity\Group")
* #ORM\JoinTable(name="fos_user_user_group",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="group_id", referencedColumnName="id")}
* )
*/
protected $groups;
/**
* #ORM\Column(type="string", unique=true, nullable=true)
*/
protected $apiKey;
/* public function __construct()
{
parent::__construct();
// your own logic
//$this->roles = array('ROLE_USER'); //default role for new users
}*/
public function __construct()
{
parent::__construct();
// your own logic
$this->groups = new ArrayCollection();
}
/**
* #return mixed
*/
public function getApiKey()
{
return $this->apiKey;
}
/**
* #param mixed $apiKey
*/
public function setApiKey($apiKey)
{
$this->apiKey = $apiKey;
}
/**
* #return mixed
*/
public function getGroups()
{
return $this->groups;
}
/**
* #param mixed $groups
*/
public function setGroups($groups)
{
$this->groups = $groups;
}
}

Doctrine generate Models. Metadata Configuration

I generated Entities with Doctrine's generating method inside Doctrine.php from CodeIgniter Controller:
function generate_classes(){
$this->em->getConfiguration()
->setMetadataDriverImpl(
new DatabaseDriver(
$this->em->getConnection()->getSchemaManager()
)
);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($this->em);
$metadata = $cmf->getAllMetadata();
$generator = new EntityGenerator();
$generator->setUpdateEntityIfExists(true);
$generator->setGenerateStubMethods(true);
$generator->setGenerateAnnotations(true);
$generator->generate($metadata, APPPATH."models/Entities");
}
The generated Entity had this syntax:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entityz
*/
class Users
{
/**
* #var integer
*
* #ORM\Column(name="idusers", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idusers;
/**
* #var string
*
* #ORM\Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
/**
* #var string
*
* #ORM\Column(name="last_name", type="string", length=45, nullable=true)
*/
Now calling these Entities form my model didn't work. The model didn't find the entity. After manually changing the syntax to the following code example, I got it to work:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #Entity #Table(name="users")
*/
class Users
{
/**
* #var integer
*
* #Column(name="idusers", type="integer", nullable=false)
* #Id
*
*/
private $idusers;
/**
*
* #GeneratedValue
* #Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
And now everything works!
I am not going to manually change every Entity.
How can I configure Doctrine to produce Entity's with the correct syntax. As used in their tutorials as well? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
Noob question. Please help!
Found the solution:
changed in application/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php the constructor from:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = 'ORM\\';
}
}
To:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = '';
}
}

doctrine 2, how do get data from the inverse side (many to one)

I have two entities, entry and comments.
comments:
/**
* #Entity(repositoryClass="\Entities\Blog\CommentRepository")
* #Table(name="blog_comment")
* #HasLifecycleCallbacks
*/
class Comment extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="id", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ManyToOne(targetEntity="Entry", inversedBy="comments")
* #JoinColumn(name="entry_id", referencedColumnName="id")
*/
protected $entry;
/** #Column(name="approved", type="string", length=255) */
protected $approved;
/** #Column(name="title", type="string", length=255) */
protected $title;
/** #Column(name="content", type="text") */
protected $content;
/** #Column(name="pub_date", type="datetime") */
protected $pub_date;
/** #Column(type="datetime") */
private $created_at;
/** #Column(type="datetime") */
private $updated_at;
/** #PreUpdate */
public function updated()
{
$this->updated_at = new \DateTime("now");
}
public function __construct()
{
$this->created_at = $this->updated_at = new \DateTime("now");
}
}
class CommentRepository extends \Entities\PaginatedRepository
{
protected $_entityClassName = 'Entities\Blog\Comment';
}
and entry:
<?php
namespace Entities\Blog;
/**
* #Entity(repositoryClass="\Entities\Blog\EntryRepository")
* #Table(name="blog_entry")
* #HasLifecycleCallbacks
*/
class Entry extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="id", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #Column(name="permalink", type="string", length=255) */
protected $permalink;
/** #Column(name="title", type="string", length=255) */
protected $title;
/** #Column(name="pub_date", type="datetime") */
protected $pub_date;
/** #Column(name="content", type="text") */
protected $content;
/** #OneToMany(targetEntity="Comment", mappedBy="entry") */
protected $comments;
/** #Column(type="datetime") */
private $created_at;
/** #Column(type="datetime") */
private $updated_at;
/** #PreUpdate */
public function updated()
{
$this->updated_at = new \DateTime("now");
}
public function __construct()
{
$this->comments = new \Doctrine\Common\Collections\ArrayCollection();
}
I can get the collection of all comments belonging to each entry via:
foreach ($comments as $comment){
$comment-$commentId;
}
but how can I get the entry information from the comments side. for example, I would like to get the entry id from a specific comment
Each time you create a #OneToMany relation, you create a Collection of proxy objects in class on "One"-side of relation, and single proxy object in class on "Many"-side of relation. Proxy classes are automatically generated by Doctrine2 from your mapping information.
To allow Doctrine2 filling proxy object with real data from DB it's important to declare it protected or private. I'm not sure about that, but seems like Doctrine tracks down any requests to proxy objects inside your entity class and ensures that proxies are populated before first usage.
To access the associated object you have to define accessor function in your Comment class:
class Comment extends \Entities\AbstractEntity{
/** other definitions */
function getEntity(){
return $this->entity;
}
}
And use it like
$comment = $em->find("Entities\Comment",1);
$entity = $comment->getEntity();
Doctrine2 will automatically populate $comment->entity proxy with actual Entity object.
See "Workin with Objects" chapter of Doctrine documentation and "Can you explain me what is a Proxy in Doctrine 2?" on details of proxies.

Resources