构造函数中的无限循环 - 创建相关对象的相同实例

Infinty loop in constuctor - Creating the same istances of related objects

我陷入了在模型中一遍又一遍地创建相同对象的无限循环。

但从头开始:

我有两个相关的模型,我们叫AB

A 可以有很多 BB 属于一个 A.

并且由于模型 B 引用 A 的字段,我将其添加为受保护的 属性 以避免在调用与 [ 相关的 B 方法时创建新对象=14=] 字段。

B 的构造函数如下所示:

public function __construct(int $id = null)
{
    parent::__construct($id);

    $a = $this->get('a_id'); 
    if ($a) {
        $this->a = new A($a);
    }
}

A

public function __construct(int $id = null)
{
    parent::__construct($id);

    $this->date = new CarbonPL($this->get('date'));
    $this->initB();
}

但后来我意识到调用 initB() 会再次创建相同的实例 o B 并创建相同的 A 等等

private function initB()
{
    if (!$this->isReferenced()) { // Checks wheter is instance exists in DB
        return;
    }
    $query = B::getIDQuery();
    $query .= ' WHERE is_del IS FALSE';
    $query .= ' AND a_id = ' . $this->id;

    $ids = Helper::queryIds($query);

    foreach ($ids as $id) {
        $this->B[] = new B($id);
    }
}

我需要 A 的对象包含 B 的加载,反之亦然,因为很多 B 引用 A 字段,反之亦然,但是如何防止自己陷入这种无限循环。

我很快想到 B 中的一个额外参数,即 A,如果是,我就不必重用构造函数了。但我不太喜欢这个解决方案,因为第二个参数,我想知道如何以不同(更好)的方式解决它,最好是这样你仍然只能输入标识符。

B 修复后的构造函数:

public function __construct(int $id = null, A $a = null)
{
    parent::__construct($id);

    if ($a) {
        $this->a = $a;
    } else {
        $a = $this->geti('a_id');
        if ($a) {
            $this->a = new A($a);
        }
    }
}

您提出的 2 参数解决方案没有任何问题,但如果您正在寻找其他选择...

解决这个问题的一种方法是为每个 class 创建一个实例缓存,这样当有人需要您的 class 实例时,他们不会调用 new() 他们仅当缓存中不存在具有该 ID 的实例时,才调用将构造新实例的工厂方法。

<?php

class A {
    private static $cache = array();
    
    private function __construct( $id ) {
        // A private constructor prevents any other class from creating new instances
        // It could be protected if you want to allow subclasses to call new A()
        parent::__construct( $id );
        $this->date = new CarbonPL($this->get('date'));
        $this->initB();
    }
    
    public static function create_for_id( $id ) {
        if ( isset( self::$cache[ $id ] ) ) {
            $result = self::$cache[ $id ];
        } else {
            $result = new A( $id );
            self::$cache[ $id ] = $result; // Save it for later reuse
        }
        return $result;
    }
}

在 class B 中,您可以做同样的事情,或者它最适合您的情况。 现在在您的 B 构造函数中(或任何您想要新 A 的地方)而不是调用 new A($id),您将调用 A::create_for_id( $id )。如果具有该 ID 的 A 已经存在,那么它将重用它,而不是构造无穷无尽的新实例。