How can I store a model that was returned from $bean->box()
in RedBean?
For example, the following code doesn't work (it just inserts an empty row):
class Model_Comment extends RedBean_SimpleModel {
public $message;
}
$bean = R::dispense('comment');
$model = $bean->box();
$model->message = "Testing";
R::store($model);
It works if I use $model->unbox()->message = "Testing"
, but that's probably gonna get annoying real quick...
Obviously the code above is just an example, I could just set the property message
on $bean
here, but I want to be able to box a bean and pass it to other methods.
Is this how it's supposed to work, or am I missing something here?
This turned out to be caused by a "gotcha" when dealing with PHP's "magic" getter- and setter methods __get()
and __set()
.
Looking at the source code for RedBean_SimpleModel
, it actually uses the magic __set()
method to update its bean when setting a property.
Here's comes the gotcha, straight from the PHP documentation:
__set() is run when writing data to inaccessible properties.
__get() is utilized for reading data from inaccessible properties.
__isset() is triggered by calling isset() or empty() on inaccessible properties.
__unset() is invoked when unset() is used on inaccessible properties.
So it turns out that __set()
is never called for an existing (accessible) class member, i.e. public $message
. So I could just remove all the public fields from the class and that would solve the problem, but then I'd lose all the autocomplete functionality and lint checking in my IDE.
So I came up with this solution instead:
class MyBaseModel extends RedBeanPHP\SimpleModel {
public function __construct(){
foreach( get_object_vars($this) as $property => $value ){
if( $property != 'bean' )
unset($this->$property);
}
}
}
class Model_Comment extends MyBaseModel {
public $message;
}
This effectively removes all member variables from the class MyBaseModel
when it's instantiated, except $bean
, which of course is a vital part of RedBeanPHP_SimpleModel
.
Now I can easily subclass MyBaseModel
and have all the public fields I need in my subclass models, and the code in the original question will work.