PatchEntities creates new records instead patching existing

Hi,
I have a command which binds another class.
In this class, after sending data to Campaign Monitor, a date should be stored for the sent entities.
For this I wanted to use the patchEntities and saveMany, but this procedure does not patch the existing but creates new ones.
Now I don’t know what to do at this point. Does anyone of you have an idea?

this is call at execute()

    public function execute(Arguments $args, ConsoleIo $io)
    {
        $this->campaignMonitorService->processData();
    }

custom class

class AbandonedCarts
{
    use CampaignMonitorTrait;

    /**
     * @var \App\Model\Table\AbandonedCartsTable
     */
    private $abandonedCartsTable;

    private $abandonedCartsToSend;

    public function __construct()
    {
        $this->abandonedCartsTable = TableRegistry::getTableLocator()->get('AbandonedCarts');
        $listId = Configure::read('CampaignMonitor.abandonedCarts.listId');

        $this->initSubscribers($listId);
    }

    public function processData()
    {
        $this->abandonedCartsToSend = $this->getCandidates();
        $this->addSubscribersToList();
    }

    private function getCandidates()
    {
        $transferDelay = Configure::read('CampaignMonitor.abandonedCarts.transferDelay');

        return $this->abandonedCartsTable
            ->find()
            ->contain([
                'AbandonedCartItems' => [
                    'Products' => [
                        'fields' => ['Products.id'], // only get the id
                        'MainImage', // Use the custom association, defined in ProductsTable
                    ],
                ],
            ])
            ->where([
                'order_id IS' => null,
                'bill_email IS NOT' => null,
                'created <' => date('Y-m-d H:i:s', strtotime($transferDelay)),
                'campaign_monitor_at IS' => null,
            ]);
    }

    /**
     * Add Subscribers to Campaign Monitor List
     *
     * @return void
     * @throws \Exception
     */
    private function addSubscribersToList()
    {
        $subscriber = [];
        $currentTime = date('Y-m-d H:i:s');

        foreach ($this->abandonedCartsToSend as $abandonedCart) {
            $subscriber['EmailAddress'] = $abandonedCart->bill_email;
            $subscriber['Name'] = trim($abandonedCart->bill_first_name) . ' ' . trim($abandonedCart->bill_name);
            $subscriber['ConsentToTrack'] = 'yes';

            $this->setCustomFields($subscriber, $abandonedCart);
            $this->subscribers->add($subscriber);
        }

        $candidates = $this->getCandidates()->toList();

        $patchedCandidates = $this->abandonedCartsTable->patchEntities(
            $candidates,
            array_fill(0, count($candidates), ['campaign_monitor_at' => $currentTime])
        );
        $result = $this->abandonedCartsTable->saveMany($patchedCandidates);
    }
}

so got a Solution using QueryBuilder

$query = $this->abandonedCartsTable->query();
$query->update()
  ->set(['campaign_monitor_at' => new Time()])
  ->where(['id IN' => $idsToPatch])
  ->execute();

The ORM is built in such a way that primarily you update data per row via entities.
So if your update functionality is better suited to be represented via this kind of id IN then surely this is fine as well.

Just be aware this this kind of update query doesn’t trigger ORM events like beforeSave()

So patchEntities/saveMany can be used at Tables/Controllers only, right?
In this case i don’t need to trigger beforeSave()/afterSave() but what when, how to patch Entities than

I’d recommend you watch my workshop from this year where I go over my personal best practice how to implement a sync.

See https://www.youtube.com/live/GaCqzBuX5sc?si=oo2Umb0VooQyKKj1&t=1388

Many Thanks for sharing your Workshop Video :slight_smile: