CodeIgniter Forums
DataMapper 1.6.0 - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forumdisplay.php?fid=20)
+--- Forum: Archived Libraries & Helpers (https://forum.codeigniter.com/forumdisplay.php?fid=22)
+--- Thread: DataMapper 1.6.0 (/showthread.php?tid=11358)



DataMapper 1.6.0 - El Forum - 12-03-2008

[eluser]OverZealous[/eluser]
Preliminary Report on 1.5.1 ;-)

I haven't made the switch yet (probably will be doing that through the morning...), but I have to say, I'm really impressed with the improvements. Some things, like using the built-in clone function (I didn't know existed) are much more elegant than my attempts.

I love the fact that the "changed" values no longer require DB calls. Much nicer!

I'm a little confused why the count() method doesn't use $this->db->count_all_results()? This uses COUNT(*) on the backend, so it should be an order of magnitude faster! There's also ->count_all() for counting the whole table.

Also, would it make more sense to use the already-loaded CIFV library by binding it to the DataMapper object? (ie: Setting and using $this->form_validation instead of creating a new CIFV everytime it is needed.)

Those are fairly minor issues.

I would, however, like one fairly important (for me) feature. I really need to the ability to hook into the model initialization routines - preferable without editing the DM class itself. What I need are two methods: one after DM has run through it's global initialization, and one after the model has been initialized.

These methods would allow me to set the labels from language files, as well as some other general cleanup.

I suggest this (most of the existing code has been hidden):
Code:
function DataMapper() {
    // ...
    if ( ! array_key_exists($this->model, DataMapper::$common))
    {
        if ($this->model == 'datamapper')
        {
            // Set up DM ...
+           $this->post_datamapper_init();
            return;
        }
        // Set up model ...
        // Just before storing the common model settings
+       $this->post_model_init();
        // Now store common settings ...
    }
    // ...
}
// add these two empty functions
function post_datamapper_init()
{
}
function post_model_init()
{
}
It only adds a few lines of code, and basically no overhead since the empty functions might get optimized away.

I realize I could handle it in my own constructors, but it would be difficult to determine if this was the *first* init or not.

One last thing. When the shared model elements are stored into the $common array, they are not stored by reference. It doesn't matter for the table name, but the $fields and $validation arrays might be getting copied there. I'm not 100% sure about that, though.

Thoughts?

Thanks for putting so much time into DataMapper!

UPDATE
I just found one other problem, easily fixed. In the _related method, the join query does not add $relationship_table to the current object's ID field. This causes problems in multiple-join / multiple-related queries. It's easily fixed:
Code:
$this->db->join($relationship_table, $object->table . '.id = ' . $this->model . '_id', 'left');
// becomes
$this->db->join($relationship_table, $object->table . '.id = ' . $relationship_table.'.'.$this->model . '_id', 'left');

// and
$this->db->join($relationship_table, $this->table . '.id = ' . $this->model . '_id', 'left');
// becomes
$this->db->join($relationship_table, $this->table . '.id = ' . $relationship_table.'.'.$this->model . '_id', 'left');
This doesn't cause an issue at this moment, unless you are using the where_related functions I posted a while back. Then you will see DB errors.


DataMapper 1.6.0 - El Forum - 12-03-2008

[eluser]stensi[/eluser]
Ah, I didn't notice the count_all_results() method, but I knew of the count_all() method. The reason I'd done it the way I did was because count_all() doesn't allow any query clauses to work with it, such as where clauses, which I need to be able to do for related queries and self referencing relationships. However, as count_all_results() works with query clauses, I can switch to use that, so thanks for the tip!

I just benchmarked the difference and you're right, it does add just that extra tiny little bit of speed, so I've added that for the next version.

Hmm, I took a look at the $common values. The first instance doesn't get assigned by reference but the rest from then on do:

Code:
function test()
{
    $u1 = new User();
    $u1->get(1);

    $u2 = new User();
    $u2->get(1);

    $u3 = new User();
    $u3->get(1);

    echo '<pre>u1:<br/>'.print_r($u1->validation['password']['rules'], TRUE).'</pre>';
    echo '<pre>u2:<br/>'.print_r($u2->validation['password']['rules'], TRUE).'</pre>';
    echo '<pre>u3:<br/>'.print_r($u3->validation['password']['rules'], TRUE).'</pre>';

    $u2->validation['password']['rules'] = array('foo' => 'bar');

    echo '<hr />';

    echo '<pre>u1:<br/>'.print_r($u1->validation['password']['rules'], TRUE).'</pre>';
    echo '<pre>u2:<br/>'.print_r($u2->validation['password']['rules'], TRUE).'</pre>';
    echo '<pre>u3:<br/>'.print_r($u3->validation['password']['rules'], TRUE).'</pre>';
}

Outputs:
Code:
u1:
Array
(
    [0] => required
    [1] => trim
    [min_length] => 3
    [max_length] => 40
    [2] => encrypt
)

u2:
Array
(
    [0] => required
    [1] => trim
    [min_length] => 3
    [max_length] => 40
    [2] => encrypt
)

u3:
Array
(
    [0] => required
    [1] => trim
    [min_length] => 3
    [max_length] => 40
    [2] => encrypt
)

_______________________________________

u1:
Array
(
    [0] => required      // not by reference so unaffected
    [1] => trim
    [min_length] => 3
    [max_length] => 40
    [2] => encrypt
)

u2:
Array
(
    [foo] => bar         // changed by reference
)

u3:
Array
(
    [foo] => bar         // changed by reference
)

So, I'll need to correct it to assign by reference when first loading it, which is an easy fix. Thanks for making me investigate Wink


I'm yet to put time into what will be the related_{clause}() methods. I'll be using __call() to feed the related clauses through the same private method, so I don't have to have separate ones for each clause type (where, or_where, like, or_like, etc). Looking at the change you need, yep, I'll put that in since I should have had it like that originally. Thanks again!


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]OverZealous[/eluser]
Crapola, apparently some of those strtolower()s you removed were important. PostGreSQL has case sensitive tables and field names, so I'm unable to query anything, since the model names have uppercase first letters.

UPDATE
Wait-a-minute - I have hand coded some of the "models". Let me see if I can work around the issue.

Follow-Up
Yes, that did the trick, changing all of my models to lowercase. I still find it weird that PHP classes are not case sensitive.

Also, the change to an associative $fields array really buggered up some code. But I have that mostly squared away for now.


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]stensi[/eluser]
Odd, I don't know how removing those strtolower()'s could have affected you as the $this->model value is always stored in lowercase (the singular() method changes the get_class() return value to lowercase), thus, the strtolower()'s weren't actually doing anything at all and is why I had removed them.

The only time the model has an uppercase first letter is when I use ucfirst() on them, and that is never stored in $this->model as it's only done temporarily to create a temporary new instance (and from then on, that instances->model value is used, which is lowercase).

Out of interest, what sort of thing were you using the $fields array for? Would you prefer it to be changed back so it just contains the field names only?



________________________________

Version 1.5.2 has been released!

View the Change Log to see what's changed.

Basically, the above mentioned assign by reference issues have been corrected, count() now uses count_all_results() and in _related() the relationship table has been specified on the join by id's.

Sorry if you find it annoying that I'm releasing several smaller versions in quick succession, but I'm sure you'd all prefer I release bug fixes while I have the time, instead of leaving you to wait on them!

Besides, the releases and feedback I get, the better the product Wink

Enjoy :-)


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]OverZealous[/eluser]
The "error" came from me having hand-coded the $model of certain classes, using the same name as the class (with a capital first letter). It was more that the error was masked by the previous strtolower calls. That's not an issue, since I just "fixed" the model name (and a ton of other strtolower and ucfirst calls).

I use the $fields array to handle automatic form saving. It's a complicated function, and very specific to my application, but it allows a subset of the $fields and $validation $fields to be automatically processed and saved from a form. Just this second, I realized that I now could be using the $validation array exclusively, since that now contains all of the $fields as well. I might do that. Anyway, I fixed it by either calling array_keys() or changing my foreach loops to handle the association. I have no problem with the associative arrays.

Actually, I think I have the update all set, including CI 1.7. I had to fix a lot of little things, and once I'm certain, I now have to commit some 160+ files to my repository ;-)

Of course, now I have to RE-import DM 1.5.2, AND make my few changes ;-)

BTW: my remaining tweaks to DM are:
* Adding the letter 'O' to the end of the timestamps, because I need the timezone stored.
* Adding in the previously mentioned post_datamapper_init and post_model_init method calls.

Otherwise, I'm able to keep all of my changes within my subclass.


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]stensi[/eluser]
Neat :-) I hope it wasn't too much of a hassle changing it all Smile

How come you store the timezone? PostGre must handle DateTime fields differently since MySQL doesn't even let you store the timezone in a DateTime field, lol. It only stores it in the exact format DM generates ('Y-m-d H:iConfused'). I added the "O" to double check and yep, it was ignored by MySQL.

If I was allowing users to choose their own timezone, I'd store the timestamps in the default DM way (local_time = FALSE) and store the user chosen timezone in a separate field in their user record. That way, I can get timestamps from any record (user or other type) and then apply the users timezone to them upon displaying, so dates are always localised to the specific user.

Alternatively, you can set local_time = TRUE in the config, then it goes by the local time setting of the server and so the timezone is automatically applied when storing timestamps. Obviously, you wont be able to do the above trick then.

I'm sure you're already aware of this. I was mainly posting in case anyone else might wonder about the possible uses.

In the next version I'll be looking at localisation for the label's so that will hopefully negate the need for those post_ methods.


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]OverZealous[/eluser]
Well, I wouldn't be able to switch away from my current localization technique - I include a lot of other information in my files (such as units, singular and plural forms, labels for related fields, and labels for other specialized items). Most of it is not loaded into the model, but accessed through special functions on my DM extension class.

Also, I am using those methods in other ways, to manipulate static arrays within a couple of models, and to manipulate the validation array dynamically. I do this mostly for my SortableModel class, which I think I sent you a copy of, stensi.

I'll keep adding them myself, though.


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]stensi[/eluser]
Version 1.5.3 has been released!

View the Change Log to see what's changed.

I feel stupid that I missed this, but this release is to fix the $parent setting that affects the validation of self referencing relationships only (I'd accidentally left it as an object reference when I should have changed it to an array).

One other change, that I hope wont cause too many headaches :red: is that the $fields property is back as a normal array of just the field names, making it easier to loop through a properties fields. Example:

Code:
$u = new User();
$u->get(1); // Get first record

// Loop through the fields of the user object
foreach ($u->fields as $field)
{
    // And show the values
    echo '<strong>' . $field . '</strong>: ' . $u->{$field} . '<br />';
}

Enjoy :-)


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]OverZealous[/eluser]
I hope you didn't switch $fields back just for me! I actually thought that I might try to make use of the database metadata at some point.

I don't know if I made it clear, but I did get both CI 1.7 and DM 1.5.2 fully upgraded. That was one of the biggest framework changes I've made. Went surprisingly smooth.

Of course, my mail server (on the same box) decided to give me trouble last night, so, that was fun. I thought my app was running slowly, turns out the mail server decided to eat up 100% of the CPU. Ain't life grand!


DataMapper 1.6.0 - El Forum - 12-04-2008

[eluser]empoleon[/eluser]
I've just started with Datamapper and am uncomfortable with the process of retrieving records when multiple joins are required. The large number of queries being executed makes me think I'm going about this the wrong way or I should bypass Datamapper for such operations?

Scenario:
A Company sends me Products made by another Company and a Job is created.

Process:
List all Jobs. For each Job, list the associated Company and Products. For each Product, show the Company that manufactured it.

My controller:
Code:
function show_jobs()
{
    // Get all Jobs
    $j = new Job();
    $j->get();

    // Loop through all Jobs
    foreach ($j->all as $job)
    {

        // Retrieve Company associated with current Job
        $job->company->get();

        // Get all Products associated with current Job
        $job->product->get();

        // Loop through each Product associated with current Job
        foreach ($job->product->all as $product)
        {

            // Retrieve Company associated with current Product
            $product->company->get();
        }
    }

    // Send data to View
}

Does this seem right? Listing all 3 Jobs required 17 queries which makes me nervous considering I'm keen to add more relationships. But I know I did it wrong- I just know it! Smile