Init with KH 3.1.3.1

This commit is contained in:
Deon George
2011-07-20 22:57:07 +10:00
commit 1dd63fc044
816 changed files with 79597 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
# Examples
- [Simple](examples/simple): Basic, one table model examples.
- [Validation](examples/validation): Full example of creating a user account and handling validation errors.
## @TODO:
The following is a sample list of examples that might be useful. Don't feel limited by this list, or consider these required. Items on the list can be combined, split up, removed or added to. All contribution are appreciated.
- Examples of changing things like $_table_name, $_labels, with, etc.
- Example of a one to one relationship.
- Example of one to many
- Example of many to many.

View File

@@ -0,0 +1,119 @@
# Simple Examples
This is a simple example of a single ORM model, that has no relationships, but uses validation on the fields.
## SQL schema
CREATE TABLE IF NOT EXISTS `members` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`first_name` varchar(32) NOT NULL,
`last_name` varchar(32) NOT NULL,
`email` varchar(127) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
## Model
<?php defined('SYSPATH') or die('No direct access allowed.');
class Model_Member extends ORM {
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'first_name' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'last_name' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array('regex', array(':value', '/^[-\pL\pN_.]++$/uD')),
),
'email' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 127)),
array('email'),
),
);
}
}
[!!] The array returned by `ORM::rules()` will be passed to a [Validation] object and tested when you call `ORM::save()`.
[!!] Please notice that defining the primary key "id" in the model is not necessary. Also the table name in the database is plural and the model name is singular.
## Controller
<?php defined('SYSPATH') or die('No direct access allowed.');
class Controller_Member extends Controller_Template {
public function action_index()
{
/**
* Example 1
*/
// Create an instance of a model
$members = ORM::factory('member');
// Get all members with the first name "Peter" find_all()
// means we get all records matching the query.
$members->where('first_name', '=', 'Peter')->find_all();
// Count records in the $members object
$members->count_all();
/**
* Example 2
*/
// Create an instance of a model
$member = ORM::factory('member');
// Get a member with the user name "bongo" find() means
// we only want the first record matching the query.
$member->where('username', '=', 'bongo')->find();
/**
* Example 3
*/
// Create an instance of a model
$member = ORM::factory('member');
// Do an INSERT query
$member->username = 'bongo';
$member->first_name = 'Peter';
$member->last_name = 'Smith';
$member->save();
/**
* Example 4
*/
// Create an instance of a model where the
// table field "id" is "1"
$member = ORM::factory('member', 1);
// Do an UPDATE query
$member->username = 'bongo';
$member->first_name = 'Peter';
$member->last_name = 'Smith';
$member->save();
}
}
[!!] $member will be a PHP object where you can access the values from the query e.g. echo $member->first_name

View File

@@ -0,0 +1,137 @@
# Validation Example
This example will create user accounts and demonstrate how to handle model and controller validation. We will create a form, process it, and display any errors to the user. We will be assuming that the Model_User class contains a method called `hash_password` that is used to turn the plaintext passwords into some kind of hash. The implementation of the hashing methods are beyond the scope of this example and should be provided with the Authentication library you decide to use.
## SQL schema
CREATE TABLE IF NOT EXISTS `members` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(32) NOT NULL,
`password` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;
## Model
<?php defined('SYSPATH') or die('No direct access allowed.');
class Model_Member extends ORM {
public function rules()
{
return array(
'username' => array(
array('not_empty'),
array('min_length', array(':value', 4)),
array('max_length', array(':value', 32)),
array(array($this, 'username_available')),
),
'password' => array(
array('not_empty'),
),
);
}
public function filters()
{
return array(
'password' => array(
array(array($this, 'hash_password')),
),
);
}
public function username_available($username)
{
// There are simpler ways to do this, but I will use ORM for the sake of the example
return ORM::factory('member', array('username' => $username))->loaded();
}
public function hash_password($password)
{
// Do something to hash the password
}
}
## HTML Form
Please forgive my slightly ugly form. I am trying not to use any modules or unrelated magic. :)
<form action="<?= URL::site('/members'); ?>" method="post" accept-charset="utf-8">
<label for="username">Username:</label>
<input id="username" type="text" name="username" value="<?= Arr::get($values, 'username'); ?>" />
<label for="username" class="error"><?= Arr::get($errors, 'username'); ?>
<label for="password">Password:</label>
<input id="password" type="password" name="password" value="<?= Arr::get($values, 'password'); ?>" />
<label for="password" class="error"><?= Arr::get($errors, 'password'); ?>
<label for="password_confirm">Repeat Password:</label>
<input id="password_confirm" type="password" name="_external[password_confirm]" value="<?= Arr::path($values, '_external.password_confirm'); ?>" />
<label for="password_confirm" class="error"><?= Arr::path($errors, '_external.password_confirm'); ?>
<button type="submit">Create</button>
</form>
## Controller
[!!] Remember that the `password` will be hashed as soon as it is set in the model, for this reason, it is impossible to validate it's length or the fact that it matches the `password_confirm` field. The model should not care about validating the `password_confirm` field, so we add that logic to the controller and simply ask the model to bundle the errors into one tidy array. Read the [filters](filters) section to understand how those work.
public function action_create()
{
$view = View::factory('members/create')
->set('values', $_POST)
->bind('errors', $errors);
if ($_POST)
{
$member = ORM::factory('member')
// The ORM::values() method is a shortcut to assign many values at once
->values($_POST, array('username', 'password'));
$external_values = array(
// The unhashed password is needed for comparing to the password_confirm field
'password' => Arr::get($_POST, 'password'),
// Add all external values
) + Arr::get($_POST, '_external', array());
$extra = Validation::factory($external_values)
->rule('password_confirm', 'matches', array(':validation', ':field', 'password'));
try
{
$member->save($extra);
// Redirect the user to his page
$this->request->redirect('members/'.$member->id);
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('models');
}
}
$this->response->body($view);
}
## Messages
**application/messages/models/member.php**
return array(
'username' => array(
'not_empty' => 'You must provide a username.',
'min_length' => 'The username must be at least :param2 characters long.',
'max_length' => 'The username must be less than :param2 characters long.',
'username_available' => 'This username is not available.',
),
'password' => array(
'not_empty' => 'You must provide a password.',
),
);
**application/messages/models/member/_external.php**
return array(
'password_confirm' => array(
'matches' => 'The password fields did not match.',
),
);

View File

@@ -0,0 +1,22 @@
# Filters
Filters in ORM work much like they used to when they were part of the Validate class in 3.0.x however they have been modified to match the flexible syntax of [Validation] rules in 3.1.x. Filters run as soon as the field is set in your model and should be used to format the data before it is inserted into the Database.
Define your filters the same way you define rules, as an array returned by the `ORM::filters()` method like the following:
public function filters()
{
return array(
'username' => array(
array('trim'),
),
'password' => array(
array(array($this, 'hash_password')),
),
'created_on' => array(
array('Format::date', array(':value', 'Y-m-d H:i:s')),
),
);
}
[!!] When defining filters, you may use the parameters `:value`, `:field`, and `:model` to refer to the field value, field name, and the model instance respectively.

View File

@@ -0,0 +1,22 @@
# ORM
Kohana 3.x includes a powerful Object Relational Mapping (ORM) module that uses the active record pattern and database introspection to determine a model's column information. ORM is integrated tightly with the [Validation] library.
The ORM allows for manipulation and control of data within a database as though it was a PHP object. Once you define the relationships ORM allows you to pull data from your database, manipulate the data in any way you like, and then save the result back to the database without the use of SQL. By creating relationships between models that follow convention over configuration, much of the repetition of writing queries to create, read, update, and delete information from the database can be reduced or entirely removed. All of the relationships can be handled automatically by the ORM library and you can access related data as standard object properties.
ORM is included with the Kohana 3.x install but needs to be enabled before you can use it. In your `application/bootstrap.php` file modify the call to Kohana::modules and include the ORM modules.
## Getting started
Before we use ORM, we must enable the modules required
Kohana::modules(array(
...
'database' => MODPATH.'database',
'orm' => MODPATH.'orm',
...
));
[!!] The database module is requried for the ORM module to work. Of course the database module has to be configured to use an existing database.
You can now create your [models](models) and [use ORM](using).

View File

@@ -0,0 +1,9 @@
## [ORM]()
- [Creating ORM Models](models)
- [Basic usage](using)
- [Relationships](relationships)
- [Validation](validation)
- [Filters](filters)
- [Examples](examples)
- [Simple](examples/simple)
- [Validation](examples/validation)

View File

@@ -0,0 +1,28 @@
# Creating your Model
To create a model for the table `members` in your database, create the file `application/classes/model/member.php` with the following syntax:
class Model_Member extends ORM
{
...
}
(this should provide more examples)
## Overriding the Table name
If you wish to change the database table that a model uses, just override the `$_table_name` variable like this:
protected $_table_name = 'strange_tablename';
## Changing the primary key
ORM assumes each model (and database table) has an `id` column that is indexed and unique. If your primary key column isn't named `id`, that's fine - just override the `$_primary_key` variable like this:
protected $_primary_key = 'strange_pkey';
## Use a non-default database
For each model, you can define which database configuration ORM will run queries on. If you override the `$_db_group` variable in your model, ORM will connect to that database. Example:
protected $_db_group = 'alternate';

View File

@@ -0,0 +1,123 @@
# Relationships
Kohana ORM supports four types of object relationships: `belongs_to`, `has_many`, `has_many "through"` and `has_one`. The `has_many "through"` relationship can be used to function like Active Record's `has_many_and_belongs_to` relationship type.
## belongs_to
A `belongs_to` relation should be used when you have one model that belongs to another. For example, a `Child` model belongs_to a `Parent` or a `Flag` model `belongs_to` a `Country`.
This is the base `belongs_to` relationship:
protected $_belongs_to = array(
'[alias name]' => array(
'model' => '[model name]',
'foreign_key' => '[column]',
),
);
You can omit any or all of the keys/values in the array on the right, in which case defaults are used:
protected $_belongs_to = array('[alias name]' => array());
The **alias name** is what is used to access the related model in your code. If you had a `Post` model that belonged to a `User` model and wished to use the default values of the `belongs_to` configuration then your code would look like this:
protected $_belongs_to = array('user' => array());
To access the user model, you would use `$post->user`. Since we're using the defaults above, the alias name will be used for the model name, and the foreign key in the posts table will be the alias name followed by `_id`, in this case it would be `user_id`. (You can change the `_id` suffix by modifying the `$foreign_key_suffix` variable in the model.)
Let's say your `Post` database table schema doesn't have a `user_id` column but instead has an `author_id` column which is a foreign key for a record in the `User` table. You could use code like this:
protected $_belongs_to = array(
'user' => array(
'foreign_key' => 'author_id',
),
);
If you wanted access a post's author by using code like `$post->author` then you would simply need to change the alias and add the `model` index:
protected $_belongs_to = array(
'author' => array(
'model' => 'user',
),
);
## has_many
The standard `has_many` relationship will likely fall on the other side of a `belongs_to` relationship. In the above examples, a post belongs to a user. From the user's perspective, a user has many posts. A has_many relationship is defined below:
protected $_has_many = array(
'[alias name]' => array(
'model' => '[model name]',
'foreign_key' => '[column]',
),
);
Again, you can omit all keys in the right array to use the defaults:
protected $_has_many = array('[alias name]' => array());
For our user and post example, this would look like the following in the user model:
protected $_has_many = array('posts' => array());
Using the above, the posts could be access using `$user->posts->find_all()`. Notice the `find_all()` used in this example. With `belongs_to` and `has_one` relationship types, the model is already loaded with necessary data. For `has_many` relationships, however, you may want to limit the number of results or add additional conditions to the SQL query; you can do so prior to the `find_all()`.
The model name used by default will be the singular name of the alias using the `inflector` class. In this case, `posts` uses `post` as the model name. The foreign key used by default is the owner model's name followed by `_id`. In this case, the foreign key will be `user_id` and it must exist in the posts table as before.
Let's assume now you want to access the posts using the name `stories` instead, and are still using the `author_id` key as in the `belongs_to` example. You would define your has_many relationship as:
protected $_has_many = array(
'stories' => array(
'model' => 'post',
'foreign_key' => 'author_id',
),
);
## has_one
A `has_one` relationship is almost identical to a `has_many` relationship. In a `has_one` relationship, there can be 1 and only 1 relationship (rather than 1 or more in a has_many). If a user can only have one post or story, rather than many then the code would look like this:
protected $_has_one = array(
'story' => array(
'model' => 'post',
'foreign_key' => 'author_id',
),
);
## has_many "through"
A `has_many "through"` relationship is used for many-to-many relationships. For instance, let's assume now we have an additional model, called `Category`. Posts may belong to more than one category, and each category may have more than one post. To link them together, an additional table is needed with columns for a `post_id` and a `category_id` (sometimes called a pivot table). We'll name the model for this `Post_Category` and the corresponding table `categories_posts`.
To define the `has_many` "through" relationship, the same syntax for standard has_many relationships is used with the addition of a 'through' parameter. Let's assume we're working with the Post model:
protected $_has_many = array(
'categories' => array(
'model' => 'category',
'through' => 'categories_posts',
),
);
In the Category model:
protected $_has_many = array(
'posts' => array(
'model' => 'post',
'through' => 'categories_posts',
),
);
To access the categories and posts, you simply use `$post->categories->find_all()` and `$category->posts->find_all()`
Methods are available to check for, add, and remove relationships for many-to-many relationships. Let's assume you have a $post model loaded, and a $category model loaded as well. You can check to see if the $post is related to this $category with the following call:
$post->has('categories', $category);
The first parameter is the alias name to use (in case your post model has more than one relationship to the category model) and the second is the model to check for a relationship with.
Assuming you want to add the relationship (by creating a new record in the categories_posts table), you would simply do:
$post->add('categories', $category);
To remove:
$post->remove('categories', $category);

View File

@@ -0,0 +1,75 @@
# Basic Usage
## Load a new model instance
To create a new `Model_User` instance, you can do one of two things:
$user = ORM::factory('user');
// Or
$user = new Model_User();
## Inserting
To insert a new record into the database, create a new instance of the model:
$user = ORM::factory('user');
Then, assign values for each of the properties;
$user->first_name = 'Trent';
$user->last_name = 'Reznor';
$user->city = 'Mercer';
$user->state = 'PA';
Insert the new record into the database by running [ORM::save]:
$user->save();
[ORM::save] checks to see if a value is set for the primary key (`id` by default). If the primary key is set, then ORM will execute an `UPDATE` otherwise it will execute an `INSERT`.
## Finding an object
To find an object you can call the [ORM::find] method or pass the id into the ORM constructor:
// Find user with ID 20
$user = ORM::factory('user')
->where('id', '=', 20)
->find();
// Or
$user = ORM::factory('user', 20);
## Check that ORM loaded a record
Use the [ORM::loaded] method to check that ORM successfully loaded a record.
if ($user->loaded())
{
// Load was successful
}
else
{
// Error
}
## Updating and Saving
Once an ORM model has been loaded, you can modify a model's properties like this:
$user->first_name = "Trent";
$user->last_name = "Reznor";
And if you want to save the changes you just made back to the database, just run a `save()` call like this:
$user->save();
## Deleting
To delete an object, you can call the [ORM::delete] method on a loaded ORM model.
$user = ORM::factory('user', 20);
$user->delete();

View File

@@ -0,0 +1,111 @@
# Validation
ORM models are tightly integrated with the [Validation] library and the module comes with a very flexible [ORM_Validation_Exception] that helps you quickly handle validation errors from basic CRUD operations.
## Defining Rules
Validation rules are defined in the `ORM::rules()` method. This method returns the array of rules to be added to the [Validation] object like so:
public function rules()
{
return array(
'username' => array(
// Uses Valid::not_empty($value);
array('not_empty'),
// Calls Some_Class::some_method('param1', 'param2');
array('Some_Class::some_method', array('param1', 'param2')),
// Calls A_Class::a_method($value);
array(array('A_Class', 'a_method')),
// Calls the lambda function and passes the field value and the validation object
array(function($value, Validation $object)
{
$object->error('some_field', 'some_error');
}, array(':value', ':validation')),
),
);
}
### Bound Values
ORM will automatically bind the following values with `Validation::bind()`:
- **:field** - The name of the field the rule is being applied to.
- **:value** - The value of the field the rule is being applied to.
- **:model** - The instance of the model that is being validated.
## Automatic Validation
All models automatically validate their own data when `ORM::save()`, `ORM::update()`, or `ORM::create()` is called. Because of this, you should always expect these methods to throw an [ORM_Validation_Exception] when the model's data is invalid.
public function action_create()
{
try
{
$user = ORM::factory('user');
$user->username = 'invalid username';
$user->save();
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors();
}
}
## Handling Validation Exceptions
The [ORM_Validation_Exception] will give you access to the validation errors that were encountered while trying to save a model's information. The `ORM_Validation_Exception::errors()` method works very similarly to `Validation::errors()`. Not passing a first parameter will return the name of the rules that failed. But unlike `Validate::errors()`, the first parameter of `ORM_Validation_Exception::errors()` is a directory path. The model's ORM::$_object_name will be appended to the directory in order to form the message file for `Validation::errors()` to use. The second parameter is identical to that of `Validation::errors()`.
In the below example, the error messages will be defined in `application/messages/models/user.php`
public function action_create()
{
try
{
$user = ORM::factory('user');
$user->username = 'invalid username';
$user->save();
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('models');
}
}
## External Validation
Certain forms contain information that should not be validated by the model, but by the controller. Information such as a [CSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) token, password verification, or a [CAPTCHA](http://en.wikipedia.org/wiki/CAPTCHA) should never be validated by a model. However, validating information in multiple places and combining the errors to provide the user with a good experience is often quite tedius. For this reason, the [ORM_Validation_Exception] is built to handle multiple Validation objects and namespaces the array of errors automatically for you. `ORM::save()`, `ORM::update()`, and `ORM::create()` all take an optional first parameter which is a [Validation] object to validate along with the model.
public function action_create()
{
try
{
$user = ORM::factory('user');
$user->username = $_POST['username'];
$user->password = $_POST['password'];
$extra_rules = Validation::factory($_POST)
->rule('password_confirm', 'matches', array(
':validation', ':field', 'password'
));
// Pass the extra rules to be validated with the model
$user->save($extra_rules);
}
catch (ORM_Validation_Exception $e)
{
$errors = $e->errors('models');
}
}
Because the validation object was passed as a parameter to the model, any errors found in that check will be namespaced into a sub-array called `_external`. The array of errors would look something like this:
array(
'username' => 'This field cannot be empty.',
'_external' => array(
'password_confirm' => 'The values you entered in the password fields did not match.',
),
);
This ensures that errors from multiple validation objects and models will never overwrite each other.
[!!] The power of the [ORM_Validation_Exception] can be leveraged in many different ways to merge errors from related models. Take a look at the list of [Examples](examples) for some great use cases.