I have two entities, View and Location
Each View can have a Location.
In my view I thus have:
class View
{
//..... Other Stuff.....
/**
* #ManyToOne(targetEntity="Location", inversedBy="views")
**/
private $location;
//...setters and getters....
public function setLocation($location){
$this->location = $location;
}
}
and then for my Location
class Location
{
//.....other stuff.....
/**
* #OneToMany(targetEntity="View", mappedBy="location")
**/
private $views;
public function __construct() {
$this->created = $this->updated = new \DateTime("now");
$this->views = new \Doctrine\Common\Collections\ArrayCollection();
}
// .... Getters and Setters ....
}
But when I try and do this:
<?php
$this->pageview = $this->em->getRepository('Entities\View')->find(1);
$this->location = $this->em->getRepository('Entities\Location')->find(1);
$this->pageview->setLocation($this->location);
$this->em->persist($this->pageview);
$this->em->flush();
?>
Or even when I create new entities:
<?php
$pv = new Entities\Pageview;
$lc = new Entities\Location;
$this->em->persist($lc);
$this->em->flush();
$pv->setLocation($lc);
$this->em->persist($pv);
$this->em->flush();
?>
Doctrine never sets the location_id in the database (it is always NULL).
I've checked the SQL queries and they're not even being attempted at being set, all I'm getting is:
INSERT INTO View (field1, field2, created, updated) VALUES ('val1', 'val2', '2013-07-17T12:10:56+01:00', '2013-07-17T12:10:56+01:00')
No reference to locations whatsoever...The weird thing is I can update field1 and field2 fine...and all other relations are working throughout my application...I just can't get views and locations to work...
EDIT
I have the exact some code working now on another computer. I don't know why it wasn't working, but I just moved the files back and restarted my computer and now it is...cacheing problem I guess?
Restarted my computer and the problem got solved...I don't know why it was going wrong!
Maybe something to do with caches or proxies...I dunno...
You could try explicitly referencing the correct columns that Doctrine needs to do a join on.
/**
* #ManyToOne(targetEntity="Location")
* #JoinColumn(name="location_id", referencedColumnName="id")
*/
private $location;
Also, in this example:
$this->pageview = $this->em->getRepository('Entities\View')->find(1);
$this->location = $this->em->getRepository('Entities\Location')->find(1);
$this->pageview->setLocation($this->location);
$this->em->persist($this->pageview);
$this->em->flush();
You do not need to persist the entity if you are just updating the existing data.
I think you need load the view in the location. So you must create a method in your Location entity like this:
public function getViews() {
return $this->views;
}
and then to persist into database, do this:
$location = new Entity\Location();
$view = new Entity\View();
$location->getViews()->add($view);
$this->em->persist($location)
$view->setLocation($location);
$this->em->persist($view);
$this->em->flush();
This is related to the Doctrine ORM cache drivers:
doctrine:
orm:
entity_managers:
default:
metadata_cache_driver: apcu
result_cache_driver: apcu
query_cache_driver: apcu
We used APCu to even on DEV do caching, clearing APCu (by restarting Apache) did the trick.
Related
I have added new entry in Document description
/**
* #MongoDB\Field(type="string")
*/
protected $city;
Then let Doctrine generate entities. Now newly created records have new field "city" with values as expected. However I can see these values only in mongo console. In Doctrine output they are allways set to "null". The entity entries seems correct
public function getFirstName()
{
return $this->firstName;
}
/**
* Get city
*
* #return string $city
*/
public function getCity()
{
return $this->city;
}
I have repository
public function allQuery($cat)
{
$q = $this->createQueryBuilder()
->sort('createdAt', 'DESC');
if ($cat) {
$q->field('category.$id')->equals(new \MongoId($cat));
}
return $q;
}
And service
function addAllPager($perPage = 10, $cat)
{
return $this->_addPager($this->repo()->allQuery($cat), $perPage);
}
In Controller
$helper = $this->get('appbundle.test.helper');
$tests = $helper->addAllPager(10, $cat);
Symfony profiler shows me query db.Test.find().sort({ "createdAt": -1 }).limit(10).skip(0). Dumped Contents of $tests
#firstName: "John"
#city: null
What I am missing?
EDIT
Cache clearing with php bin/console cache:clear solved the problem.
php bin/console doctrine:mongodb:cache:clear-metadata was not enough. Thank you malarzm.
I know this is 8 months after the question has been asked but had the same issue and fought with doctrine for a while. I am using Symfony 3 and I tried php bin/console doctrine:mongodb:cache:clear-metadata with no luck.
I finally ran the command php bin/console cache:clear or just delete the cache with this command sudo rm -rf var/cache and that fixed the issue.
I own an mssql database server, and connect to it using doctrine2(sqlsrv)
I would like to create the new entity instances with a given id. But if I try it, I get an error:
Cannot insert explicit value for identity column in table 'my_test_table' when IDENTITY_INSERT is set to OFF
I've removed the #GeneratedValue annotation. But I still get this error.
After that, I've run this script in the `SQL Server management studio:
SET IDENTITY_INSERT my_test_table ON
Unfortunately I still get the error, and I can't understand why
It has to be called on the doctrine's connection
$em->getConnection()->prepare("SET IDENTITY_INSERT my_test_table ON")->execute();
Something may be different with my setup, or something in Doctrine may have changed, but this wouldn't work for me with Doctrine ORM 2.5.6, PHP 7.0.17, and SQL Server 2014.
Despite setting it before my flush, it wouldn't work. It also couldn't work for multiple tables from a class hierarchy as IDENTITY_INSERT can be on for only one table at a time.
I was able to figure out how to do this by using a wrapper class for the connection. Doctrine supports this with the wrapperClass configuration parameter. Below is my code that worked.
<?php
declare(strict_types=1);
namespace Application\Db;
/**
* Class SqlSrvIdentityInsertConnection
* This class is to enable Identity Insert when using Doctrine with SQLServer.
* Must use this class with the "wrapperClass" configuration option
* for EntityManager::create
*/
class SqlSrvIdentityInsertConnection extends \Doctrine\DBAL\Connection
{
private $tables = [];
private $enabled = [];
public function enableIdentityInsertFor(string $tableName)
{
$this->tables[] = $tableName;
$this->enabled[$tableName] = false;
}
private function setIdentityInsert(string $statement) {
// Must turn off IDENTITY_INSERT if it was enabled, and this table
// isn't in the query. Must do this first!
foreach($this->tables as $tableName) {
if (stristr($statement, "INSERT INTO $tableName") === false) {
if ($this->enabled[$tableName]) {
parent::exec("SET IDENTITY_INSERT " . $tableName . " OFF");
$this->enabled[$tableName] = false;
}
}
}
foreach($this->tables as $tableName) {
if (stristr($statement, "INSERT INTO $tableName") !== false) {
parent::exec("SET IDENTITY_INSERT ".$tableName." ON");
$this->enabled[$tableName] = true;
// Only one can be enabled at a time
return;
}
}
}
public function prepare($statement)
{
$this->setIdentityInsert($statement);
return parent::prepare($statement);
}
}
Here is how it is used when you want to insert some entities with
$em->persist($newEntity);
/** #var SqlSrvIdentityInsertConnection $conn */
$conn = $em->getConnection();
$metadata = $this->session->getClassMetaData(MyEntityClass::class);
$metadata->setIdGeneratorType(ClassMetadata::GENERATOR_TYPE_NONE);
$conn->enableIdentityInsertFor($metadata->getTableName());
$em->flush();
so this is my prePersist on EventListener
public function prePersist(LifecycleEventArgs $args)
{
//the first entity will have the PMP, so we catch it and continue to skip this if after this
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//here it says that i have created a new entity, need to persist it via cascade={"persist"}
$taxonomicClass->setPmp($this->pmp);
}
}
}
that's fine, i had added the annotation on it:
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Pmp", cascade={"persist"})
* #ORM\JoinColumn(name="pmp_id", referencedColumnName="id", nullable=false)
**/
private $pmp;
and it saves everything from my hierarchy, even a new PMP, an object that already exist in the database!
what i want is that everything that im saving from my hierarchy needs to be related to the PMP that i passed, but when i set $taxonomicClass->setPmp($this->pmp); doctrine thinks that i created a new instance of PMP, since im not, i just want to this guy have an associaton with the PMP.
i tried put merge on the cascade option, but it only works with persist, how to make doctrine dont create a new instance, and instead use the one that i passed?
noticed my problem, i was assigning an attribute from memory, i should retrive him from database to doctrine understand.
public function prePersist(LifecycleEventArgs $args)
{
if ($this->pmp == null) {
$this->pmp = $args->getEntity()->getPmp();
}
$taxonomicClass = $args->getEntity();
if($taxonomicClass instanceof TaxonomicClass){
if(is_null($taxonomicClass->getId())){
//this solved the problem
$pmp = $args->getEntityManager()->getRepository("AppBundle:Pmp")->find($this->pmp->getId());
$taxonomicClass->setPmp($pmp);
}
}
}
i will keep in mind now that when a new entity is created, but it doesn't need to be saved, you must retrieve it from db, cascade={"persist"} wasn't even necessary
I'm working on a project that does NOT have a copy of production DB on development environment.
Sometimes we have an issue with DB migrations - they pass on dev DB but fail in production/testing.
It's often beacuse Dev environent data is loaded from Fixtures that use the latest entities - filling all tables properly.
Is there any easy way to make sure Doctrine Migration(s) will pass in production?
Do you have/know any way to write an automatic tests that will make sure data will be migrated properly without downloading the production/testing DB and running the migration manually?
I would like to avoid downloading a production/testing DB to dev machine so I can check migrations becasue that DB contains private data and it can be quite big.
First, you need to create a sample database dump in state before the migration. For MySQL use mysqldump. For postgres pg_dump, e.g.:
mysqldump -u root -p mydatabase > dump-2018-02-20.sql
pg_dump -Upostgres --inserts --encoding utf8 -f dump-2018-02-20.sql mydatabase
Then create an abstract class for all migrations tests (I assume you have configured a separate database for integration testing in config_test.yml):
abstract class DatabaseMigrationTestCase extends WebTestCase {
/** #var ResettableContainerInterface */
protected $container;
/** #var Application */
private $application;
protected function setUp() {
$this->container = self::createClient()->getContainer();
$kernel = $this->container->get('kernel');
$this->application = new Application($kernel);
$this->application->setAutoExit(false);
$this->application->setCatchExceptions(false);
$em = $this->container->get(EntityManagerInterface::class);
$this->executeCommand('doctrine:schema:drop --force');
$em->getConnection()->exec('DROP TABLE IF EXISTS public.migration_versions');
}
protected function loadDump(string $name) {
$em = $this->container->get(EntityManagerInterface::class);
$em->getConnection()->exec(file_get_contents(__DIR__ . '/dumps/dump-' . $name . '.sql'));
}
protected function executeCommand(string $command): string {
$input = new StringInput("$command --env=test");
$output = new BufferedOutput();
$input->setInteractive(false);
$returnCode = $this->application->run($input, $output);
if ($returnCode != 0) {
throw new \RuntimeException('Failed to execute command. ' . $output->fetch());
}
return $output->fetch();
}
protected function migrate(string $toVersion = '') {
$this->executeCommand('doctrine:migrations:migrate ' . $toVersion);
}
}
Example migration test:
class Version20180222232445_MyMigrationTest extends DatabaseMigrationTestCase {
/** #before */
public function prepare() {
$this->loadDump('2018-02-20');
$this->migrate('20180222232445');
}
public function testMigratedSomeData() {
$em = $this->container->get(EntityManagerInterface::class);
$someRow = $em->getConnection()->executeQuery('SELECT * FROM myTable WHERE id = 1')->fetch();
$this->assertEquals(1, $someRow['id']);
// check other stuff if it has been migrated correctly
}
}
I've figured out simple "smoke tests" for Doctrine Migrations.
I have PHPUnit test perfoming following steps:
Drop test DB
Create test DB
Load migrations (create schema)
Load fixtures (imitate production data)
Migrate to some older version
Migrate back to the latest version
This way I can test for the major issues, we've had recently.
Example of PHPUnit tests can be found on my blog: http://damiansromek.pl/2015/09/29/how-to-test-doctrine-migrations/
I am beginner to Simpletest and facing an issue while creating fixtures. As I am using cakephp 1.3.14 version for my application.
Created fixture with filename complaint_fixture.php
class ComplaintFixture extends CakeTestFixture {
var $name = 'Complaint';
var $import = array('table' => 'complaints', 'records' => true);
// do not truncate movie_stars table between tests
public function truncate($db) {
return null;
}
// do not drop movie_stars table between tests
public function drop($db) {
return null;
}
}
Created test case with name complaint.test.php
App::import('Model', 'Complaint');
class ComplaintTestCase extends CakeTestCase {
var $fixtures = array('app.Complaint');
function setUp($method) {
parent::setUp();
$this->Complaint = & ClassRegistry::init('Complaint');
// load data
$this->loadFixtures('Complaint');
}
function testFixture() {
$numberOfResults = $this->Complaint->find('count');
var_dump($numberOfResults);
}
/*
function testupdateComplaintStatus(){
$result = $this->Complaint->updateComplaintStatus(47,'ACT');
$this->assertEqual($result,1,'Status updated successfully!');
} */
}
As you can see in the above code, a fixture is created with name Complaint and then a test case is being used to load that fixture. So, what I have read on it from developer guide
- we do create a fixture with specifying the fields name and a records set
- load that fixture in test model class.
BUT, what I am looking for is to perform CRUD operations on test data which is being inserted into the test database. And, when I try to do the same with above given script, It starts affecting the production database records instead of test database.
If you see in the above code I have even stopped truncate and drop for test data, yet not able to sort out the issue.
Can anyone let me know what I have missed in the above code?