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
Related
My userland code:-
$projects = $this->doctrine->getRepository(Project::class)->findBy(['deletionDate' => new DateTime('today + 364 day')]);
foreach($projects as $project){
$project = $this->entityManager->find('App\Entity\Project', $project->getId());
$this->entityManager->remove($project);
}
$this->entityManager->flush();
Here's the error:
An exception occurred while executing a query: SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreign key constraint fails (`foo`.`entry`, CONSTRAINT `FK_2B219D70166D1F9C` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`))
and here's what I'm attempting:-
class Entry
{
/**
* #ORM\ManyToOne(targetEntity=Project::class, inversedBy="entries")
* #ORM\JoinColumn(name="project_id", referencedColumnName="id", onDelete="CASCADE")
*
* #Assert\NotBlank
*/
public ?Project $project;
}
class Project
{
/**
* #ORM\OneToMany(targetEntity="Entry", mappedBy="project", cascade={"remove"})
*/
public Collection $entries;
}
This has nothing to do with Doctrine itself but with the general database rule - you cannot delete rows that other rows in the database depend on.
Now, something that caught my eye was:
#ORM\JoinColumn(name="project_id", referencedColumnName="id", onDelete="CASCADE")
Namely, you have onDelete="CASCADE", however:
`FK_2B219D70166D1F9C` FOREIGN KEY (`project_id`) REFERENCES `project` (`id`))
tells us a whole different story.
It seems that your database and your model are not in sync. Do you manage that by migrations? If so, did you run all of them?
You could try to:
php bin/console doctrine:migrations:diff
which will generate a single migration containing all the differences.
Be careful, inspect detected changes and apply them after making any necessary adjustments.
Update:
Given that you do not manage DB changes via migrations of any sort, the only way would be to execute the ALTER query by hand in order to fix this issue.
Something like this:
ALTER TABLE entry DROP FOREIGN KEY FK_2B219D70166D1F9C;
ALTER TABLE entry ADD FOREIGN KEY FK_2B219D70166D1F9C(project_id)
REFERENCES project(id) ON DELETE CASCADE;
I have a OneToMany Unidirectional relationship between an "Employee" and "Status".
There is then also a ManyToMany biderectional relationship between "Employee" and "Documents".
When I have my a Document, i am trying to find all related employees ($Document->getEmployees()) and then "filter" (using ->matching(Criteria)) by the "Status"
I keep getting the below error:
2018-04-05T14:35:19+00:00 [error] Error thrown while running command "app:expiration-check". Message: "Notice: Undefined index: Status"
In DefaultQuoteStrategy.php line 39:
Notice: Undefined index: Status
Here is the Code i am using:
$Employees = $Document->getEmployees()->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
Interestingly enough, the exact same criteria works if i am using the Employee Repository
$Employees = $this->em->getRepository(Employee::class)->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
Matching static fields also works fine.
$Employees = $Document->getEmployees()->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('FirstName',"Keven"))
);
Here is the Status Column defintion
/**
* #ORM\ManyToOne(targetEntity="Entity\Accounts\EmployeeStatus")
* #ORM\JoinColumn(name="StatusId", referencedColumnName="id", nullable=false)
*/
private $Status;
Here is the Employees Defintion (on Document Entity)
/**
* #ORM\ManyToMany(targetEntity="Entity\Accounts\Employee", mappedBy="Documents")
*/
private $Employees;
/**
* Constructor
*/
public function __construct()
{
parent::__construct();
$this->Employees = new \Doctrine\Common\Collections\ArrayCollection();
}
and Here is the getEmployees() (also on Document)
/**
* Get employees.
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getEmployees()
{
return $this->Employees;
}
To manage ManyToMany relations, doctrine uses Doctrine\ORM\Persisters\Collection\ManyToManyPersister class.
You can see it being used here
Unfortunately, currently in the latest release, v2.6.1, method loadCriteria of this class is lacking the feature to use relation fields. Only static fields are supported.
Looking at the master branch currently, this support has been added: Doctrine\ORM\Persisters\Collection\ManyToManyPersister as of today
but it is not part of a release yet. Also having a quick look at 2.7 branch it does not look it will be there.
I am not sure whether you could use the master branch with symfony `s doctrine bundle. I think it will be difficult to get this to work now.
What you could do, is initialize the ManyToMany collection $Document->getEmployees() and then use the matching function, which means that you load all employees and then filter, not lazy load as you would expect.
So do:
$employees = $Document->getEmployees();
$employees->initialize();
$employees->matching(
Criteria::create()
->andWhere(Criteria::expr()->eq('Status',$this->GlobalSettings->getApprovedEmployeeStatus()))
);
and put a note to change the code, when the new changes are released.
I'm actually learning Symfony3 and more precisely the Doctrine2 relation between objects and I was wondering if there is a default value for the cascade parameter when you don't explicite it.
I seen in tutorials when it's necessary to use the remove value that the parameter is not specified, but there is no explanation about this fact.
So I mean is this
/**
* #ORM\ManyToOne(targetEntity="UTM\ForumBundle\Entity\UtmWebsiteTopics")
* #ORM\JoinColumn(nullable=false)
*/
private $topic;
equivalent to that ?
/**
* #ORM\ManyToOne(targetEntity="UTM\ForumBundle\Entity\UtmWebsiteTopics", cascade={"remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $topic;
Thank you for reading and I hope you'll be able to bring me an answer. :D
In short, those two snippets are not the same. If you were to want to delete a specific entity that has relations to others through FK, you would need to explicitly remove() the related entities to avoid Integrity Constraint Violations.
Examples of each
Not defining cascade={"remove"}
public function removeEntityAction($id)
{
// Get entity manager etc....
$myEntity = $em->getRepository("MyEntity")->findBy(["id" => $id]);
foreach($myEntity->getTopics() as $topic) {
$em->remove($topic);
}
$em->remove($myEntity);
}
Defining cascade={"remove"}
public function removeEntityAction($id)
{
// Get entity manager etc....
$myEntity = $em->getRepository("MyEntity")->findBy(["id" => $id]);
$em->remove($myEntity);
}
Doctrine Cascade Operations
Doctrine - Removing Entities
I am creating some Fixtures in Symfony2 using Doctrine. I get the following error:
Integrity constraint violation: 1062 Duplicate entry '206-411' for key 'PRIMARY'
when I try to persist a many-to-many unidirectional association.
I understand the error, but I'm confused: isn't obvious that some IDs are duplicate in a many-to-many relationship?
If I'm wrong please correct me. I put my code below, any clarification is welcome.
Fixture file:
namespace sociaLecomps\SuperBundle\DataFixtures\ORM;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\OrderedFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\ORM\Query;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class LoadAssociationsData extends AbstractFixture implements OrderedFixtureInterface, ContainerAwareInterface
{
private $container;
public function setContainer(ContainerInterface $container = null){
$this->container = $container;
}
public function load(ObjectManager $manager)
{
$em = $this->container->get('doctrine')->getManager('default');
/*
* COURSE - STUDENT ASSOCIATION
*/
$courses = $em->createQuery('SELECT c FROM sociaLecompsSuperBundle:Course c')->getResult();
$students = $em->createQuery('SELECT s FROM sociaLecompsSuperBundle:Student s')->getResult();
$i=0;
for($j=0; $j<count($courses); $j++){
$course = $courses[$j];
//here I'm adding two different students to the same course
$s = array($students[$i], $students[$i++]);
$course->setSubscribedStudents($s);
$em->persist($course);
$i++;
}
$manager->flush();
}
}
Relationship declaration in Course class:
/**
* #ORM\ManyToMany(targetEntity="Student")
* #ORM\JoinTable(name="relation_course_student",
* joinColumns={#ORM\JoinColumn(name="course_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="student_id", referencedColumnName="id")}
* )
**/
private $subscribed_students;
public function __construct() {
$this->subscribed_students = new ArrayCollection();
}
Entities Student and Course are created, also with Fixtures, before attempting to create the association.
If I try to insert only one student per course it all works smoothly.
I see that your courses entities already exist because you're fetching them directly from the database ($courses = $em->createQuery('SELECT c FROM sociaLecompsSuperBundle:Course c')->getResult();). So you shouldn't be trying to persist the entity a second time. I suggest you use merge() this way:
$em->merge($course);
Note 1:
I see you're using Doctrine fixtures here and that students and courses have already been created. If they have been created through a Doctrine fixture as well consider using the addReference and getReference methods. Example here: https://github.com/doctrine/data-fixtures/blob/master/README.md#sharing-objects-between-fixtures
Note 2: Also you don't have a cascade option set into your subscribed_students association. Since the students already exist should not be an issue. Otherwise you can either set the cascade option or run a merge|persist on the student entity as well.
That was the stupidest thing.
I replaced:
$s = array($students[$i], $students[$i++]);
with
$s = array($students[$i], $students[++$i]);
Since it was a post-increment, the second insertion tried to put the same student into the database, resulting with an exact row duplicate.
Hope this helps someone.
I'm facing a problem while using $em->flush ();
the object $education is persisted throw $em->persist ($education).
The location is an entity in my project and is related to the education entity throw a many-to-one relation.
The error box Contain:
A new entity was found through the relationship 'XBundle\Entity\Education#location' that was not configured to cascade persist operations for entity: NewYork. 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"}).
How can i solve this issue?
Use cascade={"persist"} on the relation. E.g.:
/**
* #ORM\OneToOne(targetEntity="Foo\Bundle\Entity\User", mappedBy="bar", cascade={"persist"})
*/
protected $foo;
In Doctrine 2, cascade persistence doesn't happen automatically. Instead, you need to explicitly indicate that you want it. If you are using docblock annotations to specify your DB schema, that's achieved by adding the cascade attribute to your #ManyToOne association:
<?php
namespace XBundle\Entity;
/**
* #Entity
*/
class Education
{
//...
/**
* #ManyToOne(targetEntity="Location", cascade={"persist"})
*/
protected $location;
//...
}