Symfony ManyToOne relationship getter returns empty object - symfony

I'll simplifly my code, I have te next:
Doctor entity:
use ...\...\Entity\Paciente;
class Doctor extends Usuario {
public function __construct() {
...
$this->pacientes = new ArrayCollection();
...
}
/**
* Número de colegiado - numColegiado
*
* #var string
*
* #ORM\Column(name="numColegiado", type="string", length=255, unique=true)
*/
protected $numColegiado;
/**
* #ORM\OneToMany(targetEntity="Paciente", mappedBy="doctor")
* #var \Doctrine\Common\Collections\ArrayCollection
*/
private $pacientes;
...
}
Paciente entity:
use \...\...\Entity\Doctor;
...
class Paciente extends Usuario {
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToOne(targetEntity="Doctor", inversedBy="pacientes")
* #ORM\JoinColumn(name="doctorNum", referencedColumnName="numColegiado", nullable=TRUE)
*
* #var type
*/
protected $doctor;
...
/**
* Set doctor
*
* #param Doctor $doctor
* #return Paciente
*/
public function setDoctor(Doctor $doctor = null)
{
$this->doctor = $doctor;
return $this;
}
/**
* Get doctor
*
* #return Doctor
*/
public function getDoctor()
{
return $this->doctor;
}
}
Ok, the matter is, when I execute that code (of course there is a relationship created and this object exists in the database):
\Doctrine\Common\Util\Debug::dump($paciente->getDoctor());
It prints that follows:
object(stdClass)#804 (28) { ["__CLASS__"]=> string(34) "Knoid\CorcheckBundle\Entity\Doctor" ["__IS_PROXY__"]=> bool(true) ["__PROXY_INITIALIZED__"]=> bool(false) ["id"]=> NULL ["numColegiado"]=> NULL ["pacientes"]=> NULL ["nombre"]=> NULL ["apellidos"]=> NULL ["dni"]=> NULL ["tipo"]=> NULL ["username"]=> NULL ["usernameCanonical"]=> NULL ["email"]=> NULL ["emailCanonical"]=> NULL ["enabled"]=> NULL ["salt"]=> NULL ["password"]=> NULL ["plainPassword"]=> NULL ["lastLogin"]=> NULL ["confirmationToken"]=> NULL ["passwordRequestedAt"]=> NULL ["groups"]=> NULL ["locked"]=> NULL ["expired"]=> NULL ["expiresAt"]=> NULL ["roles"]=> NULL ["credentialsExpired"]=> NULL ["credentialsExpireAt"]=> NULL }
As you can see, all the atributes of the "doctor" object are null, the object exists but it's empty, in my DB this object exists and it isn't empty.
Any idea of what's happening ?

This is because the proxy object is not initialised yet. One way to initialise it, is by querying the object e.g. $doctor->getId(). If you dump the object after that, you'll see that all the attributes are 'visible'

The answer of Thomas K worked for me in my own Bundle. If I translate what I did :
$myPaciente = $em->getRepository('MyBundle:Paciente')->findOneBy(array('numColegiado' => $value));
I add $myPaciente->getDoctor()->getName();
Then the initialisation was done and I could dump $myPaciente with all the information about the doctor related to it.

Related

How to access relation imageName in twig (using VichUploaderBundle)

I have an issue with {{app.user}} and Entity relation.
My user has a ManyToOne relation with an entity CustomerGroup:
**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\CustomerGroup")
* #ORM\JoinColumn(nullable=false)
*/
private $CustomerGroup;
...
My CustomerGroup Entity uses VichUploaderBundle :
/**
* #ORM\Entity(repositoryClass="App\Repository\CustomerGroupRepository")
* #Vich\Uploadable
*/
class CustomerGroup
{
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="customer_logo", fileNameProperty="imageName", size="imageSize")
*
* #var File
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255, nullable=true)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="integer", nullable=true)
*
* #var integer
*/
private $imageSize;
public function __construct(?File $imageFile = null)
{
$this->customerEntities = new ArrayCollection();
$this->models = new ArrayCollection();
$this->masterTypes = new ArrayCollection();
$this->documents = new ArrayCollection();
$this->deployModels = new ArrayCollection();
$this->imageFile = $imageFile;
if (null !== $imageFile) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->dateUpd = new \DateTimeImmutable();
}
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $imageFile
*/
public function setImageFile(?File $imageFile = null): void
{
$this->imageFile = $imageFile;
if (null !== $imageFile) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->dateUpd = new \DateTimeImmutable();
}
}
public function getImageFile(): ?File
{
return $this->imageFile;
}
public function setImageName(?string $imageName): void
{
$this->imageName = $imageName;
}
public function getImageName(): ?string
{
return $this->imageName;
}
public function setImageSize(?int $imageSize): void
{
$this->imageSize = $imageSize;
}
public function getImageSize(): ?int
{
return $this->imageSize;
}
In my Twig template, I want to access the CustomerGroup's imageName from the user. What I tried :
{{ app.user.CustomerGroup.imageName }} -> null
{{ app.user.getCustomerGroup().getImageName() }} -> null
But, if I do : `{{ app.user.CustomerGroup.name}} --> I get the correct value
When I dump {{app.user}} :
User^ {#824 ▼
-id: 1
-email: "xxxxxxxxxxxxxx"
-roles: array:1 [▶]
-password: "xxxxxxxxxxxxxxx"
-CustomerGroup: CustomerGroup^ {#809 ▼
+__isInitialized__: false
-id: 1
-name: null
-abbreviation: null
-isActive: null
-customerEntities: null
-dateAdd: null
-dateUpd: null
-createdBy: null
-modifiedBy: null
-models: null
-masterTypes: null
-documents: null
-deployModels: null
-imageFile: null
-imageName: null
-imageSize: null
…2
}
-CustomerEntity: CustomerEntity^ {#754 ▶}
-customerSites: PersistentCollection^ {#842 ▶}
-dateAdd: DateTime #1566424800 {#827 ▶}
-dateUpd: DateTime #1566579539 {#826 ▶}
-createdBy: User^ {#824}
-modifiedBy: User^ {#824}
-firstName: "xxxxx"
-lastName: "xxxxxx"
-isActive: true
-isDeleted: false
}
If I Dump app.user.CustomerGroup:
CustomerGroup^ {#809 ▼
+__isInitialized__: false
-id: 1
-name: null
-abbreviation: null
-isActive: null
-customerEntities: null
-dateAdd: null
-dateUpd: null
-createdBy: null
-modifiedBy: null
-models: null
-masterTypes: null
-documents: null
-deployModels: null
-imageFile: null
-imageName: null
-imageSize: null
…2
}
The first try only works when I'm on a controller that returns the CustomerGroup entity.
Thanks for your help
Best,
Julien
I found an ackward solution !
If I want the imageName property to be loaded, I have to load the CustomerGroup relation somewhere IN the template. This way, the entity is loaded and I can access the imageName property.
Example :
{{app.user.CustomerGroup.imageName}} ==> result null
{{app.user.CustomerGroup.name}}
{{app.user.CustomerGroup.imageName}}
Results in :
Customer1
Customer1.png
So, I call the CustomerGroup.name somewhere in the top of my twig file (in the body class for example and then I can call the imageName property.

Doctrine JoinColumn id is Null

I'm stuck with my relation.
Here's my entity :
class Orderproduct
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Product", mappedBy="orderproduct", cascade={"persist"})
*/
private $product;
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Machining", mappedBy="orderproduct", cascade={"persist"})
*/
private $machining;
And my two others entity :
class Product
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Orderproduct", inversedBy="product")
* #ORM\JoinColumn(name="orderproduct_id", referencedColumnName="id", nullable=false)
*/
private $orderproduct;
class Machining
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Orderproduct", inversedBy="machining")
* #ORM\JoinColumn(name="orderproduct_id", referencedColumnName="id", nullable=false)
*/
private $orderproduct;
And I've got this error :
SQLSTATE[23000]: Integrity constraint violation: 1048 Le champ 'orderproduct_id' ne peut être vide (null)
Here's my simple add function
public function addOrderproductAction(Request $request)
{
$orderproduct = new Orderproduct();
$formorderproduct = $this->createForm(OrderproductType::class, $orderproduct);
if($formorderproduct->handleRequest($request)->isValid())
{
$em = $this->getDoctrine()->getManager();
$em->persist($orderproduct);
$em->flush();
return $this->redirect($this->generateUrl('product_bundle_listorderproduct'));
}
return $this->render('ProductBundle:Default:neworderproduct.html.twig', array(
'formorderproduct' => $formorderproduct->createView(),
));
}
And i got this with a dump just before the flush :
Any idea ?
Thx for your help!
Edit : After put $product->getOrderproduct($this) and $machining->getOrderproduct($this).
Edit :
I'm changing my model but still have the same problem. So I have a relation between product and machining
Product
/**
* #ORM\OneToMany(targetEntity="ProductBundle\Entity\Machining", mappedBy="product", cascade={"persist"})
*/
private $machining;
Machining
/**
* #ORM\ManyToOne(targetEntity="ProductBundle\Entity\Product", inversedBy="machining")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
This is Machining table in my Db : product_id is null.
I already try to modify setProduct in Machining but it still the same.
try this:
in Orderproduct' addMachining($machining) method:
$this->machining[] = $machining;
$machining->setOrderproduct($this);
return $this;
the same for the other entity(of course different method name and properties). If this does not work try to persist all entity separately.
So I found a solution :
In the buildForm simply add
'by_reference => false'
Now it's work !

symfony2 - add value to protected object

How can I set the protected object user? After filling the form i have to add user object with current user data (for example like saving comments). I tried something like that:
if ($form->isValid()) {
$comment = $form->getData();
$comment->user = $this->contextSecurity->getToken()->getUser();
$this->model->save($comment);
}
And i've got this error
FatalErrorException: Error: Cannot access protected property AppBundle\Entity\Comment::$user in /home/AppBundle/Controller/CommentsController.php line 184
Here is my Comment entity:
class Comment
{
/**
* Id.
*
* #ORM\Id
* #ORM\Column(
* type="integer",
* nullable=false,
* options={
* "unsigned" = true
* }
* )
* #ORM\GeneratedValue(strategy="IDENTITY")
*
* #var integer $id
*/
private $id;
/**
* Content.
*
* #ORM\Column(
* name="content",
* type="string",
* length=250,
* nullable=false
* )
* #Assert\NotBlank(groups={"c-default"})
* #Assert\Length(min=3, max=250, groups={"c-default"})
*
* #var string $content
*/
private $content;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="comments")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
I'm using Symfony2.3. Any help will be appreciated.
You can't modify protected properties from outside of the object. You need a public property or a setter for that.
class Comment
{
// ...
public function setUser(User $user)
{
$this->user = $user;
}
}
And in a controller you can write:
$comment->setUser($this->getUser());
This question is not related to Symfony2, at first you should read about php types, especially about objects. read here and then here
You should understand how Visibility works. After that you will understand that access to protected/private properties of the object is only available from the object itself, so you need to create public method
setUser($user) {
$this->user = $user;
}
I always use protected, If i want edit variable or take the value, I use the getter and setter:
public function setUser($user) {
$this->user = $user;
}
public function getUser(){
return $this->user;
}

Document field (date) return null

Good day. There was such problem.
/**
* #ODM\Document
*/
class Post
{
/**
* #var string
*
* #ODM\Id
*/
private $id;
/**
* #var \DateTime
*
* #ODM\Date
*/
private $createdAt;
...
}
In controller persisted test document. At the base of enrolled:
{ "_id" : ObjectId("5603ece1147fe7322c8b4581"), "createdAt" : ISODate("2015-09-24T11:27:04Z") }
But when I make a selection from the controller, I get a null in createdAt:
Test {#531 ▼
-id: "5603ece1147fe7322c8b4581"
-createdAt: null
}
createdAt is not initialized. You can set it's value in the constructor.
public function __construct() {
$this->createdAt = new \DateTime("now");
}

symfony2 + doctrine: modify a child entity on `onFlush`: "Invalid parameter number: number of bound variables does not match number of tokens"

I have a 1:m relationship between Subitem and SubitemColor. Now I would like to save some data inside an onFlush to modify some data for SubitemColor. The problem: I get the error message below when executing the controller you can see below too:
An exception occurred while executing 'INSERT INTO SubitemColor
(code, precio, pvp_recommended, file_name, activado, en_stock, area,
lets_fix_width_or_height_in_list, lets_fix_width_or_height_in_show,
position_level_0, position_level_1, position_brand, subitem_id,
color_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)' with
params [2]:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables
does not match number of tokens
public function onFlush(Event \OnFlushEventArgs $eventArgs)
{
$em = $eventArgs->getEntityManager();
$uow = $em->getUnitOfWork();
$updates = $uow->getScheduledEntityUpdates();
$insertions = $uow->getScheduledEntityInsertions();
/////////// NEW SUBITEM_IMAGE OR SUBITEM_COLOR UPLOADED //////////
foreach ($insertions as $entity) {
if ($entity instanceof SubitemColor) {
//$entity->setLetsFixWidthOrHeightInList("jander");
//$entity->setLetsFixWidthOrHeightInList('width');
//$entity->setLetsFixWidthOrHeightInShow('width');
$entity->setEnStock(2);
$metaSubitemColor = $em->getClassMetadata(get_class($entity));
$uow->computeChangeSet($metaSubitemColor, $entity);
$uow->persist($entity);
}
}
}
//controller - controller - controller - controller
$subitem = new Subitem();
$em = $this->getDoctrine()->getManager();
$subitem->setNombre("jls");
$subitemColor = new SubitemColor();
$subitem->addSubitemColor($subitemColor);
$em->persist($subitem);
$em->persist($subitemColor);
$metaSubitem = $em->getClassMetadata(get_class($subitem));
$em->flush();
Use recomputeSingleEntityChangeSet method instead of computeChangeSet
computeChangeSet method is supposed to be called by doctrine only and calls once for every entity that marked for persistence on flush operation.
When you load entity from database doctrine saves its data to originalEntityData array, then it checks if no original data exists for entity then this entity is new and doctrine saves its current data as original and fill change set with every field value.
On second call of computeChangeSet doctrine has original data for newly created entity and computes change set only for changed fields since last call of computeChangeSet method.
Thats why you should never call computeChangeSet.
I replicated your problem as you can see in the image below.
The problem is; persist() is being used once in your controller (which you cannot do without it) and once in your onFlush() listener (which you cannot do without it as well!!!) so for that reason you get that error.
Event onFlush is called inside EntityManager#flush() after the
changes to all the managed entities and their associations have been
computed.
You're calling persist in your controller and straight after that you're calling another persist in your listener before even flushing it in your controller.
SOLUTION
Based on what you're trying to do, onFlush is not what you need anyway so the one you should use is prePersist so look at the example below.
CONTROLLER
Please checkout entity examples I added at the bottom. As you noted it is 1:N so since child SubitemColor cannot exist without parent Subitem, we're using $subitemColor->setSubitem($subitem); oppose to your example.
public function createAction()
{
$subitem = new Subitem();
$subitemColor = new SubitemColor();
$subitem->setNombre('jls');
$subitemColor->setSubitem($subitem);
$em = $this->getDoctrine()->getManager();
$em->persist($subitem);
$em->persist($subitemColor);
$em->flush();
}
YML
services:
application_backend.event_listener.subitem:
class: Application\BackendBundle\EventListener\SubitemListener
tags:
- { name: doctrine.event_listener, event: prePersist }
LISTENER
namespace Application\BackendBundle\EventListener;
use Application\BackendBundle\Entity\SubitemColor;
use Doctrine\ORM\Event\LifecycleEventArgs;
class SubitemListener
{
public function prePersist(LifecycleEventArgs $args)
{
$entity = $args->getEntity();
if ($entity instanceof SubitemColor) {
$entity->setEnStock(2);
}
}
}
RESULT
mysql> SELECT * FROM subitem;
Empty set (0.00 sec)
mysql> SELECT * FROM subitem_color;
Empty set (0.01 sec)
mysql> SELECT * FROM subitem;
+----+------+
| id | name |
+----+------+
| 1 | jls |
+----+------+
1 row in set (0.00 sec)
mysql> SELECT * FROM subitem_color;
+----+------------+------+----------+
| id | subitem_id | code | en_stock |
+----+------------+------+----------+
| 1 | 1 | NULL | 2 |
+----+------------+------+----------+
1 row in set (0.00 sec)
SUBITEM ENTITY
namespace Application\BackendBundle\Entity;
use Application\BackendBundle\Entity\SubitemColor;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="subitem")
*/
class Subitem
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="name", type="string", length=20)
*/
protected $nombre;
/**
* #ORM\OneToMany(targetEntity="SubitemColor", mappedBy="subitem", cascade={"persist", "remove"})
*/
protected $subitemColor;
/**
* Constructor
*/
public function __construct()
{
$this->subitemColor = new ArrayCollection();
}
/**
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #param string $nombre
* #return Subitem
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
return $this;
}
/**
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
/**
* #param SubitemColor $subitemColor
* #return Subitem
*/
public function addSubitemColor(SubitemColor $subitemColor)
{
$this->subitemColor[] = $subitemColor;
return $this;
}
/**
* #param SubitemColor $subitemColor
*/
public function removeSubitemColor(SubitemColor $subitemColor)
{
$this->subitemColor->removeElement($subitemColor);
}
/**
* #return Collection
*/
public function getSubitemColor()
{
return $this->subitemColor;
}
}
SUBITEMCOLOR ENTITY
namespace Application\BackendBundle\Entity;
use Application\BackendBundle\Entity\Subitem;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="subitem_color")
*/
class SubitemColor
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(name="code", type="string", length=20, nullable=true)
*/
protected $code;
/**
* #ORM\Column(name="en_stock", type="integer", length=5, nullable=true)
*/
protected $enStock;
/**
* #ORM\ManyToOne(targetEntity="Subitem", inversedBy="subitemColor")
* #ORM\JoinColumn(name="subitem_id", referencedColumnName="id", onDelete="CASCADE", nullable=false)
*/
protected $subitem;
/**
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #param string $code
* #return SubitemColor
*/
public function setCode($code)
{
$this->code = $code;
return $this;
}
/**
* #return string
*/
public function getCode()
{
return $this->code;
}
/**
* #param integer $enStock
* #return SubitemColor
*/
public function setEnStock($enStock)
{
$this->enStock = $enStock;
return $this;
}
/**
* #return integer
*/
public function getEnStock()
{
return $this->enStock;
}
/**
* #param Subitem $subitem
* #return SubitemColor
*/
public function setSubitem(Subitem $subitem)
{
$this->subitem = $subitem;
return $this;
}
/**
* #return Subitem
*/
public function getSubitem()
{
return $this->subitem;
}
}
This may or may not solve your problem, but when I do this in my code I call $uow->persist($entity); then I call $uow->computeChangeSet($metaSubitemColor, $entity);
Order seems important here as you have persisted changes that then have to be recalculated in the unit of work. So persisting after calling computeChangeSet seems likely to cause problems.

Resources