Rails依赖于Ruby的一些巧妙方面。其中之一就是能够回应未定义的方法。
考虑Dog
和Owner.
所有者has_many :dogs
和狗belongs_to :owner
之间的关系。
如果您进入script/console
,请使用fido = Dog.find(1)
获取狗对象,并查看该对象,您将看不到名为Owner.
的方法或属性
将看到的是owner_id
。如果你要求fido.owner
,对象会做这样的事情(至少,这就是它在我看来):
.owner
属性。我没有其中一个!NoMethodError
之前,我是否有关于如何处理这个问题的规则?owner_id
。PHP的文档是 - 咳咳 - 有时候有点缺乏,所以我想知道这里是否有人知道答案:
我可以为PHP中的对象定义类似的行为吗?
如果没有,您知道这些灵活模型连接的解决方法吗?
答案 0 :(得分:8)
您可以在PHP中实现__call()
方法,这对于调用其他无法访问的方法来说是一个全部。
class MyObj {
public function __call($name, $args) {
$list = $args ? '"' . implode('", "', $args) . '"' : '';
echo "Call $name($list)\n";
}
}
$m = new MyObj;
$m->method1(1, 2, 3);
$m->method2();
某些语言(例如Javascript)也具有所谓的一等函数。这实际上允许您动态添加或删除对象(或类)中的方法。 PHP语法(从5.3开始)排序支持这一点但它实际上并不可用。
$obj = new stdClass;
$obj->foo = function() {
echo "hello\n";
};
print_r($obj);
输出:
stdClass Object
(
[foo] => Closure Object
(
)
)
但请尝试:
$obj->foo();
你得到:
Fatal error: Call to undefined method stdClass::foo() in C:\xampp\htdocs\test.php on line 8
然而:
$f = $obj->foo;
$f();
正确输出hello
。
答案 1 :(得分:0)
经过一些进一步的研究,看来我真正想要的是PHP's overloading methods。戈登提供的链接代码,特别是他们提供的可下载示例,非常有启发性。
(尽管我的问题标题,我真正想要的是让对象响应未定义的属性。)
因此,__get()
和__set()
允许您指定用于获取和设置对象属性的方法,并且在这些方法中,您可以告诉对象如果不存在此类属性该怎么做。我们暂时只看__get()
。
回到我的Dog
示例,您可以像这样使用它:
class Dog{
// Private attributes, not accessible except through the __get method
private $bark_volume = 'loud';
private $owner_id = '5';
public function __get($name){
// If there's a property by that name, return it
if (isset($this->$name)){
return $this->$name;
}
// If not, let's see if there's an id with a related name;
// if you ask for $this->owner, we'll check for $this->owner_id
$join_id = $name . "_id";
if(isset($this->$join_id)){
// This is pretty useless, but the id could be used
// to do a join query instead
return $this->$join_id;
}
}
}
$Fido = new Dog;
echo $Fido->bark_volume; //outputs 'loud'
echo '<br/>';
echo $Fido->owner; //outputs '5'