I need to set up the validation rules to validate the related items on a specific object, ie: A user can have no more than 3 products related to it.
I believe DataMapper can check for this validation using _related_max_size rule, but I can't figure out how to use it on the $validation array in the model.
So far I've tried this in both my user and product models:
var $validation = array(
'product' => array(
'rules' => array('max_size' => 3)
)
);
Can somebody show me an example on how to set up this at the model, controller and finally the view?
Edit: What I mean is, a user has many products, and can create a certain amount of them, let's say 3 products, when that amount is reached, the user can no longer create products, and this validation rule should not permit the user to create more products.
This would be the DB Schema:
Users table
------------------
id | username |
------------------
Products table
------------------------
id | user_id | name |
------------------------
More info here: http://codeigniter.com/forums/viewthread/178045/P500/
Thanks!
EDIT:
Ok, I got it all working now… Except, I need to do the following:
var $validation = array(
'product' => array(
'label' => 'productos',
'rules' => array('required','max_size' => $products_limit)
)
);
The $products_limit comes from the “plan” the user has associated, and it’s stored in the session when the user logs in. When I try to run this I get:
Parse error: syntax error, unexpected T_VARIABLE in /var/www/stocker/application/models/user.php on line 11
Is there any way to make this setting dynamic?
In model
var $validation = array(
array(
'field' => 'username',
'label' => 'Username',
'rules' => array('required')
)
);
In controller. $this -> $object = new Your_model();
$object->validate();
if ($object->valid)
{ $object->save();
// Validation Passed
}
else
{ $data['error'] = $object->error;
// Validation Failed
}
In view.
echo $error->field_name
I never use Codeigniter before, but give me a chance to help you. So far I didn't found any built-in validation in Code-igniter (correct me if I'm wrong).
One workaround that I could think of is to Callback:Your own Validation Functions. Below is a snip. Pardon me if it didn't work as you want.
In Model: (create something like)
function product_limit($id)
{
$this->db->where('product_id',$id);
$query = $this->db->get('products');
if ($query->num_rows() > 3){
return true;
}
else{
return false;
}
}
In controller: (create something like)
function productkey_limit($id)
{
$this->product_model->product_exists($id);
}
public function index()
{
$this->form_validation->set_rules('username', 'Username', 'callback_product_limit');
}
For more information Please refer to the manual page which gives more complete. I am also new to CodeIgniter. But I hope this helps you, not complicate you.
First, set up a custom validation rule in libraries/MY_Form_validation.php
If the file doesn't exist, create it.
Contents of MY_Form_validation.php:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Form_validation extends CI_Form_validation
{
function __construct($config = array())
{
parent::__construct($config);
}
function valid_num_products()
{
//Perhaps it would be better to store a maxProducts column in your users table. That way, every user can have a different max products? (just a thought). For now, let's be static.
$maxProducts = 3;
//The $this object is not available in libraries, you must request an instance of CI then, $this will be known as $CI...Yes the ampersand is correct, you want it by reference because it's huge.
$CI =& get_instance();
//Assumptions: You have stored logged in user details in the global data array & You have installed DataMapper + Set up your Product and User models.
$p = new Product();
$count = $p->where('user_id', $CI->data['user']['id'])->count();
if($count>=$maxProducts) return false;
else return true;
}
}
Next, set up your rule in config/form_validation.php.
Contents of form_validation.php
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
$config = array
(
'addProduct' => array
(
array
(
'field' => 'name',
'label' => 'Product Name',
'rules' => 'required|valid_num_products'
)
)
);
Next, set up your error message in language/english/form_validation_lang.php. Add the following line:
$lang['valid_num_products'] = "Sorry, you have exceeded your maximum number of allowable products.";
Now in the Controller, you'll want something along the lines of:
class Products extends MY_In_Controller
{
function __construct()
{
parent::__construct();
$this->load->library('form_validation');
}
function add()
{
$p = $this->input->post();
//was there even a post to the server?
if($p){
//yes there was a post to the server. run form validation.
if($this->form_validation->run('addProduct')){
//it's safe to add. grab the user, create the product and save the relationship.
$u = new User($this->data['user']['id']);
$x = new Product();
$x->name = $p['name'];
$x->save($u);
}
else{
//there was an error. should print the error message we wrote above.
echo validation_errors();
}
}
}
}
Finally, you might wonder why I've inherited from MY_In_Controller. There is an excellent article written by Phil Sturgeon over on his blog entitled Keeping It Dry. In the post he explains how to write controllers that inherit from access-controlling Controllers. By using this paradigm, controllers that inherit from MY_In_Controller can be assumed to be logged in, and the $this->data['user']['id'] stuff is therefore assumed to be available. In fact, $this->data['user']['id'] is SET in MY_In_Controller. This helps you seperate your logic in such a way that you're not checking for logged in status in the constructors of your controllers, or (even worse) in the functions of them.
Related
I have created MY_Form_validation that extends CI_Form_validation. I have loaded the library in Customer Controller. The validate_email function in customer model checks whether the customer exists while logging in. It is working fine on localhost but the method always returns false on the remote server even when a user actually exists. What could be wrong?
I have loaded the library like this
$this->load->library('form_validation');
Login email validation rules
$validate_data = array(
array(
'field' => 'login_email',
'label' => 'Email',
'rules' => 'trim|required|validate_email|is_active|xss_clean'
)
);
My_Form_validation library
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
class MY_Form_validation extends CI_Form_validation {
public function __construct(){
parent::__construct();
}
public function validate_email($field){
$this->CI->load->model('customer_model');
$email = $this->CI->customer_model->validate_email($field);
if($email === true){
return true;
}else{
$this->set_message('validate_email','The {field} does not exist');
return false;
}
}//end method validate_email
}
?>
Validate email model function
public function validate_email($email){
$sql = "SELECT `customer_id` FROM `customer` WHERE `email` = ? AND `status` != -1";
$query = $this->db->query($sql,array($email));
return ($query->num_rows() == 1) ? true : false;
}//end method validate_email
I like using -1 to represent deleted accounts in my databases
I am also autoloading my database
I had the a similar problem. According to the database configurations in codeigniter database.php, I noticed the following
char_set' => 'utf8',
'dbcollat' => 'utf8_general_ci',
So i dropped my database and recreated it. Changed my database collation to utf8_general_ci and changed my tables charset from armscii8 to utf8 and everything worked
By trusting your words about the data that is currently on your database your function should work, but I would advise you to change the model validate function to something more similiar to CodeIgniter approach.
public function validate_email($email)
{
$this->db->where('email', $email)
->where('status !=', '-1');
$query = $this->db->get('customer');
if($query->num_rows() > 0)
return TRUE;
}
Secondly, create a function to retrieve all the results according to your where condition.
public function getAllEmailsByEmail($email)
{
$this->db->where('email', $email)
->where('status !=', '-1');
return $this->db->get('customer')->result();
}
And just check out many rows it's returned.
print_r($this->CI->customer_model->getAllEmailsByEmail($field));
I am using uri segment to delete info in my database:
anchor('site/delete_note/'.$row->id, 'Delete')
Model:
function delete_note()
{
$this->db->where('id', $this->uri->segment(3));
$this->db->delete('note');
}
It works fine, but I want to do the same for updating my info and can't get it work
So this is link in view:
anchor('site/edit_note/'.$row->id, 'Edit')
My controller:
function edit_note()
{
$note_id = $this->uri->segment(3);
$data['main_content'] = 'edit_note';
$this->load->view('includes/template', $data);
$this->load->library('form_validation');
$this->form_validation->set_rules('content', 'Message', 'trim|required');
if($this->form_validation->run() == TRUE)
{
$this->load->model('Note_model');
$this->Note_model->edit_note($note_id);
redirect('site/members_area');
}
}
My model:
function edit_note($note_id)
{
$content = $this->input->post('content');
$data = array('content' => $content);
$this->db->where('id', $note_id);
$this->db->update('note', $data);
}
My view of edit_note:
<?php
echo form_open('site/edit_note');
echo form_textarea('content', set_value('content', 'Your message'));
echo form_submit('submit', 'Change');
echo anchor('site/members_area', 'Cancel');
echo validation_errors('<p class="error">'); ?>
Edit doesn't work as delete, when i am trying to get segment directly in edit model, as I used in delete model.
If I set $note_id to a number in my controller, instead of this '$this->uri->segment(3)', it updates my database. But if I use getting segment it doesn't work. I thought uri segments are available in controller as in model, but there is something I don't know.
Better yet, instead of manually reading the IDs via the segments, you could change your functions to be:
function delete_note($note_id)
and
function edit_note($note_id)
And remove the $note_id = $this->uri->segment(3); lines.
And as silly as it'll sound, the generated URL is definitely correct, right?
And last question, have you done anything with routes?
Edit
I've also noticed that in edit, you use this in your form:
echo form_open('site/edit_note');
So when the form submits, the URL it submits to is site/edit_note instead of site/edit_note/{SOME_ID}. So once you make your changes, and the form submits, there won't be a 3rd URL segment!
Well there are some logical errors in your code.
function edit_note()
{
$note_id = $this->uri->segment(3);
$data['main_content'] = 'edit_note';
$this->load->view('includes/template', $data);
//// What is the use of loadig a view when you are editing
$this->load->library('form_validation');
$this->form_validation->set_rules('content', 'Message', 'trim|required');
if($this->form_validation->run() == TRUE)
{
$this->load->model('Note_model');
$this->Note_model->edit_note($note_id);
redirect('site/members_area');
}
}
Instead do it like this
function edit_note()
{
if($this->input->post()){
$this->load->library('form_validation');
$this->form_validation->set_rules('content', 'Message', 'trim|required');
if($this->form_validation->run() == TRUE)
{
$this->load->model('Note_model');
$this->Note_model->edit_note($note_id);
redirect('site/members_area');
}
}else{
$note_id = $this->uri->segment(3);
$data['main_content'] = 'edit_note';
$this->load->view('includes/template', $data);
}
}
MOST IMPORTANT
And the other thing you should note that you are using anchor to access edit note but not actually submitting a form so it is not getting any post data to update.
In my view it's a 'bad' approach to use uri segments in your models... you should pass an id as a parameter from your controller functions ..
function delete_note()
{
$this->db->where('id', $this->uri->segment(3));
$this->db->delete('note');
}
What if you want to re-use this delete method? e.g. deleting notes from an admin panel, via a cron job etc then the above relies upon the uri segment and you will need to create additional delete methods to do the job. Also, if you were to continue with the same you don't even need a model then .. just call these lines in your controllers if you know what I mean ...
$this->db->where('id', $this->uri->segment(3));
$this->db->delete('note');
so best is to change it to similar to your edit_note() model function.
please look at this.
The code below is from my model class (using datamapper orm)
function login()
{
$u = new User();
$u->where('username', $this->username)->get();
$this->salt = $u->salt;
$this->validate()->get();
if (empty($this->id))
{
// Login failed, so set a custom error message
$this->error_message('login', 'Username or password invalid');
return FALSE;
}
else
{
// Login succeeded
$data = array
(
'username' => $u->username,
'usergroup' => $u->usergroup->get(),
'is_logged_in' => true
);
$this->session->set_userdata($data);
return TRUE;
}
}
when i do this i get
**Fatal error: Call to a member function set_userdata() on a non-object**
but when i do this instead
$data = array
(
'username' => $u->username,
'usergroup' => $u->usergroup->get(),
'is_logged_in' => true
);
$obj=& get_instance();
$obj->session->set_userdata($data);
It works.
Please what is the right way to get this working ?
Thanks in advance.
your model did not extends CI_Model
after that you have to add constructor to your model
add this code to yours
function __construct()
{
parent::__construct();
$this->load->library('session');
}
Well, you didn't provide enough information.
The first code looks fine, provided that:
You actually load the session class before calling it (you also need to create an encryption key in your configs).
$this->load->library('session');
$this->session->set_userdata($data);
The above code, or your code, is inside a controller, a model or a view.
$this relates to the CI's superclass, in particular to an instance of the Session class, so if you're calling that inside a helper (collection of functions), or inside a library (where you need to create a CI instance first), it won't work.
Hi
I am following the getting started guide for Codeigniterr given at http://www.ibm.com/developerworks/web/library/wa-codeigniter/
I have followed the instruction to create the front view and added controller to handle form submission. Ideally, when i submit the form, it should load the model class and execute the function to put details on the database, but instead it is just printing out the code of the model in the browser.
**Code of view (Welcome.php)**
----------------
<?php
class Welcome extends Controller {
function Welcome()
{
parent::Controller();
}
function index()
{
$this->load->helper('form');
$data['title'] = "Welcome to our Site";
$data['headline'] = "Welcome!";
$data['include'] = 'home';
$this->load->vars($data);
$this->load->view('template');
}
function contactus(){
$this->load->helper('url');
$this->load->model('mcontacts','',TRUE);
$this->mcontacts->addContact();
redirect('welcome/thankyou','refresh');
}
function thankyou(){
$data['title'] = "Thank You!";
$data['headline'] = "Thanks!";
$data['include'] = 'thanks';
$this->load->vars($data);
$this->load->view('template');
}
}
/* End of file welcome.php */
/* Location: ./system/application/controllers/welcome.php */
**Code of Model**
--------------
class mcontacts extends Model{
function mcontacts(){
parent::Model();
}
}
function addContact(){
$now = date("Y-m-d H:i:s");
$data = array(
'name' => $this->input->xss_clean($this->input->post('name')),
'email' => $this->input->xss_clean($this->input->post('email')),
'notes' => $this->input->xss_clean($this->input->post('notes')),
'ipaddress' => $this->input->ip_address(),
'stamp' => $now
);
$this->db->insert('contacts', $data);
}
**OUTPUT after clicking submit**
-----------------------------
class mcontacts extends Model{ function mcontacts(){ parent::Model(); } } function addContact(){ $now = date("Y-m-d H:i:s"); $data = array( 'name' => $this->input->xss_clean($this->input->post('name')), 'email' => $this->input->xss_clean($this->input->post('email')), 'notes' => $this->input->xss_clean($this->input->post('notes')), 'ipaddress' => $this->input->ip_address(), 'stamp' => $now ); $this->db->insert('contacts', $data); }
I have tried doing these things
1. Making all PHP codes executable
2. Change ownership of files to www-data
3. make permission 777 for whole of www
But, the code of model seems to be just printed ... PLEASE HELP
Just a few minor points that might help you:
In your controller, point the index method at the method you would like to call on that page. For example:
function index()
{
$this->welcome();
}
That will help keep things clean and clear, especially if anyone else comes in to work on the code with you later. I chose welcome because that's the name of your controller class and that will keep things simple.
In your model, this:
class mcontacts extends Model{
Should be:
class Mcontacts extends Model{
Capitalize those class names! That could be giving you the trouble you describe.
See here for more info on this: http://codeigniter.com/user_guide/general/models.html
Don't use camel case in your class or method names. This isn't something that will cause your code to fail, but it's generally accepted practice. See Codeigniter's PHP Style guide for more information on this: http://codeigniter.com/user_guide/general/styleguide.html
It's difficult to see with the formatting as it is, but do have an extra curly brace after the constructor method (mcontacts()) in the model? This would cause problems! Also although the code looks generally ok, there are probably better ways to use the framework especially if you do anything more complicated than what you've shown. For example, autoloading, form validation etc. Can I suggest you have a read of the userguide? It's very thorough and clear and should help you alot. http://codeigniter.com/user_guide/index.html
I'm using the technique of saving codeigniter form validation rules to a config file as specified here, but can't seem to get it working.
I am also attempting to invoke validation callback functions from a Validation_Callbacks.php library which I'm autoloading via the autoload.php mechanism.
Below is a snippet from my form_validation.php form validation rules config file:
<?php
/* This config file contains the form validation sets used by the application */
$config = array(
'register_user' => array(
array(
'field' => 'dob',
'label' => 'lang:register_dob',
'rules' => 'trim|required|date_check|age_check|xss_clean'
), ...
)
)
And here is the Validation_Callbacks.php file, that lives under application/libraries:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
/**
* Custom validator library containing app-specific validation functions
*/
class Validation_Callbacks {
/* Checks that the given date string is formatted as expected */
function date_check($date) {
$ddmmyyyy='(0[1-9]|[12][0-9]|3[01])[- \/.](0[1-9]|1[012])[- \/.](19|20)[0-9]{2}';
if(preg_match("/$ddmmyyyy$/", $date)) {
return TRUE;
} else {
$this->form_validation->set_message('date_check', $this->lang->line('error_register_dob_format'));
return FALSE;
}
}
/* Checks that the given birthday belongs to someone older than 18 years old. We assume
* that the date_check method has already been run, and that the given date is in the
* expected format of dd/mm/yyyy */
function age_check($date) {
$piecesDate = explode('/', $date);
$piecesNow = array(date("d"), date("m"), date("Y"));
$jdDate = gregoriantojd($piecesDate[1], $piecesDate[0], $piecesDate[2]);
$jdNow = gregoriantojd($piecesNow[1], $piecesNow[0], $piecesNow[2]);
$dayDiff = $jdNow - $jdDate;
if ($dayDiff >= 6570) {
return TRUE;
} else {
$this->form_validation->set_message('age_check', $this->lang->line('error_register_age_check'));
return FALSE;
}
}
}
I'm invoking this using the standard:
if ($this->form_validation->run('register_user') == FALSE) {
My callback functions are not being called. Is there something I'm missing here? Thanks in advance for any help you may be able to provide!
Ensure that:
When a rule group is named identically
to a controller class/function it will
be used automatically when the run()
function is invoked from that
class/function.
To test you can try using:
$this->load->config('configname');