Symfony2 - cannot update doctrine schema. Base table or view already exists - php

I have two separated Symfony projects working with one database.
The first project is on Symfony 3.2 and the second is on Symfony 2.8.
Database is MySQL.
All is in production stage and all is working fine.
Now I have some Entity classes in the first project and don't have them in the second one. We haven't needed the entities in the second project before but now I need to work with them in the second project.
I copied the entities from the first project to the second. We use annotations.
After this I checked my database and executed the command on the second project:
app/console doctrine:schema:update --force
And got the error: Base table or view already exists: 1050 Table 'crm_user' already exists.
If I execute the command with --dump-sql option (app/console doctrine:schema:update --dump-sql) I see creation the table that already exists!
CREATE TABLE crm_user (id INT AUTO_INCREMENT NOT NULL, ...
So the doctrine schema update doesn't see that the DB table has been already created. How to fix it?
I tried to clear all cache (cache:clear), doctrine metadata cache (doctrine:cache:clear-metadata), query cache (doctrine:cache:clear-query) and no success. I got the same error after this.
If I try to validate doctrine schema there will not be the new table.
And of course I cannot drop tables data because all is in production stage.
May be someone faced problems like this. I appreciate any suggestions.

I highly recommend not messing like this with two different projects and a single DB. If you need so, then simply let one be the Doctrine "master" where you do the modifications, and only there run the schema:update.
Better than that, and way more elegant, would be to create a vendor that you can import with composer and it's your Doctrine entities vendor.
This will then manage all the DB / Repositories and you can re-use it for many different projects having the code only in one place.
That will solve this and fix what you are not doing right in my point of view:
Having duplicate entities pointing to the same DB structure, which will be always be a pain to maintain, and it does not deal well with the KISS principle and code duplication.

If you can't include orm definitions using a shared project via composer like Jason suggested, you could create a regex to filter which tables each app should be concerned with.
Example with zend expressive:
/**
* #var \Doctrine\ORM\EntityManager $em
*/
$em = $container->get('doctrine.entity_manager.orm_default'); // substitute with how you get your entity manager
$filter = 'crm_user|other_table|another_table';
$em->getConnection()
->getConfiguration()
->setFilterSchemaAssetsExpression('/'.$filter.'/');
dynamically...
/**
* #var \Doctrine\ORM\EntityManager $em
*/
$em = $container->get('doctrine.entity_manager.orm_default');
$metadata = $em->getMetadataFactory()->getAllMetadata();
if(!empty($metadata)){
$filter = '';
/**
* #var \Doctrine\ORM\Mapping\ClassMetadata $metadatum
*/
foreach($metadata as $metadatum){
$filter .= ($metadatum->getTableName().'|');
$assocMappings = $metadatum->getAssociationMappings();
if(!empty($assocMappings)){
// need to scoop up manyToMany association table names too
foreach($assocMappings as $fieldName => $associationData){
if(isset($associationData['joinTable'])){
$joinTableData = $associationData['joinTable'];
if(isset($joinTableData['name']) && strpos($filter, $joinTableData['name']) === false){
$filter .= ($joinTableData['name'].'|');
}
}
}
}
}
$filter = rtrim($filter, '|');
$em->getConnection()->getConfiguration()
->setFilterSchemaAssetsExpression('/'.$filter.'/');
}

Related

ORMInvalidArgumentException: A new entity was found through the relationship For multiple iterations

I am getting doctrine error as explained below. I checked the solutions given in other questions but it's not something I am looking for.
So basically i have an action for generating exam cards as(just putting relevant code)-
function generateExamCardAction{
$examSetting = $this->getExamSetting();
$this->insertExamCard($classId, $examSetting);
$insertedExamCard = $this->fetchExamCard($classId);
$this->generatePdf($insertedExamCard);
}
And the insertExamCard function goes like this:
function insertExamCard($classId, $examSetting){
$em = $this->getEntityManager();
$examCard = new ExamCard();
$examCard->setClassId($classId);
$examCard->setExamSetting($examSetting);
$em->persist($examCard);
$em->flush();
$em->clear();
}
And the doctrine association between ExamCard and ExamSetting is as:
/**
* #ORM\ManyToOne(targetEntity="ExamSetting")
* #ORM\JoinColumn(name="ExamSettingId", referencedColumnName="id")
*/
protected $examSetting;
Please note that i have not cascaded it using cascade={"persist"} beacuse i dont want to manipulate ExamSetting table at any cost as it's master table.
Till here everything is working fine. ExamCard is getting inserted properly, then fetched properly and then generating pdf properly.
But now I have to iterate the logic in generateExamCardAction() for multiple classes.
So it would be like this:
function generateExamCardAction{
$examSetting = $this->getExamSetting();
foreach($classes as $classId){
$this->insertExamCard($classId, $examSetting);
$insertedExamCard = $this->fetchExamCard($classId);
$this->generatePdf($insertedExamCard);
}
}
In this case the the first iteration is working fine. That is for 1st class it's inserting ExamCard, then fetching and generating pdf properly.
But for second iteration it's giving below error:
Doctrine\ORM\ORMInvalidArgumentException: A new entity was found
through the relationship
'Application\Entity\ExamCard#examSetting' that was not
configured to cascade persist operations for entity
I believe the problem is with $em->clear().
I tried by changing it to $em->clear($examCard). In that case it doesn't give error but the data fetched by fetchExamCard() doesn't have ExamSetting fields set in ExamCard.
Could you please help me find why it's working for single iteration but not for multiple iterations?

How to prevent Doctrine from dropping computed/generated column

I use MariaDB for a Symfony project and have setup a computed column with:
ALTER TABLE history_event ADD quote_status_change SMALLINT AS (JSON_VALUE(payload, '$.change_set.status[1]'));
When I run Doctrine migrations with bin/console doctrine:schema:update, the computed column is dropped, probably because it doesn't appear anywhere in the HistoryEvent entity class.
How can I prevent Doctrine from dropping computed columns when I run migrations ?
I solved this in doctrine 2.10 using the OnSchemaColumnDefinition Event. My code looked something like this:
public function onSchemaColumnDefinition(SchemaColumnDefinitionEventArgs $eventArgs)
{
if ($eventArgs->getTable() === 'my_table') {
if (!in_array($eventArgs->getTableColumn()['field'], ['id', 'column_1', 'column_2'])) {
$eventArgs->preventDefault();
}
}
}
In my case I'm using Symfony 4.2 so I set the event listener class up as per.
I'm afraid you might be out of luck, there is an open feature request but it's not implemented yet: https://github.com/doctrine/doctrine2/issues/6434

Doctrine 2 - How to use objects retrieved from cache in relationships

I'm working in a project that use Doctrine 2 in Symfony 2 and I use MEMCACHE to store doctrine's results.
I have a problem with objects that are retrieved from MEMCACHE.
I found this post similar, but this approach not resolves my problem: Doctrine detaching, caching, and merging
This is the scenario
/**
* This is in entity ContestRegistry
* #var contest
*
* #ORM\ManyToOne(targetEntity="Contest", inversedBy="usersRegistered")
* #ORM\JoinColumn(name="contest_id", referencedColumnName="id", onDelete="CASCADE"))
*
*/
protected $contest;
and in other entity
/**
* #var usersRegistered
*
* #ORM\OneToMany(targetEntity="ContestRegistry", mappedBy="contest")
*
*/
protected $usersRegistered;
Now imagine that Contest is in cache and I want to save a ContestRegistry entry.
So I retrieve the object contest in cache as follows:
$contest = $cacheDriver->fetch($key);
$contest = $this->getEntityManager()->merge($contest);
return $contest;
And as last operation I do:
$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contest);
$this->entityManager->persist($contestRegistry);
$this->entityManager->flush();
My problem is that doctrine saves the new entity correctly, but also it makes an update on the entity Contest and it updates the column updated. The real problem is that it makes an update query for every entry, I just want to add a reference to the entity.
How I can make it possible?
Any help would be appreciated.
Why
When an entity is merged back into the EntityManager, it will be marked as dirty. This means that when a flush is performed, the entity will be updated in the database. This seems reasonable to me, because when you make an entity managed, you actually want the EntityManager to manage it ;)
In your case you only need the entity for an association with another entity, so you don't really need it to be managed. I therefor suggest a different approach.
Use a reference
So don't merge $contest back into the EntityManager, but grab a reference to it:
$contest = $cacheDriver->fetch($key);
$contestRef = $em->getReference('Contest', $contest->getId());
$contestRegistry = new ContestRegistry();
$contestRegistry->setContest($contestRef);
$em->persist($contestRegistry);
$em->flush();
That reference will be a Proxy (unless it's already managed), and won't be loaded from the db at all (not even when flushing the EntityManager).
Result Cache
In stead of using you own caching mechanisms, you could use Doctrine's result cache. It caches the query results in order to prevent a trip to the database, but (if I'm not mistaken) still hydrates those results. This prevents a lot of issues that you can get with caching entities themselves.
What you want to achieve is called partial update.
You should use something like this instead
/**
* Partially updates an entity
*
* #param Object $entity The entity to update
* #param Request $request
*/
protected function partialUpdate($entity, $request)
{
$parameters = $request->request->all();
$accessor = PropertyAccess::createPropertyAccessor();
foreach ($parameters as $key => $parameter) {
$accessor->setValue($entity, $key, $parameter);
}
}
Merge requires the whole entity to be 100% fullfilled with data.
I haven't checked the behavior with children (many to one, one to one, and so on) relations yet.
Partial update is usually used on PATCH (or PUT) on a Rest API.

Add a column to an existing entity in Symfony

I've been playing around with Symfony on my web server and I've been creating entity with doctrine for my database. I wanted to add a column to one of these entity... I wanted to do something like:
php app/console doctrine:modify:entity
Now I know that this command doesn't exists, but is there a way (without doing a whole migration) to simply add a column.
P.S. I know I could open the php file and textually add the column there and then update the schema, but I'm distributing this to some clients and I like a more "command-line-like" approach.
Actually, using Doctrine does not make sense at all to do something like you suggested.
Doctrine is a ORM (Object-Relational Mapping) tool. It means that you want to abstract the database from your PHP code, you delegate database stuff to Doctrine. Doctrine does a wonderful job on that area.
As you want to keep your customers/peers updated with the latest version of the model, you should use the Doctrine Migrations ( http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html ). That's the way to manage database updates. Moreover, it gives you complete control on what to do when upgrading/downgrading the database. You could, e.g., set default values before you add the FK.
The steps for adding a new property on the class should be:
For Symfony 2:
modify the class by:
Acme\MyBundle\Entity\Customer and add the property you want;
Acme\MyBundle\Entity\Customer
or modify the doctrine file:
Acme/MyBundle/Resources/config/doctrine/customer.yml
run the console command (it will add the proper set/get in the class)
php app/console doctrine:generate:entities AcmeMyBundle:Customer
run the console command
php app/console doctrine:migrations:diff
run the console command (it will place a new file on app/DoctrineMigrations)
php app/console doctrine:migrations:migrate
When you're deploying the new version of the code, all you got to do is update the source code and run the command above.
For Symfony 3:
modify the class by:
Acme\MyBundle\Entity\Customer and add the property you want;
Acme\MyBundle\Entity\Customer
or modify the doctrine file:
Acme/MyBundle/Resources/config/doctrine/customer.yml
run the console command (it will add the proper set/get in the class)
php bin/console doctrine:generate:entities AcmeMyBundle:Customer
run the console command (update database)
php bin/console doctrine:schema:update --force
Add new Column in Existing entity on Symfony. I have the same problem are there . after I long research best solutions are there.
solution work on Symfony 4
Example:
Blog entity already created inside one name column are there and I want to add description column. so simple enter
php bin/console make:entity Blog
After run this command you want to add new column
You definitely DO want to open the PHP/XML/YML file where your entity is defined and add the column there. Then, you use the commandline and say
console doctrine:schema:update
That way, your entity definitions are in sync with the database and your database gets updated.
There is a pitfall on step php app/console doctrine:migrations:diff described above when using doctrine migrations.
You made changes in entity, all seems valid, but the command on creating migrations php app/console doctrine:migrations:diff says "No changes detected in your mapping information."
And you can't find where's the old database structure placed, search in files found nothing.
For me, the key was to clear Redis cache.
php app/console redis:flushdb
It happens because of config.yml
snc_redis:
clients:
default:
type: predis
alias: default
dsn: redis://localhost
doctrine:
query_cache:
client: default
entity_manager: default
namespace: "%kernel.root_dir%"
metadata_cache:
client: default
entity_manager: default
document_manager: default
namespace: "%kernel.root_dir%"
result_cache:
client: default
namespace: "%kernel.root_dir%"
entity_manager: [default, read] # you may specify multiple entity_managers
After that, migrations:diff reread all of entities (instead of taking outdated ones metadata from cache) and created the right migration.
So, the full chain of steps for modifying entities is:
Modify your entity class (edit Entity file)
Clear Redis cache of metadata ( php app/console redis:flushdb )
Create (and may be edit) migration ( php app\console doctrine:migrations:diff )
Execute migration ( php app\console doctrine:migrations:migrate )
Of course, your doctrine metadata cache may be not Redis but something else, like files in app/cache or anything other. Don't forget to think about cache clearing.
May be it helps for someone.
I found a way in which I added the new column inside YourBundle/Resources/Config/doctrine/Yourentity.orm.yml.
Then added getter and setter methods inside Entity class.
Then did php app/console doctrine:schema:update --force from console. It worked for me.
FOR SYMFONY3 USERS...
here you have to follow two steps to make changes in your entity
Step1: Open Your Entity File..For EX: "Acme\MyBundle\Entity\Book"
Current Entity is having few fields like:id,name,title etc.
Now if you want to add new field of "image" and to make change constraint of "title" field then add field of "image" with getter and setter
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=500)
*/
private $image;
And add getter and setter
/**
* Set image
*
* #param string $image
*
* #return Book
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getimage()
{
return $this->image;
}
To update existing field constraint of title from length 255 to 500
/**
* #var string
*
* #ORM\Column(name="title", type="string", length=500)
*/
private $title;
Ok...You have made changes according to your need,And You are one step away.
Step 2: Fire this command in project directory
php bin/console doctrine:schema:update --force
Now,check your table in database ,It's Done!!
f you need to add a new field property to an existin entity you can use make:entity
$ php bin/console make:entity
Class name of the entity to create or update
"existingentity"
New property name (press to stop adding fields):
description
Field type (enter ? to see all types) [string]:
text
Can this field be null in the database (nullable) (yes/no) [no]:
no
New property name (press to stop adding fields):
(press enter again to finish)
information extracted from https://symfony.com/doc/current/doctrine.html
I think, you need to update your relation table manually to map the relations.
I found this:discussion
Again, We can generate entities from existing database, as a whole, but separately? :(
That's how it worked for me:
add new vars, setters and getters inside the existing class:
src-->AppBundle-->Entity-->YourClassName.php
update the ORM File:
src-->AppBundle-->Resources-->config-->doctrine-->YourClassName.orm.yml
run bash command:
php bin/console doctrine:schema:update --force

Copy a model to another database in symfony 1.4

Using Symfony 1.4 and doctrine I'd like to save a retrieved model to a different database connection:
retrieve model from master-database
change database connection to slave-database
save the model to the slave-database
I have the 2 connections defined in databases.yml.
here in pseudo-code:
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model->save($slaveConnection);
If I create a new model, $model=new model(); the "code" above successfully saves the model to the slave-connection.
What is going wrong?
According to the Symfony log, Symfony recognizes the model as existing and issues an update (instead of an insert).
UPDATE model SET updated_at = '2011-10-21 17:37:32' WHERE id = '1';
Although Symfony is using the correct database connection ('slave-connection'), the update fails because the model isn't present in the slave-database, yet.
And the insert into the slave-database should use all values of the model, not only the changed ones, too.
Anyone can point me to the right direction to save an existing model to a different database?
edit with my solution.
Thanks samura!
Just some additions:
After performing deep copy Symfony saved a new id. But I wanted to really clone the model object to the slave db and so, I had to modify the id.
That caused unique constraint exceptions, so I had to delete first. So this is it:
$id = $model->getId();
$slaveConnection->execute("delete from modeltable where id=".$id);
$model_copy = $model->copy(true); # deep copy
$model_copy->setId($id);
$model_copy->save($slaveConnection);
hope this helps if someone else stumbles.
You could use the public function copy($deep = false) method of the Doctrine_Record class.
$model = [retrieved from master-database];
$slaveConnection = Doctrine_Manager::getInstance()
->getConnection('slave-connection');
$model_copy = $model->copy(true); # deep copy
$model_copy->save($slaveConnection);

Categories