I'm opening this issue/bug report on request in this topic: https://forum.phalconphp.com/discussion/19903/robot-parts-not-updated-after-querying-parts-first
Please have a look at the code below. It's the default Robots/Parts/RobotsParts setup. Adding parts to the robot works fine. But when querying the parts first using $robot->parts and then adding new parts, nothing is saved to the database.
I commented the code below to illustrate what goes wrong. The models are stored in MySQL tables with cascading foreign keys.
<?php
class Robot extends \Phalcon\Mvc\Model
{
public $id;
public $name;
public function initialize()
{
$this->setSource('robots');
$this->hasManyToMany(
'id', RobotsParts::class, 'robot_id',
'part_id', Part::class, 'id',
['alias' => 'parts']
);
}
}
<?php
class RobotsParts extends \Phalcon\Mvc\Model
{
public $id;
public $robot_id;
public $part_id;
}
<?php
class Part extends \Phalcon\Mvc\Model
{
public $id;
public $name;
public function initialize()
{
$this->setSource('parts');
}
}
<?php
use Phalcon\Cli\Task;
class MainTask extends Task
{
/**
* Test sequence
*/
public function testAction()
{
$this->initAction();
$this->addAction();
$this->bugAction();
$robot = Robot::findFirstByName("bob");
foreach ($robot->parts as $part) {
printf("%s (id %d)\n", $part->name, $part->id);
}
}
/**
* Delete everything in the database,
* then add 1 robot with 2 parts.
*/
public function initAction()
{
Robot::find()->delete();
Part::find()->delete();
// The link table (RobotsParts) has cascading foreign keys,
// so they will be deleted by MySQL.
$part1 = new Part;
$part1->name = "arm";
$part2 = new Part;
$part2->name = "leg";
$robot = new Robot;
$robot->name = "bob";
$robot->parts = [$part1, $part2];
$robot->save(); // Works as expected
}
/**
* Add 2 more parts
*/
public function addAction()
{
$part3 = new Part;
$part3->name = "hand";
$part4 = new Part;
$part4->name = "foot";
$robot = Robot::findFirstByName("bob");
$robot->parts = [$part3, $part4];
$robot->save(); // Works as expected, Bob now has 4 parts.
}
/**
* Add 2 more parts, but query the existing parts first.
*/
public function bugAction()
{
$part5 = new Part;
$part5->name = "finger";
$part6 = new Part;
$part6->name = "toe";
$robot = Robot::findFirstByName("bob");
// Query the parts, for some reason,
// for example to check if the parts are in the db already...
$temp = $robot->parts;
$robot->parts = [$part5, $part6];
var_dump($robot->save()); // Returns true, but nothing is added to the db.
foreach ($robot->getMessages() as $message) {
printf("%s\n", $message); // No messages either...
}
}
}
Running the test:
$ php app/cli.php test
/home/gerben/public_html/link/app/tasks/MainTask.php:52:
bool(true)
arm (id 68)
leg (id 69)
hand (id 70)
foot (id 71)
Expected: 1 robot with 6 parts
Actual: 1 robot with 4 parts
This feels like a bug to me. Instead of using $robot->parts one can use $robot->getRelated('parts') or $robot->getParts() to work around this problem.
~I think this issue is also happening in V4.~
@gwijnja do you have the possibility to test this under v4-beta.1 ?
@ruudboon, I think I've already fixed this issue for v4 in #14035.
There were caching issues, where the queried related records and relations were stored along with the new ones.
Looks like it's fixed in v4 indeed.
Most helpful comment
@ruudboon, I think I've already fixed this issue for v4 in #14035.
There were caching issues, where the queried related records and relations were stored along with the new ones.