We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

New instances are created between related models

Hi there I have a little relationship. For example

class Invoice extends \Phalcon\Mvc\Model {
    const COL_ID = 'id';
    const COL_DATE = 'date';
    const REL_ITEMS = 'relItems';

    private $id;
    private $date;

    public function initialize() {
        $this->setSource('invoices');

        $this->hasMany(
            self::COL_ID,
            Item::class,
            Item::COL_INVOICE_ID,
            [
                'alias'      => self::REL_ITEMS,
                'reusable'   => true,
            ]
        );
    }

    public function getItems(): Simple 
    {
        return $this->{self::REL_ITEMS};
    }

    public function setItems(array $items = []) : self
    {
        $this->{self::REL_ITEMS} = $items;
        return $this;
    }

    // setters getters
}

class Item extends \Phalcon\Mvc\Model {
    const COL_ID = 'id';
    const COL_NAME = 'name';
    const COL_PRICE = 'price';
    const COL_INVOICE_ID = 'invoiceId';
    const REL_INVOICE = 'relInvoice';

    private $id;
    private $name;
    private $price;
    private $invoiceId;

    public function initialize() {
        $this->setSource('invoices_items');

        $this->belongsTo(
            self::COL_INVOICE_ID,
            Invoice::class,
            Invoice::COL_ID,
            [
                'alias'      => self::REL_INVOICE,
                'foreignKey' => [
                    'allowNulls' => false,
                    'message'    => 'Hey! where is my invoice?',
                ],
                'reusable'   => true,
            ]
        );
    }

    public function getInvoice(): ?Invoice 
    {
        return $this->{self::REL_INVOICE} ?: null;
    }

    public function setInvoice(Invoice $invoice) : self
    {
        $this->{self::REL_INVOICE} = $invoice;
        return $this;
    }

    // setters getters
}

$invoice = Invoice::findFirst(1234);
var_dump(spl_object_hash($invoice)); // ie: #000000003cc56d770000000007fa48c5

$item = $invoice->getItems()->getFirst();
var_dump(spl_object_hash($item->getInvoice())); // ie#000000003ccfffff0000000007fa48c5

Why I get a new instance of the same invoice?

Rgds

edited Aug '18

Static call vs. model instance? Try using instance in both cases.

I guess you would expect that both static and object instance would yield the same pointer in memory - only if the model was Singleton.

Using model instances didn't work

$invoice = (new Invoice())->findFirst(1234);
var_dump(spl_object_hash($invoice)); // 00000000546e8820000000005fdc6415

var_dump(spl_object_hash($invoice->getItems()->getFirst()->getInvoice())); // 00000000546efff0000000005fdc6415

How can I implement singleton pattern if constructor is reserved by Phalcon ORM?

edited Aug '18

Even the singleton pattern is not implementable because when you have to consult or manage more than one invoice you would always have a single instance of it.

We need a kind of model container for the fetched/mapped models and re-use them as they are related to the same element/record

Phalcon isn't smart enough to realize they are the same record. In order to make this work I think you'd need to create some sort of central registry or cache, then overwrite \Phalcon\Mvc\Model::find() and \Phalcon\Mvc\Model::__get() to set and check the registry respectively. You would want to be sure the registry check was optional, in case there is a time you do actually want to get a new object.