Symfony 2 doctrine persist doesn't work after updating Relationship Mapping - symfony

I updated my entity file to include relationship mapping.
Persist worked before the update now it doesn't.
Maybe it's something I forgot to do.
namespace classes\classBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* advisersplans
*
* #ORM\Table()
* #ORM\Entity
*/
class advisersPlans
{
/**
*
* #ORM\ManyToOne(targetEntity="plans", inversedBy="adviserPlans")
* #ORM\JoinColumn(name="planid", referencedColumnName="id")
*/
public $plan;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
public $id;
/**
* #var integer
*
* #ORM\Column(name="userid", type="integer")
*
*
*/
public $userid;
/**
* #var integer
*
* #ORM\Column(name="adviserid", type="integer")
*
*
*/
public $adviserid;
/**
* #var integer
*
* #ORM\Column(name="planid", type="integer")
*
*
*/
public $planid;
/**
* #var string
*
* #ORM\Column(name="participantLoginWebsiteAddress", type="string", length=255)
*/
public $participantLoginWebsiteAddress;
public function __construct()
{
$class_vars = get_class_vars(get_class($this));
foreach ($class_vars as $key => $value)
{
if ($key != "plan")
$this->$key = "";
}
}
}
Perist returns error saying planid is null. If I remove the following it works.
/**
*
* #ORM\ManyToOne(targetEntity="plans", inversedBy="adviserPlans")
* #ORM\JoinColumn(name="planid", referencedColumnName="id")
*/
Here is my code while persisting.
$adviserPlan = new advisersPlans();
$adviserPlan->planid = $planid;
$adviserPlan->userid = $this->userid();
$adviserPlan->adviserid = $session->get("editadviserid");
$em->persist($adviserPlan);
Am I supposed to populate the plan field and not the planid field or is my entity file coded wrong.

You shouldn't set ids. You should set entities:
$adviserPlan = new advisersPlans();
// You should retrieve the plan before doing this, of course.
$adviserPlan->setPlan($plan);
$plans->addAdviserPlan(§adviserPlan);
$em->persist($adviserPlan);
The methods for adding an entity to a collection should be generated by doctrine when you run:
php app/console doctrine:generate:entities YourBundle

Related

Error creating relationship with FOSUserBundle and custom entity with easy-extends

I followed this documentation in sonata, step by step and it worked.
Then I added a new entity and tried to generate a relation many to many to user entity, and when I validate it return this error
$ bin/console doctrine:schema:validate
Mapping
-------
[FAIL] The entity-class AppBundle\Entity\Business mapping is invalid:
* The association AppBundle\Entity\Business#user refers to the owning side field Application\Sonata\UserBundle\Entity\User#business which does not exist.
Database
--------
[OK] The database schema is in sync with the mapping files.
This are my two entities
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
/**
* Business
*
* #ORM\Table(name="business")
* #ORM\Entity(repositoryClass="AppBundle\Repository\BusinessRepository")
*/
class Business
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="BusinessName", type="string", length=255)
*/
private $businessName;
/**
* #var string
*
* #ORM\Column(name="fantasyName", type="string", length=255)
*/
private $fantasyName;
/**
* #var string
*
* #ORM\Column(name="cuit", type="string", length=13)
*/
private $cuit;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\BankAccountType", inversedBy="business")
*/
private $bankAccountType;
/**
* #var \DateTime $created
*
* #Gedmo\Timestampable(on="create")
* #ORM\Column(type="datetime")
*/
private $created;
/**
* #var \DateTime $updated
*
* #Gedmo\Timestampable(on="update")
* #ORM\Column(type="datetime")
*/
private $updated;
/**
* #ORM\ManyToMany(targetEntity="\Application\Sonata\UserBundle\Entity\User", mappedBy="business")
*/
private $user;
/**
* #var bool
*
* #ORM\Column(name="isActive", type="boolean")
*/
private $isActive = true;
And this
namespace Application\Sonata\UserBundle\Entity;
use Sonata\UserBundle\Entity\BaseUser as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* This file has been generated by the SonataEasyExtendsBundle.
*
* #link https://sonata-project.org/easy-extends
*
* References:
* #link http://www.doctrine-project.org/projects/orm/2.0/docs/reference/working-with-objects/en
*/
class User extends BaseUser
{
/**
* #var int $id
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="\AppBundle\Entity\Business", inversedBy="user")
* #ORM\JoinTable(name="business_user")
*/
private $business;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->business = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id.
*
* #return int $id
*/
public function getId()
{
return $this->id;
}
}
Any idea?
Here is my code for the same thing and it works.
class Role
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\ManyToMany(targetEntity="wizai\WMC\UserBundle\Entity\User", mappedBy="customRoles", fetch="EAGER")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
}
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\ManyToMany(targetEntity="wizai\WMC\UserBundle\Entity\Role", inversedBy="users", fetch="EAGER")
* #ORM\JoinTable(name="user_role",
* joinColumns={#ORM\JoinColumn(name="user", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="role", referencedColumnName="id")}
* )
*/
protected $customRoles;
/**
* User constructor.
*/
public function __construct()
{
$this->customRoles = new ArrayCollection();
}
}
if its still not possible, can you first run a migration or a force update ?
Commands
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
if not, then try.
bin/console doctrine:schema:update --force --complete

Persist create new entity while already exists (Ont-To-Many Many-To-One relation)

I have an Angular4 app working with Symfony3/Doctrine2 Rest Api.
Both in Angular and Symfony, I have those entities :
Table
TableNode
Node
The relation between Table and Node is :
Table (OneToMany) TableNode (ManyToOne) Node
What is a "ManyToMany" relation with attributes.
In the Angular app, I create a new Table (form a TableModel that has exactly the same properties that the Table entity in the Symfony app).
This Table contains several Node entities that come from the Api (so they already exists in my database).
What I want is to create a new Table that contains new TableNode entities and each TableNode should contain existing Node entities.
When I want to save my table within the db, I call my Api through a Put action :
/**
* PUT Route annotation
* #Put("/tables")
*/
public function putTableAction(Request $request)
{
$em = $this->getDoctrine()->getManager('psi_db');
$serializer = $this->container->get('jms_serializer');
$dataJson = $request->query->get('table');
$table = $serializer->deserialize($dataJson, Table::class, 'json');
// Here, my $table has no id (that's ok), the TableNode subentity has no id (ok) and my Node subentity already have an id (because they come from the db)
$em->persist($table);
// Here, my $table has a new id (ok), my TableNode has a new id (ok) BUT my Node subentity have a NEW id, so it will be duplicated
$em->flush();
$view = $this->view();
$view->setData($table);
return $this->handleView($view);
}
I tried to use $em->merge($table) instead of $em->persist($table) and my node subentities keep there own id (so they may not be duplicated within the flush) BUT the table and tableNode have no id (null) and are not persisted.
The only solution I found is to loop through the TableNode entities, retrieve the Node entity from the database and do a tableNode->setNode :
$tns = $table->getTableNodes();
foreach ($tns as $tn) {
$nodeId = $tn->getNode()->getId();
$dbNode = $nodeRepo->find($nodeId);
$tn->setNode($dbNode);
}
But it's not a good solution because I make a db search within a loop and a table could contains more than a hundred of TableNode/Node so it might take a lot of resources.
Does anyone have a cleaner solution ?
Thanks.
edit : add classes
Table :
/**
* Table_
* Doctrine "Table" is a reserved name, so we call it Table_
*
* #ORM\Table(name="psi_table")
* #ORM\Entity(repositoryClass="AppBundle\Repository\Table_Repository")
*
* #ExclusionPolicy("all")
*/
class Table_
{
public function __construct()
{
$this->tNodes = new ArrayCollection();
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255, nullable=true)
*
* #Expose
*/
private $name;
/**
* #var \stdClass
*
* #ORM\Column(name="author", type="object", nullable=true)
*
* #Expose
*/
private $author;
/**
* #var \stdClass
*
* #ORM\OneToMany(targetEntity="AppBundle\Entity\TableNode", mappedBy="table", cascade={"persist"})
*
* #Expose
* #Type("ArrayCollection<AppBundle\Entity\TableNode>")
* #SerializedName("tNodes")
*/
private $tNodes;
}
TableNode :
/**
* TableNode
*
* #ORM\Table(name="psi_table_node")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TableNodeRepository")
*
* #ExclusionPolicy("all")
*/
class TableNode
{
public function __construct($table = null, $node = null, $position = null)
{
if($table) $this->table = $table;
if($node) $this->node = $node;
if($position) $this->position = $position;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*
* #Expose
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="groupSocio", type="string", nullable=true)
*
* #Expose
* #SerializedName("groupSocio")
*/
private $groupSocio;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Table_", inversedBy="tNodes", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Table_")
*/
private $table;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Node", inversedBy="tables", cascade={"persist", "merge"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Node")
*/
private $node;
}
Node :
/**
* TableNode
*
* #ORM\Table(name="psi_table_node")
* #ORM\Entity(repositoryClass="AppBundle\Repository\TableNodeRepository")
*
* #ExclusionPolicy("all")
*/
class TableNode
{
public function __construct($table = null, $node = null, $position = null)
{
if($table) $this->table = $table;
if($node) $this->node = $node;
if($position) $this->position = $position;
}
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*
* #Expose
*/
private $id;
/**
* #var integer
*
* #ORM\Column(name="position", type="integer")
*
* #Expose
*/
private $position;
/**
* #var string
*
* #ORM\Column(name="groupSocio", type="string", nullable=true)
*
* #Expose
* #SerializedName("groupSocio")
*/
private $groupSocio;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Table_", inversedBy="tNodes", cascade={"persist"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Table_")
*/
private $table;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Node", inversedBy="tables", cascade={"persist", "merge"})
* #ORM\JoinColumn(nullable=false)
*
* #Expose
* #Type("AppBundle\Entity\Node")
*/
private $node;
}
Submitted data (example) :
{"tNodes":[{"id":0,"position":0,"groupSocio":"group1","node":{"id":683,"frontId":"1502726228584","level":"synusy","repository":"baseveg","name":"A synusy from my Angular app!","geoJson":{"type":"FeatureCollection","features":[{"type":"Feature","properties":[],"geometry":{"type":"Point","coordinates":[-10.0634765625,42.0982224112]}}]},"lft":1,"lvl":0,"rgt":2,"children":[{"id":684,"frontId":"1502726228586","level":"idiotaxon","repository":"baseflor","name":"poa annua","coef":"1","geoJson":{"type":"FeatureCollection","features":[{"type":"Feature","properties":[],"geometry":{"type":"Point","coordinates":[-10.0634765625,42.0982224112]}}]},"lft":1,"lvl":0,"rgt":2,"validations":[{"id":171,"repository":"baseflor","repositoryIdTaxo":"7075","repositoryIdNomen":"50284","inputName":"poa annua","validatedName":"Poa annua L."}]}],"validations":[]}}]}
The purpose putTableAction is to:
create new instances of Table_
create new instance of TableNode
do nothing with Node
It means that:
1.You do not need to submit any details of Node. Id field is enough:
{"tNodes":[{"id":0,"position":0,"groupSocio":"group1","nodeId": 683}]}
2.You can add one more field to TableNode, called $nodeId, and map it with "node" field in DB. The purpose of this field is to simplify deserialization, in all other places you can use $node field.
/**
* #var integer
*
* #ORM\Column(name="node", type="integer")
*
* #Expose
*/
private $nodeId;

Symfony entity validation verify foreign key exists

I would like to be able to use entity validator constraints to verify if the foreign key book_id is valid, please see below:
Book.php
/**
* Book
*
* #ORM\Table("book")
* #ORM\Entity
* #ORM\Entity(repositoryClass="AppBundle\Repository\BookRepository")
*/
class Book
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\Length(
* max = 250,
* maxMessage = "Name cannot be longer than {{ limit }} characters",
* groups={"create","update"}
* )
*/
private $name;
/**
* #ORM\OneToOne(targetEntity="Loan", mappedBy="book", fetch="LAZY")
*/
protected $loan;
}
Loan.php
/**
* Loan
*
* #ORM\Table("loan")
* #ORM\Entity
* #ORM\Entity(repositoryClass="AppBundle\Repository\LoanRepository")
*/
class Loan
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var integer
* #ORM\Column(name="book_id", type="integer")
*/
protected $book_id;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\Length(
* max = 500,
* maxMessage = "Person cannot be longer than {{ limit }} characters",
* groups={"create","update"}
* )
*/
private $person;
/**
* #ORM\OneToOne(targetEntity="Book", inversedBy="loan")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
*/
protected $book;
}
Here is how I am currently validating the loan entity
$loan = new Loan();
$loan->setPerson($person);
$loan->setBookId($id);
/** #var ConstraintViolation $error */
foreach ($this->get('validator')->validate($loan,null,['create'])->getIterator() as $index => $error) {
$errorMessages[] = $error->getMessage();
}
I figured maybe I can add a custom validator like this to the loan entity:
/**
* #Assert\IsTrue(message = "The book does not exist")
* #return bool
*/
public function isBookLegal(BookRepository $bookRepository)
{
return !$bookRepository->fetchById($this->book_id);
}
But I end up with the follow exception:
Type error: Too few arguments to function
AppBundle\Entity\Loan::isBookLegal(), 0 passed and exactly 1 expected
First of all, you should not have both $book_id and $book in your Loan entity. You should remove $book_id, which is enough for your entity relationship.
Then, all you need to do is add an #Assert\NotBlank() on $book:
use Symfony\Component\Validator\Constraints as Assert;
...
/**
* #ORM\OneToOne(targetEntity="Book", inversedBy="loan")
* #ORM\JoinColumn(name="book_id", referencedColumnName="id")
* #Assert\NotBlank()
*/
protected $book;
I'm not sure what code you are using to get all of your loans, but as Edwin states that's not really good form. You want want something more like:
foreach ($loans as $loan) {
$errors = $this->get('validator')->validate($loan);
// do something here if there is an error
}
The assertion function you wrote isn't going to work because you can't pass in a value to your isBookLegal() function there, nor can you ever use the database connection/repository from within your Entity class.
I'm not really sure what you are trying to accomplish without greater context here. Doctrine is already going to validate your $book member because of your #ORM\OneToOne annotation in the first place. You don't have to perform any additional validation. I'm guessing you are trying to pass in a value directly to $book_id, which is incorrect. You should only be passing already-valid $book entities to your Loan class, via $loan->setBook(Book $book);

Embeed forms - inserting to database

I created two entities automatically ( using this manual http://symfony.com/doc/2.8/doctrine/reverse_engineering.html) based on ER model generated in Workbench. My intention was to create one-to-one relationship but annotation show it is one-to-many relationship. I created also embeed forms. I would like to insert client and new adress to database. I still get an error:
A new entity was found through the relationship 'UlaBundle\Entity\Client#adres' that was not configured to cascade persist operations for entity: qqq. To solve this issue: Either explicitly call EntityManager#persist() on this unknown entity or configure cascade persist this association in the mapping for example #ManyToOne(..,cascade={"persist"}).
Error is shown even if i set #ManyToOne(..,cascade={"persist"}) and __toString function. What is the problem? Please help. Below my code:
///Client Entity
class Client
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade= {"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;
/// Adres Entity
class Adres
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=45, nullable=true)
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
///Controller
/**
* #Route("/client", name="client")
*/
public function clientAction(Request $request) {
$c = new Client();
$form = $this->createForm(ClientType::class,$c);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$m = $this->getDoctrine()->getManager();
$m->persist($c);
$m->flush();
return new Response('Added');
}
return $this->render('UlaBundle:Default:client_form.html.twig', array('form' => $form->createView()));
}
I think your problem come from the blank space in cascade= {"persist"}, you should remove it
/**
* #var \UlaBundle\Entity\Adres
*
* #ORM\ManyToOne(targetEntity="UlaBundle\Entity\Adres", cascade={"persist"})
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="adres_id", referencedColumnName="id")
* })
*/
private $adres;

Symfony2 doctrine ManyToMany associations mapping invalid?

I have two entities relating to this issue, and I want a join table to be able to cross reference values in each table.
Here is an explanation:
Entity ContainerType.php:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\Containers;
/**
* ContainerType
*
* #ORM\Table(name="container_type")
* #ORM\Entity
* #ORM\HasLifecycleCallbacks()
*/
class ContainerType
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="type", type="string", length=255)
*/
private $type;
/**
* #var \DateTime
*
* #ORM\Column(name="date_added", type="datetime")
*/
private $dateAdded;
/**
* #var \DateTime
*
* #ORM\Column(name="date_modified", type="datetime", nullable=true)
*/
private $dateModified;
/**
* #ORM\ManyToMany(targetEntity="Containers", inversedBy="type")
* #ORM\JoinTable(name="container_type_containers")
**/
private $container;
public function __construct()
{
$this->container = new ArrayCollection();
}
And entity Containers.php:
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\ContainerType;
/**
* Containers
*
* #ORM\Table(name="containers")
* #ORM\Entity
*/
class Containers
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="number", type="string", length=255)
*/
private $number;
/**
* #ORM\ManyToMany(targetEntity="Containers", mappedBy="container")
*/
private $type;
public function __construct()
{
$this->type = new ArrayCollection();
}
And although the schema update works without problems, when I do a doctrine:schema:validate I get the following fail:
[Mapping] FAIL - The entity-class 'AppBundle\Entity\Containers' mapping is invalid:
* The association AppBundle\Entity\Containers#type refers to the owning side field AppBundle\Entity\Containers#container which does not exist.
But the $container field DOES exist in ContainerType so I do not understand why it's trying to reference a field called container in the Containers entity?
Can anyone shed any light on this?
Thank you
Michael
I think this code should works fine for you :)
ContainerType.php
/**
* #ORM\ManyToMany(targetEntity="Containers", inversedBy="containersType")
* #ORM\JoinTable(name="container_type_containers",
* joinColumns={#ORM\JoinColumn(name="container_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="container_type_id", referencedColumnName="id")})
*/
protected $containers;
Containers.php
/**
* #ORM\ManyToMany(targetEntity="ContainerType", mappedBy="containers")
*/
protected $containersType;
I have just realized my error!
I had used the wrong target Entity in the Containers.php file, I should have used ContainerType as the target, instead I had Containers which is why it was trying to find the field in the wrong table!

Resources