A model is a place to put validation and business logic. Imagine a Jazz band that can only have up to 4 members. We could implement this rule like this:
if (count($members) > 4)
throw new Exception('Too many!');
$band->ownMember = $members;
R::store($band);
However, now we need to add this check everytime we call R::store(). It would be much more convenient if R::store() was smart enough to do this check itself. We can accomplish this by putting the validation rule in our model. RedBeanPHP automatically discovers the models that belong to beans, so we can implement this validation like this:
class Model_Band extends RedBean_SimpleModel {
public function update() {
if (count($this->bean->ownMember)>4)
throw new Exception('Too many members!');
}
}
list($band,$members) = R::dispenseAll('band,member*5');
$band->ownMember = $members;
R::store($band); //will trigger exception
Now, every time we call store and something is wrong with the number of members, an exception will be triggered automatically. The mechanism that connects beans to models is called FUSE, because beans are fused with their models. Within a model, $this->bean refers to the bean. To create a model for your bean simply add a class and name it:
class Model_X extends RedBean_SimpleModel { ... }
where X is the name of the bean, starting with an uppercase character. So if you have a bean called: 'car', add a class named 'Model_Car'. Also make sure you extend RedBean_SimpleModel. On R::store(), RedBeanPHP will now call the method update() on the model instance.
Within the model, the $this->bean variable refers to the bean. Simply $this also refers to the bean but without returning references, in practice this can be very confusing so I recommend to use $this->bean.
If you use PHP namespaces and your model resides in namespace \Models simply add the following constant on top of your code:
define( 'REDBEAN_MODEL_PREFIX', '\Models\' );
After this line a bean of type 'dog' will connect to \Model\Dog. You can also use this constant to remap model classes in other namespaces. For more complex mappings see Custom Model Mappings.
Besides update() RedBeanPHP FUSE calls other methods on the model as well, here is a quick overview:
Action on bean | Invokes method on Model |
---|---|
R::store | $model->update() |
R::store | $model->after_update() |
R::load | $model->open() |
R::trash | $model->delete() |
R::trash | $model->after_delete() |
R::dispense | $model->dispense() |
To demonstrate the order and use of all of these methods let's consider an example:
$lifeCycle = '';
class Model_Bandmember extends RedBean_SimpleModel {
public function open() {
global $lifeCycle;
$lifeCycle .= "called open: ".$this->id;
}
public function dispense(){
global $lifeCycle;
$lifeCycle .= "called dispense() ".$this->bean;
}
public function update() {
global $lifeCycle;
$lifeCycle .= "called update() ".$this->bean;
}
public function after_update(){
global $lifeCycle;
$lifeCycle .= "called after_update() ".$this->bean;
}
public function delete() {
global $lifeCycle;
$lifeCycle .= "called delete() ".$this->bean;
}
public function after_delete() {
global $lifeCycle;
$lifeCycle .= "called after_delete() ".$this->bean;
}
}
$bandmember = R::dispense('bandmember');
$bandmember->name = 'Fatz Waller';
$id = R::store($bandmember);
$bandmember = R::load('bandmember',$id);
R::trash($bandmember);
echo $lifeCycle;
output:
called dispense() {"id":0}
called update() {"id":0,"name":"Fatz Waller"}
called after_update() {"id":5,"name":"Fatz Waller"}
called dispense() {"id":0}
called open: 5
called delete() {"id":"5","band_id":null,"name":"Fatz Waller"}
called after_delete() {"id":0,"band_id":null,"name":"Fatz Waller"}
Besides the standard methods mentioned above, any method on the model can be invoked by calling it on the bean (assuming it does not collide with a native bean method):
$dog = R::dispense('dog');
//call bark() on Model_Dog:
$dog->bark();
If you have a bean and you want to obtain the corresponding model use:
$dogBean = R::dispense('dog');
//get reference to Model_Dog
$dogModel = $dogBean->box();
Similarly, if you have a model and you want its inner bean, call:
$dogBean = $dogModel->unbox();
We call this technique boxing (and unboxing). This can be handy if you want to make use of typehinting:
public function addDog(Model_Dog $dog) {
...
}
Otherwise, we would have to use type RedBean_OODBBean which is less descriptive.
This page describes how RedBeanPHP automatically finds model classes for its beans. This means you can start your application with beans and add models later, without modifying the original code. This is exactly what RedBeanPHP is made for, to allow your code to evolve from simple scripts to a full-fledged, rich domain model. There is no need to start building the domain models first, you can prototype your application first and then let it grow into a domain model. If this is the first time you work with models and a domain model I recommend to read some more about domain driven design also known as DDD.
RedBeanPHP Easy ORM for PHP © 2024 Gabor de Mooij and the RedBeanPHP community - Licensed New BSD/GPLv2