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-01-2008

[eluser]ntheorist[/eluser]
hehe, i know.. i hope we aren't pulling him in too many directions. I'm gonna keep working on this stuff before posting too many more ideas. I think i do also need to get up to speed on static variables and methods, to allow for better memory usage. work work work!

I know that adding more config data to the models is going to increase memory usage, and i don't necessarily like it per se, but so far it seems to be working for me. It would be nice if DM could even parse the table columns themselves. So for instance, if you have a field defined as ENUM, which naturally becomes a dropdown, perhaps DM could read that and create a static array containing it's values. Although the only thing i found so far Here uses regular expressions, which are rather slow..

I did start using array_key_exists($value, $array), but even that seems to be slightly slower than using isset(), as isset is a language construct and doesn't need to lookup the function hash, and doesn't trigger errors, only returns true/false. Here's an article comparing the two (btw, i try to take everything i read online with a grain of salt) I mean, the performance boost is really pretty negligable and it really could come down to personal preference. The one nice thing i DO really like about it though, is you can check a variable to any depth in an array, so you can do, for instance
Code:
isset($this->validation[$field]['rules']['matches']);

instead of having to do
Code:
if( array_key_exists($field, $this->validation) )
{
    if(array_key_exists('rules', $this->validation[$field])
    {
       if(array_key_exists('matches', $this->validation[$field]['rules'])
       {
          return TRUE;
       }
    }
}
because if any of the keys to get to 'matches' didn't exist, or weren't arrays it would trigger an error.

I do like your idea of having the option to bypass validation. I was very reluctant to remove it from DM, and i'm not suggesting stensi do that either. Yeah much of the datamap input is done through forms which are validated, but some stuff is automatic, and other fields are strict (like a status list), which are often individually toggled, and their values don't need it really. But again, it depends a lot on how its being used, and which way makes one more comfortable.

cheers,

CC


DataMapper 1.6.0 - El Forum - 12-01-2008

[eluser]stensi[/eluser]
Lots of great ideas there, most requiring a lot of work, lol! Wink

I'm thinking I might have to make a release with all the current changes I've done, which is a lot of optimisations and performance benefits as well as some new methods, before going ahead with any of these possibly drastic changes.

Being able to validate relations is a bit of a tricky subject. At the moment, you have to save a record first, before you're able to save a relation. For example:

Code:
// Get moderator group
$g = new Group();
$g->where('name', 'Moderator')->get();

// Save a new user
$u = new User();
$u->username = 'foo';
$u->password = 'bar';

// Save new record
$u->save();

// Save relationship
$u->save($g);

As you can see, I have to save the user first before I can save the group relationship. If I have a validation rule that states a user can't be saved without a valid group relationship, I'd never be able to save a new user. So, that would require a reworking to allow the possibility of saving a relationship at the same time as saving a new object. For example:

Code:
// Get moderator group
$g = new Group();
$g->where('name', 'Moderator')->get();

// Save a new user
$u = new User();
$u->username = 'foo';
$u->password = 'bar';

// Save new record as well as relationship
$u->save($g);

It's do-able. Obviously when deleting a relationship, I wouldn't want it to delete the objects record as well. On save, I'd have checks like:

- if record does not exist yet (empty ID), save both it and save the relationship
- else if record exists, save any changed fields and save the relationship

If you were saving a relationship on an existing item, would you want it to update any changed details on the record at the same time? And if the record didn't exist you'd want it created? If so, then there's no issue and I can go ahead with putting this in.

Alternatively, but not as clean, I could have a second parameter, so the developer has to specify the saving of both (not my preference):
Code:
$u->save($g, TRUE);

I'll try to find time to work on the above today, and how to validate relationships, since this is a pretty important requirement.


Merging the validation array into the fields array would make sense for the most part, but yes, a pretty big change. I'm dreading more having to update the documentation than modify the code for that though, lol, since the code would be easy to do. Also depends on how big of a headache this would cause people currently using it, in having to change all their models. Any drastic changes like this I'll have to think about.

At some point I will be adding a type property to the validation array, as well as one possibly called options which includes the list of possible values a user could select, in say a drop-down list. That would mean I'd obviously have to have a default setting too, so the default option could be selected.

Lots to do and think about!


@Muser, @sankai: Sorry, DM is currently not setup by default to work with HMVC. You'll need to modify the autoload method in DM for it to be able to search the HMVC modules path. I expect that you'd just need to modify the $path setting. Search for the following:

Code:
// Prepare path
$path = APPPATH . 'models';

And I'm guessing for HMVC you'd change it to:

Code:
// Prepare path
$path = APPPATH . 'modules';



DataMapper 1.6.0 - El Forum - 12-01-2008

[eluser]Muser[/eluser]
Thank you stensi!

Just another question:

1 user has only 1 role
1 role is used for many users

Having two tables...
users(id,name,role_id)
roles(id,name)

Is this possible to get it work with DM now?

Or I must to have 3 tables?
users(id,name)
roles(id,name)
users_roles(id,user_id,role_id)


DataMapper 1.6.0 - El Forum - 12-01-2008

[eluser]stensi[/eluser]
At the moment you'll need the 3 tables but with the joining table named in alphabetical order. Example:

users
id
name

roles
id
name

roles_users
id
role_id
user_id


To setup the relationship in the model's as you've described (one to many):

In your User model:
Code:
$has_one = array('role');

In your Role model:
Code:
$has_many = array('user');


UPDATE:

Before, when I was talking about looking into saving relationships at the same time as saving a new record, well, this is now possible in the coming version. You will also be able to update an existing record (it will update changed fields only) at the same time you create or update a relationship on it.

What I'm trying to figure out now is the validation, since that still goes in the same order as I mentioned... I'll most likely have a separate validation process for relationship rules that get run when saving a relationship. The validation array would simply have the rules for related items added as normal. For example:

A User object must relate to a Group object. The last entry in the validation array is the rule that a relationship to a group is required. DM will be able to figure out that "group" is not a related field, not a field of user so it can validate it as a relationship.

User
Code:
var $validation = array(
    array(
        'field' => 'username',
        'label' => 'Username',
        'rules' => array('required', 'trim', 'unique', 'min_length' => 3, 'max_length' => 20)
    ),
    array(
        'field' => 'password',
        'label' => 'Password',
        'rules' => array('required', 'trim', 'min_length' => 3, 'max_length' => 40, 'encrypt')
    ),
    array(
        'field' => 'confirm_password',
        'label' => 'Confirm Password',
        'rules' => array('required', 'encrypt', 'matches' => 'password')
    ),
    array(
        'field' => 'email',
        'label' => 'Email Address',
        'rules' => array('required', 'trim', 'unique', 'valid_email')
    ),
    array(
        'field' => 'group',
        'label' => 'Group',
        'rules' => array('required')
    )
);

Besides the required rule, I'll be adding a couple of other rules so you can specify an upper limit on how many relations each object can have (obviously this would only be intended for One to Many or Many to Many, since One to One is already restricted).

For example, you might have an Author object that can have one or more Books, but you want to restrict it to be from 1 to 20 books. You'd do:

Author
Code:
var $validation = array(
    array(
        'field' => 'book',
        'label' => 'Book',
        'rules' => array('required', 'max_size' => 20)
    )
);

Hmm, there's also the matter of not allowing you to delete your last relationship, such as with the example that a user must relate to a group, so that you instead must update it or delete the user to delete the relationship.


DataMapper 1.6.0 - El Forum - 12-01-2008

[eluser]ntheorist[/eluser]
yeah i've run into that bump in terms of validating relationships on 'new' models. basically i open the form class with $edit_mode = ! empty($this->model->id), and then that can be used as a switch for whether to test relations. But still, in order for a required relationship to be properly saved, they both need to exist and both need to be saved simultaneously.

At the moment i have it set up so you can only select from pre-existing relations, and for any non-file model that's a form element (typically dropdown) with the name {$relation} and with <option value="{$id}">{$primary_value}</option> as the standard. So, then i process forms by collecting all possible values from the fields/relations in post array, also checking for file relations, and inserting their values in arrays then running validation on them. If it all checks out then copy all values and proceed with a save (where needed).

It gets a bit trickier with 'many' relationships, in that i have them producing form elements with the name {$relation}_{$id}, and it scans for changes in those, but you don't want multiple select lists and allow the same relation chosen twice, plus the old relationship has to be deleted on an update. It get's even hairier when dealing with files, heh..

I think i may build some more common functions like get_value() for generic datamapper usage, such as collect(), for grabbing data from post/get/files, which i have running in my form class.

also, one other thing i'm trying, and i'm kinda just curious whether you think its a good idea or not, is I replaced the $related array which just has the model, table and id, with an actual object reference to the model that set it. Then you could do a save_to_parent() method or something.. i dunno it seems helpful to have a two way road between models and their relations, plus this may help with form collection, validation and saving, too. But can you think of any reason why that would affect performance if done correctly?

@Muser Dang it, i haven't even gotten into modules yet, but HMVC seems pretty cool.. let me know if that solution works for you.

thx,

CC


DataMapper 1.6.0 - El Forum - 12-02-2008

[eluser]stensi[/eluser]
Good news. I've finished implementing the validation of relationships and being able to save a new or existing object optionally with relationships at the same time. Due to the complicated nature of relationships, there's a different type of validation rule you can setup. Normally you'd have an underscore followed by the name of the validation rule: _{rule}(), for example "_encrypt", but for related validation rules it is an underscore follwed by related and then the name of the validation rule: _related_{rule}()

Currently there are only 3 inbuilt related validation rules, as I couldn't think of any others that would actually be necessary:

_related_required (used as 'required')
_related_min_size (used as 'min_size' => 123)
_related_max_size (used as 'max_size' => 123)

The only thing is, there's no validation when deleting an object, so unfortunately, I haven't implemented a check when delete is called to see if deleting the object will drop it below the min_size or if there would be no relationships left, thus required fails. I might add that in since it makes sense to.

Everyone will have to make absolutely sure they're checking the boolean return value when saving and deleting so you're handling it correctly if something isn't saved or deleted due to it failing validate(), which you should be doing anyway Tongue

@commandercool: Replacing the $related array is an awesome little improvement. I've gone ahead and done this, replacing it with a $parent property that is a reference to the parent object. Makes the code look much cleaner and no, there shouldn't be any performance issues from doing it this way.


DataMapper 1.6.0 - El Forum - 12-02-2008

[eluser]DominixZ[/eluser]
I have a little bug for version 1.4.5

I use Codeigniter 1.7.0 and MySQL 5.0.51b when i use
$u = new User();
$u->where('username',$this->session->userdata('username'))->get();
$u->bookmark->get();

that bookmark and user has associated * to 1 but i get this


A Database Error Occurred

Error Number: 1054

Unknown column 'bookmarks.*' in 'field list'

SELECT `bookmarks`.`*` FROM (`bookmarks`) LEFT JOIN `bookmarks_users` ON `bookmarks`.`id` = `bookmark_id` LEFT JOIN `users` ON `users`.`id` = `user_id` WHERE `users`.`id` = 3

i just notice that if i remove some symbol it can run on mysql like

SELECT bookmarks.* FROM (`bookmarks`) LEFT JOIN `bookmarks_users` ON `bookmarks`.`id` = `bookmark_id` LEFT JOIN `users` ON `users`.`id` = `user_id` WHERE `users`.`id` = 3

I don't know why i have this bug can you give a solution for this version. I don't like the old way of 1.3.4 that works fine for me

Edit. Well 1.3.4 is not work fine :'( so how can i fixed this

i have this table
bookmarks
users
bookmarks_users (id,bookmark_id,user_id)

Edit2. I just test again with Codeigniter 1.6.3 with DataMapper 1.4.5 it works fine.


DataMapper 1.6.0 - El Forum - 12-02-2008

[eluser]stensi[/eluser]
@DominixZ: The issue you're having is due to a bug in CodeIgniter 1.7.0, not DataMapper. The CodeIgniter Team have fixed it in the SVN though. You'll need to download the latest version of the mysql_driver.php file.

I'm including instructions for this in the coming version of DM, until there's a new CI release that includes the fixed driver.




_____________________________________________

Version 1.5.0 has been released!

View the Change Log to see what's changed.

There's a lot of performance improvements as well as several new methods and greater functionality. I recommend taking a look at the Change Log for a summary but definitely re-read the DataMapper User Guide.

Feedback and bug testing is welcomed!

Enjoy :-)


Note that this version does not include any of the HTML display functionality that was previously discussed, as that will be coming in a later version.

Also, I still want to include the ability to do better "related" queries, such as related_where(), related_like() etc. but I haven't had time to properly look at this yet.


DataMapper 1.6.0 - El Forum - 12-03-2008

[eluser]Murodese[/eluser]
Performance decreased by 8-9% ;[

Test query below;

Code:
foreach($c->project->where('name', 'Sqripted')->get()->milestone->where('version', '1.0alpha')->get()->task->where('name', 'Performance Bug in Assets')->get()->entry->get()->all as $f)
{
    echo $f->comment;
}

Under 1.4.5, execution was an average of 0.0297s, 1.5.0 is up to 0.0323s.

That said, this is a great library and will be incredibly handy for a big project we have coming up.


DataMapper 1.6.0 - El Forum - 12-03-2008

[eluser]stensi[/eluser]
Interesting. What sort of volume of records is that with? It shouldn't be slower as there are a lot less foreach loops taking place as well as many other speed improvements, and generally much better code.

I'll setup a comprehensive test suite to stress test both versions and let you know the results. I wonder if it's anything to do with the use of static variables or perhaps the magic __call() method.