I'm confused. Please have a look at the code below. It's the default robots/parts setup, with a hasManyToMany from robots to parts. I can add parts to a robot just fine. But if I query the parts just before adding new parts, nothing happens. I commented the code to illustrate what goes wrong. I'm not sure if this is expected behaviour or a bug related to i.e. the Model's dirtyState.
The models are stored in MySQL tables with cascading foreign keys. Here are the 3 model classes:
These are the model classes and my test code:
<?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
{
    public function initAction()
    {
        Robot::find()->delete();
        Part::find()->delete();
        // 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
    }
    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.
    }
    // Basically the same as addAction()
    public function bugAction()
    {
        $part5 = new Part;
        $part5->name = "finger";
        $part6 = new Part;
        $part6->name = "toe";
        $robot = Robot::findFirstByName("bob");
        $temp = $robot->parts; // Query the parts, for some reason. Not used here.
        $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...
        }
    }
    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);
        }
    }
}$ 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)Why are the $part5 and $part6 ignored? Is this a bug or expected behavior?