FatalError when flushing after persisting datas - doctrine-orm

I want to persist datas from a new Entity into the database, one of the most basic operation right?
But when I flush, I get the following fatal error:
FatalErrorException in DateTimeType.php line 53:
Error: Call to a member function format() on string
So here's how I want to persist my datas, on Symfony 3.2, PHP 5.6 (sigh) and mariadb 10.3:
$myEntity = new myEntity();
$myEntity->setFoo('a');
$myEntity->setBar('b');
$myEntity->setDateCreation(new \DateTime());
$this->em->persist($myEntity);
$this->em->flush();
My entity:
class myEntity
{
// ...
/**
* #var string
*
* #ORM\Column(name="sFoo", type="string", length=255, nullable=false, unique=true)
*/
private $foo;
/**
* #var string
*
* #ORM\Column(name="sBar", type="string", length=255, nullable=false)
*/
private $bar;
/**
* #var \DateTime
*
* #ORM\Column(name="dDateCreation", type="date")
*/
private $dateCreation;
// Getter and setters are classics, here's the one for the date
public function setDateCreation($dateCreation)
{
$this->dateCreation = $dateCreation;
}
}
I get absolutely no error if I stop the script before flushing. When I dump my entity just before, all the properties are set correctly :
MyEntity {#2301 ▼
-id: null
-foo: "a"
-bar "b
-dateCreation: DateTime {#2204 ▼
+"date": "2020-07-06 12:32:17.000000"
+"timezone_type": 3
+"timezone": "Europe/Paris"
}
}
From the database side : the columns seems to be correctly set: sFoo and sBar are VARCHAR(255) dDateCreation is date.
I eventually changed the date fields type from date to string, with a string as argument like "20200706", just for trying. And... I get the same error, even if there are no DateTime anymore involved in this.
Either I'm completely blind and there is an obvious thing I forgot in the code I've posted, or the reason comes from elsewhere.
I tried the same operation with an other Entity, same result.

Related

doctrine2 entity number in month generation

I've got Invoice entity, in which I'd like to generate subsequent numbers within a given month.
Entity code:
/**
* Class Invoice
* #package App\Entity
* #ORM\Entity()
* #ORM\HasLifecycleCallbacks()
*/
class Invoice
{
(...)
/**
* #var int
* #ORM\Column(type="integer")
*/
private $year;
/**
* #var int
* #ORM\Column(type="integer")
*/
private $month;
/**
* #var int
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="CUSTOM")
* #ORM\CustomIdGenerator(class="App\Helper\InvoiceNumberGenerator")
*/
private $counter;
(...)
/**
* #ORM\PrePersist
* #ORM\PreUpdate
*/
public function numberGenerator()
{
if ($this->getYear() === null) {
$this->setYear(date('Y'));
$this->setMonth(date('m'));
}
}
And App\Helper\InvoiceNumberGenerator code is:
<?php
namespace App\Helper;
use App\Entity\Invoice;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Id\AbstractIdGenerator;
use Exception;
class InvoiceNumberGenerator extends AbstractIdGenerator
{
/**
* Generates an invoice number
*
* #param EntityManager $em
* #param Invoice $entity
* #return mixed
* #throws Exception
*/
public function generate(EntityManager $em, $entity)
{
if (!$entity instanceof Invoice) {
throw new Exception('Generator służy tylko do generowania numerów faktur.');
}
/** #var ObjectRepository | EntityRepository $invoiceRepository */
$invoiceRepository = $em->getRepository(Invoice::class);
/** #var Invoice $lastInvoice */
$lastInvoice = $invoiceRepository->findOneBy(
array(
'year' => $entity->getYear(),
'month' => $entity->getMonth()
),
array(
'counter' => 'desc'
)
);
if (empty($lastInvoice)) {
return 1;
}
return $lastInvoice->getCounter() + 1;
}
}
When I dump $lastInvoice, it shows:
Invoice {#5522 ▼
-id: 1
-generated: false
-fileName: "example"
-year: 2019
-month: 11
-counter: 1
-name: "AG"
-company: "Gall"
-address: "Street 1"
-address2: "Gliwice"
-nip: "6314567890"
-reservation: Reservation {#5855 ▶}
-date: null
}
So it looks like the generator gets to selecting last one correctly, but nevertheless I got error when trying to create new Invoice:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'counter'
cannot be null
Any advise on what I'm doing wrong?
the #CustomIdGenerator annotation is only called when the column is also marked with #Id. From the docs:
This annotations allows you to specify a user-provided class to generate identifiers. This annotation only works when both #Id and #GeneratedValue(strategy="CUSTOM") are specified.
Ids are always a special kind of thing and thus must sometimes be perfect before inserting. To solve your problem - because the counter is not an id column -, you could use lifecycle events instead (prePersist, probably) and use the event's entity manager in an event listener/subscriber to run your query.

Is there a way to add a aggregated/calculated field (concat) to a Doctrine entity?

I have a USER entity, which countains two fields : firstname and lastname.
I would like to know how I can add a virtual column called fullname (firstname + ' ' + upper(lastname)) to my entity.
I have read already the Doctrine documentation about aggregated fields but I am not sure how to use this in my context.
I don't want to add a concat and upper formula to every DQL I will launch on my users but I would like to add a computed field once in my entity. Doing a PHP getter is not a solution.
/**
* User
*
* #ORM\Table(name="[user]")
* #ORM\Entity(repositoryClass="UserRepository")
*/
class User
{
/**
* #var string|null
*
* #ORM\Column(name="lastname", type="string")
*/
private $lastname;
/**
* #var string|null
*
* #ORM\Column(name="firstname", type="string")
*/
private $firstname;
}
if I add an aggregate field like this to my entity :
/**
* #ORM\Column(type="string")
*/
private $fullName='';
public getFullName(){
return $this->firstname . ' ' . $this->lastname;
}
my code crashes when I call find() or findAll() on a User entity (invalid column name)
Maybe that's the wrong approach do do that "magic". If you need that fullname field only for the view, you can use your getter as it is. In case you need it persisted, you should concatinate the first- and lastname string in its setters and set the fullname property. in this case, your getter should return that property.

How to set parent id on a child entity when submitting symfony3 form

How can I set the task_id on the TaskLine entity?
Similar Questions:
Doctrine: How to insert foreign key value
Best practice for inserting objects with foreign keys in symfony2
Doctrine 2 entity association does not set value for foreign key
I get this error:
Neither the property "task_id" nor one of the methods "getTaskId()", "taskId()", "isTaskId()", "hasTaskId()", "__get()" exist and have public access in class "AppBundle\Entity\TaskLine"
NOTE: Normally I work with PropelORM.
I'm trying to save a new TaskLine entity which is related to Task entity. I'm posting JSON payload which look something like this.
{
"id": null,
"task_id": 1,
"note" : "new note"
}
In the controller I json_decode the request payload and submit that to $form->submit($note_data), $form is an instance of:
class TaskNoteType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add( 'task_id', NumberType::class )
->add( 'note', TextType::class )
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\TaskLine'
));
}
}
Here is my Task entity
class Task
{
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=150, nullable=true)
*/
private $description;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
}
TaskLine entity
class TaskLine
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var \AppBundle\Entity\Task
*
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Task")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="task_id", referencedColumnName="id")
* })
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set task
*
* #param \AppBundle\Entity\Task $task
*
* #return TaskLine
*/
public function setTask(\AppBundle\Entity\Task $task = null)
{
$this->task = $task;
return $this;
}
/**
* Get task
*
* #return \AppBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
}
I found my answer in here:
Best Practice for inserting objects with foreign keys in Symfony2
Answered by: Tuan nguyen
In ORM you have to set Foreign key by an object which your entity
associated with. You could use EntityManager#getReference to get a
reference to category entity without loading it from DB. Like this
$category = $entityManager->getReference('YourProject\Entity\Category', $categoryId);
$product = new Product();
$product->setCategory($category);
Similar questions:
Doctrine: How to insert foreign key value
Following function you should have already in your Slider entity (or
similar).
public function addImage(Image $image) {
$image->setSlider($this); // This is the line you're probably looking for
$this->images[] = $image;
return $this; }
What it does is if you persist the entity it writes the ID of the Slider (sid) into your Image.
Doctrine 2 entity
association does not set value for foreign key
I found something in the Doctrine 2 documentation:
Changes made only to the inverse side of an association are ignored.
Make sure to update both sides of a bidirectional association (or at
least the owning side, from Doctrine’s point of view) As in my case
the owning side is the User I must update it. Doctrine 1 was able to
manage it automatically... too bad.

Doctrine2, uniqueConstraint on JoinColumn for ZF2?

I have a data feed that has duplicated content (no idea why, it's an external feed), however we need to insert all items with a constraint on the title and type, i.e.
These can exist:
Name_A, Type_A
Name_A, Type_B
But only one of these should exist:
Name_A, Type_A
Name_A, Type_A
Here's the Entity code I'm using:
/**
* Restauration
*
* #ORM\Table(name="restauration", uniqueConstraints={#ORM\UniqueConstraint(name="name_unique", columns={"name_1", "restauration_type"})})
* #ORM\Entity(repositoryClass="iMotionTools\Repository\RestaurationRepository")
*/
class Restauration
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="NONE")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name_1", type="string", length=128, nullable=true)
*/
private $name1;
/**
* #var string
*
* #ORM\ManyToOne(targetEntity="RestaurationType", cascade={"persist"})
* #ORM\JoinColumn(name="restauration_type", referencedColumnName="id")
*/
private $type;
}
But I get this error when parsing and inserting the data:
SQLSTATE[23000]: Integrity constraint violation: 19 columns name_1, restauration_type are not unique:91:C:\coding\currate\vendor\doctrine\dbal\lib\Doctrine\DBAL\DBALException.php
I'm wondering whether the easy way is to just ignore the thrown exception? Looks like it's a driverExceptionDuringQuery that gets thrown during my call to $em->persist(); but I'm not sure how I would ignore if the call contains the above error?
If you want to ignore that, stop using constraint integrity.
{#ORM\UniqueConstraint(name="name_unique", columns={"name_1" //etc...
Your data has name_1 not unique this is why you have this error, integrity constraint check this, you can't ignore that, without remove the unique constraint parameters.
Edit :
You have to then, before persist data, check with your actual data in the table, if there is a duplicate entry for both name_1 AND Type, and do not persist them.
for check both you can use :
#UniqueEntity({"name", "type"})
found here :
validation multiple constraint columns
Even if it's for SF2, it's the same concept
I've removed the UniqueConstraint attribute from the table and added a function to check the objects list (which later get $entity->persist()-ed), using an array so that I can easily use it for different entity types, and it seems to work well now.
$key = $hashList ? '' : $page['id'];
foreach ($hashList as $method) {
$val = $object->{$method}();
if(is_object($val)) {
$val = $val->getId();
}
$key .= $val;
}
$key = md5($key);
$objects[$key] = $object;
Where $hashList = array('getName', 'getType') - and getType returns an object (since it's another entity), but which always has the getId() function... probably not the best solution but it works for my situation...

doctrine2 OneToMany relationship inserts NULL as the foreign key

I have a bi-directional OneToMany relationship using Doctrine 2 as an ORM within the ZendFramework 1.11.2.
Note: Doctrine did not create the database tables. The database is MySQL.
For some reason, when I persist and flush a new link entity to the link table (see below) the foreign key field (container_id) gets set to NULL. However, if the '#' symbol is removed from the 'ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links")' line, the foreign key field is populated properly.
Since the entity is added properly to the database when the '#' symbol is removed, there is something wrong with the OneToMany relationship somewhere.
For example, if I have a link model named $link (see pseudo-code below)...
$link (Shepherd\Navigation\Domain\Link\Model)
{
id: '' // auto generated value
cid: 23 // the foreign key value
label: test
uri: test.com
... // other values not listed here for brevity
}
...when the new link model is persisted and the entity manager is flushed, the container_id (foreign key) value from the newly inserted row in the link (shepherd_navigation_link) table is NULL.
$em // Assume $em is the Entity Manager
$em->persist($link);
$em->flush();
// The container_id in the newly added row in the
// link table (shepherd_navigation_link) is NULL
The link table schema:
CREATE TABLE `shepherd_navigation_link` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`container_id` int(10) unsigned DEFAULT NULL,
`node_id` int(10) unsigned DEFAULT NULL,
`parent_id` int(10) unsigned DEFAULT NULL,
`label` varchar(100) NOT NULL,
`options` text,
`events` text,
`privilege` varchar(100) NOT NULL,
`resource` varchar(100) DEFAULT NULL,
`uri` varchar(300) NOT NULL,
`visible` int(10) unsigned DEFAULT '1',
PRIMARY KEY (`id`),
KEY `container_id` (`container_id`)
) ENGINE=InnoDB
ALTER TABLE `shepherd_navigation_link` ADD FOREIGN KEY (container_id) REFERENCES shepherd_navigation_container(id)
Link entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_link")
*/
class
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column(name="container_id", type="integer", nullable=false)
*/
protected $cid;
/**
* #Column(name="node_id", type="integer")
*/
protected $nid;
/**
* #Column(name="parent_id", type="integer", nullable=false)
*/
protected $pid;
/**
* #Column
*/
protected $label;
/**
* #Column(nullable=true)
*/
protected $options;
/**
* #Column(nullable=true)
*/
protected $events;
/**
* #Column
*/
protected $privilege;
/**
* #Column(nullable=true)
*/
protected $resource;
/**
* #Column
*/
protected $uri;
/**
* #Column(type="integer", nullable=true)
*/
protected $visible;
/**
* #OneToMany(targetEntity="Model", mappedBy="parent")
*/
private $children;
/**
* #ManyToOne(targetEntity="Model", inversedBy="children")
*/
private $parent;
/**
*) #ManyToOne(targetEntity="Shepherd\Navigation\Domain\Container\Model", inversedBy="links"
*/
private $container;
/**
* #OneToOne(targetEntity="Shepherd\Navigation\Domain\Link\Position", inversedBy="link")
*/
private $node;
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
Note: the protected property $cid maps to the container_id column above.
The container table schema:
CREATE TABLE `shepherd_navigation_container` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(100) NOT NULL,
`description` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB
The container entity model:
/**
* #Entity
* #Table(name="shepherd_navigation_container")
*/
class Model
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
protected $id;
/**
* #Column
*/
protected $name;
/**
* #Column(nullable=true)
*/
protected $description;
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container")
*/
private $links;
/**
* Constructor
*/
public function __construct()
{
$this->links = new \Doctrine\Common\Collections\ArrayCollection();
}
/** Accessors and Mutators excluded for brevity **/
}
What am I missing? What am I doing wrong?
I figured out the problem (by reading the documentation http://www.doctrine-project.org/docs/orm/2.0/en/tutorials/getting-started-xml-edition.html). It turns out there were actually a few problems.
Problem 1 => I did not provide a method to set the container variable.
// Inside the Link Entity class...
public function setContainer($container)
{
$this->container = $container;
}
Problem 2 => I did not set the container value. In error, I thought Doctrine 2 did this internally, but I found out the container variable needs to be set prior to flushing.
Foolish oversight on my part.
$link = new Link();
$link->setContainer($container);
// $em is the Entity Manager
$em->persist($link);
$em->flush();
Problem 3 => The container ($container) needed to either be persisted prior to flushing or the #OneToMany definition on the container entity needed to change. I chose to update the container entity definition. Take a look here (http://www.doctrine-project.org/docs/orm/2.0/en/reference/working-with-associations.html#transitive-persistence-cascade-operations) for more information.
// Inside the Container Entity class...
/**
* #OneToMany(targetEntity="Shepherd\Navigation\Domain\Link\Model", mappedBy="container", cascade={"persist"})
*/
After making these changes and removing the #OneToOne node relationship in the link entity class (turns out I didn't need it), everything worked fine. I hope this helps someone.