Init with KH 3.1.3.1
This commit is contained in:
49
includes/kohana/modules/orm/auth-schema-mysql.sql
Normal file
49
includes/kohana/modules/orm/auth-schema-mysql.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
CREATE TABLE IF NOT EXISTS `roles` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(32) NOT NULL,
|
||||
`description` varchar(255) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_name` (`name`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation');
|
||||
INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `roles_users` (
|
||||
`user_id` int(10) UNSIGNED NOT NULL,
|
||||
`role_id` int(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`user_id`,`role_id`),
|
||||
KEY `fk_role_id` (`role_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(127) NOT NULL,
|
||||
`username` varchar(32) NOT NULL DEFAULT '',
|
||||
`password` varchar(64) NOT NULL,
|
||||
`logins` int(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`last_login` int(10) UNSIGNED,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_username` (`username`),
|
||||
UNIQUE KEY `uniq_email` (`email`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `user_tokens` (
|
||||
`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`user_id` int(11) UNSIGNED NOT NULL,
|
||||
`user_agent` varchar(40) NOT NULL,
|
||||
`token` varchar(40) NOT NULL,
|
||||
`type` varchar(100) NOT NULL,
|
||||
`created` int(10) UNSIGNED NOT NULL,
|
||||
`expires` int(10) UNSIGNED NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uniq_token` (`token`),
|
||||
KEY `fk_user_id` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
ALTER TABLE `roles_users`
|
||||
ADD CONSTRAINT `roles_users_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT `roles_users_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE `user_tokens`
|
||||
ADD CONSTRAINT `user_tokens_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE;
|
53
includes/kohana/modules/orm/auth-schema-postgresql.sql
Normal file
53
includes/kohana/modules/orm/auth-schema-postgresql.sql
Normal file
@@ -0,0 +1,53 @@
|
||||
CREATE TABLE roles
|
||||
(
|
||||
id serial,
|
||||
"name" varchar(32) NOT NULL,
|
||||
description text NOT NULL,
|
||||
CONSTRAINT roles_id_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT roles_name_key UNIQUE (name)
|
||||
);
|
||||
|
||||
CREATE TABLE roles_users
|
||||
(
|
||||
user_id integer,
|
||||
role_id integer
|
||||
);
|
||||
|
||||
CREATE TABLE users
|
||||
(
|
||||
id serial,
|
||||
email varchar(318) NOT NULL,
|
||||
username varchar(32) NOT NULL,
|
||||
"password" varchar(64) NOT NULL,
|
||||
logins integer NOT NULL DEFAULT 0,
|
||||
last_login integer,
|
||||
CONSTRAINT users_id_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT users_username_key UNIQUE (username),
|
||||
CONSTRAINT users_email_key UNIQUE (email),
|
||||
CONSTRAINT users_logins_check CHECK (logins >= 0)
|
||||
);
|
||||
|
||||
CREATE TABLE user_tokens
|
||||
(
|
||||
id serial,
|
||||
user_id integer NOT NULL,
|
||||
user_agent varchar(40) NOT NULL,
|
||||
token character varying(32) NOT NULL,
|
||||
created integer NOT NULL,
|
||||
expires integer NOT NULL,
|
||||
CONSTRAINT user_tokens_id_pkey PRIMARY KEY (id),
|
||||
CONSTRAINT user_tokens_token_key UNIQUE (token)
|
||||
);
|
||||
|
||||
CREATE INDEX user_id_idx ON roles_users (user_id);
|
||||
CREATE INDEX role_id_idx ON roles_users (role_id);
|
||||
|
||||
ALTER TABLE roles_users
|
||||
ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
ADD CONSTRAINT role_id_fkey FOREIGN KEY (role_id) REFERENCES roles(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE user_tokens
|
||||
ADD CONSTRAINT user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
INSERT INTO roles (name, description) VALUES ('login', 'Login privileges, granted after account confirmation');
|
||||
INSERT INTO roles (name, description) VALUES ('admin', 'Administrative user, has access to everything.');
|
3
includes/kohana/modules/orm/classes/auth/orm.php
Normal file
3
includes/kohana/modules/orm/classes/auth/orm.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Auth_ORM extends Kohana_Auth_ORM { }
|
277
includes/kohana/modules/orm/classes/kohana/auth/orm.php
Normal file
277
includes/kohana/modules/orm/classes/kohana/auth/orm.php
Normal file
@@ -0,0 +1,277 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
/**
|
||||
* ORM Auth driver.
|
||||
*
|
||||
* @package Kohana/Auth
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2011 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Kohana_Auth_ORM extends Auth {
|
||||
|
||||
/**
|
||||
* Checks if a session is active.
|
||||
*
|
||||
* @param mixed $role Role name string, role ORM object, or array with role names
|
||||
* @return boolean
|
||||
*/
|
||||
public function logged_in($role = NULL)
|
||||
{
|
||||
// Get the user from the session
|
||||
$user = $this->get_user();
|
||||
|
||||
if ( ! $user)
|
||||
return FALSE;
|
||||
|
||||
if ($user instanceof Model_User AND $user->loaded())
|
||||
{
|
||||
// If we don't have a roll no further checking is needed
|
||||
if ( ! $role)
|
||||
return TRUE;
|
||||
|
||||
if (is_array($role))
|
||||
{
|
||||
// Get all the roles
|
||||
$roles = ORM::factory('role')
|
||||
->where('name', 'IN', $role)
|
||||
->find_all()
|
||||
->as_array(NULL, 'id');
|
||||
|
||||
// Make sure all the roles are valid ones
|
||||
if (count($roles) !== count($role))
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( ! is_object($role))
|
||||
{
|
||||
// Load the role
|
||||
$roles = ORM::factory('role', array('name' => $role));
|
||||
|
||||
if ( ! $roles->loaded())
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return $user->has('roles', $roles);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a user in.
|
||||
*
|
||||
* @param string username
|
||||
* @param string password
|
||||
* @param boolean enable autologin
|
||||
* @return boolean
|
||||
*/
|
||||
protected function _login($user, $password, $remember)
|
||||
{
|
||||
if ( ! is_object($user))
|
||||
{
|
||||
$username = $user;
|
||||
|
||||
// Load the user
|
||||
$user = ORM::factory('user');
|
||||
$user->where($user->unique_key($username), '=', $username)->find();
|
||||
}
|
||||
|
||||
// If the passwords match, perform a login
|
||||
if ($user->has('roles', ORM::factory('role', array('name' => 'login'))) AND $user->password === $password)
|
||||
{
|
||||
if ($remember === TRUE)
|
||||
{
|
||||
// Token data
|
||||
$data = array(
|
||||
'user_id' => $user->id,
|
||||
'expires' => time() + $this->_config['lifetime'],
|
||||
'user_agent' => sha1(Request::$user_agent),
|
||||
);
|
||||
|
||||
// Create a new autologin token
|
||||
$token = ORM::factory('user_token')
|
||||
->values($data)
|
||||
->create();
|
||||
|
||||
// Set the autologin cookie
|
||||
Cookie::set('authautologin', $token->token, $this->_config['lifetime']);
|
||||
}
|
||||
|
||||
// Finish the login
|
||||
$this->complete_login($user);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Login failed
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces a user to be logged in, without specifying a password.
|
||||
*
|
||||
* @param mixed username string, or user ORM object
|
||||
* @param boolean mark the session as forced
|
||||
* @return boolean
|
||||
*/
|
||||
public function force_login($user, $mark_session_as_forced = FALSE)
|
||||
{
|
||||
if ( ! is_object($user))
|
||||
{
|
||||
$username = $user;
|
||||
|
||||
// Load the user
|
||||
$user = ORM::factory('user');
|
||||
$user->where($user->unique_key($username), '=', $username)->find();
|
||||
}
|
||||
|
||||
if ($mark_session_as_forced === TRUE)
|
||||
{
|
||||
// Mark the session as forced, to prevent users from changing account information
|
||||
$this->_session->set('auth_forced', TRUE);
|
||||
}
|
||||
|
||||
// Run the standard completion
|
||||
$this->complete_login($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a user in, based on the authautologin cookie.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function auto_login()
|
||||
{
|
||||
if ($token = Cookie::get('authautologin'))
|
||||
{
|
||||
// Load the token and user
|
||||
$token = ORM::factory('user_token', array('token' => $token));
|
||||
|
||||
if ($token->loaded() AND $token->user->loaded())
|
||||
{
|
||||
if ($token->user_agent === sha1(Request::$user_agent))
|
||||
{
|
||||
// Save the token to create a new unique token
|
||||
$token->save();
|
||||
|
||||
// Set the new token
|
||||
Cookie::set('authautologin', $token->token, $token->expires - time());
|
||||
|
||||
// Complete the login with the found data
|
||||
$this->complete_login($token->user);
|
||||
|
||||
// Automatic login was successful
|
||||
return $token->user;
|
||||
}
|
||||
|
||||
// Token is invalid
|
||||
$token->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently logged in user from the session (with auto_login check).
|
||||
* Returns FALSE if no user is currently logged in.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function get_user($default = NULL)
|
||||
{
|
||||
$user = parent::get_user($default);
|
||||
|
||||
if ( ! $user)
|
||||
{
|
||||
// check for "remembered" login
|
||||
$user = $this->auto_login();
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a user out and remove any autologin cookies.
|
||||
*
|
||||
* @param boolean completely destroy the session
|
||||
* @param boolean remove all tokens for user
|
||||
* @return boolean
|
||||
*/
|
||||
public function logout($destroy = FALSE, $logout_all = FALSE)
|
||||
{
|
||||
// Set by force_login()
|
||||
$this->_session->delete('auth_forced');
|
||||
|
||||
if ($token = Cookie::get('authautologin'))
|
||||
{
|
||||
// Delete the autologin cookie to prevent re-login
|
||||
Cookie::delete('authautologin');
|
||||
|
||||
// Clear the autologin token from the database
|
||||
$token = ORM::factory('user_token', array('token' => $token));
|
||||
|
||||
if ($token->loaded() AND $logout_all)
|
||||
{
|
||||
ORM::factory('user_token')->where('user_id', '=', $token->user_id)->delete_all();
|
||||
}
|
||||
elseif ($token->loaded())
|
||||
{
|
||||
$token->delete();
|
||||
}
|
||||
}
|
||||
|
||||
return parent::logout($destroy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the stored password for a username.
|
||||
*
|
||||
* @param mixed username string, or user ORM object
|
||||
* @return string
|
||||
*/
|
||||
public function password($user)
|
||||
{
|
||||
if ( ! is_object($user))
|
||||
{
|
||||
$username = $user;
|
||||
|
||||
// Load the user
|
||||
$user = ORM::factory('user');
|
||||
$user->where($user->unique_key($username), '=', $username)->find();
|
||||
}
|
||||
|
||||
return $user->password;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the login for a user by incrementing the logins and setting
|
||||
* session data: user_id, username, roles.
|
||||
*
|
||||
* @param object user ORM object
|
||||
* @return void
|
||||
*/
|
||||
protected function complete_login($user)
|
||||
{
|
||||
$user->complete_login();
|
||||
|
||||
return parent::complete_login($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare password with original (hashed). Works for current (logged in) user
|
||||
*
|
||||
* @param string $password
|
||||
* @return boolean
|
||||
*/
|
||||
public function check_password($password)
|
||||
{
|
||||
$user = $this->get_user();
|
||||
|
||||
if ( ! $user)
|
||||
return FALSE;
|
||||
|
||||
return ($this->hash($password) === $user->password);
|
||||
}
|
||||
|
||||
} // End Auth ORM
|
1688
includes/kohana/modules/orm/classes/kohana/orm.php
Normal file
1688
includes/kohana/modules/orm/classes/kohana/orm.php
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* ORM Validation exceptions.
|
||||
*
|
||||
* @package Kohana/ORM
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2010 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Kohana_ORM_Validation_Exception extends Kohana_Exception {
|
||||
|
||||
/**
|
||||
* Array of validation objects
|
||||
* @var array
|
||||
*/
|
||||
protected $_objects = array();
|
||||
|
||||
/**
|
||||
* The _object_name property of the main ORM model this exception was created for
|
||||
* @var string
|
||||
*/
|
||||
protected $_object_name = NULL;
|
||||
|
||||
/**
|
||||
* Constructs a new exception for the specified model
|
||||
*
|
||||
* @param string $object_name The _object_name of the model this exception is for
|
||||
* @param Validation $object The Validation object of the model
|
||||
* @param string $message The error message
|
||||
* @param array $values The array of values for the error message
|
||||
* @param integer $code The error code for the exception
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($object_name, Validation $object, $message = 'Failed to validate array', array $values = NULL, $code = 0)
|
||||
{
|
||||
$this->_object_name = $object_name;
|
||||
$this->_objects['_object'] = $object;
|
||||
|
||||
parent::__construct($message, $values, $code);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Validation object to this exception
|
||||
*
|
||||
* // The following will add a validation object for a profile model
|
||||
* // inside the exception for a user model.
|
||||
* $e->add_object('profile', $validation);
|
||||
* // The errors array will now look something like this
|
||||
* // array
|
||||
* // (
|
||||
* // 'username' => 'This field is required',
|
||||
* // 'profile' => array
|
||||
* // (
|
||||
* // 'first_name' => 'This field is required',
|
||||
* // ),
|
||||
* // );
|
||||
*
|
||||
* @param string $alias The relationship alias from the model
|
||||
* @param Validation $object The Validation object to merge
|
||||
* @param mixed $has_many The array key to use if this exception can be merged multiple times
|
||||
* @return ORM_Validation_Exception
|
||||
*/
|
||||
public function add_object($alias, Validation $object, $has_many = FALSE)
|
||||
{
|
||||
if ($has_many === TRUE)
|
||||
{
|
||||
// This is most likely a has_many relationship
|
||||
$this->_objects[$alias][]['_object'] = $object;
|
||||
}
|
||||
elseif ($has_many)
|
||||
{
|
||||
// This is most likely a has_many relationship
|
||||
$this->_objects[$alias][$has_many]['_object'] = $object;
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_objects[$alias]['_object'] = $object;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges an ORM_Validation_Exception object into the current exception
|
||||
* Useful when you want to combine errors into one array
|
||||
*
|
||||
* @param string $alias The relationship alias from the model
|
||||
* @param ORM_Validation_Exception $object The exception to merge
|
||||
* @param mixed $has_many The array key to use if this exception can be merged multiple times
|
||||
* @return ORM_Validation_Exception
|
||||
*/
|
||||
public function merge($alias, ORM_Validation_Exception $object, $has_many = FALSE)
|
||||
{
|
||||
if ($has_many === TRUE)
|
||||
{
|
||||
// This is most likely a has_many relationship
|
||||
$this->_objects[$alias][] = $object->objects();
|
||||
}
|
||||
elseif ($has_many)
|
||||
{
|
||||
// This is most likely a has_many relationship
|
||||
$this->_objects[$alias][$has_many] = $object->objects();
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->_objects[$alias] = $object->objects();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a merged array of the errors from all the Validation objects in this exception
|
||||
*
|
||||
* // Will load Model_User errors from messages/orm-validation/user.php
|
||||
* $e->errors('orm-validation');
|
||||
*
|
||||
* @param string $directory Directory to load error messages from
|
||||
* @param mixed $translate Translate the message
|
||||
* @return array
|
||||
* @see generate_errors()
|
||||
*/
|
||||
public function errors($directory = NULL, $translate = TRUE)
|
||||
{
|
||||
if ($directory !== NULL)
|
||||
{
|
||||
// Everything starts at $directory/$object_name
|
||||
$directory .= '/'.$this->_object_name;
|
||||
}
|
||||
|
||||
return $this->generate_errors($this->_objects, $directory, $translate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursive method to fetch all the errors in this exception
|
||||
*
|
||||
* @param array $array Array of Validation objects to get errors from
|
||||
* @param string $directory Directory to load error messages from
|
||||
* @param mixed $translate Translate the message
|
||||
* @return array
|
||||
*/
|
||||
protected function generate_errors(array $array, $directory, $translate)
|
||||
{
|
||||
$errors = array();
|
||||
|
||||
foreach ($array as $alias => $object)
|
||||
{
|
||||
if ($directory === NULL)
|
||||
{
|
||||
// Return the raw errors
|
||||
$file = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
$file = trim($directory.'/'.$alias, '/');
|
||||
}
|
||||
|
||||
if (is_array($object))
|
||||
{
|
||||
// Recursively fill the errors array
|
||||
$errors[$alias] = $this->generate_errors($object, $file, $translate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Merge in this array of errors
|
||||
$errors += $object->errors($directory, $translate);
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the protected _objects property from this exception
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function objects()
|
||||
{
|
||||
return $this->_objects;
|
||||
}
|
||||
} // End Kohana_ORM_Validation_Exception
|
29
includes/kohana/modules/orm/classes/model/auth/role.php
Normal file
29
includes/kohana/modules/orm/classes/model/auth/role.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
/**
|
||||
* Default auth role
|
||||
*
|
||||
* @package Kohana/Auth
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license.html
|
||||
*/
|
||||
class Model_Auth_Role extends ORM {
|
||||
|
||||
// Relationships
|
||||
protected $_has_many = array('users' => array('through' => 'roles_users'));
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return array(
|
||||
'name' => array(
|
||||
array('not_empty'),
|
||||
array('min_length', array(':value', 4)),
|
||||
array('max_length', array(':value', 32)),
|
||||
),
|
||||
'description' => array(
|
||||
array('max_length', array(':value', 255)),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
} // End Auth Role Model
|
240
includes/kohana/modules/orm/classes/model/auth/user.php
Normal file
240
includes/kohana/modules/orm/classes/model/auth/user.php
Normal file
@@ -0,0 +1,240 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
/**
|
||||
* Default auth user
|
||||
*
|
||||
* @package Kohana/Auth
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2011 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Model_Auth_User extends ORM {
|
||||
|
||||
/**
|
||||
* A user has many tokens and roles
|
||||
*
|
||||
* @var array Relationhips
|
||||
*/
|
||||
protected $_has_many = array(
|
||||
'user_tokens' => array('model' => 'user_token'),
|
||||
'roles' => array('model' => 'role', 'through' => 'roles_users'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Rules for the user model. Because the password is _always_ a hash
|
||||
* when it's set,you need to run an additional not_empty rule in your controller
|
||||
* to make sure you didn't hash an empty string. The password rules
|
||||
* should be enforced outside the model or with a model helper method.
|
||||
*
|
||||
* @return array Rules
|
||||
*/
|
||||
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')),
|
||||
array(array($this, 'username_available'), array(':validation', ':field')),
|
||||
),
|
||||
'password' => array(
|
||||
array('not_empty'),
|
||||
),
|
||||
'email' => array(
|
||||
array('not_empty'),
|
||||
array('min_length', array(':value', 4)),
|
||||
array('max_length', array(':value', 127)),
|
||||
array('email'),
|
||||
array(array($this, 'email_available'), array(':validation', ':field')),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters to run when data is set in this model. The password filter
|
||||
* automatically hashes the password when it's set in the model.
|
||||
*
|
||||
* @return array Filters
|
||||
*/
|
||||
public function filters()
|
||||
{
|
||||
return array(
|
||||
'password' => array(
|
||||
array(array(Auth::instance(), 'hash'))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Labels for fields in this model
|
||||
*
|
||||
* @return array Labels
|
||||
*/
|
||||
public function labels()
|
||||
{
|
||||
return array(
|
||||
'username' => 'username',
|
||||
'email' => 'email address',
|
||||
'password' => 'password',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the login for a user by incrementing the logins and saving login timestamp
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function complete_login()
|
||||
{
|
||||
if ($this->_loaded)
|
||||
{
|
||||
// Update the number of logins
|
||||
$this->logins = new Database_Expression('logins + 1');
|
||||
|
||||
// Set the last login date
|
||||
$this->last_login = time();
|
||||
|
||||
// Save the user
|
||||
$this->update();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the reverse of unique_key_exists() by triggering error if username exists.
|
||||
* Validation callback.
|
||||
*
|
||||
* @param Validation Validation object
|
||||
* @param string Field name
|
||||
* @return void
|
||||
*/
|
||||
public function username_available(Validation $validation, $field)
|
||||
{
|
||||
if ($this->unique_key_exists($validation[$field], 'username'))
|
||||
{
|
||||
$validation->error($field, 'username_available', array($validation[$field]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the reverse of unique_key_exists() by triggering error if email exists.
|
||||
* Validation callback.
|
||||
*
|
||||
* @param Validation Validation object
|
||||
* @param string Field name
|
||||
* @return void
|
||||
*/
|
||||
public function email_available(Validation $validation, $field)
|
||||
{
|
||||
if ($this->unique_key_exists($validation[$field], 'email'))
|
||||
{
|
||||
$validation->error($field, 'email_available', array($validation[$field]));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a unique key value exists in the database.
|
||||
*
|
||||
* @param mixed the value to test
|
||||
* @param string field name
|
||||
* @return boolean
|
||||
*/
|
||||
public function unique_key_exists($value, $field = NULL)
|
||||
{
|
||||
if ($field === NULL)
|
||||
{
|
||||
// Automatically determine field by looking at the value
|
||||
$field = $this->unique_key($value);
|
||||
}
|
||||
|
||||
return (bool) DB::select(array('COUNT("*")', 'total_count'))
|
||||
->from($this->_table_name)
|
||||
->where($field, '=', $value)
|
||||
->where($this->_primary_key, '!=', $this->pk())
|
||||
->execute($this->_db)
|
||||
->get('total_count');
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows a model use both email and username as unique identifiers for login
|
||||
*
|
||||
* @param string unique value
|
||||
* @return string field name
|
||||
*/
|
||||
public function unique_key($value)
|
||||
{
|
||||
return Valid::email($value) ? 'email' : 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* Password validation for plain passwords.
|
||||
*
|
||||
* @param array $values
|
||||
* @return Validation
|
||||
*/
|
||||
public static function get_password_validation($values)
|
||||
{
|
||||
return Validation::factory($values)
|
||||
->rule('password', 'min_length', array(':value', 8))
|
||||
->rule('password_confirm', 'matches', array(':validation', ':field', 'password'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
*
|
||||
* Example usage:
|
||||
* ~~~
|
||||
* $user = ORM::factory('user')->create_user($_POST, array(
|
||||
* 'username',
|
||||
* 'password',
|
||||
* 'email',
|
||||
* );
|
||||
* ~~~
|
||||
*
|
||||
* @param array $values
|
||||
* @param array $expected
|
||||
* @throws ORM_Validation_Exception
|
||||
*/
|
||||
public function create_user($values, $expected)
|
||||
{
|
||||
// Validation for passwords
|
||||
$extra_validation = Model_User::get_password_validation($values)
|
||||
->rule('password', 'not_empty');
|
||||
|
||||
return $this->values($values, $expected)->create($extra_validation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an existing user
|
||||
*
|
||||
* [!!] We make the assumption that if a user does not supply a password, that they do not wish to update their password.
|
||||
*
|
||||
* Example usage:
|
||||
* ~~~
|
||||
* $user = ORM::factory('user')
|
||||
* ->where('username', '=', 'kiall')
|
||||
* ->find()
|
||||
* ->update_user($_POST, array(
|
||||
* 'username',
|
||||
* 'password',
|
||||
* 'email',
|
||||
* );
|
||||
* ~~~
|
||||
*
|
||||
* @param array $values
|
||||
* @param array $expected
|
||||
* @throws ORM_Validation_Exception
|
||||
*/
|
||||
public function update_user($values, $expected = NULL)
|
||||
{
|
||||
if (empty($values['password']))
|
||||
{
|
||||
unset($values['password'], $values['password_confirm']);
|
||||
}
|
||||
|
||||
// Validation for passwords
|
||||
$extra_validation = Model_User::get_password_validation($values);
|
||||
|
||||
return $this->values($values, $expected)->update($extra_validation);
|
||||
}
|
||||
|
||||
} // End Auth User Model
|
@@ -0,0 +1,70 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
/**
|
||||
* Default auth user toke
|
||||
*
|
||||
* @package Kohana/Auth
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2007-2011 Kohana Team
|
||||
* @license http://kohanaframework.org/license
|
||||
*/
|
||||
class Model_Auth_User_Token extends ORM {
|
||||
|
||||
// Relationships
|
||||
protected $_belongs_to = array('user' => array());
|
||||
|
||||
/**
|
||||
* Handles garbage collection and deleting of expired objects.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($id = NULL)
|
||||
{
|
||||
parent::__construct($id);
|
||||
|
||||
if (mt_rand(1, 100) === 1)
|
||||
{
|
||||
// Do garbage collection
|
||||
$this->delete_expired();
|
||||
}
|
||||
|
||||
if ($this->expires < time() AND $this->_loaded)
|
||||
{
|
||||
// This object has expired
|
||||
$this->delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all expired tokens.
|
||||
*
|
||||
* @return ORM
|
||||
*/
|
||||
public function delete_expired()
|
||||
{
|
||||
// Delete all expired tokens
|
||||
DB::delete($this->_table_name)
|
||||
->where('expires', '<', time())
|
||||
->execute($this->_db);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function create(Validation $validation = NULL)
|
||||
{
|
||||
$this->token = $this->create_token();
|
||||
|
||||
return parent::create($validation);
|
||||
}
|
||||
|
||||
protected function create_token()
|
||||
{
|
||||
do
|
||||
{
|
||||
$token = sha1(uniqid(Text::random('alnum', 32), TRUE));
|
||||
}
|
||||
while(ORM::factory('user_token', array('token' => $token))->loaded());
|
||||
|
||||
return $token;
|
||||
}
|
||||
|
||||
} // End Auth User Token Model
|
7
includes/kohana/modules/orm/classes/model/role.php
Normal file
7
includes/kohana/modules/orm/classes/model/role.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Model_Role extends Model_Auth_Role {
|
||||
|
||||
// This class can be replaced or extended
|
||||
|
||||
} // End Role Model
|
7
includes/kohana/modules/orm/classes/model/user.php
Normal file
7
includes/kohana/modules/orm/classes/model/user.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Model_User extends Model_Auth_User {
|
||||
|
||||
// This class can be replaced or extended
|
||||
|
||||
} // End User Model
|
7
includes/kohana/modules/orm/classes/model/user/token.php
Normal file
7
includes/kohana/modules/orm/classes/model/user/token.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php defined('SYSPATH') or die('No direct access allowed.');
|
||||
|
||||
class Model_User_Token extends Model_Auth_User_Token {
|
||||
|
||||
// This class can be replaced or extended
|
||||
|
||||
} // End User Token Model
|
3
includes/kohana/modules/orm/classes/orm.php
Normal file
3
includes/kohana/modules/orm/classes/orm.php
Normal file
@@ -0,0 +1,3 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
class ORM extends Kohana_ORM {}
|
@@ -0,0 +1,10 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
/**
|
||||
* ORM Validation exceptions.
|
||||
*
|
||||
* @package Kohana/ORM
|
||||
* @author Kohana Team
|
||||
* @copyright (c) 2008-2009 Kohana Team
|
||||
* @license http://kohanaphp.com/license
|
||||
*/
|
||||
class ORM_Validation_Exception extends Kohana_ORM_Validation_Exception {}
|
23
includes/kohana/modules/orm/config/userguide.php
Normal file
23
includes/kohana/modules/orm/config/userguide.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php defined('SYSPATH') or die('No direct script access.');
|
||||
|
||||
return array(
|
||||
// Leave this alone
|
||||
'modules' => array(
|
||||
|
||||
// This should be the path to this modules userguide pages, without the 'guide/'. Ex: '/guide/modulename/' would be 'modulename'
|
||||
'orm' => array(
|
||||
|
||||
// Whether this modules userguide pages should be shown
|
||||
'enabled' => TRUE,
|
||||
|
||||
// The name that should show up on the userguide index page
|
||||
'name' => 'ORM',
|
||||
|
||||
// A short description of this module, shown on the index page
|
||||
'description' => 'Official ORM module, a modeling library for object relational mapping.',
|
||||
|
||||
// Copyright message, shown in the footer for this module
|
||||
'copyright' => '© 2008–2011 Kohana Team',
|
||||
)
|
||||
)
|
||||
);
|
13
includes/kohana/modules/orm/guide/orm/examples.md
Normal file
13
includes/kohana/modules/orm/guide/orm/examples.md
Normal 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.
|
119
includes/kohana/modules/orm/guide/orm/examples/simple.md
Normal file
119
includes/kohana/modules/orm/guide/orm/examples/simple.md
Normal 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
|
137
includes/kohana/modules/orm/guide/orm/examples/validation.md
Normal file
137
includes/kohana/modules/orm/guide/orm/examples/validation.md
Normal 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.',
|
||||
),
|
||||
);
|
22
includes/kohana/modules/orm/guide/orm/filters.md
Normal file
22
includes/kohana/modules/orm/guide/orm/filters.md
Normal 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.
|
22
includes/kohana/modules/orm/guide/orm/index.md
Normal file
22
includes/kohana/modules/orm/guide/orm/index.md
Normal 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).
|
9
includes/kohana/modules/orm/guide/orm/menu.md
Normal file
9
includes/kohana/modules/orm/guide/orm/menu.md
Normal 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)
|
28
includes/kohana/modules/orm/guide/orm/models.md
Normal file
28
includes/kohana/modules/orm/guide/orm/models.md
Normal 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';
|
123
includes/kohana/modules/orm/guide/orm/relationships.md
Normal file
123
includes/kohana/modules/orm/guide/orm/relationships.md
Normal 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);
|
0
includes/kohana/modules/orm/guide/orm/tutorials.md
Normal file
0
includes/kohana/modules/orm/guide/orm/tutorials.md
Normal file
75
includes/kohana/modules/orm/guide/orm/using.md
Normal file
75
includes/kohana/modules/orm/guide/orm/using.md
Normal 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();
|
||||
|
111
includes/kohana/modules/orm/guide/orm/validation.md
Normal file
111
includes/kohana/modules/orm/guide/orm/validation.md
Normal 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.
|
Reference in New Issue
Block a user