雄辩的ORM-Laravel为什么不认为这两个对象是相同的?

时间:2019-01-11 19:14:43

标签: php laravel

我对Laravel还是很陌生。当我在两个模型之间able to get multiple Eloquent relationships defined之后,我注意到可以将实例传递给它不属于的父模型实例中的方法。因此,我认为在方法本身中强制建立这种关系可能会很好。

在Organization.php中:

/**
 * @param \App\Contact The Contact to delete
 */
public function deleteContact(Contact $contact)
{
    if($contact->organization == $this)
    {
        $this->contacts->find($contact->id)->delete();
    }
    else
    {
        dd('Error: Contact ' . $contact->fname . ' ' . $contact->lname . ' #' . $contact->id . ' does not belong to Organization ' . $this->name . ' #' . $this->id);
    }
}

但是,在传递实际属于的联系人时,我一直收到自定义错误消息,或者至少我认为他们确实如此!当我在tinker中测试关系时,它为Bob和Bob的组织返回了不同的实例id

>>> $org = Organization::find(17)
=> App\Organization {#2929
     id: 17,
     name: "Test Org",
     primary_contact_id: 33,
   }
>>> $org->contacts
=> Illuminate\Database\Eloquent\Collection {#2925
     all: [
       App\Contact {#2926
         id: 33,
         organization_id: 17,
         fname: "Alice",
         lname: "Abbot",
       },
       App\Contact {#2943
         id: 34,
         organization_id: 17,
         fname: "Bob",
         lname: "Baker",
       },
     ],
   }
>>> $bob = Contact::find(34)
=> App\Contact {#2940
     id: 34,
     organization_id: 17,
     fname: "Bob",
     lname: "Baker",
   }
>>> $bob->organization == $org
=> false
>>> $bob->organization
=> App\Organization {#2932
     id: 17,
     name: "Test Org",
     primary_contact_id: 33,
   }

显然,它正在比较两个单独的实例。为了解决这个问题,我必须将条件更改为$contact->organization->id == $this->id才有效。但是,这样做的口才要少得多。

  1. 为什么要生成两个不同的实例?这些是随后的调用(理论上应该产生相同的实例)(或者我认为是这样)。

  2. 是否还需要这种额外的保护?由于内部代码的缘故,我们至少受到了部分保护,但是try / catch周围没有什么可以保护我的内容,并且无法妥善处理违规行为-我需要先进行测试,然后再尝试删除。 (如果传递的Contact不是Organization的有效相关实例,则find()返回null,导致PHP错误而不是Exception。)是否存在更好的方法来做到这一点,还是我想得太多?

2 个答案:

答案 0 :(得分:6)

从Laravel的角度来看,我们不能仅仅依靠相等运算符来比较对象,在Eloquent模型的情况下,这可能相当不可靠且难以比较。

正如@Devon和@N Mahurin也正确地说的那样,如果要比较更可预测的stdClass对象,则使用相等运算符比较对象会很好。

在过去,这是一场斗争。幸运的是,这个PR使其成为5.3中Laravel的核心。

要知道两个模型是否基本相同,Laravel会检查

  1. 主键相同
  2. 表是一样的
  3. 数据库连接是相同的。

基于此,我们可以假设我们正在比较两个相等的行。因此,在您的情况下,以下方法应该起作用:

$bob->organization->is($org);

请参考documentation进行比较。

答案 1 :(得分:0)

已经针对第一个问题进行了一些测试。似乎当您在模型内部调用关系时,该模型实例的关系数组充满了该关系。您的$this->contacts->find($contact->id)->delete();造成了这种情况。即使您在比较之后之后调用了关系。我对此行为感到有些惊讶,但是事实确实如此。由于两个模型实例现在包含不同的值(一个实例具有填充的关系数组),因此比较失败。不过,有一个新的is()方法,请参见https://www.neontsunami.com/posts/comparing-model-equality-in-laravel。这可能对您有帮助。

有几种不同的方式来处理第二个问题。我个人只是像这样在已经使用的if/else中汇总测试:

if(($contact->organization == $this) && ($this->contacts->find($contact->id)?? false))

使用??如果您的find()返回null,运算符将返回false。