I execute migrations to create the database, after that, I want to fill database with some data. Why generate id strategy is not working?
bin/console doctrine:migrations:migrate
Migrations
WARNING! You are about to execute a database migration that could result in schema changes and data lost. Are you sure you wish to continue? (y/n)y
Migrating up to 20160510103145 from 0
++ migrating 20160510103145
-> CREATE TABLE navieras (id INTEGER NOT NULL, nombre VARCHAR(255) NOT NULL)
++ migrated (0.14s)
------------------------
++ finished in 0.14s
++ 1 migrations executed
++ 1 sql queries
bin/console doctrine:fixtures:load
Careful, database will be purged. Do you want to continue y/N ?y
> purging database
> loading AppBundle\DataFixtures\ORM\LoadNavieraData
[Doctrine\DBAL\Exception\NotNullConstraintViolationException]
An exception occurred while executing 'INSERT INTO navieras (nombre) VALUES (?)' with params ["Royal Caribbean"]:
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: navieras.id
[Doctrine\DBAL\Driver\PDOException]
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: navieras.id
[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: navieras.id
Fixture load function
public function load(ObjectManager $manager)
{
$naviera = new Naviera();
$naviera->setNombre('aaaaaaaaaaaaaaa');
$naviera2 = new Naviera();
$naviera2->setNombre('bbbbbbbbbbbbbb');
$manager->persist($naviera);
$manager->persist($naviera2);
$manager->flush();
}
Migration
public function up(Schema $schema)
{
$navieras = $schema->createTable('navieras');
$navieras->addColumn('id', "integer");
$navieras->addColumn('nombre', "string");
}
/**
* #param Schema $schema
*/
public function down(Schema $schema)
{
$schema->dropTable('navieras');
}
Naviera Entity
/**
* Modelo naviera
*
* #Entity
* #Table(name="navieras")
*/
class Naviera
{
/**
* #Id
* #Column(type="integer")
* #GeneratedValue
*/
private $id;
/**
* #Column(type="string");
*/
private $nombre;
/**
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #param integer $id
*/
public function setId($id)
{
$this->$id = $id;
}
/**
* #return string
*/
public function getNombre()
{
return $this->nombre;
}
/**
* #param string $nombre
*/
public function setNombre($nombre)
{
$this->nombre = $nombre;
}
}
I'm using SQLite
Your migration up does not create an auto incrementing id column. Just an integer column. If you change your migration up function to the following it should fix your issue:
public function up(Schema $schema)
{
$navieras = $schema->createTable('navieras');
$navieras->addColumn('id', 'integer', ['Autoincrement' => true]);
$navieras->addColumn('nombre', 'string');
$navieras->setPrimaryKey('id', true);
}
Related
I have a problem with doctrine sortable extension.
first of all, i have a House entity with 1:n Releation to HouseImage entity (setup to save the image postion)
and a HouseImage entity 1:1 releation to File entity.
class House
{
/**
* #var HouseImage[]|Collection
*
* #ORM\OneToMany(
* targetEntity="HouseImage",
* mappedBy="houses",
* cascade={"persist", "remove"},
* orphanRemoval=true
* )
* #ORM\OrderBy({"position" = "ASC"})
*/
protected $images;
}
/**
* HouseImage
*
* #ORM\Table(name=house_image)
* #ORM\Entity(repositoryClass="Gedmo\Sortable\Entity\Repository\SortableRepository")
*/
class HouseImage {
/**
* #var House
*
* #ORM\ManyToOne(
* targetEntity="House",
* inversedBy="images",
* )
* #ORM\JoinColumn(
* name="house_id",
* referencedColumnName="id",
* onDelete="SET NULL"
* )
* #Gedmo\SortableGroup
*/
protected $houses;
/**
* #var File
*
* #ORM\OneToOne(
* targetEntity="File",
* )
*
* #ORM\JoinColumn(
* name="image_id",
* referencedColumnName="id",
* nullable=false,
* )
*/
protected $image;
/**
* #var integer
*
* #Gedmo\SortablePosition
* #ORM\Column(name="position", type="integer")
*/
protected $position;
....
}
//so i create some HouseImage-Objects
$HouseImage = new HouseImage();
$HouseImage->setImage($myFile);
$HouseImage2 = new HouseImage();
$HouseImage2->setImage($myFile2);
$HouseImage3 = new HouseImage();
$HouseImage3->setImage($myFile3);
//add first image-relation to house
$house->setImages([$HouseImage]);
$em->persist($house);
$em->flush();
//add second image-relation, should be inserted at first position
$house->setImages([$HouseImage2, $HouseImage]);
$em->persist($house);
$em->flush();
//add new list of image-relation
$house->setImages([$HouseImage2, $HouseImage3]);
$em->persist($house);
$em->flush();
///after flush the entitymanager this error occurred
An exception occurred while executing 'INSERT INTO house_image (position, house_id, image_id) VALUES (?, ?, ?)' with params [0, 123, 999]:\n
SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '999' for key 'UNIQ_E0C3790C3DA5256D
the some error occurred with form factory
$data['images'] = [
[
'house' => 181,
'image' => 123
],
[
'house' => 181,
'image' => 1234
],
[
'house' => 181,
'image' => 12345
],
];
$form = $formFactory->create(HouseType::class, $houseObject);#
$form->submit($data, false);
Question:
How can i update the HouseImage-postion in the list by add a list of HouseImage
or
How can i cleanup all entites befor insert a complete new list of HouseImage-Releations
Edit:
I have fixed my issue by removing the addImage() method an implement this setImage() Method:
public function setImages(Collection $images): void
{
$col = new ArrayCollection();
$i = 0;
/* #var $image HouseImage */
foreach ($images as $image) {
$image->setHouse($this);
$image->setPosition($i++);
$col->add($image);
}
$this->images = $col;
}
Your way to create the house image entities is a little bit wrong, you can define that House entity should cascade persist your HouseImage collection. You should add this in your House entity:
class House
{
function __construct()
{
$this->images = new ArrayCollection();
}
public function addHouseImage(HouseImage $houseImage)
{
$this->images->add($houseImage);
$houseImage->setHouse($this);
}
}
Also rename $houses to $house in your HouseImage. You are linking HouseImage to House(not many houses). Also don't forget to link HouseImage with File entity. If you want to set the position after the last image on a new image you do that in addHouseImage function or anywhere else in your application. With the above code you code could look like this:
$image1 = ...;
$image2 = ...;
$image3 = ...;
$house->addHouseImage($image1);
add2.
add2.
// This will persist all house images along with house.
$em->persist($house);
$em->flush();
If I didn't cover something related to your question pleas let me know
Also the exception
'INSERT INTO house_image (position, house_id, image_id) VALUES (?, ?, ?)' with params [0, 123, 999]:\n SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '999' for key 'UNIQ_E0C3790C3DA5256D
tells you that there already is a File with id 999 in relation and you are trying to set 2 HouseImage entities to the same File entity. I can't spot it in your code but I'm almost certain that it happens somewhere in your code.
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.
I am working with my database already a month. Suddenly i started to get the error:
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Array to string conversion
, when loading fixtures. Finally, i deleted all database, rerun migrations, left only 1 fixtures file (thus i commented our OrderedFIxturesInterface, and of course i cleared the cache, but the error remains).
C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj2_27\src\MeetingBundle\DataFixtures\ORM\LoadUsers01.php
<?php
namespace MeetingBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\FixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use MeetingBundle\Entity\User;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
class LoadUsers01 extends AbstractFixture
#implements OrderedFixtureInterface
{
public function load(ObjectManager $manager)
{
for ($i = 1; $i <= 10; $i++) {
$fuser = new User();
$fuser->setUsername('name'.$i);
$fuser->setEmail('g.statkute'.$i.'#gmail.com');
$fuser->setIsActive(True); // i also used 1
$fuser->setPassword('pswd'.$i);
$manager->persist($fuser);
# $this->addReference('fuser:name'.$i, $fuser);
}
$manager->flush();
}
public function getOrder()
{
return 1;
}
}
.
C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj2_27\src\MeetingBundle\Entity\User.php
..
/**
* User
*
* #ORM\Table(name="tuser")
* #ORM\Entity()
*/
class User implements AdvancedUserInterface, \Serializable
..
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(name="email", type="string", nullable=true, unique=true)
*
* #var string
*/
private $email;
/**
* #var bool
*
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=255, unique=true)
*/
private $password;
..
/**
* Set username
*
* #param string $username
*
* #return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* #param string $email
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* #param boolean $isActive
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
}
/**
* Set password
*
* #param string $password
*
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
Table contains varchar fields except of tinyint for boolean IsActive. There is only one tabel with name users.
--
-- Table structure for table `tuser`
--
CREATE TABLE IF NOT EXISTS `tuser` (
`id` int(11) NOT NULL,
`username` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`email` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`phone` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`roles` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`is_active` tinyint(1) NOT NULL,
`gravatar` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`webpages` longtext COLLATE utf8_unicode_ci NOT NULL COMMENT '(DC2Type:json_array)',
`createdAtInt` decimal(10,0) DEFAULT NULL,
`loggedInInt` decimal(10,0) DEFAULT NULL,
`loggedOutInt` decimal(10,0) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
--
-- Indexes for dumped tables
--
--
-- Indexes for table `tuser`
--
ALTER TABLE `tuser`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `UNIQ_66A7B847F85E0677` (`username`);
THE VERBOSE ERROR:
c:\Bitnami\wampstack-5.5.30-0\sym_prog\proj3_27>php app/console doctrine:fixture
s:load -vv
[2015-12-18 05:45:10] event.DEBUG: Notified event "console.command" to listener
"Symfony\Component\HttpKernel\EventListener\DebugHandlersListener::configure".
[2015-12-18 05:45:10] event.DEBUG: Notified event "console.command" to listener
"Symfony\Bridge\Monolog\Handler\ConsoleHandler::onCommand".
[2015-12-18 05:45:10] event.DEBUG: Notified event "console.command" to listener
"Symfony\Bridge\Monolog\Handler\ConsoleHandler::onCommand".
Careful, database will be purged. Do you want to continue y/N ?y
> purging database
> loading [1] MeetingBundle\DataFixtures\ORM\LoadUsers01
[Symfony\Component\Debug\Exception\ContextErrorException]
Notice: Array to string conversion
Exception trace:
() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj3_27\vendor\doctrine\dbal\lib\
Doctrine\DBAL\Driver\PDOStatement.php:67
Symfony\Component\Debug\ErrorHandler->handleError() at n/a:n/a
PDOStatement->bindValue() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj3_27\ve
ndor\doctrine\dbal\lib\Doctrine\DBAL\Driver\PDOStatement.php:67
Doctrine\DBAL\Driver\PDOStatement->bindValue() at C:\Bitnami\wampstack-5.5.30-0
\sym_prog\proj3_27\vendor\doctrine\dbal\lib\Doctrine\DBAL\Statement.php:120
Doctrine\DBAL\Statement->bindValue() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\
proj3_27\vendor\doctrine\orm\lib\Doctrine\ORM\Persisters\Entity\BasicEntityPersi
ster.php:277
Doctrine\ORM\Persisters\Entity\BasicEntityPersister->executeInserts() at C:\Bit
nami\wampstack-5.5.30-0\sym_prog\proj3_27\vendor\doctrine\orm\lib\Doctrine\ORM\U
nitOfWork.php:1018
Doctrine\ORM\UnitOfWork->executeInserts() at C:\Bitnami\wampstack-5.5.30-0\sym_
prog\proj3_27\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php:378
Doctrine\ORM\UnitOfWork->commit() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\pro
j3_27\vendor\doctrine\orm\lib\Doctrine\ORM\EntityManager.php:356
Doctrine\ORM\EntityManager->flush() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\p
roj3_27\src\MeetingBundle\DataFixtures\ORM\LoadUsers01.php:32
MeetingBundle\DataFixtures\ORM\LoadUsers01->load() at C:\Bitnami\wampstack-5.5.
30-0\sym_prog\proj3_27\vendor\doctrine\data-fixtures\lib\Doctrine\Common\DataFix
tures\Executor\AbstractExecutor.php:121
Doctrine\Common\DataFixtures\Executor\AbstractExecutor->load() at C:\Bitnami\wa
mpstack-5.5.30-0\sym_prog\proj3_27\vendor\doctrine\data-fixtures\lib\Doctrine\Co
mmon\DataFixtures\Executor\ORMExecutor.php:83
Doctrine\Common\DataFixtures\Executor\ORMExecutor->Doctrine\Common\DataFixtures
\Executor\{closure}() at n/a:n/a
call_user_func() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj3_27\vendor\doct
rine\orm\lib\Doctrine\ORM\EntityManager.php:233
Doctrine\ORM\EntityManager->transactional() at C:\Bitnami\wampstack-5.5.30-0\sy
m_prog\proj3_27\vendor\doctrine\data-fixtures\lib\Doctrine\Common\DataFixtures\E
xecutor\ORMExecutor.php:85
Doctrine\Common\DataFixtures\Executor\ORMExecutor->execute() at C:\Bitnami\wamp
stack-5.5.30-0\sym_prog\proj3_27\vendor\doctrine\doctrine-fixtures-bundle\Comman
d\LoadDataFixturesDoctrineCommand.php:118
Doctrine\Bundle\FixturesBundle\Command\LoadDataFixturesDoctrineCommand->execute
() at C:\Bitnami\wampstack-5.5.30-0\sym_prog\proj3_27\vendor\symfony\symfony\src
\Symfony\Component\Console\Command\Command.php:256
Symfony\Component\Console\Command\Command->run() at C:\Bitnami\wampstack-5.5.30
-0\sym_prog\proj3_27\vendor\symfony\symfony\src\Symfony\Component\Console\Applic
ation.php:846
Symfony\Component\Console\Application->doRunCommand() at C:\Bitnami\wampstack-5
.5.30-0\sym_prog\proj3_27\vendor\symfony\symfony\src\Symfony\Component\Console\A
pplication.php:189
Symfony\Component\Console\Application->doRun() at C:\Bitnami\wampstack-5.5.30-0
\sym_prog\proj3_27\vendor\symfony\symfony\src\Symfony\Bundle\FrameworkBundle\Con
sole\Application.php:96
Symfony\Bundle\FrameworkBundle\Console\Application->doRun() at C:\Bitnami\wamps
tack-5.5.30-0\sym_prog\proj3_27\vendor\symfony\symfony\src\Symfony\Component\Con
sole\Application.php:120
Symfony\Component\Console\Application->run() at C:\Bitnami\wampstack-5.5.30-0\s
ym_prog\proj3_27\app\console:27
The problem was with roles field, which i changed yesterday evening to the string instead of array. But i forget to remove initialization $roles = array();
/**
* #var string
*
* #ORM\Column(name="roles", type="string", nullable=true)
*/
private $roles; // it was $roles= array();
Thus if you see this error, search for keyword "array" in related files and check if assign variable is supposed to be equal to array, etc..
This is the fourth time when i have redone all project because of simple error. The conclusion is that better it to keep thinking where can be a mistake. On the other hand, when doctrine was not generating crud, reinstalling the project with different Symfony version helped.
private $roles; // = array();
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.
I have three tables in a PostgreSQL database where I run this queries:
ALTER TABLE waccount ALTER COLUMN balance SET DEFAULT 0;
ALTER TABLE waccount ALTER COLUMN created SET DEFAULT now();
ALTER TABLE waccount ALTER COLUMN modified SET DEFAULT now();
Now when I trying to run a INSERT from Symfony2 controller using this code:
$entity = new Account();
$form = $this->createForm(new AccountType(), $entity);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($entity);
$em->flush();
return $this->redirect($this->generateUrl('wba_show', array('id' => $entity->getAccountId())));
}
I get this:
An exception occurred while executing 'INSERT INTO waccount
(account_id, account_number, bank_name, balance, created, modified,
account_type) VALUES (?, ?, ?, ?, ?, ?, ?)' with params [1,
"01234567890121345678", "BankOfAmerica", null, null, null, 6]:
SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column
"balance" violates not-null constraint DETAIL: Failing row contains
(1, 01234567890121345678, BankOfAmerica, 6, null, null, null).
Why Symfony or Doctrine doesn't deal with default values? I'm doing something wrong or I miss something here?
Account Entity
This is my account entity:
namespace BankBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Account
*
* #ORM\Entity
* #ORM\Table(name="waccount")
*/
class Account {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*
*/
protected $account_id;
/**
*
* #ORM\Column(type="string", length=20)
*/
protected $account_number;
/**
*
* #ORM\Column(type="string", length=150)
*/
protected $bank_name;
/**
* #ORM\OneToOne(targetEntity="BankBundle\Entity\AccountType")
* #ORM\JoinColumn(name="account_type", referencedColumnName="type_id")
*/
protected $account_type;
/**
*
* #ORM\Column(type="float")
*/
protected $balance;
/**
* #var \DateTime
* #ORM\Column(type="datetime")
*/
protected $created;
/**
* #var \DateTime
* #ORM\Column(type="datetime")
*/
protected $modified;
public function getAccountId() {
return $this->account_id;
}
public function setAccountNumber($account_number) {
$this->account_number = $account_number;
}
public function getAccountNumber() {
return $this->account_number;
}
public function setAccountType($account_type) {
$this->account_type = $account_type;
}
public function setBankName($bank_name) {
$this->bank_name = $bank_name;
}
public function getBankName() {
return $this->bank_name;
}
public function getAccountType() {
return $this->account_type;
}
public function setBalance($balance) {
$this->balance = (float) $balance;
}
public function getBalance() {
return $this->balance;
}
public function setCreated($created) {
$this->created = $created;
}
public function getCreated() {
return $this->created;
}
public function setModified($modified) {
$this->modified = $modified;
}
public function getModified() {
return $this->modified;
}
}
The table waccount exists on DB