SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    6
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    User class example? (forum)

    Hey, I've been looking around the forum for an example of an OO implementation of a User/Member class for a forum or similar, but I can't find any... What I mean is a class which is used to modify/retrieve User data in a forum software.

    The only good example I found for the moment is in Vanilla 1.0 - but I'd like to know if there are better alternatives: Vanilla does it this way:

    PHP Code:
    class User {
       var 
    $name
       
    var $password
       
    //... long list of vars

       
    function GetPropertiesFromDataSet()
       {
           
    // gets values for all fields and inputs them 1 by 1 in the vars
       
    }


    ... so it retrieves everything and stores back the whole thing when needed - there are no variable specific functions, like say: getUsername()

    Which is the best way to go? Is it really necessarly to create a variable for every single database field? why not an array?

    I was also wondering if PHP5s __set and __get functions would be the better solution?

    Well, any help or insight would be appreciated.

    Alex

  2. #2
    SitePoint Addict
    Join Date
    Jan 2005
    Location
    United Kingdom
    Posts
    208
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have been using the __get and __set magic methods to allow access to recordset properties, but more and more I am now thinking in the mindset that any sort of getter and setter is a sign of immature design. The Tell Don't Ask principle highlights this:
    [snip]...you should endeavor to tell objects what you want them to do; do not ask them questions about their state, make a decision, and then tell them what to do.
    A code example might help, related to the class above
    PHP Code:
    class User
    {
      private 
    $firstname$lastname;
      public function 
    __get$key )
      {
        return isset( 
    $this->$key ) ? $this->$key false;
      }
      public function 
    getRealName()
      {
        return 
    $this->firstname ' ' $this->lastname;
      }
    }
    // Asking
    $realname $user->firstname ' ' $user->lastname;
    // Telling
    $realname $user->getRealName(); 

  3. #3
    SitePoint Addict
    Join Date
    Mar 2005
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    We use an ActiveRecord implementation which uses __get and __set to write to a $this->row array.

    That way $user->firstname maps to $user->row['firstname']
    Another nice feature that you can use then is passing the fetch onto methods if the value doesn't exist in the row.

    So a call to $user->fullname searches the row doesn't find a value and then runs the following method...

    public function fullname() {
    return $this->firstname." ".$this->surname;
    }

  4. #4
    SitePoint Member
    Join Date
    Dec 2006
    Posts
    6
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for the help! - I've been looking through Vanilla a bit more, and it seems it uses the variables directly, without even a get() or set() - and then just writes everything to the DB.

    PHP Code:
    $this->User->UserID $UserID
    So UserID here, is a public var, and doesn't go through a function... doesn't seem like very good practice..

    Shrike: Interesting way of putting things - I think it seems like the cleanest method - and also the easiest to read. Do you query the database when the User is instantiated, or when it is 'told' to retrieve a value?

    rossriley: That's what I was thinking of when I said 'array' - this seems like the simplest method, but again, maybe not the best in terms of OOP - I like how it runs a function with the name of the value it didn't find - is that done with a hook? like: $requestedValue(); // which basically is converted to fullname(); ?

    In general, is it best to read and write the objects to the DB at the beginning and end of the page? or each time a value is requested/stored? The 2nd method wouldn't require the object to have a list of vars/fields...

  5. #5
    SitePoint Guru silver trophy Luke Redpath's Avatar
    Join Date
    Mar 2003
    Location
    London
    Posts
    794
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ignoring the persistance side of things, obviously part of the user object would be little more than a simple DTO (so you'll need getters/setters for various user properties such as username, password, name etc.). In terms of solid behaviour, then I'd imagine there would need to be some kind of password encryption that needs doing before the user is persisted to the database. There would probably be some kind of authenticate behaviour and possibly some validation.

    Here's an example of a user object from one of my Rails apps - this is using ActiveRecord so the simple user attributes are automatically available from the database (including setters and getters). I only store an encrypted password and its salt in the database.

    Code:
    class User < ActiveRecord::Base
      attr_accessor  :password
      attr_protected :salt, :crypted_password, :remember_token, :remember_token_expires
      
      validates_presence_of :username, :first_name, :surname
      validates_presence_of :password, :message => 'is required',
                            :if => :password_required?
      
      before_save :encrypt_password!
      
      def self.authenticate(username, password)
        user = find_by_username(username)
        return false if user.nil?
        return user if user.matching_password?(password)
      end
      
      def matching_password?(password)
        self.crypted_password == encrypted(password, self.salt)
      end
      
      def full_name
        "#{self.first_name} #{self.surname}"
      end
      
      def admin?
        self.status == 'admin'
      end
      
      def make_admin
        self.status = 'admin'
      end
      
      def make_admin!
        self.update_attributes(:status => 'admin')
      end
      
      def remember_login!(until_date=2.weeks.from_now)
        self.remember_token = encrypted(self.crypted_password, self.username)
        self.remember_token_expires = until_date
        self.save
      end
      
      def remember_login?
        !self.remember_token.nil? and self.remember_token_expires > Time.now
      end
      
      protected
        def password_required?
          self.crypted_password.blank?
        end
        
        def encrypt_password!
          return if self.password.blank?
          generate_salt!
          self.crypted_password = encrypted(self.password, self.salt)
        end
        
        def generate_salt!
          self.salt = Digest::SHA1.hexdigest("---#{Time.now}---")
        end
        
        def encrypted(password, salt)
          Digest::SHA1.hexdigest("--#{password}--#{salt}--")
        end
    end
    There is also some very simple authorization stuff in there - its implemented as a property for the user in this case as it was literally a case of admin or not admin. This wouldn't really scale for any kind of full-scale auth system.

    For those who don't know Rails, the attr_protected statement protects attributes from mass assignment with functions such as User.new(attributes) and User.create(attributes). Also, Rails makes object attributes public by default but if I was designing this without using ActiveRecord things such as the salt and encrypted password would be completely private as there is no need to expose them - they are completely encapsulated by the authentication methods.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •