Auth (A1) and ACL (A2, ACL): Pt 2, the Model

I’m no model lady. A model’s just an imitation of the real thing. – Mae West

Part 1: Introduction
Part 2: The Data Model
Part 3: The Controller
Part 4: It becomes a module
Part 5: Adding ACL

Need a Database

--
-- Table structure for table `roles`
--

DROP TABLE IF EXISTS `roles`;
CREATE TABLE IF NOT EXISTS `roles` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) NOT NULL,
  `description` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=3 ;

--
-- Dumping data for table `roles`
--

INSERT INTO `roles` (`id`, `name`, `description`) VALUES
(1, 'login', NULL),
(2, 'admin', NULL);

-- --------------------------------------------------------

--
-- Table structure for table `roles_users`
--

DROP TABLE IF EXISTS `roles_users`;
CREATE TABLE IF NOT EXISTS `roles_users` (
  `user_id` int(11) NOT NULL,
  `role_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- --------------------------------------------------------

--
-- Table structure for table `users`
--

DROP TABLE IF EXISTS `users`;
CREATE TABLE IF NOT EXISTS `users` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(32) NOT NULL,
  `password` char(64) NOT NULL,
  `email` varchar(127) DEFAULT NULL,
  `created` int(11) NOT NULL,
  `modified` int(11) DEFAULT NULL,
  `logins` int(10) unsigned NOT NULL DEFAULT '0',
  `last_login` int(10) unsigned DEFAULT NULL,
  `failed_logins` int(10) unsigned NOT NULL DEFAULT '0',
  `last_failure` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

And a Model to access the table
Don’t need a Model_Role because the default one in orm/classes/Model/Auth/Role.php will do nicely

<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
 * Default A1 (auth) user
 * Extend from Wouterrrs A1_User_ORM module
 * (not Kohana ORM User, modulesormclassesModelAuthUser.php)
 *
 * @package    Silver-Bullet Auth
 * @author     Silver-Bullet
 * @copyright  (c) 2013
 * @license    http://kohanaframework.org/license
 */
class Model_User extends Model_A1_User_ORM implements Acl_Role_Interface {

   /**
    * set ORM class variables because we want these fields (created, modified)
    * automatically updated
    * format=TRUE means we use UTC (Universal Time Coordinated)
    * could have used 'format' => 'Y-m-d H:i:s' BUT UTC is better
    * in this instance because:
    * 1. We don't need to do MySQL date manipulations
    * 2. Timestamps unambiguously reference a particular point in time
    * 3. Easy to manipulate with PHP
    */
   protected $_created_column = array('column' => 'created',
				      'format' => TRUE);
   protected $_updated_column = array('column' => 'modified',
				      'format' => TRUE);

   /**
    * A user can have many roles
    */
   protected $_has_many = array(
      'roles' => array(
	 'model'   => 'Role',
	 'through' => 'User_Role'));

   // default filters
   protected $_filters = array(
      TRUE => array(
	 'trim' => NULL
	 )
      );

   // default rules
   protected $_rules = array(
      'username' => array(
	 array('not_empty'),
	 array('min_length', array(':value',4)),
	 array('max_length', array(':value',30)),
	 array('alpha_dash')
	 ),
      'email' => array(
	 array('not_empty'),
	 array('email'),
	 array('email_domain')
	 ),
      'password' => array(
	 'not_empty'  => NULL,
	 'min_length' => array(5),
	 'max_length' => array(42),
	 ),
      'password_confirm' => array(
	 'matches' => array('password'),
	 )
      );

   protected $_callbacks = array(
      'username' => array('username_available')
      );

   /**
    * Columns to ignore, password_confirm does not exist in
    * the User table, it is just used for the validation rule
    */
   protected $_ignored_columns = array('password_confirm');

   /**
    * get user role(s)
    * Return the names of the roles for the given user.
    * These need to match the roles defined in the configuration
    * file (a2.php).
    * NB: The configuration file (a2.php) and role values in the
    * database (Roles) must be kept in sync.
    *
    * @return  array     Array of available roles
    */
   public function get_role_id()
   {
      $roles = array();
      foreach( $this->roles->find_all() as $role)
      {
	 $roles[] = $role->name;
      }
      return $roles;
   }

   /**
    * Tests if a username exists in the database. This can be used as a
    * Valdidation callback.
    *
    * @param   object    Validate object
    * @param   string    Field
    * @param   array     Array with errors
    * @return  array     (Updated) array with errors
    */
   public function username_available(Validate $array, $field)
   {
      if ($this->loaded() AND $this->_object[$field] === $array[$field])
      {
	 // This value is unchanged
	 return TRUE;
      }

      if( ORM::factory($this->_user_model)->where($field,'=',$array[$field])->find_all(1)->count() )
      {
	 $array->error($field,'username_available');
      }
   }
} // End User Model