Welcome Guest, Not a member yet? Register   Sign In
[In the Works] Datamapper ORM Class
#1

[eluser]Michael Wales[/eluser]
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
Code:
<?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
Code:
<?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 = '[email protected]';
        $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.
#2

[eluser]Michael Wales[/eluser]
We also created a new user from our signup form. That form looks like:
Code:
<form method="post" action="signup" name="signup" id="signup"><!-- Note: This doesn't matter -->
<p>Username:<br />
&lt;input type="text" name="signup[username]" id="signup_username" maxlength="20" /&gt;&lt;/p>
<p>Password:<br />
&lt;input type="password" name="signup[password]" id="signup_password" maxlength="20" /&gt;&lt;/p>
<p>Email Address:<br />
&lt;input type="email" name="signup[email]" id="signup_email" maxlength="120" /&gt;&lt;/p>
<p>&lt;input type="submit" name="signup[submit]" value="Signup" /&gt;&lt;/p>
&lt;/form&gt;

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.
#3

[eluser]zdknudsen[/eluser]
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.
#4

[eluser]Michael Wales[/eluser]
@Zacharies
Yes - that is the definite intent of it. Enabled by default, for development but easily disabled for production environments.
#5

[eluser]wiredesignz[/eluser]
I like this, good work man, will be eager to get into it.
#6

[eluser]Sam Dark[/eluser]
Good one. Like it better than our Automodels.
#7

[eluser]m4rw3r[/eluser]
Makes me almost a bit jealous Smile
#8

[eluser]Michael Wales[/eluser]
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.
#9

[eluser]erik.brannstrom[/eluser]
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!
#10

[eluser]Michael Wales[/eluser]
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.




Theme © iAndrew 2016 - Forum software by © MyBB