Welcome Guest, Not a member yet? Register   Sign In
DMZ 1.7.1 (DataMapper OverZealous Edition)

[eluser]lsemel[/eluser]
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:

Code:
$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:

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: http://ellislab.com/forums/viewthread/92212/P90/#578755
     */
    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");
    }



}

?&gt;


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

[eluser]12vunion[/eluser]
[quote author="NachoF" date="1273043831"]I just ran into a little trouble

I need to use the sql query syntax like so.
Code:
$o=new Object();
$where="id=2";
$o->where($where)->get();

I get Database error
Quote:ERROR: column "id=2" does not exist
LINE 3: WHERE "id=2"

SELECT *
FROM "entities"
WHERE "id=2"
Am I missing something obvious?[/quote]
Try:
Code:
$o=new Object();
$o->where('id', 2)->get();

[eluser]OverZealous[/eluser]
@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:
Code:
$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));

[eluser]lsemel[/eluser]
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 &lt;?= form_error('field') ?&gt; 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.

[eluser]OverZealous[/eluser]
[quote author="lsemel" date="1273112453"]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...[/quote]

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.

Quote: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! ;-)

Quote:And wanted to display errors, wherever they came from, using the same code &lt;?= form_error('field') ?&gt; 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}.)

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

[eluser]Alface[/eluser]
For everyone know, I posted a new modification on codeigniter wiki
http://codeigniter.com/wiki/HMVC_for_DMZ...eparation/

[eluser]Atas[/eluser]
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 !

[eluser]OverZealous[/eluser]
@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.

[eluser]Atas[/eluser]
Thank you for your response!

I have the following code.

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:

Code:
/*** 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.




Theme © iAndrew 2016 - Forum software by © MyBB