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.

DMZ 1.7.1 (DataMapper OverZealous Edition)

March 14, 2010 11:43pm

Subscribe [104]
  • #211 / May 05, 2010 3:41pm

    lsemel

    18 posts

    Hi Phil,
    I was thinking about what the best way to integrate DataMapper model validation with regular CI form validation.  I ended up writing an extension to the form validator in CI that lets you process forms involving DataMapper models in only a few lines of code.  Assuming you have a DataMapper object Status that has its own validations, and your form’s fields match the fields from Status, you can write this:

    $status = new Status();
    $status->get_by_id($status_id_to_update);
    $this->form_validation->set_model($status);
    # Add any additional form validation rules here
    if ($this->form_validation->run($this)) {
          $model->save();
    }

    Here’s the full code:

    <?php
    
    /**
     * This form validation class extends the one that comes with Code Igiter to solve
     * a few problems:
     * - The Code Igniter one does not work well with Modules.  This gives the validation
     *   run() function an extra parameter to indicate what class contains any
     *   custom validation methods.
     * - Form validation from Datamapper is completely separate from form validation
     *   Code Igniter, and this unites them.  Call $this->form_validation->set_model($dm_object)
     *   to have the form validator include the data model's validations along with
     *   any rules you set up directly
     * - This also copies data from the form into the model, so it doesn't have to be done
     *   manually.  You can validate and save a model's 'text' field like this
     *
     *   $status = new Status();
     *   $status->get_by_id($status_id_to_update);
     *   $this->form_validation->set_model($status,array('text'));
     *   if ($this->form_validation->run($this)) {
     *      $model->save();
     *      $this->form_validation->clear();
     *      $this->data->message = 'Your item was saved';
     *   }
     *
     *  Any error messages from the model will appear using the regular form_error()
     *  helper.  The fields used in the form must match the field names of the model
     *  - A clear() method is provided which will clear out any data submitted by the
     *    post, in cases where the form results are shown on the same page as the form
     *    itself
     */
    class MY_Form_Validation extends CI_Form_Validation {
        var $model;
        var $model_fields = array();
    
        /**
         * Sets the error delimiters the way we want them on this site
         */
        function __construct() {
            parent::__construct();
            $this->set_error_delimiters('<div class=\"error\">','</div>');
        }
    
        /**
         * Call this to validate on the given model.  The model's fields will be set from the
         * contents of the post.  Specify an array of fields to set, otherwise all the
         * fields from the post will be set in the model.  The model's validation rules
         * will be called and added to the form validation object as if they had been
         * set up using the usual Code Igniter way
         *
         * This can also be called with a dummy model (e.g. new Account()) if you want
         * to ensure that the fields don't get saved
         */
        function set_model(&$model,$fields = array()) {
            $this->model =& $model;
            if (is_string($fields)) $fields = preg_split('/\s*,\s*/',$fields);
            $this->model_fields = $fields;
        }
    
        /**
         * Modification needed to work with Modules
         * See: <a href="http://ellislab.com/forums/viewthread/92212/P90/#578755">http://ellislab.com/forums/viewthread/92212/P90/#578755</a>
         */
        function run($module = '', $group = '') {
            (is_object($module)) AND $this->CI =& $module;
    
            // CodeIgniter convention is to return false if nothing was posted
            if (!$_POST) return false;
            
            // Skip CI's validiton if nothing was posted, or if there are no validation rules
            $result = ($_POST && (count($this->_config_rules) > 0 || count($this->_field_data) > 0)) ? parent::run($group) : true;
      
            // Perform the model's own validation
            $model_result = $this->model? $this->validate_model($this->model) : true;
            return $result && $model_result;
        }
    
        /**
         * Clears out all the posted data
         */
        function clear() {
            $this->_field_data = array();
        }
    
        /**
         * Sets an error message on a particular field, which will be accessible
         * via the form_error() helper
         *
         * @param <type> $field
         * @param <type> $message
         */
        function set_error($field,$message) {
            $this->_field_data[$field]['error'] = $message;
            $this->_error_array[$field] = $message;
        }
    
    
        /**
         * Given a DataMapper object, run validations on it and add any additional error messages to the form
         * This way we can use one validation mechanism.  Returns true or false if the model is valid
         * Form fields are pulled from the POST
         */
        function validate_model(&$model) {
            $ci =& get_instance();
    
           
            $model->error_prefix = $this->_error_prefix;
            $model->error_suffix = $this->_error_suffix;
    
            if (is_callable(array($model,'validate'))) {
    
                // Populate the model with the posted fields
                while (list($k,$v) = each($_POST)) {
                    if ((!$this->model_fields) || in_array($k,$this->model_fields)) {
                        $model->$k = $v;
                    }
                }
                $model->validate();
                if (!$model->valid) {
                    $errors = (array)($model->error);
                    unset($errors['all']);
                    unset($errors['string']);
                    while(list($k,$v) = each($errors)) {
                        if ($v && !$this->error($k)) {
                            $this->set_error($k,$v);
                        }
                    }
                }
                return $model->valid;
            }
            trigger_error("The object provided is not a Datamapper model");
        }
    
    
    
    }
    
    ?>


    Is there a better way to integrate the two distinct types of validation?

  • #212 / May 05, 2010 3:59pm

    12vunion

    36 posts

    I just ran into a little trouble

    I need to use the sql query syntax like so.

    $o=new Object();
    $where="id=2";
    $o->where($where)->get();

    I get Database error

    ERROR:  column “id=2” does not exist
    LINE 3: WHERE “id=2”

    SELECT *
    FROM “entities”
    WHERE “id=2”

    Am I missing something obvious?

    Try:

    $o=new Object();
    $o->where('id', 2)->get();
  • #213 / May 05, 2010 6:08pm

    OverZealous

    1030 posts

    @lsemel

    I don’t know if you are aware, but DMZ already uses the CodeIgniter Form_Validation library.  Any rules under Form_Validation are available to DMZ models.

    You shouldn’t use Form_Validation the way you have, because DMZ has internal validation routines that are called on every save.  It means you don’t have to manually check the rules ever.  The normal usage is this:

    $object = new Object();
    if($this->input->post('name') !== FALSE) {
        // set the fields
        $object->name = $this->input->post('name');
        if($object->save()) {
            // success, redirect or load in the correct view.
            exit();
        }
    }
    
    // render the form here, including outputting any errors
    $this->load->view('my_form', array('object' => $object));
  • #214 / May 05, 2010 6:20pm

    lsemel

    18 posts

    Got it…  I had a some cases where I wanted to keep the bulk of the validation rules within my models, but add others for other fields that exist on the form but don’t correspond to one in a model, and didn’t want to have to deal with those two sources of rules separately. And didn’t want to manually copy all the fields from the form into my model with a bunch of $model->whatever = $this->input->post(‘whatever’), that should happen automagically.  And wanted to display errors, wherever they came from, using the same code <?= form_error(‘field’) ?> in my views, and not have the views worry about whether an error comes from the form validation class or DataMapper.  So this solves for the problem of having two distinct sources of validation rules.

  • #215 / May 05, 2010 10:07pm

    OverZealous

    1030 posts

    I had a some cases where I wanted to keep the bulk of the validation rules within my models, but add others for other fields that exist on the form but don’t correspond to one in a model…

    Seems like you know this, but you can add validation for “virtual” fields, like the confirm_password example.  However, they can get in the way when you don’t need them.

    And didn’t want to manually copy all the fields from the form into my model with a bunch of $model->whatever = $this->input->post(‘whatever’)

    See the Array extension, using from_array!  😉

    And wanted to display errors, wherever they came from, using the same code <?= form_error(‘field’) ?> in my views, and not have the views worry about whether an error comes from the form validation class or DataMapper.

    You also can add your own errors to the object’s error, using error_message.  This might help, as well.  (You can get a specific error message using object->error->{$field}.)

  • #216 / May 05, 2010 10:26pm

    lsemel

    18 posts

    Thanks, I’ll check that out. 

    I was mainly looking for a consistent syntax for doing form validation sitewide.  Some forms don’t use datamapper objects, some do.  Normally there are two distinct syntaxes for validating forms and displaying errors, depending on if you’re working with a model or not.  I want to be able to tell everyone on the site to always do things one way, and add the one line of code if they are validating against a model.

  • #217 / May 06, 2010 7:09am

    Alface

    41 posts

    For everyone know, I posted a new modification on codeigniter wiki
    http://codeigniter.com/wiki/HMVC_for_DMZ_Modular_Separation/

  • #218 / May 08, 2010 6:06pm

    Atas

    45 posts

    Hello, this orm is great. But i have a question.

    I want to set a One to many relationship, i have the following tables:

    Publication
    -id
    -name
    - other fields

    Image
    -id
    -publication_id
    -image_src

    Image has many linked records from publciation table.

    Must i use $has_one, $has_many or what in the models class?

    I don’t want to create a dedicated table to do this.

    Thank in advance !

  • #219 / May 08, 2010 8:19pm

    OverZealous

    1030 posts

    @Atas

    You can use In-Table Foreign Keys for Many-to-One or One-to-One relationships, without a dedicated join table.

    See In-Table Foreign Keys on the Database Tables page for more information.

  • #220 / May 08, 2010 9:02pm

    Atas

    45 posts

    Thank you for your response!

    I have the following code.

    /***controller***/
    $oPublication = new oPublication();
    $oPublication->where('id', $aParams['id'])->get();
    
    //i want to get all images of this publciation
    $oPublicacion->image->get();


    My models are:

    /*** Model Publication ***/
    class Publication extends DataMapper {
        
        var $table = 'publication';
        var $has_one = array("image")
    
        
        function __construct($id = NULL)
        {
            parent::__construct($id);
        }
        
    }
    
    /*** Model Image***/
    class Image extends DataMapper {
        
        var $table = 'image';
        //var $has_many = array("publication") this is wrong
        //what should i do here ????
        
        function __construct($id = NULL)
        {
            parent::__construct($id);
        }
        
    }

    I don’t want to annoy you but i dont understand how build my models to do this.

    Thanks in advance.

  • #221 / May 08, 2010 9:29pm

    OverZealous

    1030 posts

    You have it backwards.  😉

    Each publication has many images, so put ‘image’ into the $has_many array for Publication.  Each image has one publication, so put ‘publication’ into the $has_one array for Image.

    Your ‘images’ table should have the column ‘publication_id’.  Then DMZ should figure out everything else for you.

    Good Luck!  😊

  • #222 / May 08, 2010 10:12pm

    Atas

    45 posts

    Thank you very much for your time, works ok !

  • #223 / May 09, 2010 6:58pm

    introvert

    83 posts

    Hello.

    I want to update “updated” field when I update the relationship objects.
    DM OZ seems not to do that, although I set updated to “” (updated = “”) and call save(); function.

    What should I do?

    Thanks for help!

  • #224 / May 09, 2010 8:55pm

    Wible

    4 posts

    I’m a recent DMZ convert (from Datamapper). I’ve gone through your code and what you offer and I have to say I can’t wait to really get into it.

    That said, I’m working on a major project and I’m building it from the ground up. I would like your advice if you have a minute… is there any authentication/user library/extension that you recommend for integrating with DMZ?

    I’ve reviewed several (probably a dozen or more) libraries and found MAYBE one or two that I felt had good, clean code, blah blah blah. BUT - none of them leverages what you offer with DMZ.

    I could write my own, and I’m a hair into that already, but I’m a strong believer in not reinventing the wheel. But cleaning up someone else’s junk just to make it work with DMZ isn’t my idea of a good time.

    Do you have any recommendations? My “inner Yoda” is telling me to write my own and offer it to the community but I have a feeling someone else already has - or maybe you have one?

    All that aside - thank you for doing what you do. There’s a page slotted in my “master plan” to credit my contributors with a cozy spot for you when this is all done. Doesn’t count for much, but I’ll be making a donation when the time comes too 😊

    Your code rocks and helps my code rock. Thanks!

  • #225 / May 09, 2010 9:02pm

    OverZealous

    1030 posts

    I want to update “updated” field when I update the relationship objects.
    DM OZ seems not to do that, although I set updated to “” (updated = “”) and call save(); function.

    As you noted, updated only changes when the object itself has changed.  There’s no supported way to change this behavior.

    One possible way, though, is to create an extra database column (like an int), and then override the save() function to increment this column whenever save() is called with an object.

    Or maybe someone has a better idea.  😊

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

ExpressionEngine News!

#eecms, #events, #releases