I find examples and tutorials about models and about validation. And I places that say the validation (or most of it at least) should be in the model, which I agree with. But I can't any examples or tutorials that show how that should be done.
Could anyone help me with a simple example on how that could be done? Where would you have the rules in the model? Where would the validation happen? How would the controller know if the validation passed or fail? How would the controller get error messages and things like that?
Hope someone can help, cause feel a bit lost here :p
I too had difficulty finding examples for Kohana3, bestattendance's example is for Kohana2.
Here's an example I threw together in my own testing:
application / classes / model / news.php
<?php defined('SYSPATH') OR die('No Direct Script Access');
Class Model_News extends Model
{
/*
CREATE TABLE `news_example` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`title` VARCHAR(30) NOT NULL,
`post` TEXT NOT NULL);
*/
public function get_latest_news() {
$sql = 'SELECT * FROM `news_example` ORDER BY `id` DESC LIMIT 0, 10';
return $this->_db->query(Database::SELECT, $sql, FALSE)
->as_array();
}
public function validate_news($arr) {
return Validate::factory($arr)
->filter(TRUE, 'trim')
->rule('title', 'not_empty')
->rule('post', 'not_empty');
}
public function add_news($d) {
// Create a new user record in the database
$insert_id = DB::insert('news_example', array('title','post'))
->values(array($d['title'],$d['post']))
->execute();
return $insert_id;
}
}
application / messages / errors.php
<?php
return array(
'title' => array(
'not_empty' => 'Title can\'t be blank.',
),
'post' => array(
'not_empty' => 'Post can\'t be blank.',
),
);
application / classes / controller / news.php
<?php defined('SYSPATH') OR die('No Direct Script Access');
Class Controller_News extends Controller
{
public function action_index() {
//setup the model and view
$news = Model::factory('news');
$view = View::factory('news')
->bind('validator', $validator)
->bind('errors', $errors)
->bind('recent_posts', $recent_posts);
if (Request::$method == "POST") {
//added the arr::extract() method here to pull the keys that we want
//to stop the user from adding their own post data
$validator = $news->validate_news(arr::extract($_POST,array('title','post')));
if ($validator->check()) {
//validation passed, add to the db
$news->add_news($validator);
//clearing so it won't populate the form
$validator = null;
} else {
//validation failed, get errors
$errors = $validator->errors('errors');
}
}
$recent_posts = $news->get_latest_news();
$this->request->response = $view;
}
}
application / views / news.php
<?php if ($errors): ?>
<p>Errors:</p>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error ?></li>
<?php endforeach ?>
</ul>
<?php endif ?>
<?php echo Form::open() ?>
<dl>
<dt><?php echo Form::label('title', 'title') ?></dt>
<dd><?php echo Form::input('title', $validator['title']) ?></dd>
<dt><?php echo Form::label('post', 'post') ?></dt>
<dd><?php echo Form::input('post', $validator['post']) ?></dd>
</dl>
<?php echo Form::submit(NULL, 'Post') ?>
<?php echo Form::close() ?>
<?php if ($recent_posts): ?>
<ul>
<?php foreach ($recent_posts as $post): ?>
<li><?php echo $post['title'] . ' - ' . $post['post'];?></li>
<?php endforeach ?>
</ul>
<?php endif ?>
To get this code working in a default install, you would have to enable the database module and configure it for authentication. Then you can access it from index.php/news using default configuration.
It is tested in Kohana 3.0.7 and should give you a good starting point of how you might lay out code. Unlike other frameworks, Kohana seems to be very open ended as to where you put your logic so this is just what made sense to me. If you want to use the ORM instead of rolling your own database interaction, it has its own syntax for validation which you can find here
An example of KO3 validation used with ORM models. Example was posted with permission by a1986 (blaa) in #kohana (freenode).
<?php defined('SYSPATH') or die('No direct script access.');
class Model_Contract extends ORM {
protected $_belongs_to = array('user' => array());
protected $_rules = array(
'document' => array(
'Upload::valid' => NULL,
'Upload::not_empty' => NULL,
'Upload::type' => array(array('pdf', 'doc', 'odt')),
'Upload::size' => array('10M')
)
);
protected $_ignored_columns = array('document');
/**
* Overwriting the ORM::save() method
*
* Move the uploaded file and save it to the database in the case of success
* A Log message will be writed if the Upload::save fails to move the uploaded file
*
*/
public function save()
{
$user_id = Auth::instance()->get_user()->id;
$file = Upload::save($this->document, NULL, 'upload/contracts/');
if (FALSE !== $file)
{
$this->sent_on = date('Y-m-d H:i:s');
$this->filename = $this->document['name'];
$this->stored_filename = $file;
$this->user_id = $user_id;
}
else
{
Kohana::$log->add('error', 'Não foi possível salvar o arquivo. A gravação da linha no banco de dados foi abortada.');
}
return parent::save();
}
/**
* Overwriting the ORM::delete() method
*
* Delete the database register if the file was deleted
*
* If not, record a Log message and return FALSE
*
*/
public function delete($id = NULL)
{
if (unlink($this->stored_filename))
{
return parent::delete($id);
}
Kohana::$log->add('error', 'Não foi possível deletar o arquivo do sistema. O registro foi mantido no banco de dados.');
return FALSE;
}
}
Here's a simple example that works for me.
In my model (client.php):
<?php defined('SYSPATH') or die('No direct script access.');
class Client_Model extends Model {
public $validation;
// This array is needed for validation
public $fields = array(
'clientName' => ''
);
public function __construct() {
// load database library into $this->db (can be omitted if not required)
parent::__construct();
$this->validation = new Validation($_POST);
$this->validation->pre_filter('trim','clientName');
$this->validation->add_rules('clientName','required');
}
public function create() {
return $this->validation->validate();
}
// This might go in base Model class
public function getFormValues() {
return arr::overwrite($this->fields, $this->validation->as_array());
}
// This might go in base Model class
public function getValidationErrors() {
return arr::overwrite($this->fields, $this->validation->errors('form_errors'));
}
}
?>
In my controller (clients.php):
<?php defined('SYSPATH') OR die('No direct access allowed.');
class Clients_Controller extends Base_Controller {
public function __construct() {
parent::__construct();
}
public function index() {
$content = new View('clients/read');
$content->foobar = 'bob.';
$this->template->content = $content;
$this->template->render(TRUE);
}
/* A new user signs up for an account. */
public function signup() {
$content = new View('clients/create');
$post = $this->input->post();
$client = new Client_Model;
if (!empty($post) && $this->isPostRequest()) {
$content->message = 'You submitted the form, '.$this->input->post('clientName');
$content->message .= '<br />Performing Validation<br />';
if ($client->create()) {
// Validation passed
$content->message .= 'Validation passed';
} else {
// Validation failed
$content->message .= 'Validation failed';
}
} else {
$content->message = 'You did not submit the form.';
}
$contnet->message .= '<br />';
print_r ($client->getFormValues());
print_r ($client->getValidationErrors());
$this->template->content = $content;
$this->template->render(TRUE);
}
}
?>
In my i18n file (form_errors.php):
$lang = Array (
'clientName' => Array (
'required' => 'The Client Name field is required.'
)
);
I have a brief write up with how to handle this at the link below since it took me a while to figure out and I couldn't find a single good example.
http://www.matt-toigo.com/dev/orm_with_validation_in_kohana_3
Related
I want to create one controller file which is creating automatically a function if i create a menu dynamically and also want to create view page which is connencted to this main controller.. how to do that?
Current code:
public function our_history()
{
$data['category']= $this->menu_model->getCategory('$lang');
$data['subcategory']= $this->menu_model->getSubCategory('$lang');
$this->load->view('vwMain',$data);//Left Menu }
}
Follow below steps Hope that makes sense.
-- Admin section --
/*content.php -- controller starts here */
class Content extends VCI_Controller {
# Class constructor
function __construct()
{
parent::__construct();
$this->load->model('content_model');
}
/*
Add page logic
*/
function edit_page($id = null)
{
$this->_vci_layout('your_layoutname');
$this->load->library('form_validation');
$view_data = array();
//Set the view caption
$view_data['caption'] = "Edit Content";
//Set the validation rules for server side validation
// rule name editcontent should be defined
if($this->form_validation->run('editcontent')) {
//Everything is ok lets update the page data
if($this->content_model->update(trim($id))) {
$this->session->set_flashdata('success', "<li>Page has been edited successfully.</li>");
$this->output->set_header('location:' . base_url() . 'content/manage_content');
} else {
$this->session->set_flashdata('error', "<li>Unknown Error: Unable to edit page.</li>");
$this->output->set_header('location:' . base_url() . 'content/manage_content');
}
} else {
$page = $this->content_model->get_content_page(trim($id));
$view_data["id"] = $page->id;
$view_data["page_title"] = $page->page_title;
$view_data["page_menu_slug"] = $page->page_menu_slug;
$view_data["page_name"] = $page->page_name;
$view_data["page_content"] = $page->page_content;
$view_data["status"] = $page->status;
$this->_vci_view('content_editpage', $view_data);
}
}
/*
Edit page logic
*/
function add_page()
{
$this->_vci_layout('your_layoutname');
$this->load->library('form_validation');
$view_data = array();
$view_data['caption'] = "Edit Content";
if($this->form_validation->run('editcontent')) {
// after passing validation rule data to be saved
// editcontent rule must be defined in formvalidations file
//Everything is ok lets update the page data
if($this->content_model->add()) {
$this->session->set_flashdata('success', "<li>Page has been edited successfully.</li>");
$this->output->set_header('location:' . base_url() . 'content/manage_content');
} else {
$this->session->set_flashdata('error', "<li>Unknown Error: Unable to edit page.</li>");
$this->output->set_header('location:' . base_url() . 'content/manage_content');
}
} else {
$page = $this->content_model->get_content_page(trim($id));
$view_data["id"] = $page->id;
$view_data["page_title"] = $page->page_title;
$view_data["page_menu_slug"] = $page->page_menu_slug;
$view_data["page_name"] = $page->page_name;
$view_data["page_content"] = $page->page_content;
$view_data["status"] = $page->status;
$this->_vci_view('content_editpage', $view_data);
}
}
}
/**
* content.php -- controller ends here
*/
/*
Content_model starts here
*/
class Content_model extends CI_Model {
// update logic goes here
function update($id = null) {
if(is_null($id)) {
return false;
}
$data = array(
'page_title' => htmlspecialchars($this->input->post('page_title',true)),
'page_name' => htmlspecialchars($this->input->post('page_name',true)),
'page_content' => $this->input->post('page_content',true),
'page_menu_slug' => htmlspecialchars($this->input->post('page_menu_slug',true)),
'status' => htmlspecialchars($this->input->post('status',true))
);
$this->db->where('id', $id);
$this->db->update('content', $data);
return true;
}
// Add logic goes here
function add() {
$data = array(
'page_title' => htmlspecialchars($this->input->post('page_title',true)),
'page_name' => htmlspecialchars($this->input->post('page_name',true)),
'page_content' => $this->input->post('page_content',true),
'page_menu_slug' => htmlspecialchars($this->input->post('page_menu_slug',true)),
'status' => htmlspecialchars($this->input->post('status',true))
);
$this->db->where('id', $id);
$this->db->insert('content', $data);
return true ;
}
}
/*
Content_model ends here # Admin section changes ends here
*/
-- Add view files also to admin section content_editpage.php
Now go to your routes.php file for front section --
add below line at last --
$route['(:any)'] = 'page/view_usingslug/$1';
This will be for all urls like --- http://yourdomainname/your_slug_name
// create again a controller in front section page.php --
class page extends VCI_Controller {
function __construct()
{
parent::__construct();
}
function view_usingslug($slug='')
{
// retrieve the data by slug from content table using any model class and assign result to $view_dat
$this->_vci_view('page',$view_data);
//page.php will be your view file
}
}
Suppose Your URL is
www.example.com/controllername/methodname/menutitle1
or
www.example.com/controllername/methodname/menutitle2
So this is how you handle these pages.
public function method()
{
$menutitle = $this->uri->segment(3);
$query = $this->db->get_where('TableName',array('Menutitle'=>$menutitle))
$data['content'] = $query->row()->page_content;
$this->load->view('common_page',$data);
}
I used a component name upload.php and save it in /app/controllers/components/ directory.
After that I used code in my controller like
<?php
App::uses('AppController', 'Controller');
App::uses('BarcodeHelper','Vendor');
/**
* OesUsers Controller
*
* #property OesUser $OesUser
*/
class OesUsersController extends AppController {
var $name = 'Images';
var $helpers = array('Html', 'Form');
var $components = array('upload');
function upload() {
if (empty($this->data)) {
$this->render();
} else {
$this->cleanUpFields();
// set the upload destination folder
$destination = realpath('../../app/webroot/img/uploads/') . '/';
// grab the file
$file = $this->data['Image']['filedata'];
// upload the image using the upload component
$result = $this->Upload->upload($file, $destination, null, array('type' => 'resizecrop', 'size' => array('400', '300'), 'output' => 'jpg'));
if (!$result){
$this->data['Image']['filedata'] = $this->Upload->result;
} else {
// display error
$errors = $this->Upload->errors;
// piece together errors
if(is_array($errors)){ $errors = implode("<br />",$errors); }
$this->Session->setFlash($errors);
$this->redirect('/images/upload');
exit();
}
if ($this->Image->save($this->data)) {
$this->Session->setFlash('Image has been added.');
$this->redirect('/images/index');
} else {
$this->Session->setFlash('Please correct errors below.');
unlink($destination.$this->Upload->result);
}
}
}
After save that I find error that is missing component. I could not find any mistake.Can any body help me?
You have placed the component in the wrong folder. In CakePHP 2.x, components are placed in app/Controller/Component. You also have to rename the component from upload.php to UploadComponent (and probably adapt the code to make it work with CakePHP 2.x).
I want to limit my registration to emails with #mywork.com I made the following in My_Form_validation.
public function email_check($email)
{
$findme='mywork.com';
$pos = strpos($email,$findme);
if ($pos===FALSE)
{
$this->CI->form_validation->set_message('email_check', "The %s field does not have our email.");
return FALSE;
}
else
{
return TRUE;
}
}
I use it as follows. I use CI rules for username and password and it works, for email it accepts any email address. Any I appreciate any help.
function register_form($container)
{
....
....
/ Set Rules
$config = array(
...//for username
// for email
array(
'field'=>'email',
'label'=>$this->CI->lang->line('userlib_email'),
'rules'=>"trim|required|max_length[254]|valid_email|callback_email_check|callback_spare_email"
),
...// for password
);
$this->CI->form_validation->set_rules($config);
The problem with creating a callback directly in the controller is that it is now accessible in the url by calling http://localhost/yourapp/yourcontroller/yourcallback which isn't desirable. There is a more modular approach that tucks your validation rules away into configuration files. I recommend:
Your controller:
<?php
class Your_Controller extends CI_Controller{
function submit_signup(){
$this->load->library('form_validation');
if(!$this->form_validation->run('submit_signup')){
//error
}
else{
$p = $this->input->post();
//insert $p into database....
}
}
}
application/config/form_validation.php:
<?php
$config = array
(
//this array key matches what you passed into run()
'submit_signup' => array
(
array(
'field' => 'email',
'label' => 'Email',
'rules' => 'required|max_length[255]|valid_email|belongstowork'
)
/*
,
array(
...
)
*/
)
//you would add more run() routines here, for separate form submissions.
);
application/libraries/MY_Form_validation.php:
<?php
class MY_Form_validation extends CI_Form_validation{
function __construct($config = array()){
parent::__construct($config);
}
function belongstowork($email){
$endsWith = "#mywork.com";
//see: http://stackoverflow.com/a/619725/568884
return substr_compare($endsWith, $email, -strlen($email), strlen($email)) === 0;
}
}
application/language/english/form_validation_lang.php:
Add: $lang['belongstowork'] = "Sorry, the email must belong to work.";
Are you need validation something like this in a Codeigniter callback function?
$this->form_validation->set_rules('email', 'email', 'trim|required|max_length[254]|valid_email|xss_clean|callback_spare_email[' . $this->input->post('email') . ']');
if ($this->form_validation->run() == FALSE)
{
// failed
echo 'FAIL';
}
else
{
// success
echo 'GOOD';
}
function spare_email($str)
{
// if first_item and second_item are equal
if(stristr($str, '#mywork.com') !== FALSE)
{
// success
return $str;
}
else
{
// set error message
$this->form_validation->set_message('spare_email', 'No match');
// return fail
return FALSE;
}
}
A correction to Jordan's answer, the language file that you need to edit should be located in
system/language/english/form_validation_lang.php
not application/.../form_validation_lang.php. If you create the new file under the application path with the same name, it will overwrite the original in the system path. Thus you will lose all the usage of the original filters.
I am currently trying to add data to the database using codeigniter. I have already set up a registration page using the active method and attempted to use the same method for the add news form but was unsuccessful.
When I click submit it is saying page cannot be found and the url shows the controller function name. This is the same when i purposely leave any fields blank. I have checked my database and no records have been added and no php log errors.
Here is my snippets of code:
View:
<?php echo form_open('add/add_article'); ?>
<?php echo form_input('title', set_value('title', 'Title')); ?><br />
<?php echo form_textarea('content', set_value('content', 'Content')); ?><br />
<?php echo form_input('author', set_value('author', 'Author')); ?>
<?php echo form_submit('submit', 'Add Article'); ?>
<?php echo validation_errors('<p class="error">' );?>
<?php echo form_close(); ?>
Controller:
class Add extends CI_Controller {
public function __construct() {
parent::__construct();
}
public function index() {
$this->load->view('admin/add');
}
public function add_article() {
$this->load->library('form_validation');
$this->form_validation->set_rules('title', 'Title', 'trim|required');
$this->form_validation->set_rules('content', 'Content', 'trim|required');
$this->form_validation->set_rules('author', 'Author', 'trim|required');
if($this->form_validation->run() == FALSE) {
$this->index();
}else{
$this->load->model('news_model');
if($query = $this->news_model->addArticle()) {
$this->load->view('news');
}else {
$this->load->view('news');
}
}
}
}
Model:
public function __construct() {
parent::__construct();
}
function addArticle() {
$data =array(
'title' => $this->input->post('title'),
'content' => $this->input->post('content'),
'author' => $this->input->post('author'),
'username' => $this->input->post('username'));
$insert = $this->db->insert('news', $data);
return $insert;
}
}
If it's the server that's throwing the page not found it's almost certainly a URL issue as opposed to a CI/PHP issue.
Is your base url defined properly in the config file? Is your .htaccess configured properly (an old configuration could be routing /add requests away from CI)?
Try adding the following action to the Add controller, and navigating to it directly at http://[base]/add/thetest
public function thetest() {
echo 'Controller accessed';
die;
}
If it still says page not found it's not your code, it's your config (either server config or CI).
Instead of insert use update in your model like:
$insert = $this->db->update('news', $data);
return $insert;
And I think that this part of your code in controller is wrong too (wrong if statement and no data send to model):
if($query = $this->news_model->addArticle()) {
$this->load->view('news');
}else {
$this->load->view('news');
}
try this:
$data =array(
'title' => $this->input->post('title'),
'content' => $this->input->post('content'),
'author' => $this->input->post('author'),
'username' => $this->input->post('username')
);
$query = $this->news_model->addArticle($data);
if($query)
{
// query ok
$this->load->view('news');
}
else {
// no query
$this->load->view('news');
}
I've followed the instructions in the manual for setting up Translate Behavior with CakePHP 2.1, as well as this question here on Stack. I am not getting any errors, but my translated posts are not saving to my i18n table.
Here is my PostModel.php:
class Post extends AppModel {
public $title = 'Post';
public $name = 'Post';
public $body = 'Post';
public $actAs = array(
'Translate' => array(
'title' => 'titleTranslation',
'body' => 'bodyTranslation'
)
);
public $validate = array(
'title' => array(
'rule' => 'notEmpty'
),
'body' => array(
'rule' => 'notEmpty'
)
);
}
And here are my add and edit functions in PostsController.php:
public function add() {
if ($this->request->is('post')) {
$this->Post->locale = 'fre';
$this->Post->create();
if ($this->Post->save($this->request->data)) {
$this->Session->setFlash(__('Your post has been saved.', true));
$this->redirect(array('action' => 'admin'));
} else {
$this->Session->setFlash(__('Unable to add your post.', true));
}
}
}
public function edit($id = null) {
$this->Post->id = $id;
if ($this->request->is('get'))
{
$this->Post->locale = 'fre';
$this->request->data = $this->Post->read();
}
else
{
if ($this->Post->save($this->request->data))
{
$this->Post->locale = 'fre';
$this->Session->setFlash(__('Your post has been updated.', true));
$this->redirect(array('action' => 'admin'));
}
else
{
$this->Session->setFlash(__('Unable to update your post.', true));
}
}
}
I initialized the i18n table using Console. Should I drop the table and try reinitializing? Not sure why there would be a problem there.
More reusable solution is to add to your AppModel:
class AppModel extends Model {
public $actsAs = array('Containable');
/**
* Converts structure of translated content by TranslateBehavior to be compatible
* when saving model
*
* #link http://rafal-filipek.blogspot.com/2009/01/translatebehavior-i-formularze-w.html
*/
public function afterFind($results, $primary = false) {
if (isset($this->Behaviors->Translate)) {
foreach ($this->Behaviors->Translate->settings[$this->alias] as $value) {
foreach ($results as $index => $row) {
if (array_key_exists($value, $row)) {
foreach($row[$value] as $locale) {
if (isset($results[$index][$this->alias][$locale['field']])) {
if (!is_array($results[$index][$this->alias][$locale['field']])) {
$results[$index][$this->alias][$locale['field']] = array();
}
$results[$index][$this->alias][$locale['field']][$locale['locale']] = $locale['content'];
}
}
}
}
}
}
return $results;
}
}
This code automatically converts what returns TranslateBehavior to be able to create multi-language form like:
echo $this->Form->input('Category.name.eng');
echo $this->Form->input('Category.name.deu');
echo $this->Form->input('Category.name.pol');
Tested on CakePHP 2.3. And I discovered that now Model->saveAssociated() is required instead of Model->save().
Try to add this in your form on add.ctp:
<?php
echo $this->Form->create('Post');
echo $this->Form->input('Post.title.fre');
echo $this->Form->input('Post.body.fre');
//Something more...
echo $this->Form->end(__('Submit'));
?>
in your edit.ctp:
<?php
echo $this->Form->create('Post');
echo $this->Form->input('id');
echo $this->Form->input('Post.title.fre', array('value'=>$this->request->data['titleTranslation'][0]['content'));
echo $this->Form->input('Post.body.fre', array('value'=>$this->request->data['bodyTranslation'][0]['content'));
//Something more...
echo $this->Form->end(__('Submit'));
?>
assuming index of data['titleTranslation'][0] is in French, to easily see an array in the view I recommend to use DebugKit https://github.com/cakephp/debug_kit
I hope this will help
Use saveMany instedof save. it's working fine for me.
For those who have the same problem: the correct variable is $actsAs, not $actAs.