Part of the EllisLab Network
   
1 of 3
1
[In the Works] Datamapper ORM Class
Posted: 27 August 2008 08:50 AM   [ Ignore ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  2280
Joined  07-30-2007

Many of you have noticed that I haven’t been posting much - needless to say I have been insanely busy. Only one more month in the desert then it is back home with the family.

In the small amount of free time I still have I’ve been working on quite a few projects. My favorite of which is my Datamapper ORM Class although I’m pretty sure it doesn’t meet the Datamapper pattern completely.

This is, as of now, not yet ready for release - but here’s a taste of what is to come.

Here’s a model, let’s say - for a user:

model/user.php

<?php
class User extends Datamapper {

    
var $username;
    var 
$email;
    var 
$password;
    var 
$salt;
    
    var 
$validation = array(
        
'username' => array('required''unique''min' => 3'max' => 20'trim'),
        
'email' => array('required''unique''valid_email''trim'),
        
'password' => array('required''min' => 3'max' => 20'constraint' => 120));
        
    function 
save_salt() {
        $this
->load->helper('string');
        if (!isset(
$this->salt)) {
            $this
->salt random_string('alnum'10);
        
}
    }
    
    
function save_password() {
        $this
->load->helper('security');
        if (!isset(
$this->salt)) {
            $this
->save_salt();
        
}
        $this
->password dohash($this->salt $this->password);
    
}

One of the first things you’ll notice is the validation array. Yes, Validation is being moved 100% completely into the model. All of the CodeIgniter standard validation rules are in-place, in addition to a few new ones (unique can be seen above, which is self explanatory).

You also should note the constructor as it’s passing an array to it’s parent class. Datamapper models are designed to really be used as objects, not just a class to make your organization a bit better. More on this in the Controller area below.

Finally, you’ll see the method save_salt(). As of now only data modification upon save is implemented not sure if there is really a need for it anywhere else. Some might suggest upon “getting” the data but I dgress - isn’t the View in charge of making data pretty?

A few more bits you might not pick up from reviewing the code within the model:

- There is a class variable $salt but there are no validation rules to match! Datamapper understands this as meaning “this is a user property but it can’t be changed via a form - only in the code.” This allows us to use a handy little function later to just pull in all POST data to the model and believe it will be okay (no worrying about whether a malicious user has added an admin ENUM to your form hoping you just accept everything).

- All models have an id, created_on, updated_on field that is automatically updated and managed by the Datamapper model.

- Currently enabled in the model (because it is not explicitly disabled) is database analysis. The Datamapper automatically reviews your model, determines what the table structure should be, then checks to see if the table is created and matches the correct structure. In plain English: you write your model, Datamapper automatically creates the table for you. You need to add a new parameter to your model? Just write it in. No more hopping back and forth between code and phpMyAdmin. There’s still a lot of work to do on this front, primarily more intelligent column type identification and better parameters within the model. I’m trying to think of a graceful way to accomplish this - right now it’s just part of the validation array (see the constraint key within password, that tells Datamapper to make the field limit 120 characters).

Controllers

<?php
class Users extends Controller {

    
function Users() {
        parent
::Controller();
        
$this->output->enable_profiler(TRUE);
    
}
    
    
function index() {
        $this
->datamapper->model('user');
        
// Get the user whose username == walesmd and echo his email
        
$u = new User(array('username'=>'walesmd'));
        echo 
$u->email;
        
        
// Get all users who are admins
        
$u = new User(array('type'=>'admin'));
        
// We fully expect more than one user, so we foreach the all class variable
        
foreach ($u->all as $users{
            
echo $u->username;
        
}

        
// Let's create a new user
        
$u = new User();
        
$u->username 'dallard';
        
$u->password 'robots';
        
$u->save();

        
// Let's create another new user - this time after a form has been submitted
        // Form Input names are: signup[username], signup[password], and signup[email]
        
$u = new User();
        
$u->fromForm('signup');
        
$u->save();

        
// Let's update user #1's email address
        
$u = new User(array('id'=>1));
        
$u->email 'null@codeigniter.com';
        
$u->save();
    
}

Lots of stuff here but it should be pretty easy to understand. When creating a new Datamapper object you can pass an array - this will be used within a CodeIgniter ActiveRecord get_where() query that will return all objects that match that query.

If you are expecting more than one result to come back, go ahead and foreach through the Models all variable. Note: This will still work if you only return one object, Datamapper is designed around your expectations. If you expect more than one, loop it. If you expect only one - don’t.

We created a new user, which was pretty simple.

 Signature 

Follow me on twitter here.
MichaelWales.com | MichaelWales.info

Profile
 
 
Posted: 27 August 2008 08:56 AM   [ Ignore ]   [ # 1 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  2280
Joined  07-30-2007

We also created a new user from our signup form. That form looks like:

<form method="post" action="signup" name="signup" id="signup"><!-- NoteThis doesn't matter -->
<p>Username:<br />
<input type="text" name="signup[username]" id="signup_username" maxlength="20" /></p>
<p>Password:<br />
<input type="password" name="signup[password]" id="signup_password" maxlength="20" /></p>
<p>Email Address:<br />
<input type="email" name="signup[email]" id="signup_email" maxlength="120" /></p>
<p><input type="submit" name="signup[submit]" value="Signup" /></p>
</form> 

The magic happens because of how we named all of our input fields (not id - just the name parameter). We can now use the fromForm() method to easily pull all these values down into our model, validate them against our rules, and save them to the database. Remember how we didn’t add any validation rules for fields like salt? That means no matter if a user changes your form to add in a salt field - it’s not going anywhere near your database. All, without any real work on your behalf.

Finally we updated a user’s information. You’ll be glad to know that Datamapper doesn’t just issue a wide-open UPDATE statement to the database - that would be to simple. Datamapper is optimized to the gills and will only issue updates for fields that actually change.

 Signature 

Follow me on twitter here.
MichaelWales.com | MichaelWales.info

Profile
 
 
Posted: 27 August 2008 09:26 AM   [ Ignore ]   [ # 2 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  369
Joined  06-16-2006

This is awesome Michael! With enough work I think the database analysis feature will be very powerful indeed. But is it possible to disable it? I wouldn’t like unnecessary queries in a production environment.

 Signature 

Best regards. Zacharias.
Matchbox (Modular Separation) | Wick (Controller Loader)

Profile
 
 
Posted: 27 August 2008 09:31 AM   [ Ignore ]   [ # 3 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  2280
Joined  07-30-2007

@Zacharies
Yes - that is the definite intent of it. Enabled by default, for development but easily disabled for production environments.

 Signature 

Follow me on twitter here.
MichaelWales.com | MichaelWales.info

Profile
 
 
Posted: 27 August 2008 09:36 AM   [ Ignore ]   [ # 4 ]  
Sr. Research Associate
Avatar
RankRankRankRankRank
Total Posts:  3216
Joined  06-10-2007

I like this, good work man, will be eager to get into it.

 Signature 

URI Language Identifier | Modular Extensions - HMVC | View Object | Widget plugin | Access Control library

Profile
 
 
Posted: 27 August 2008 11:00 AM   [ Ignore ]   [ # 5 ]  
Lab Assistant
Avatar
RankRank
Total Posts:  286
Joined  02-13-2008

Good one. Like it better than our Automodels.

Profile
 
 
Posted: 27 August 2008 11:31 AM   [ Ignore ]   [ # 6 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  727
Joined  08-03-2006

Makes me almost a bit jealous smile

 Signature 

RapidDataMapper: My new ORM, is now released!

IgnitedRecord: Old ORM

MPTtree: A model to handle trees in a database.

YAYParser - Yet Another YAML Parser

Profile
 
 
Posted: 27 August 2008 11:34 AM   [ Ignore ]   [ # 7 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  2280
Joined  07-30-2007

Heh - don’t m4r. To be completely honest, I’ve wasted the past 2-3 weeks writing my own framework only to scrap it because it wasn’t exactly how I wanted it (or I was just rewriting CI).

This class will be as good as I can make my “optimal development environment” within PHP but I was on the verge of writing a language parser in PHP and designing my own language - so frustrating.

There’s still a lot of work to be done on this but it’s definitely getting to the point that it is amazingly useful.

 Signature 

Follow me on twitter here.
MichaelWales.com | MichaelWales.info

Profile
 
 
Posted: 28 August 2008 04:10 AM   [ Ignore ]   [ # 8 ]  
Lab Assistant
RankRank
Total Posts:  135
Joined  06-25-2008

Regarding the use of more advanced queries. Will you implement methods that can, for example, compare values in one table against another, or will this require a seperate Model?

Anyhow, this looks really cool! I’m amazed at the simple yet clever approach to database handling. Looking forward to trying it out!

 Signature 

Blog | Twitter | Last.fm
MY_Form_Validation - extended for protection using nonce words

Profile
 
 
Posted: 28 August 2008 11:52 AM   [ Ignore ]   [ # 9 ]  
Lab Technician
Avatar
RankRankRankRank
Total Posts:  2280
Joined  07-30-2007

Comparing values - what exactly do you mean?

There will be relational features implement - your typical has_many and belong_to with an optional through table.

 Signature 

Follow me on twitter here.
MichaelWales.com | MichaelWales.info

Profile
 
 
Posted: 28 August 2008 02:32 PM   [ Ignore ]   [ # 10 ]  
Lab Assistant
RankRank
Total Posts:  135
Joined  06-25-2008

Sorry, I was in a hurry smile I guess my question is if a class extending Datamapper can fetch other values than those in the specific table (in this case containing users), perhaps in a similair way to the save_salt and save_password function? So if I had a, say, load_movies function that would grab a users favorite movies from another table, that value would be passed along to the User object.

Maybe I’m out on a limb here. I know what ORM stands for in a literal way, however all deeper meaning is beyond me smile

 Signature 

Blog | Twitter | Last.fm
MY_Form_Validation - extended for protection using nonce words

Profile
 
 
   
1 of 3
1