TIP: Use Markdown or, <pre> for multi line code blocks / <code> for inline code.
These forums are read-only and for archival purposes only!
Please join our new forums at discourse.kohanaframework.org
Custom Schema with Auth Module
  • Hi! I was trying to extend the Auth Module so I can use my custom Schema (with same relations but differents names in tables and columns)
    I wrote this:
    (basically it's a transparent extenstion for Model_User where we can set the table name, PK, and colum names...)

    <?php defined('SYSPATH') or die('No direct access allowed.');
    
    class Model_User extends Model_Auth_User {
    
        protected $_table_name = 'strange_tablename';
    
        protected $_primary_key = 'strange_pkey';
    
        protected $_db_group = 'default'; 
    
        protected $_has_many = array(
            'user_tokens' => array('model' => 'user_token', 'foreign_key' => 'user_id'),
            'roles'       => array('model' => 'role', 'foreign_key' => 'user_id', 'through' => 'roles_users'),
        );
    
        protected $_columns = array(
            'username' => 'columna_username',
            'password' => 'columna_password',
            'email' => 'columna_email', 
            'logins' => 'columna_logins',
            'last_login' => 'columna_last_login',       
        );
    
        public $password_confirm = 'password_confirm';
    
        /*  
         *  ==================================================
         *  ----------------- end of Schema config ------------------ 
         *  ==================================================  
         */
    
        public static function column($key)
        {
            return $this->_columns[$key];
        }
    
        /**
         * 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(
                Model_User::column('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')),
                ),
                Model_User::column('password') => array(
                    array('not_empty'),
                ),
                Model_User::column('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(
                Model_User::column('password') => array(
                    array(array(Auth::instance(), 'hash'))
                )
            );
        }
    
        /**
         * Labels for fields in this model
         *
         * @return array Labels
         */
        public function labels()
        {
            return array(
                Model_User::column('username')         => 'username',
                Model_User::column('email')            => 'email address',
                Model_User::column('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->{Model_User::column('logins')} = new Database_Expression(Model_User::column('loigins').' + 1');
    
                // Set the last login date
                $this->{Model_User::column('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], Model_User::column('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], Model_User::column('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) ? Model_User::column('email') : Model_User::column('username');
        }
    
        /**
         * Password validation for plain passwords.
         *
         * @param array $values
         * @return Validation
         */
        public static function get_password_validation($values)
        {
            return Validation::factory($values)
                ->rule(Model_User::column('password'), 'min_length', array(':value', 8))
                ->rule($this->password_confirm, 'matches', array(':validation', ':field', Model_User::column('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(Model_User::column('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[Model_User::column('password')]))
            {
                unset($values[Model_User::column('password')], $values[$this->password_confirm]);
            }
    
            // Validation for passwords
            $extra_validation = Model_User::get_password_validation($values);
    
            return $this->values($values, $expected)->update($extra_validation);
        }
    
    
    
    } // End User Model
    

    I have not tested it yet, but I think it should work...

    the problem is in Model_User_Token, because it has a constructor...
    So, if redefine this constructor, then when I call parent::_construct(), the "old" constructor which uses hardcoded colum names it's executed anyway.
    I think I'm walking the wrong way, does somebody have a better solution?

    Thanks!

  • I'll to explain, briefly, my "solution":

    Create an associative array with the names of the columns in our DB:

    protected $_columns = array(
            'username' => 'columna_username',
            'password' => 'columna_password',
            'email' => 'columna_email', 
            'logins' => 'columna_logins',
            'last_login' => 'columna_last_login',       
        );
    

    Create a static function to access $_columns array (just because I think is nicer...)

    public static function column($key)
        {
            return $this->_columns[$key];
        }
    

    Now, instead of using 'username' we use Model_User::column('username')
    and instead of using
    $this->logins
    we use:
    $this->{Model_User::column('logins')}

    I hope that helps if to read the whole code is annoying.

  • I found 4 solutions for this problem (bypass __construct from Model_Auth_User_Token:
    1)

    // use this instead of parent::__construct($id) in Model_User_Token  
    ORM::__construct($id);
    

    2)

    // use this instead of parent::__construct($id) in Model_User_Token  
    call_user_func(array(get_parent_class(get_parent_class($this)), '__construct'), $id);  
    

    3)

    // don't use transparent extension and instead of:   
    class Model_User_Token extends Model_Auth_User_Token {
    // use
    class Model_User_Token extends ORM  
    

    4)

    // don't use extension at all, create a new Auth Module with my custom Schema and use that
    

    What do you recommend?
    (anyway, I feel this solution very ugly, maybe there's a better way to use custom Schema with Auth Module, with Zend_Auth we can create an adapter with our custom table names and column names)

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion