Fuse
From RedBean
Contents |
Fuse
Fuse is a very handy technology to turn beans into models. With Fuse you can turn Beans into models without rewriting your old code. You can easily add validation rules for beans as well as other domain specific logic.
What is the difference between a model and a bean ?
A bean is just a container that holds some data. Besides data it has some methods to deal with the data; i.e. export it or store Meta data. In essence a bean is not really part of object oriented programming. A model, like a bean contains data but also contains logic that works with that data. A model binds together logic and data in a meaningful way. It makes sure the data is valid by implementing validation methods and it may offer additional methods operating directly on the data.
The idea behind Fuse
Before we discuss more practical examples of Fuse let me explain the basic idea. In essence Fuse is a simple idea. Fuse enables you to use beans as if they were models. If you invoke a non-existent method on a bean, the call is passed to the corresponding model. If you make RedBeanPHP open,dispense,store or delete (Observers) a bean a correspondig method is invoked on the model as well to make sure the model is in control.
Practical Fuse
Importing user data and storing it with RedBean is really simple (Tutorial):
$comment = R::dispense("comment");
$comment->import($_POST,"nickname,message");
R::store( $comment );
But how do you prevent users from using illegal nicknames? Of course you could check before you import the bean:
if (preg_match( $regexRule, $_POST["nickname"] )) { ... }
But this easily leads to validation rules scattering all over the place. It's better to put these rules into a model; but then you cannot use import() anymore? Well, actually with Fuse you can.
To add a validation rule to comment beans we type:
class Model_Comment extends RedBean_SimpleModel {
public function update() {
if (!preg_match( $somePattern, $this->nickname )) {
throw new Exception("Illegal nickname!");
}
}
}
$comment = R::dispense("comment");
$comment->import($_POST,"nickname,message");
R::store( $comment );
This will just work; RedBean will auto-discover the model (ModelFormatter) and all
events (update, open, delete) will be handled by this model.
Fuse works like this: imagine you have a bean of type coffee. Once you store a coffee bean Fuse will look for a model named Model_Coffee. If it finds such a model (ModelFormatter) it will create an instance of it, load the bean so that all the bean properties become accessible inside the model:
$bean->taste = "great";
will be accessible as:
Model_Coffee { ...
$this->taste;
... }
Then it will invoke one of the FUSE methods:
- update()
- open()
- delete()
- after_update() (not in legacy version yet)
- after_delete() (not in legacy version yet)
- dispense() (not in legacy version yet)
Using FUSE with associations
Imagine the following scenario. For a clinic you are writing a scheduling system. In the first version you add a line like:
R::associate( $doctor, $patient );
However in the next version of your software you need to take into account that in order to treat the patient both the patient and the physician need to reside in the same clinic. Of course you don't want to find all the associate-calls (Associations) and modify them. Thanks to FUSE you can add the validation logic for this on the fly as well:
class Model_Doctor_Patient extends RedBean_SimpleModel {
public function update() {
$patient = R::load("patient", $this->patient_id);
$doctor = R::load("doctor", $this->doctor_id);
if ($patient->clinicID !== $doctor->clinicID) {
throw new Exception("Clinic mismatch!");
}
}
}
R::setup();
$patient = R::dispense("patient");
$doctor = R::dispense("doctor");
$patient->clinicID = 9;
$doctor->clinicID = 10;
R::associate( $doctor, $patient );
This code will prevent the association from happening and throw an exception "Clinic mismatch!". As you see the mechanism is the same as for regular beans. Because an association is just another type of bean you can easily trap the association mechanism and hook-in your validation rule.
How it works
Fuse adds an event listener (Observers) to the RedBean object database. If an event occurs it creates an instance of the model that belongs to the bean. It looks for a class with the name Model_X where X is the type of the bean. If such a model exists, it creates an instance of that model and calls loadBean(), passing the bean. This will copy the bean to the internal bean property of the model (defined by the superclass [SimpleModel]). All bean properties will become accessible to $this because Fuse relies on magic getters and setters.
Custom Methods
Note that if there is model for a bean all calls will be routed to the model:
class Model_Dog extends RedBean_SimpleModel {
public function bark() { echo 'whaf!'; }
}
$dog = R::dispense("dog");
$dog->bark(); //works
Bean Server
Fuse can also be used to create a RedBeanPHP middleware server or for AJAX/JSON-RPC web applications. Visit the BeanCan_Server.
Formatting Your Model
The default name convention for models is Model_X, where X is the name of the bean. If you want to change this you can use the ModelFormatter
Also see: Observers,ModelFormatter


