Strange Doctrine 2 behaviour - symfony

I am using a Symfony command to retrieve member details from an API afor updating in a local database.
I query my local database and then loop through each member to retrieve data and update individually. However, doctrine is not updating these member details.
I have tried to use persist and merge but none of them seems to be working which is really baffling. Below is the code I am using, hopefully an extra pair of eyes will spot what I am not:
$members = $em->getRepository('AppBundle:Member')->findByNot(array('pin' => "'NULL'"),array('updated_at' => 'ASC'),4,0);
foreach($members as $member)
{
$details = json_decode($api_url->apiConnect(array('login' => $member->getId(), 'password' => $member->getPassword())), true);
$member->setFirstName($details['firstName']);
$member->setLastName($details['lastName']);
$member->setCard($details['card']);
//$em->merge($member);
$em->persist($member);
$em->flush();
}
When I check the mysql query log, none of the update queries are run.

Related

"There is no active transaction" in Doctrine migrations

I try to apply migrations, first three of them to create table, the last one - insert data.
When i run php bin/console doctrine:migrations:migrate it gives me an error "There is no active transaction" after each migration and had stoped migration. So i had to run migrations:migrate 4 times.
Whats could be the problem?
This issue existed before, but now is visible after PHP 8 PDO.
I will quote a great explanation located at your
vendor/doctrine/migrations/docs/en/explanation/implicit-commits.rst:
Implicit commits
Since PHP8, if you are using some platforms with some drivers such as
MySQL with PDO, you may get an error that you did not get before when
using this library: There is no active transaction. It comes from
the fact that some platforms like MySQL or Oracle do not support DDL
statements (CREATE TABLE, ALTER TABLE, etc.) in transactions.
The issue existed before PHP 8 but is now made visible by e.g. PDO,
which now produces the above error message when this library attempts
to commit a transaction that has already been commited before.
Consider the following migration.
public function up(Schema $schema): void
{
$users = [
['name' => 'mike', 'id' => 1],
['name' => 'jwage', 'id' => 2],
['name' => 'ocramius', 'id' => 3],
];
foreach ($users as $user) {
$this->addSql('UPDATE user SET happy = true WHERE name = :name AND id = :id', $user);
}
$this->addSql('CREATE TABLE example_table (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))');
}
When you run that migration, what actually happens with some platforms
is you get the updates inside an implicitly commited transaction, then
the CREATE TABLE happens outside that transaction, and then there
is an attempt to commit an non-existent transaction.
In that sort of situation, if you still wish to get the DML statements
inside a transaction, we recommend you split the migration in 2
migrations, as follows.
final class Version20210401193057 extends AbstractMigration
{
public function up(Schema $schema): void
{
$users = [
['name' => 'mike', 'id' => 1],
['name' => 'jwage', 'id' => 2],
['name' => 'ocramius', 'id' => 3],
];
foreach ($users as $user) {
$this->addSql('UPDATE user SET happy = true WHERE name = :name AND id = :id', $user);
}
}
}
final class Version20210401193058 extends AbstractMigration
{
public function up(Schema $schema): void
{
$this->addSql('CREATE TABLE example_table (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) DEFAULT NULL, PRIMARY KEY(id))');
}
public function isTransactional(): bool
{
return false;
}
}
Please refer to the manual of your database platform to know if you
need to do this or not.
At the moment, this library checks if there is an active transaction
before commiting it, which means you should not encouter the error
described above. It will not be the case in the next major version
though, and you should prepare for that.
To help you deal with this issue, the library features a configuration
key called transactional. Setting it to false will cause new
migrations to be generated with the override method above, making new
migrations non-transactional by default.
Solution 1
Disable the parameter transactional in your migrations config file as explained here.
Solution 2
Disable transactional migration only this time by adding --all-or-nothing=0 in your php bin/console doctrine:migrations:migrate command.
If you use PHP 8.0, implements "isTransactional" in migration class and return false (See https://github.com/doctrine/DoctrineMigrationsBundle/issues/393)
For me following code worked
public function isTransactional(): bool
{
return false;
}

how to stop saving data in associated tables in cakephp 3

i have a table(user table) which is associated to many tables. While saving data, it is getting saved in all associated table. But in some scenario i need to save only in base table (User) not in assoicatied table.
In cakephp 2 we have option callback => false, but how can we achive this in cake php 3?
You may specify the associated tables in which you want to save (cf: CakePHP ORM Documentation).
You could then do :
$this->Users->save($user, ['associated' => false]);
To disable the save in associated tables. (I have not tested as I'm at work, I will edit my message if it does not work for me !)
following code worked for me
$entity = $this->Users->newEntity($this->request->data, ['ignoreCallbacks' => true,'associated' => []]);
$result = $this->Users->save($entity);

Symfony 2.7 findOneBy() Function Finds the Wrong Entity

I've got a controller action that's supposed to be looking for semi-duplicate entries in a collection, and removing them from that Entity's list. New ones should not have an ID yet, and existing ones do, so I'm running findOneBy() with an array of parameters to match on (leaving out ID).
I am baffled and deeply troubled by the error I am getting, where it finds the wrong entity! I've got some relevant code below, I hope this is just a dream or a silly mistake, I can't reproduce the output on my own windows development environment, but my co-worker was testing on his mac, and was getting errors, so I went on his machine and did some quick echoing to see what was going on. See the code and the result below.
CODE
foreach($entity->getTechnicians() as $tech) {
//find new addtions, see if they exist
if (!$tech->getId()) {
echo "Technician " . $tech->getFirstName() . " has no ID, trying to find one that does<br/>";
$found = $em->getRepository('TechnicianBundle:Technician')->findOneBy(array(
'firstName' => $tech->getFirstName(),
'lastName' => $tech->getLastName(),
'email' => $tech->getEmail(),
'residentEngineer' => $tech->getResidentEngineer(),
'officeNumber' => $tech->getOfficeNumber()
));
//if one with an ID already exists
if ($found) {
echo "found technician " . $found->getFirstName() . " that already has id " . $found->getId() . "<br/>";
...
OUTPUT
Technician Four has no ID, trying to find one that does
found technician khjvuov that already has id 7
Probably it's not a findOneBy() issue.
If without add/remove calls it works, it may be caused by modified current pointer of ArrayCollection so when $tech->getFirstName() is called it actually points to another entry.
You may try to iterate your collection like this:
$iterator = $entity->getTechnicians()->getIterator();
while ($iterator->valid()) {
$tech = $iterator->current();
... $entity->addTechnician()/$entity->removeTechnician()
$iterator->next();
}
It will create new ArrayIterator object, so you can modify underlying object preserving ArrayCollection's internal pointer.
This may be obsolete if you are running PHP7 (reference - Note box)
Ultimately, the findOneBy() method uses the load() method of the Doctrine ORM.
In the doc, the parameter $criteria is described as such :
#param array $criteria The criteria by which to load the entity.
You should probably dig deeper into Doctrine API to make sure, but there is a big chance that in the array you are passing as an argument, only the first key => value pair is taken into account (in your example, 'firstName' => $tech->getFirstName()).
If this is the case, you'll probably need to add your own custom method in your entity repository, one that would allow you to query your database with more advanced statements (using OR/AND or LIKE() in SQL for instance).

foreign key not persisted symfony2 after a clone

I'm using symfony + Doctrine and I'm stuck with a problem:
I cloned an existing object and I would like to change a FK on the clone. It should be like that:
$dafCloned = clone $daf;
$dafState = $dafStateRepository->findOneBy(
array(
'name' => 'saved',
'dafType' => 'invoice',
'company' => $daf->getSeller(),
));
$dafCloned->setDafState($dafState);
var_dump($dafState->getId());
var_dump($dafCloned->getDafState()->getId());
$this->em->persist($dafCloned);
$this->em->flush();
As you may have noticed, I got 2 var_dump here. Here are the print of the Custom Command calling this code :
int(5500)
int(5499)
5500 is the id I should have in db for $dafCloned, 5499 is the id I have for $daf.
I'd like to know WHY I got different id...My dafState should be the same. I'm probably missing something really stupid but I'm stuck on it since 9am...I even tried to delete all caches we have, moving flush() and persist() but cant help :s
EDIT : added the setDafState() method if needed, but this is basic :
public function setDafState(DafState $dafState) {
$this->dafState = $dafState;
return $this;
}
EDIT2 :
Here getDafState() :
/**
* Get dafState
*
* #return MyPath\Entity\DafState
*/
public function getDafState() {
return $this->dafState;
}
If you need more code sample, just ask for it, I'll edit ;)
For the object, both are huge (Doctrine Object) and i can't find any way to get what could be useful :s. I cant grep dafState on $daf Object, output is still huge.
EDIT 3 :
if ($daf->getId() == 8902) // daf test which should be duplicated
var_dump($dafCloned->getDafState() === $dafState);
output
bool(true)
$dafCloned = clone $daf; // Here your clone is the same object as the old one
$dafState = $dafStateRepository->findOneBy( // Here you get some fresh object
array(
'name' => 'saved',
'dafType' => 'invoice',
'company' => $daf->getSeller(),
));
$dafCloned->setDafState($dafState); // Because this object is still managed by the entity manager it will set the $dafState on the old object (tracked by Id most likely)
var_dump($dafState->getId()); // Show the Id on the fresh object
var_dump($dafCloned->getDafState()->getId()); // Show the Id on the old object
$this->em->persist($dafCloned); // overwrite the old object
$this->em->flush();
This Post will be helpful to you: How to re-save the entity as another row in Doctrine 2
I will update my answer if this doesn't solve your issue
Here we go.
Thanks to #cheesemacfly i find out i have a prePersistListener which was resetting my dafState !
So, next time you have something weird looking like the above problem, check your listener !

Multiple concurrent database connections in drupal 7

I'm writing a wrapper class for my drupal 7 site which lets me connect to and query my phpbb database.
When connecting to an external data source (as per drupal documentation) you have set the active db, run the query, then set the active db back to the default.
e.g.
db_set_active('phpbb');
$result = db_query($sql,$args,$opts);
db_set_active();//back to default
But is there any way to use drupal's database wrapper to create a brand new connection which can be permanently set to the new database without having to do this switching back-and-forth nonsense? surely we can handle connections to multiple databases concurrently.
I have done some googling but haven't found anybody trying to do this as yet.
Typical. 5 minutes after posting i figure it out... so, for future googlers:
Basically, you don't use db_query, instead you run the query on your connection without setting the active link.
you can figure this out by looking at how db_query works:
http://api.drupal.org/api/drupal/includes--database--database.inc/function/db_query/7
So it looks like this:
$target='default';
$key = 'phpbb';
$phpbb = Database::getConnection($target,$key);
$result = $phpbb->query($sql,$args,$opts);
This assumes you have a database configured in your settings.php like the following:
$databases['phpbb']['default'] = array(
'driver' => 'mysql',
'database' => 'forum',
'username' => 'username',
'password' => 'password',
'host' => 'mysql.host.com',
'prefix' => 'phpbb3_'
);
Database::addConnectionInfo() perhaps?
This method allows the addition of new connection credentials at
runtime. Under normal circumstances the preferred way to specify
database credentials is via settings.php. However, this method allows
them to be added at arbitrary times, such as during unit tests, when
connecting to admin-defined third party databases, etc.
If the given key/target pair already exists, this method will be
ignored.
The definition for getConnection cites a different order for arguments than used above.
function getConnection($target = 'default', $key = NULL)
This is sadly different from Database::addConnectionInfo() which is
public static function addConnectionInfo($key, $target, $info)
Also, on DB_select, the $key is not a parameter, though it is in the options array:
function db_select($table, $alias = NULL, array $options = array()) {
if (empty($options['target'])) {
$options['target'] = 'default';
}
return Database::getConnection($options['target'])->select($table, $alias, $options);
}
while
final public static function getConnection($target = 'default', $key = NULL) {
so this implies that the 'master' or 'slave' or 'default' is always used as set, but not the key to the alternative database/schema, requiring the db_set_active('...'); and db_set_active(); around the db_select.
Since calls to other dbs can easily be required within the processing of the db_select (such as devel calls or calls in alters), this is inflexible design. Changing this call:
return Database::getConnection($options['target'])->select($table, $alias, $options);
to add the Key parameter (it is already spec'd as an argument!!) is needed but insufficient so far as I can now see.

Resources