Symfony2 Relationship returns null, should return object - php

I have run into a problem that defies all my attempts to unproblem it. Its probably simple, I have just totally exhausted myself on this one :)
Basically I want to give the user the ability to add modules (facebook id, a bio, text inputs) and assets (image logo, pdf, et al) to a page object.
I have set up a onetomany relationship for Module and Asset to Page.
Module works as expected, however Asset will not work at all: when PageController calls getAsset() from its entity, it is null. There is no error until I try to iterate over the Assets.
Also, in PageController there are the following namespace declarations:
use Linkme\SiteBundle\Entity\Module;
use Linkme\SiteBundle\Entity\Asset;
If I remove the Module declaration I get an error, but if I remove the Asset line, nothing changes. Therefore I believe the relationship is not created.
If I run
app/console doctrine:schema:create --dump-sql
then amongst others I get the following line:
ALTER TABLE Asset ADD CONSTRAINT FK_C36E75589D3B65E3 FOREIGN KEY (pageId) REFERENCES Page(id);
which makes me think the schema is correct. It as least understands Asset is related to Page
Im starting to feel I have a typo or I am totally missing something equally as obvious - any assistance on troubleshooting or other suggestions would be much appreciated!
app/console --version
Symfony version 2.0.1 - app/dev/debug
Page.php
/*
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
/**
* #ORM\OneToMany(targetEntity="Module", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $modules
*/
protected $modules;
/**
* Set assets
* #param Asset $assets
*/
public function setAssets(Asset $assets = null)
{
$this->assets = $assets;
}
/**
* Get assets
*
* #return Asset
*/
public function getAssets()
{
echo '<br />Asset is '.gettype($this->assets); // outut: Asset is NULL
return $this->assets;
}
/**
* Set modules
* #param Module $modules
*/
public function setModules(Module $modules = null)
{
$this->modules = $modules;
}
/**
* Get modules
* #return Module
*/
public function getModules()
{
echo '<br />Module is '.gettype($this->assets); // output: Module is object
return $this->modules;
}
Asset.php
/**
* #var integer $pageId
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="assets")
* #ORM\JoinColumn(name="pageId", referencedColumnName="id")
*/
protected $pageId;
Module.php
/**
* #var integer $pageId
*
* #ORM\ManyToOne(targetEntity="Page", inversedBy="modules")
* #ORM\JoinColumn(name="pageId", referencedColumnName="id")
*/
protected $pageId;
PageController.php
use Linkme\SiteBundle\Entity\Module;
use Linkme\SiteBundle\Entity\Asset;
/**
* Add modules and assets to a page
*
* #Route("/{id}/wizardmodule", name="page_wizardmodule")
* #Template()
*/
public function wizardmoduleAction($id)
{
$em = $this->getDoctrine()->getEntityManager();
$page = $em->getRepository('LinkmeSiteBundle:Page')->find($id);
$modules = $page->getModules();
$assets = $page->getAssets();
depmod
[symfony]
git=http://github.com/symfony/symfony.git
version=v2.0.1
[twig]
git=http://github.com/fabpot/Twig.git
version=v1.1.2
[monolog]
git=http://github.com/Seldaek/monolog.git
version=1.0.1
[doctrine-common]
git=http://github.com/doctrine/common.git
version=2.1.1
[doctrine-dbal]
git=http://github.com/doctrine/dbal.git
version=2.1.1
[doctrine]
git=http://github.com/doctrine/doctrine2.git
version=2.1.1
[swiftmailer]
git=http://github.com/swiftmailer/swiftmailer.git
version=v4.1.1
[assetic]
git=http://github.com/kriswallsmith/assetic.git
version=v1.0.1
[twig-extensions]
git=http://github.com/fabpot/Twig-extensions.git
[metadata]
git=http://github.com/schmittjoh/metadata.git
version=1.0.0
[SensioFrameworkExtraBundle]
git=http://github.com/sensio/SensioFrameworkExtraBundle.git
target=/bundles/Sensio/Bundle/FrameworkExtraBundle
version=v2.0.1
[JMSSecurityExtraBundle]
git=http://github.com/schmittjoh/JMSSecurityExtraBundle.git
target=/bundles/JMS/SecurityExtraBundle
version=v2.0.1
[SensioDistributionBundle]
git=http://github.com/sensio/SensioDistributionBundle.git
target=/bundles/Sensio/Bundle/DistributionBundle
version=v2.0.1
[SensioGeneratorBundle]
git=http://github.com/sensio/SensioGeneratorBundle.git
target=/bundles/Sensio/Bundle/GeneratorBundle
version=v2.0.1
[AsseticBundle]
git=http://github.com/symfony/AsseticBundle.git
target=/bundles/Symfony/Bundle/AsseticBundle
version=v1.0.0

I got it! and as I predicted, it was an incredibly annoying PEBKAC....
The relationship was not being created because the annotations were not being read, because I was missing a * on the annotations comment box..... ddddoooohhhhh!
Page.php
Before:
/* <========================== there is only one * here. It needs to be two: **
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
After:
/** <========================== like this.
* #ORM\OneToMany(targetEntity="Asset", mappedBy="pageId", cascade={"persist", "remove"})
* #ORM\OrderBy({"type" = "ASC"})
* #ORM\OrderBy({"id" = "ASC"})
*
* #var ArrayCollection $assets
*/
protected $assets;
I'd just like to say a big thanks to everyone who helped out with this problem.

Check this:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional
Do you correctly initialize collections into your page constructor?

This is because you don't initialize your $assets collection in your Page constructor.
public function __construct(){
$this->assets = new ArrayCollection();
}
Then I think you haven't run the doctrine:generate:entities command, it simplifies your life a little, creating get, set and add methods for every mapped field. In your case it would create a
public function addModules(Module $modules)
{
$this->modules[] = $modules;
}
Note that actually you simply assign $modules to $this->modules, this is wrong, it's an array not a scalar.
And to add the reference to the page referred by every module you'll have to add another instruction:
public function addModules(Module $modules)
{
$this->modules[] = $modules;
$modules->setPage($this);
}
I've done this in my code and it works, let me know if it works for you too, bye
Linuxatico

Related

Add extra option to LoggableListener

I use Loggable to backup changes in Entities.
The default AbstractLogEntry does not have enough columns for my needs.
Thats why i extended the class and added extra getters and setters.
See the code below
/**
* EmployeeBackup
*
* #ORM\Table(name="employee_backup")
* #ORM\Entity(repositoryClass="Gedmo\Loggable\Entity\Repository\LogEntryRepository")
*
*/
class EmployeeBackup extends AbstractLogEntry
{
/**
* #var int
*
* #ORM\Column(name="division_id", type="integer", unique=true)
*/
private $divisionId;
/**
* #return int
*/
public function getDivisionId(): int
{
return $this->divisionId;
}
/**
* #param string $divisionId
*/
public function setDivisionId(string $divisionId): void
{
$this->divisionId = $divisionId;
}
}
The extension is using the class above. So it works.
But now i need to set the divisionId when a new version is stored.
I tried the code below
$loggable = new LoggableListener();
$loggable->setDivision($division);
$evm->addEventSubscriber($loggable);
And this is what i get:
Attempted to call an undefined method named "setDivision" of class "Gedmo\Loggable\LoggableListener".
And thats true because LoggableListener does not have a setDivision function. My question is: Do i need to override the listener and if so, how do i do that?
Thanks ;)

Failed to set up a ManyToMany

I want to be able to select a school (that has its own entity) while creating a mission (also has its entity)
Since a school can have several missions, and you can select several schools at the mission's creation, I used a ManyToMany.
The problem is that after creating this "ManyToMany", generating the entities and updating my schema, Symfony created a table, but left it totally empty, without the two columns that I asked for. I'm not really used to Symfony nor to the ManyToMany system, so I might have done some mistake without noticing it, still I find this weird.
Here's the interesting part of my ecole (school) entity:
class Ecole{
// ...
/**
* #ORM\ManyToMany(targetEntity="MissionBundle\Entity\Mission", mappedBy="ecolesDispo")
*/
protected $missionsDispos;
// ...
/**
* Add missionsDispo
*
* #param \MissionBundle\Entity\Mission $missionsDispo
*
* #return Ecole
*/
public function addMissionsDispo(\MissionBundle\Entity\Mission $missionsDispo)
{
$this->missionsDispos[] = $missionsDispo;
return $this;
}
/**
* Remove missionsDispo
*
* #param \MissionBundle\Entity\Mission $missionsDispo
*/
public function removeMissionsDispo(\MissionBundle\Entity\Mission $missionsDispo)
{
$this->missionsDispos->removeElement($missionsDispo);
}
/**
* Get missionsDispos
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getMissionsDispos()
{
return $this->missionsDispos;
}
And here is the interesting part of my mission entity:
/**
* #ORM\ManyToMany(targetEntity="EcoleBundle\Entity\Ecole", inversedBy="missionsDispo")
* #ORM\JoinTable(name="Mission2Ecole",
* joinColumns={#ORM\JoinColumn(name="em_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="me_id", referencedColumnName="id")}
* )
*/
protected $ecolesDispo;
// ...
/**
* Constructor
*/
public function __construct()
{
$this->ecolesDispo = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*
* #return Mission
*/
public function addEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo[] = $ecolesDispo;
return $this;
}
/**
* Remove ecolesDispo
*
* #param \EcoleBundle\Entity\Ecole $ecolesDispo
*/
public function removeEcolesDispo(\EcoleBundle\Entity\Ecole $ecolesDispo)
{
$this->ecolesDispo->removeElement($ecolesDispo);
}
/**
* Get ecolesDispo
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEcolesDispo()
{
return $this->ecolesDispo;
}
After all this was created, I was supposed to get a multi selector with the list of all the schools saved in the database (I already added it to the missionType file), but I get absolutely nothing.
I don't really know if I inverted the annotations, or if the "joinTable" part is correct, but I'm completely lost here.
Does anyone have an idea?
Thank you in advance
Just wrong typo "s"? inversedBy="missionsDispo" >>> inversedBy="missionsDispos"
PS. Official doc here
http://doctrine-orm.readthedocs.io/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-bidirectional

Doctrine and Symfony2 : Association refers to inverse side that doesn't exist error

I have a page address.html.twig , the user can add many addresses in the table UserAddress. when he added his address in the database , the address should be render in the same page that he added his address then he can choose which one he would like to use. Unfortunately the address is not render.
First i thought that i have a problem in my controller action or in my twig page. I even asked a question here about it => here
I verified all my tables in phpmyadmin and all of them are well link but if i'm doing this: php app/console doctrine:schema:validate
i have this error :
[Mapping] FAIL - The entity-class
'FLY\BookingsBundle\Entity\Commandes' mapping is invalid:
* The association FLY\BookingsBundle\Entity\Commandes#user refers to the inverse side field
Application\Sonata\UserBundle\Entity\User#commandes which does not
exist.
[Mapping] FAIL - The entity-class
'FLY\BookingsBundle\Entity\UserAddress' mapping is invalid:
* The association FLY\BookingsBundle\Entity\UserAddress#user refers to the inverse side field
Application\Sonata\UserBundle\Entity\User#address which does not
exist.
Have a look at this picture:
This is my UserAddress.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="address")
* #ORM\JoinColumn(nullable=true)
*/
private $user;
Commandes.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="commandes")
* #ORM\JoinColumn(nullable=true)
*/
private $user;
User.php
/**
* #ORM\Entity(repositoryClass="FLY\UserBundle\Repository\UserRepository")
* #ORM\Table(name="fos_user_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
$this->commandes = new \Doctrine\Common\Collections\ArrayCollection();
$this->address = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\Commandes", mappedBy="user", cascade={"remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $commandes;
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\UserAddress", mappedBy="user", cascade={"remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $address;
Here you can see my var dump:
User {#124 ▼
#id: 21
-commandes: null
-address: null
}
I've had an issue which has popped up 2-3 times in the last few years, where the mappings were incorrect but the schema update was successful. After the mappings were fixed this wasn't reflected in the schema and symfony assumed it was already up-to-date.
I recommend you try removing the relevent relationships manually from your user, commande and address tables and then run:
php app/console doctrine:schema:update --force
- it may fix your issue.
Heres an example from one of my apps - I've done this for your commandes entity.
You'll be able to piece together your UserAddress Entity from this example yourself!
Here goes:
User.php
/**
* #ORM\OneToMany(targetEntity="FLY\BookingsBundle\Entity\Commandes", mappedBy="commandesUser")
*/
protected $commandes;
User.php - Getters and Setters
/**
* Add commandes
*
* #param FLY\BookingsBundle\Entity\Commandes $commandes
*/
public function addCommandes(\FLY\BookingsBundle\Entity\Commandes $commandes)
{
$this->commandes[] = $commandes;
}
/**
* Get commandes
*
* #return Doctrine\Common\Collections\Collection
*/
public function getCommandes()
{
return $this->commandes;
}
Commandes.php
/**
* #ORM\ManyToOne(targetEntity="Application\Sonata\UserBundle\Entity\User", inversedBy="commandes")
* #ORM\JoinColumn(name="user", referencedColumnName="id")
*/
private $commandesUser;
Commandes.php - Getters and Setters
/**
* Set commandesUser
*
* #param Application\Sonata\UserBundle\Entity\User $commandesUser
*/
public function setCommandesUser(\Application\Sonata\UserBundle\Entity\User $commandesUser = null)
{
$this->commandesUser = $commandesUser;
}
/**
* Get $commandesUser
*
* #return Application\Sonata\UserBundle\Entity\User
*/
public function getCommandesUser()
{
return $this->commandesUser;
}
It's quite likely this doesn't happen to anyone else, but there's a chance.
In my case, this error appeared because there was a duplicate. My entity had 2 fields, which are ManyToOne relationships. And they both had the same inversed names, which gave this error.
So this is the relevant bit of code:
class TaskIngredient
{
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Ingredient", inversedBy="taskIngredients")
* #ORM\JoinColumn(nullable=false)
*/
private $ingredient;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Task", inversedBy="taskIngredients")
* #ORM\JoinColumn(nullable=false)
*/
private $task;
}
The solution was relatively easy. I tried changing the inversedBy name, manually. However this didn't fix it (even after applying php app/console doctrine:schema:update --force and removing the var/cache folder).
So I just:
Removed one of the problematic entities (and it's setter/getter)
Ran the php bin/console make:entity tool and readded the field with a different name
Voilà! Issue fixed.

zend framework 2 - ServiceManager error while saving data in database

I am trying to create a saveAction in zend2 framework using doctrine.
in my PromotionsController i have this action:
public function saveLinkAction() {
$view = new ViewModel();
$salonId = (int) $this->params()->fromPost('salon_id', null);
$addLink = $this->getServiceLocator()->get('Promotions\Model\Link');
$linkData['salon_id'] = $salonId;
$linkData['link'] = '/link/example';
$addLink->setData($linkData);
return $view;
}
This is just for learning how to write data in database.
$addLink = $this->getServiceLocator()->get('Promotions\Model\Link');
This line of code is showing an error and i don't know what is the cause?
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Promotions\Model\Link
I have created a Link.php in Model directory.
<?php
namespace Link\Model;
use Application\Model\Entity;
use Zend\Form\Annotation;
/**
* #Entity
* #Table(name="promo_link")
*/
class Link extends Entity {
/********** PROPERTIES **********/
/**
* #Id #GeneratedValue(strategy="AUTO") #Column(name="id", type="integer")
* #var int
*
* #Annotation\Exclude()
*/
protected $id;
/**
* #Column(name="salon", type="integer")
* #var int
*
* #Annotation\Options({"label":"Salon"})
* #Annotation\Validator({"name": "Digits"})
*/
protected $salon;
/**
* #Column(name="link", type="string")
* #var string
*/
protected $link;
/**
* #Column(name="start_date", type="string")
* #var string
*/
protected $start_date;
/**
* #Column(name="end_date", type="string")
* #var string
*/
protected $end_date;
}
?>
The error tells you where the problem is:
Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for Promotions\Model\Link
Meaning: The ServiceManager doesn't know what Promotions\Model\Link is supposed to be. This key either doesn't exist in your SMConfig or while creating the instance to be returned some error occurs.
TL/DR - Check your ServiceManager Configuration regarding the key Promotions\Model\Link
In order to save your data in your database, you will need the entitymanager.
$link = new Link();
$link->setSalonId($salonId);
$link->setLink('/link/example');
$em = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
$em->persist($link);
$em->flush();
You can start with the above code.
However, preferably you would create a repository and a servicelayer. The service should have access to the entityManager and hold your logic. Your controller should have access to this service.

Can't use Doctrine PersistentCollection for one of my entities, another I can

I have a pair of associations. They are many-to-many and I use an explicitly created entity to join them so I can have metadata about the relationship. Though they are identical, one works and the other doesn't. Worse, last week, they both worked and I haven't touched them since. In MuSQL Workbench, I can select the correct data.
When I extract data into an array for one, life is good. When I try for the other, I get:
Call to a member function setValue() on a non-object
I also get it when I try to count() it, access it ($blah[0]) or iterate over it (foreach).
When I execute:
echo get_class($inData)."<BR>";
echo get_class($inData->accountPurchaseNodes)."<BR>";
echo get_class($inData->accountPurchaseNodes[0])."<BR>";
echo "<HR>";
echo get_class($inData)." x<BR>";
echo get_class($inData->purchaseOrderNodes)."<BR>";
echo get_class($inData->purchaseOrderNodes[0])."<BR>";
echo "<HR>";
exit;
I get:
GE\Entity\Purchase
Doctrine\ORM\PersistentCollection
GE\Entity\AccountPurchaseNode
GE\Entity\Purchase
Doctrine\ORM\PersistentCollection
( ! ) Fatal error: Call to a member function setValue() on a non-object in
/Users/tqwhite/Documents/webdev/goodEarth/goodearth.com/library/
Doctrine/ORM/PersistentCollection.php on line 168
Below, I include the pertinent parts of the entity definitions. I have burned hours trying this and that. I will be immensely grateful for your suggestions.
THIS ONE WORKS:
//==Purchase Entity=====================================
/**
* #param \Doctrine\Common\Collections\Collection $property
* #OneToMany(targetEntity="AccountPurchaseNode", mappedBy="account", cascade={"persist", "remove"});
*/
private $accountPurchaseNodes;
//in __construct()
$this->accountPurchaseNodes = new \Doctrine\Common\Collections\ArrayCollection();
//==AccountPurchaseNode Entity=====================================
/**
*
* #ManyToOne(targetEntity="Purchase", cascade={"all"}, fetch="EAGER")
* #JoinColumn(name="purchaseRefId", referencedColumnName="refId")
*
**/
private $purchase;
/**
*
* #ManyToOne(targetEntity="Account", cascade={"all"}, fetch="EAGER")
* #JoinColumn(name="accountRefId", referencedColumnName="refId")
*
**/
private $account;
//==Account Entity=====================================
/**
* #param \Doctrine\Common\Collections\Collection $property
* #OneToMany(targetEntity="AccountPurchaseNode", mappedBy="purchase", cascade={"persist", "remove"});
*/
private $accountPurchaseNodes;
//in __construct()
$this->accountPurchaseNodes = new \Doctrine\Common\Collections\ArrayCollection();
THIS ONE DOES NOT
//==Purchase =====================================
/**
* #param \Doctrine\Common\Collections\Collection $property
* #OneToMany(targetEntity="PurchaseOrderNode", mappedBy="purchases", cascade={"persist", "remove"});
*/
private $purchaseOrderNodes;
//in __construct()
$this->purchaseOrderNodes = new \Doctrine\Common\Collections\ArrayCollection();
//==PurchaseOrderNode =====================================
/**
*
* #ManyToOne(targetEntity="Purchase", cascade={"all"}, fetch="EAGER")
* #JoinColumn(name="purchaseRefId", referencedColumnName="refId")
*
**/
private $purchase;
/**
*
* #ManyToOne(targetEntity="Order", cascade={"all"}, fetch="EAGER")
* #JoinColumn(name="orderRefId", referencedColumnName="refId")
*
**/
private $order;
//==Order =====================================
/**
* #param \Doctrine\Common\Collections\Collection $property
* #OneToMany(targetEntity="PurchaseOrderNode", mappedBy="order", cascade={"persist", "remove"});
*/
private $purchaseOrderNodes;
//in __construct()
$this->purchaseOrderNodes = new \Doctrine\Common\Collections\ArrayCollection();
It was an error in a referencing entity purchases. It says mappedBy="purchases". It should be purchase.
Please note, the consequences of this error were impossible. It produced a huge data structure that could not be listed in almost any useful way. It gave bizarre results when touched.
The solution to this problem was that the mappedBy field name was incorrect. It did not match the actual name in the target entity (in this case, error in the Purchase entity misspelled the target association name in PurchaseOrderNodes).
It was made much more difficult to see because of the naming convention of plural table names. Watch out for that inflection!

Categories