My symfony app is using Doctrine to persist entities in mysql.
Today I updated my entities "Advertiser" and "Report" so there are relations between the two - as suggested in this post: When using EntityType to show a select, can't save entity from Symfony form
When I try creating a migration, it says that the database is already in sync.
php bin/console make:migration
Returns:
[WARNING] No database changes were detected.
The database schema and the application mapping information are already in sync.
However if I look at the table for the report, I see it still has the old schema:
+---------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------------+------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| advertiser_id | int(11) | NO | MUL | NULL | |
| start_date | date | NO | | NULL | |
| end_date | date | NO | | NULL | |
| deleted | tinyint(1) | NO | | NULL | |
+---------------+------------+------+-----+---------+----------------+
Even though my entity looks like this now:
class Report
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="date")
*/
private $start_date;
/**
* #ORM\Column(type="date")
*/
private $end_date;
/**
* #ORM\Column(type="boolean")
*/
private $deleted;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Advertiser", inversedBy="reports")
* #ORM\JoinColumn(nullable=false)
*/
private $advertiser;
public function getId(): ?int
{
return $this->id;
}
public function getStartDate(): ?\DateTimeInterface
{
return $this->start_date;
}
public function setStartDate(\DateTimeInterface $start_date): self
{
$this->start_date = $start_date;
return $this;
}
public function getEndDate(): ?\DateTimeInterface
{
return $this->end_date;
}
public function setEndDate(\DateTimeInterface $end_date): self
{
$this->end_date = $end_date;
return $this;
}
public function getDeleted(): ?bool
{
return $this->deleted;
}
public function setDeleted(bool $deleted): self
{
$this->deleted = $deleted;
return $this;
}
public function getAdvertiser(): ?Advertiser
{
return $this->advertiser;
}
public function setAdvertiser(?Advertiser $advertiser): self
{
$this->advertiser = $advertiser;
return $this;
}
}
I've been searching for solutions and have tried these, but no luck:
php bin/console doctrine:cache:clear-metadata
php bin/console doctrine:schema:update --force
Please let me know if you have any suggestion on how I can update my database with my updated schema.
I had the same issue, try clearing the cache with:
symfony console cache:clear
As a many to one relation, it's normal that you're database advertiser column only stores the key of the report as a "link" to it, so that's why Symfony doesn't see any changes in your DB.
Maybe you can also use :
php bin/console doctrine:schema:update --dump-sql
to see changes in your DB
php bin/console doctrine:schema:update --force
to apply changes without using migrations
Maybe it's useful for someone, but when using annotations make sure that the comment block follows the DocBlock format, the first line should have two asterisks: /** and not a single asterisk /*
DocBlock
/**
*
*/
PHP Multiline comment
/*
*
*
*/
Try using proper annotation setup may be missing required configuration, you can always try to validate your schema with bin/console doctrine:schema:validate:
/**
* #ManyToOne(targetEntity="App\Entity\Advertiser", inversedBy="reports")
* #JoinColumn(name="advertiser_id", referencedColumnName="id")
*/
private $advertiser;
And check Advertiser entity for issues as well, maybe it is missing primary key or something.
I believe that the problem was that I previously had a property called "advertiser_id" (int) on the report object. And possibly I was trying to change too many things at once for doctrine to manage. Previously, I had tried to remove the advertiser_id while adding the relation property for advertiser.
To get doctrine working again, I removed the advertiser property from the Report object - along with the getters and setters. I also removed the reverse lookup stuff from the Advertiser object. When I tried to run the migration, it seems like there were several migrations that it was trying to run - all doing the same thing: dropping a foreign key that doesn't exist. So I commented out all of those commands in the migration files and finally was able to get it to migrate. I also removed the advertiser_id property. The app is working again.
Then I tried adding the "advertiser" relation property back to the report. This time it worked as expected and I was able to migrate. So I think the issue is related to my object already having an advertiser_id property. Now that I've added the advertiser relation to the Report object, I see that doctrine added an advertiser_id column to the table. I suspect that it being present previously was the reason things broke down.
Thanks for the replies! Glad to have it working again.
Related
I have a simple question about Fixtures in Symfony and Doctrine.
I have an Entity "Project" and I would like to self-referencing this project. For example I tried this :
$project1 = new Question();
$this->addReference("initiatives", $project1); //referencing
$project1->setItem('Project Initiatives');
$project1->setInverse(false);
$project1->setProjectHead($this->getReference("initiatives")); // get the reference of himself
$manager->persist($project1);
My entity (extract) :
/**
* #ORM\ManyToOne(targetEntity=Project::class, inversedBy="projects")
*/
private $project_head;
/**
* #ORM\OneToMany(targetEntity=Project::class, mappedBy="project_head")
*/
private $projects;
public function setProjectHead(?self $project_head): self
{
$this->project_head = $project_head;
return $this;
}
But It does not work. I have this error :
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails
I have just ONE line in my db.
I think he can't UDPATE because when I try to add others fixtures, the message appears. But I don't know how to fix this.
Thanks a lot for helping
I have scoured the internet, books and forums looking for an answer to this, and am hoping someone on here can help.
I have a standard Symfony 2 project setup, using entity annotations in doctrine. The problem I have is that any of my entities that contain a datetime type constantly want to migrate. So after initial migration to the db i can re run docrine:schema:update --dump-sql and I still see this:
ALTER TABLE log CHANGE date date DATETIME NOT NULL;
ALTER TABLE message CHANGE created_on created_on DATETIME NOT NULL, CHANGE updated_on updated_on DATETIME NOT NULL;
ALTER TABLE module CHANGE start_date start_date DATETIME DEFAULT '0000-00-00 00:00:00' NOT NULL, CHANGE end_date end_date DATETIME NOT NULL;
ALTER TABLE scheduled_commands CHANGE last_execution last_execution DATETIME NOT NULL;
I could sit here all day running migrations and checking this and it will stay the same. The entities have nothing special in them either:
/**
* #var \DateTime
*
* #ORM\Column(name="start_date", type="datetime")
*/
private $startDate;
/**
* #var \DateTime
*
* #ORM\Column(name="end_date", type="datetime")
*/
private $endDate;
Does anyone have any ideas on this? I am now completely stumped :( .
The mysql setup is current 5.6.25 too
the columns structure look like this once migrated
`start_date` | DATETIME | NOT NULL,
`end_date` | DATETIME | NOT NULL
You need to ensure that types.datetime is correctly set up in doctrine.yaml
For a Collection, I want to check if a new added Part already exists in the database. And if so, that it'll be overwritten with the new value.
/**
* Add Part
*/
public function addPart(\MyBundle\Entity\FooTypePart $Part)
{
$part->setProduct($this);
$this->part[] = $part;
return $this;
}
/**
*/
public function removePart(\MyBundle\Entity\FooTypePart $part)
{
$this->part->removeElement($part);
}
/**
* Get Part
* #return \Doctrine\Common\Collections\Collection
*/
public function getPart()
{
return $this->part;
}
/**
* Set Part
*/
public function setPart($part)
{
$this->part = $part;
return $this;
}
The Part Entity has: ID, Category_id (FK), Product_id (FK), Part (Collection)
It is possible at the moment to add a new Part with the same name, also when there is already a Part with the same Product_id AND Category_id.
Making Part unique isn't the fix, because Part can be used for many Products/Categories.
The following example already exists in the database, with a different 'Part'. So it should do a update command.
<?php
$part = new FooTypePart();
$part->setCategory($specification);
$part->setProduct($product);
$part->setPart('DifferentNamingThenCurrentOne');
$xx->addSpecificationValue($part);
How? :-)
Just use the UniqueEntity validator to find an already existant Item in the collection.
You can specify uniqueness using multiple properties or a repository method. This way you can search for items only having a unique combination of name, product-id and category-id.
Create a validation group i.e. "unique" and find the non-unique/existant entity in your collection by looking for invalid entities.
... then update the existing entity with your new value. You will probably need some extra logic because there might be multiple fields with the same name added to your form collection.
The context is a Symfony2 project (2.0.23) using Doctrine2.
I have a Candidate entity with one to one relation Website. When creating a new candidate I set the Website entity like this:
Candidate.php:
<?php
use MyProject\Bundle\CoreBundle\Entity\Website;
public function initialize(Website $website)
{
$this->setWebsite($website);
The one to one relation is declared like this:
<?php
/**
* #var Website $website
*
* #ORM\OneToOne(targetEntity="Website")
* #ORM\JoinColumn(name="Website", referencedColumnName="Code")
*/
private $website;
Everything works fine locally. But on out testing and production servers, when creating a new candidate with a given Website, sometimes the associated entity is updated with default values just when persisting and flushing the main Candidate entity, here is the MySQL log:
/*!*/;
# at 11400207
#130628 9:26:32 server id 1 end_log_pos 11400399 Query thread_id=53611133 exec_time=0 error_code=0
SET TIMESTAMP=1372404392/*!*/;
UPDATE website SET Name = NULL, bEnabled = NULL ... WHERE Id = 2 AND Code = 2000
I persist with the method of the FOSUserBundle:
<?php
/**
* Updates a user.
*
* #param UserInterface $user
* #param Boolean $andFlush Whether to flush the changes (default true)
*/
public function updateUser(UserInterface $user, $andFlush = true)
{
$this->updateCanonicalFields($user);
$this->updatePassword($user);
$this->em->persist($user);
if ($andFlush) {
$this->em->flush();
}
}
I don't understand why. What is really weird is that only happens sometimes and it is quiet unpredictable.
Any suggestion or hint would be welcome... Thanks.
PS: The doctrine metadata cache was deactivated.
Edit1: Added persist, note that this is called through a FormHandler service.
Well it seems it was related to the main entity that had 2 #ORM\Id annotations. As it is an old database, it is not "Doctrine" friendly. There is a PK an auto-increment field, but the joins are made on a second column. So I removed the #ORM\Id annotation for the PK field.
I don't know why, maybe i am missing some basic logic but I always run again into the same issue. I can't persists ManyToMany collections, and it also faces me with OneToMany collections, though I can work around that.
I read through the doctrine documentation, and I think I do understand the thing with mappedBy and inversedBy (where the last one is always the owner and therefor responsible for persisting the data, please correct me if I am wrong).
So here's a basic example that I have right now, which I can't figure out.
I have an Entity called Site:
#Site.php
...
/**
* #ORM\ManyToMany(targetEntity="Category", mappedBy="sites")
*/
protected $categories;
and another one called Category:
#Category.php
...
/**
* #ORM\ManyToMany(targetEntity="Site", inversedBy="categories")
* #ORM\JoinTable(name="sites_categories")
*/
protected $sites;
Using the Symfony2 entity genenerator it added me some getters and setters to my Entites which look like this.
Site:
#Site.php
...
/**
* Add categories
*
* #param My\MyBundle\Entity\Category $categories
*/
public function addCategory(\My\MyBundle\Entity\Category $categories)
{
$this->categories[] = $categories;
}
/**
* Get categories
*
* #return Doctrine\Common\Collections\Collection
*/
public function getCategories()
{
return $this->categories;
}
The same counts for
Category:
#Category.php
...
/**
* Add sites
*
* #param My\MyBundle\Entity\Site $sites
*/
public function addSite(\My\MyBundle\Entity\Site $sites)
{
$this->sites[] = $sites;
}
/**
* Get sites
*
* #return Doctrine\Common\Collections\Collection
*/
public function getSites()
{
return $this->sites;
}
Fair enough.
Now in my controller, I am trying to persist a Site object:
public function newsiteAction() {
$site = new Site();
$form = $this->createFormBuilder($site); // generated with the FormBuilder, so the form includes Category Entity
// ... some more logic, like if(POST), bindRequest() etc.
if ($form->isValid()) {
$em = $this->getDoctrine()
->getEntityManager();
$em->persist($site);
$em->flush();
}
}
The result is always the same. It persists the Site Object, but not the Category entity. And I also know why (I think): Because the Category entity is the owning side.
But, do I always have to do something like this for persisting it? (which is actually my workaround for some OneToMany collections)
$categories = $form->get('categories')->getData();
foreach($categories as $category) {
// persist etc.
}
But I am running into many issues here, like I would have to do the same loop as above for deleting, editing etc.
Any hints? I will really give a cyber hug to the person who can clear my mind about that. Thanks!
.
.
.
UPDATE
I ended up changing around the relationship (owning and inverse side) between the ManyToMany mapping.
If somebody else runs into that problem, you need to be clear about the concept of bidrectional relationships, which took me a while to understand too (and I hope I got it now, see this link).
Basically what anserwed my question is: The object you want to persist must always be the owning site (The owning site is always the entity that has "inversed by" in the annotiation).
Also there is a concept of cascade annotation (see this link, thanks to moonwave99)
So thanks, and I hope that helps somebody for future reference! :)
Regarding OneToMany relationship, you want to know about cascade annotation - from Doctrine docs [8.6]:
The following cascade options exist:
persist : Cascades persist operations to the associated entities.
remove : Cascades remove operations to the associated entities.
merge : Cascades merge operations to the associated entities.
detach : Cascades detach operations to the associated entities.
all : Cascades persist, remove, merge and detach operations to associated entities.
following docs example:
<?php
class User
{
//...
/**
* Bidirectional - One-To-Many (INVERSE SIDE)
*
* #OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"})
*/
private $commentsAuthored;
//...
}
When you add comments to the author, they get persisted as you save them - when you delete the author, comments say farewell too.
I had same issues when setting up a REST service lately, and cascade annotation got me rid of all the workarounds you mentioned before [which I used at the very beginning] - hope this was helpful.