【问题标题】:has_many through using custom table and field nameshas_many 通过使用自定义表和字段名称
【发布时间】:2012-12-27 21:44:22
【问题描述】:

我正在使用php.activerecord,我正在尝试将表格链接在一起。我没有使用 他们的 结构,但 php.activerecord 假设我是,所以它并不总是有效。我正在尝试在已经制作的应用上使用它,所以我无法更改数据库。

我从上一个问题 - Model association with custom table and key names 中了解到,我需要尽可能明确地使用 primary_keyforeign_key 字段。

我现在在使用 has_many through 时遇到问题。我不断收到NULL,我不知道为什么。

所以,这是一个场景:我有 3 个表,contactscontactPrefspreferences。这些表如下

contacts
--------
contactID
name
status

contactPrefs
------------
contactID
prefID
prefValue

preferences
-----------
prefID
name
description

每个contact 有多个contactPrefs。每个contactPrefs 都有一个preferences。我尝试使用has_many 来使其正常工作,但事实并非如此。这是我的模型:

Contacts.php:

<?php
class Contact extends ActiveRecord\Model {
    static $primary_key = 'contactID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid',
            'class_name' => 'ContactPref'
        ),
        array(
            'preferences',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'through' => 'prefs',
            'class_name' => 'Preference'
        )
    );
}

ContactPref.php:

<?php
class ContactPref extends ActiveRecord\Model {
    static $table_name = 'contactPrefs';

    static $belongs_to = array(
        array(
            'contact',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid'
        ),
        array(
            'preference',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid'
        )
    );
}

Preference.php:

<?php
class Preference extends ActiveRecord\Model {
    static $primary_key = 'prefID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'class_name' => 'ContactPref'
        )
    );
}

根据the docs,我现在应该能够做到以下几点:

<?php
var_dump(Contact::find(1234)->preference);

我不能。我得到NULL。奇怪的是,我可以这样做:

<?php
var_dump(Contact::find(1234)->prefs[0]->preference);

这工作正常。但是,我不应该能够通过contact 对象直接 访问preference 对象吗?我是否误解了文档(在我看来,它们不是最好的)?我是不是做错了什么?

【问题讨论】:

  • 我多次阅读您的问题,您似乎对如何访问属性有一种错觉。 一个 联系人 => 许多 联系人首选项。 一个 联系人首选项 => 一个 偏好。通过这个假设,Contact::find(1234) 导致一个联系人与许多首选项。您不能直接访问preference,您需要知道您指的是他的众多联系偏好中的哪一个。意思是-&gt;prefs[index]-&gt;preference。这是你已经知道的。您可以做的是为偏好设置一个特定的__get(),这将导致 ->prefs[0]->preference 始终或仅在您定义了一个偏好时。
  • @Khez:我不完全确定如何使用 php.activerecord,我还是新手。 Contact::find(1234)-&gt;prefs[0]-&gt;preference 工作正常,正如您所注意到的,但在 docs 中,您似乎应该能够“直接”访问 -&gt;preferences。文档不是很好,所以我不确定我的解释是否正确。
  • 文档特别说您可以使用$order-&gt;users(用户对象数组),在您的情况下是Contact::finc(1234)-&gt;prefs(首选项对象数组)。您可以通过它的索引(在您的示例中为 0)从该数组中选择一个特定的 pref 对象,然后通过执行 prefs[0]->preference 来获得唯一的一对一标识的偏好。对你来说不幸的是,你做的一切都是正确的,你特别想要的是黑客/特殊情况,或者不可能。你希望 Contact::find(1234)->preference 给你一个 Preference 对象数组吗?
  • @Khez:事情是Contact::find(1234)-&gt;prefs[0]-&gt;preference 工作没有 though。我试图理解through 的意义,但我想我不会。
  • 不得不做一些检查,我不确定为什么没有通过作品。可能是因为您给出了明确的外键关系。但!检查Unit Tests 的活动记录,您可以在 test_has_many_through 中查看预期的行为。你实际经历的那个。我将在稍后发布一个关于如何在这种情况下获得不同行为的答案

标签: php phpactiverecord


【解决方案1】:

首先,您正在阅读带有小缺陷的文档。在显示的文档中:

$order = Order::first();
# direct access to users
print_r($order->users); # will print an array of User object

您已经通过Contact::find(1234)-&gt;prefs 做的事情。让我稍微简化一下

$contact = Contact::find(1234);
# direct access to prefs
print_r($contact->prefs); # will print an array of ContactPref object

其次,你真正想要的是undefinedContact::find(1234)-&gt;preference 实际上应该做什么?返回第一个 ContactPref 的偏好?返回 Preference 对象的数组?

我想同时提供:

<?php
class Contact extends ActiveRecord\Model {
    static $primary_key = 'contactID';

    static $has_many = array(
        array(
            'prefs',
            'foreign_key' => 'contactid',
            'primary_key' => 'contactid',
            'class_name' => 'ContactPref'
        ),
        array(
            'preferences',
            'foreign_key' => 'prefid',
            'primary_key' => 'prefid',
            'through' => 'prefs',
            'class_name' => 'Preference'
        )
    );

    public function get_preference() {
        return isset($this->prefs[0])
            ? $this->prefs[0]->preference
            : null
        ;
    }
    public function get_preferences() {
        $preference=array();
        foreach($this->prefs as $pref) {
            $preference[]=$pref;
        }
        return $preference;
    }
}

让我解释一下我做了什么。 ActiveRecord\Model 类有一个 __get($name) 函数,用于查找另一个名为 get_$name 的函数>,其中 $name 在您的情况下是 preference(对于第一个结果)和 preference(对于整个集合)。这意味着您可以执行Contact::find(1234)-&gt;preference,这与执行Contact::find(1234)-&gt;prefs[0]-&gt;preference(但更安全,由于检查)和Contact::find(1234)-&gt;preferences 相同,以获取整个首选项集合。

这可以通过多种方式变得更好或优化,因此请不要照原样接受,但请尝试根据您的具体情况进行调整。 例如,您可以使用首选项的 id 作为数组中的索引,或者不强制从 ContactPrefs 加载比您将要使用的数据更多的数据,并尝试更复杂的查询获取您特别需要的首选项对象。

如果我通过通过在关系定义中工作找到了更好的实现,我会回来的。但是看到Unit Tests 的活跃记录,我很怀疑。

【讨论】:

  • 在文档中为什么$order-&gt;users 有效?为什么不是$order-&gt;payments[0]-&gt;user?这就是我感到困惑的地方。我希望Contact::find(1234)-&gt;preferences 给我一个Preference 对象数组,就像文档中的$order-&gt;users 给我一个User 对象数组一样。文档对 through 的实际作用非常困惑。
  • 我已经问过了,它似乎应该做你想做的事。有可能直接强制关键关系以某种方式使其失败。有时间就去做一些测试。附: Ty 进行编辑:D
  • 我认为主要问题是:我正在尝试在已经制作的数据库上执行此操作,因此它不符合他们的架构要求。另外,我想我对文档有点困惑。 Contact::find(1234)-&gt;prefs[0]-&gt;preference 让我得到我想要的。另外,感谢您花时间帮助我:-)
【解决方案2】:

有几件事情看起来很奇怪,所以很难为你找到“这会解决它”,但至少这是一个问题:

字段名在 phpactiverecord 中应该总是小写。无论哪种方式,SQL 都不介意(不是表名区分大小写,但列名不区分)。所以做这个:

static $primary_key = 'contactID';

进入

static $primary_key = 'contactid';

连接 // find 命令可以在 SQL 中使用,在这种情况下,你的键字符串如何“大小写”并不重要,所以有些东西可以工作。但是如果连接通过 phpmyadmin 的内部工作,它将失败。所以看看这个contactID还有prefID

同样,这仅适用于 COLUMN 名称,因此不要将类名或表名更改为小写。

(额外点:phpmyadmin 在组合主键方面存在问题。因此,虽然它可能很难看,但您可以在您的contactprefs 表中添加一个额外的行(如果您还没有),称为id,使该表实际上可以使用。它不会给您带来太多麻烦,并且会对 activerecord 库有很大帮助)

【讨论】:

  • php.activerecord 的假设在使用现有数据库时确实使事情变得具有挑战性。更改$primary_key 的大小写似乎并没有改变任何东西,将rowID 添加到contactPrefs 也没有。我可以得到contactscontactPrefs 链接,我可以得到contactPrefspreferences 链接,但我无法访问preferences 通过 contacts。我不知道我是否在我的模型中使用through
【解决方案3】:

尝试以下方法:

<?php 
var_dump(Contact::find(1234)->preferences);

文档说,对于 has_many 关系,它应该由复数 (http://www.phpactiverecord.org/projects/main/wiki/Associations#has_many_through) 引用。 Contact::find(1234) 返回一个 Contact 对象,该对象具有多个 contactPrefs 及其每个 Preference。此外,在您的联系人模型中,您将 has_many 指定为 preferences

static $has_many = array(
    array(
        'prefs',
        'foreign_key' => 'contactid',
        'primary_key' => 'contactid',
        'class_name' => 'ContactPref'
    ),
    array(
        'preferences',
        'foreign_key' => 'prefid',
        'primary_key' => 'prefid',
        'through' => 'prefs',
        'class_name' => 'Preference'
    )
);

通过修改编辑:

尝试以下联系模式

<?php
class Contact extends ActiveRecord\Model {
static $primary_key = 'contactID';

static $has_many = array(
    array(
        'prefs',
        'foreign_key' => 'contactid',
        'class_name' => 'ContactPref'
    ),
    array('preferences', 
          'through' => 'prefs', 
          'class_name' => 'Preference',
          'primary_key' => 'prefID')
);
}

【讨论】:

  • Contact::find(1234)-&gt;preferences 返回 NULL,Contact::find(1234)-&gt;preference 给出“未定义属性”错误。出于某种原因,through 链接不正确。
  • 我认为 foreign_keyprimary_key 字段是您为该表加入的字段。
  • 抱歉,我误读了数据库架构。请参阅更新的模型。根据架构,ContactPref 表没有主键。 contactid 和 prefid 都是分别引用联系人记录和偏好记录的外键。首选项表没有外键。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-09
  • 2011-09-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多