ExpressionEngine CMS
Open, Free, Amazing

Thread

This is an archived forum and the content is probably no longer relevant, but is provided here for posterity.

The active forums are here.

DataMapper 1.6.0

September 05, 2008 12:32pm

Subscribe [115]
  • #16 / Sep 16, 2008 9:19pm

    stensi

    109 posts

    Version 1.2 has been released!

    View the Change Log to see what’s changed.

    In short, the find method has been removed and replaced with the get method, using functionality similar to retrieving data with CodeIgniter’s Active Record class.  My post above shows one of the many ways in which you can now populate your objects (including method chaining).  Validation has been improved too, most specifically the error messages.  Automated timestamping is also included.

    Enjoy 😊

  • #17 / Sep 17, 2008 12:05am

    BaRzO

    105 posts

    thanks for your work i am following your posts and i am reading your user guide
    thanks for all 😉

  • #18 / Sep 17, 2008 7:16pm

    Boyz26

    28 posts

    Great job!!

    I like your very friendly user guide especially.

    Question: Will there be a big difference if I use many-to-many for one-to-many or even one-to one relationships?

    Thanks!

  • #19 / Sep 17, 2008 10:40pm

    stensi

    109 posts

    Glad you like it 😊

    Yep, there is quite an important difference actually.  If you use Many to Many for what should be a One to Many or One to One relationship, then DataMapper will not be able to ensure the integrity of your relationships.

    <u>For example:</u>
    Let’s say you have a users table and a groups table and you have a business requirement that each user can belong to ONLY ONE group but a group can have many users.  So, this means the users table will have a One to Many relationship with the groups table.

    To set this relationship in your DataMapper models, the User DataMapper model would have:

    $has_one = ("group" => "groups");

    And the Group DataMapper model would have:

    $has_many = ("user" => "users");


    DataMapper has a series of integrity checks when a relationship is added/updated/deleted between tables with One to One and One to Many relationships.

    When saving a relationship between a user and a group (One to Many) DataMapper will check to see if the user already has an existing relationship with a group, and if it does, it will update that relationship record rather than adding a new relationship record.  If the user has no relationship record, one is added.  This ensures there is only one relationship record for each user, between the users and groups tables.

    If you had set it up to be a Many to Many, this would mean a user can belong to many groups (and a group can have many users).  So, if user FOO was related to group A and you then saved a relationship between user FOO and group B, user FOO would be related to both group A and group B.

    Now, since user FOO belongs to more than one group we’ve broken our initial business requirement that each user can belong to only one group.  Setting it up as One to Many would have ensured the business requirement was adhered to.

    A situation where you would use a Many to Many relationship would be something like skills and employees.  Each employee can have multiple skills (you’d hope), and a skill can be listed against multiple employees, thus a Many to Many relationship.

    I hope that example makes it clearer for you.

    You can read a longer explanation of the relationship types in the DataMapper User Guide.

  • #20 / Sep 17, 2008 10:47pm

    Boyz26

    28 posts

    That’s great!! Thanks for your wonderful reply. Now I value your DataMapper more!

  • #21 / Sep 18, 2008 12:14pm

    BaRzO

    105 posts

    stensi i have a problem to show errors

    in your example i have added
    echo $this->error->username;
    but i can not touch to error message
    i get PHP error

    A PHP Error was encountered
    Severity: Notice
    Message: Undefined property: Examples::$error
    Filename: controllers/examples.php
    Line Number: 105

    // Save User
    
            if ($u->save())
    
            {
    
                echo '
    ';
    
                echo 'Current values:';
    
                
    
                echo '<code><strong>ID</strong>: ' . $u->id . '
    ' .
    
                '<strong>Username</strong>: ' . $u->username . '
    ' .
    
                '<strong>Email</strong>: ' . $u->email . '
    ' .
    
                '<strong>Password</strong>: ' . $u->password . '
    ' .
    
                '<strong>Salt</strong>: ' . $u->salt . '
    '; } else { echo 'User has already been created'; echo $this->error->username; }</code>

    Thanks for reply…

  • #22 / Sep 18, 2008 6:38pm

    stensi

    109 posts

    Hi BaRzO.  The errors are set on the object itself, not the Controller, so all you need to do with your code example is replace $this with $u. Like so:

    Replace:

    echo $this->error->username;

    With:

    echo $u->error->username;

    Alternatively, you can show all errors as a single string (each error within it will be wrapped by the error prefix and suffix tags):

    echo $u->error->string;
    // This might output something like:
    //   Username is taken.
    //   Password must be at least 6 characters long.

    Looping through all errors:

    foreach ($u->error->all as $error)
    {
        echo $error;
    }

    If you had instantiated your object like so:

    $vehicle = new Vehicle();

    You would access its errors like so:

    echo $vehicle->error->property;

    If you think I should update my User Guide to make this clearer, let me know.

  • #23 / Sep 18, 2008 7:09pm

    BaRzO

    105 posts

    // Create User
    $u = new User();
    $u->username = 'Fred Smith';
    $u->email = '[email protected]';
    $u->password = 'apples';
    echo '
    ';
    echo 'Current values:';
    echo '<code><strong>ID</strong>: ' . $u->id . '
    ' .
    '<strong>Username</strong>: ' . $u->username . '
    ' .
    '<strong>Email</strong>: ' . $u->email . '
    ' .
    '<strong>Password</strong>: ' . $u->password . '
    ' .
    '<strong>Salt</strong>: ' . $u->salt . '
    '; echo '<hr >'; echo 'Now to save it to the database.'; echo ' // Save User $u->save();'; // Save User if ($u->save()) { echo ' '; echo 'Current values:'; echo '<strong>ID</strong>: ' . $u->id . ' ' . '<strong>Username</strong>: ' . $u->username . ' ' . '<strong>Email</strong>: ' . $u->email . ' ' . '<strong>Password</strong>: ' . $u->password . ' ' . '<strong>Salt</strong>: ' . $u->salt . ''; } else { echo 'User has already been created'; echo $u->error->username; // Unable to access an error message corresponding to your field name. // print_r($u->error->all); }</code>

    i did it but is giving this result…

    Unable to access an error message corresponding to your field name.

    edit :
    Your user guide is very good i did read it all..
    but if you can put some more example would be very nice i think maybe for beginers like me 😊

  • #24 / Sep 18, 2008 7:52pm

    stensi

    109 posts

    Ah, I see.  I didn’t give an example of setting up the error messages for that, sorry.

    What DataMapper is trying to do is read an error message for the “unique” validation rule from your validation language file.  One is not there by default since CodeIgniter’s Validation class doesn’t have the “unique” validation rule, DataMapper does, so it’s not finding it.

    To solve this for now, copy the following into a file named validation_lang.php, and put it in your system/application/language/english folder.

    <?php
    
    // This is the new error message for the unique validation rule
    $lang['unique']         = "The %s you supplied is already taken.";
    
    $lang['required']         = "The %s field is required.";
    $lang['isset']            = "The %s field must have a value.";
    $lang['valid_email']    = "The %s field must contain a valid email address.";
    $lang['valid_emails']     = "The %s field must contain all valid email addresses.";
    $lang['valid_url']         = "The %s field must contain a valid URL.";
    $lang['valid_ip']         = "The %s field must contain a valid IP.";
    $lang['min_length']        = "The %s field must be at least %s characters in length.";
    $lang['max_length']        = "The %s field can not exceed %s characters in length.";
    $lang['exact_length']    = "The %s field must be exactly %s characters in length.";
    $lang['alpha']            = "The %s field may only contain alphabetical characters.";
    $lang['alpha_numeric']    = "The %s field may only contain alpha-numeric characters.";
    $lang['alpha_dash']        = "The %s field may only contain alpha-numeric characters, underscores, and dashes.";
    $lang['numeric']        = "The %s field must contain a number.";
    $lang['integer']        = "The %s field must contain an integer.";
    $lang['matches']        = "The %s field does not match the %s field.";
    
    
    /* End of file validation_lang.php */
    /* Location: ./system/application/language/english/validation_lang.php */

    I’ll make an updated version of DataMapper that includes a language file with this sort of thing already set up for you, since it will need a similar message for the other inbuilt DataMapper validation rules.

    For any custom rules you make in your models that extend DataMapper, you can use the error_message() function.

    UPDATE

    A language file has been included with version 1.2.1 that has the validation error messages for the DataMapper-only validation rules.

  • #25 / Sep 18, 2008 8:07pm

    BaRzO

    105 posts

    Ok now it fixed 😊
    thanks i will try my self to learn what can i do with DM 😉

    Edit :
    It’s me again 😊
    I want to ask… How we will drive with validation fields

    $rules['username']    = "required";
    $rules['password']    = "required";
    $rules['passconf']    = "required";
    $rules['email']        = "required";
    
    $this->validation->set_rules($rules);
    
    $fields['username']    = 'Username';
    $fields['password']    = 'Password';
    $fields['passconf']    = 'Password Confirmation';
    $fields['email']    = 'Email Address';
    
    $this->validation->set_fields($fields);
  • #26 / Sep 20, 2008 3:43am

    stensi

    109 posts

    Good question, and thanks for bringing it up since I think you’ve pointed out a shortcoming in the current version that I didn’t notice.

    I’ve got the $rules side covered with the $validation array in the models, but I don’t think I covered off the $fields naming side of things for error messsages.  At the moment it uses the exact name of the field (username, email, etc) but as you’ve shown, sometimes you’ll want it like “Email Address” or “Password Confirmation” which is currently not accomodated.

    It’ll require only a small change though, probably a change in format of the $validation array, or splitting it into a $rules and $fields array.  I’ll see what works best when I get the time, and then I’ll write up a full working “real world” example on how to use all aspects of the validation, including the naming of the fields for error messages etc.

  • #27 / Sep 21, 2008 11:49pm

    GregX999

    39 posts

    Hi,

    Two quick questions about DataMapper:

    1. Why do you use join tables for one-one and one-many relationships instead of just using foreign keys?

    2. Can you search based on relationships? (ie: if you have a “restaurant” model, a “menu” model and a “menu_item” model, and restaurants have one menu and menus have many menu_items, can you find all restaurants that serve items with “chicken” in the name? Can you find all menu_items served at Joe’s Diner?)

    Greg

  • #28 / Sep 22, 2008 2:44am

    stensi

    109 posts

    1:
    I know I could have used foreign keys for One to One and One to Many relationships but my personal preference is to make it so the normal tables have no information at all about any other tables (including foreign keys).  So, they only contain the information relevant to themselves.  I also found doing it this way made the implementation of DataMapper much easier.

    DataMapper ensures the integrity of relationships stored in joining tables are kept intact for each of the different relationship types, so you don’t need to worry about your One to One relationship becoming something else, it wont.

    2:
    Ok, so the Restaurant has a One to One relationship with Menu, and Menu has a One to Many relationship with MenuItem.

    There’s several ways you can find which restaurants have the “chicken” dish.

    NOTE: I’m assuming the “menuitems” table has a “dish” field, and the “restaurants” table has a “name” field.

    Here’s one way (starting with the MenuItem):

    // Dish to find
    $dish = "chicken";
    
    // Get the menu item the user wants
    $menuitem = new MenuItem();
    $menuitem->where('dish', $dish)->get();
    
    // Loop through the menu's this dish is on
    foreach ($menuitem->menu->all as $menu)
    {
        // Show the restaurants that the menu belongs to (that has the dish)
        echo 'Restaurant ' . $menu->restaurant->name . ' has ' . $dish . '
    ';
    }

    Here’s another way (starting with all restaurants, to find the restaurants with the dish):

    // Dish to find
    $dish = "chicken";
    
    // Get all restaurants
    $restaurant = new Restaurant();
    $restaurant->get();
    
    // Loop through all restaurants
    foreach ($restaurant->all as $r)
    {
        // Loop through all menu items in the restaurants menu
        foreach ($r->menu->menuitem->all as $mi)
        {
            // If the meni item's dish is the same dish we're looking for
            if ($mi->dish == $dish)
            {
                // Show the restaurant that has it
                echo 'Restaurant ' . $r->name . ' has ' . $dish . '
    ';
            }
        }
    }

    All MenuItems served at “Joe’s Diner”:

    // Name of Restaurant
    $name = "Joe's Diner";
    
    $r = new Restaurant();
    $r->where('name', $name)->get();
    
    echo 'Restaurant ' . $r->name . ' has the following menu items:
    ';
    
    foreach ($r->menu->menuitems->all as $mi)
    {
        echo $r->dish . '
    ';
    }
  • #29 / Sep 22, 2008 4:15am

    ray73864

    268 posts

    From what i have read so far in the User Guide, i can see how powerful of a system this is.

    I was however reading the section up on ‘Automated Timestamps’ and see at least one main problem with it.

    The problem i see with it is that the ‘Created’ and ‘Updated’ fields have to be set ‘DateTime’ type, however when i am creating fields that hold dates i never store them that way i always have it as an integer field which stores the unix_time_stamp.

    Is there a possibility of making it so that the automated timestamps can accept either of the 2?

    Ray

  • #30 / Sep 22, 2008 5:23am

    stensi

    109 posts

    Yep, that’s do-able although if you need unix style timestamps urgently, you can manage them manually.  I chose to use MySQL’s DateTime for the automated timestamps because it’s much more flexible.  I’ll make it a simple TRUE/FALSE setting as to which type gets used.

    Just a bit of trivia, although a long way off the unix timestamp has a year 2038 problem.  Not really an issue though since most people will fix their systems before then 😉

.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases