Auto-generate forms with Formo + Kohana's ORM.

Formo makes it easy to share model fields, rules and filters with Kohana’s official ORM. You can generate a form directly from your ORM model. To integrate Formo with your ORM model, use the `Formo_ORM` trait in your model: ``` class Model_User extends ORM { use Formo_ORM; } ``` To make a form from your model, use `get_form()`. Here’s an example of making a form from the user model: ``` $user = ORM::factory('User', 10); // This makes a form from email, first, and last fields from the model $form = $user->get_form(['email', 'first', 'last']); if ($form->load()->validate()) { // Save the user model $user ->values($form->val()) ->save(); } ``` It’s that simple. ### Asterisk wildcard You can tell Formo to get all the model fields by using an asterisk. ``` // This adds all the fields from the model $form = $model->get_form(['*']); // This gets all the fields from the model and the custom field 'confirm_password' $form = $model->get_form(['*', 'custom_password']); ```
## Model rules When you create a form using `get_form()`, Formo automatically looks for and applies any rules returned from the `$model->rules()` method. Formo uses Kohana’s built-in validation library for all its validation, so it can use and understand rules inside the model. That means you don’t have to repeat any of these. Just define ‘em once inside your model. ## Sharing filters with the model Like rules, when you create a form using `get_form()`, Formo also looks for any filters returned from the `$model->filters()` method. Formo will use the filters as they are defined inside your model. ## The special `$model::formo()` method. After `get_form()` has added all your fields, rules and filters as defined in your model, Formo will then look for the presence of `$model::formo($form)` inside your model. If the method exists, Formo will call it. This gives you a chance to do anything else you may want to do with your form. Since the entire form object is passed into the method, you can manipulate it however you want. Here’s an example of using `model::formo()`: ``` $user = ORM::factory('User', 10); $form = $user->get_form(); // When the trait calls formo(), it passes the Formo object into the method public function formo($form) { $form->set_fields([ 'id' => ['render' => FALSE], 'class_id' => ['label' => 'Class', 'blank' => TRUE], 'email' => ['driver' => 'input|email',], 'logins' => ['render' => FALSE], 'last_login' => ['render' => FALSE], 'password' => ['render' => FALSE], ]); } ``` In this example, we are setting many fields (id, logins, last_login, password) to not render by default. Also, we are defining any special parameters for `class_id` and `email` fields so they render correctly by default. You can see the sky’s the limit inside your `$model::formo()` method. If the method is not defined in your model, Formo will not call anything. ### How to skip loading base rules from the model Though it's a good idea to always tack on the base rules from the mysql field definition data (such as NOT NULL, digit, range, maxlength), there may be times when you need to not append base rules to a form generated from formo. You define whether to add base rules during `$model->get_form([])` by the config setting `model_base_rules`. You can set this in the `formo.php` config file, or by individually setting adding the config setting to the object like this: ``` $form = Formo::form(['config.model_base_rules' => FALSE]) ->add($model->get_form()); ``` Or by using `Formo::set()`: ``` $form->set('config.model_base_rules', FALSE); ```
### Formo can auto-load relationship fields To do this, add `'formo' => TRUE` to your relationship definition for `has_one` and `belongs_to` relationships. Here's an example of a model's relationship definitions telling formo to auto-load relationships: ``` // In the model protected $_belongs_to = array('class' => array('formo' => TRUE)); // Then $form = $user->get_form(['name', 'class']); ```
Formo doesn't auto-load has_many relationships because the creating a field from every entry in the database gets ridiculous very quickly. Also, you may find that more often than not you want to narrow result sets for fields like belongs_to, etc. It's very easy with the Formo ORM trait. ## How to load a custom relationship You define it in your fields list as a custom relationship by making it an array. For instance, if your model **User** has many **Assignments**, you could do this: ``` $user = ORM::factory('User', 10); $form = $user->get_form([ 'email', // This means use the costom relationship 'assignments' and fetch the 'name' and 'date' records from it 'assignments' => [ 'name', 'date', ] ]); ``` When the Formo_ORM trait sees an array definition like this, it looks for a method in the model prepended by `formo_`. Thus in the example above, Formo_ORM looks for the method `$user->formo_assignments()`. In that custom formo relationship method, Formo expects either a Database Collection or Model to be returned. For this example, let's return a collection: ``` class Model_User extends ORM { use Formo_ORM; protected $_has_many => ['assignments' => []]; public function formo_assignments() { return $this->assignments->where('date', '>', strtotime('-1 week'))->find_all(); } } ``` The form creates a blueprint named 'assignments' and loads all the records as blueprint copies. This makes it very easy to add new dynamic records or delete any directly from the form. Also, in this example, only the fields 'name' and 'date' are taken from the assignment records and added to the form. ## Custom relationship summary There really is no limit to custom relationships with the Formo_ORM trait. The rules are simple: 1. If the custom relationship method returns a Database Collection, then a blueprint is created and padded with each row from the collection. 2. If the custom relationship method returns a model, the model is added to the form. ## Custom relationship model example Here's an example of using a custom formo relationship 'class'; In the user model: ``` public function formo_class() { return ORM::factory('Class')->where('teacher_id', '=', $this->id)->find(); } ``` Where you are creating your form ``` $user = ORM::factory('User', 10); $form = $user->get_form(['email', 'first', 'last', 'class' => ['start_time', 'name']]); ```
### Formo needs a primary val from the model When Formo auto-loads a foreign model, it uses the **primary_val** in to use for the display name in a select field. By default, Formo looks for a field named `name` in the model for this display name. ### How to define `primary_val` for a model If your table doesn't use a field name **name** for its primary value, you can set this by definining property `protected $_primary_val = 'myField'`;
The Formo_ORM trait will automatically understand enum and set fields using the metadata from the model to pre-populate exceptable values. By default, enums use the select driver, and sets use the checkboxes driver.
You may need to make formo field definition for special form fields that aren't fields in your ORM models. A good example is you may want to retrieve a "create user" form but include an extra field named "confirm password" that you re-use in several places in your site, but isn't a field inside the model. ### Request a non-model field To ask the Formo_ORM `get_form()` method to use a custom field, just add it to the array. ``` $form = $user->get_form(['email', 'password', 'confirm_password']); ``` In the example above, `email` and `password` are properties in the User ORM Model. But `confirm_password` isn't. ### Custom field naming convention Formo's naming convention is it looks for method named `_formo_$fieldname()`; Thus, in the example above, Formo looks for `User_Model::_formo_confirm_password()`. ### The costom field method must return an array Formo expects your custom field method to return an array. The minimum requirement for this array is that it has an alias definition. Here's an example of this method using the `confirm_password` example above: ``` protected function _formo_confirm_password() { return [ 'alias' => 'confirm_password', 'rules' => [ ['not_empty'], ['matches', [':value', ':confirm_password', ':password']] ] ]; } ```
comments powered by Disqus