How do you perform Form validation without a model in Cakephp? - cakephp

I need to perform some validation. I don't have the model in the application.
Does anyone know how to do the validation without a model?
Can you show me using a small sample or statement?

Honestly, I'd create a model just for the validation. You can create a model that doesn't use a table by adding
var $useTable = false;
And then create a validation array with rules for each field you want to validate:
var $validate = array('login' => 'alphaNumeric','email' => 'email','born' => 'date');
Then, in your controller, do something like:
$this->MyModel->set($this->data);
if($this->MyModel->validates()){
// do stuff with valid data
}
If you really, really can't use a model, then you'll have to simply loop over each value in $this->data in your controller action and validate it against a regular expression or use the Validation::[rule]() stuff, like:
if(Validation::email($someThingThatMightBeAnEmailAddress)){
// do stuff with valid email address.
}

You can perform validation of form data in CakePHP without having to create a model.php file. There are many times when I need to do this, and storing model.php files that do nothing more then validation is a poor usage of the model design pattern.
Another problem with CakePHP is that sometimes validation rules are common across multiple models. It would be nice to move validation out of the model, much in the way behaviors are to their own subfolder. That way we can re-use them or use them without a model.
Another problem with validation is that it's dependent upon the model alias. If you have a model called "Email" without a table to perform validation, then the posted form must also use "Email". If the form uses a alias different from the controller, then you have to set the action. A lot of extra steps just to do validation. You can't re-use that model again if your form uses a different model.
So here is my alternative approach.
In your controller's action that receives the posted form data. You can create a default CakePHP model, add some validation rules and then use that model for validation.
An example action might look like this;
function edit()
{
$model = ClassRegistry::init(array('class'=>'Email','table'=>false,'type'=>'Model'));
if(!empty($this->data))
{
$model->validate = array(
'subject'=>array(
'rule'=>'notEmpty',
'required'=>true
),
'message'=>array(
'rule'=>'notEmpty',
'required'=>true
)
);
if($model->save($this->data))
{
// validation was successful, but no data was actually saved
}
}
}
The key here is the creation of an automatic model by CakePHP.
$model = ClassRegistry::init(array('class'=>'Email','table'=>false,'type'=>'Model'));
The above attempts to find a model by Email in the applications model folder. When it is not found CakePHP will auto-create an in memory model for that class. Since we set the value of 'table' to false, then this should tell CakePHP that this model doesn't use a table.
This works as long as there really isn't a email.php file in the applications model folder. Once this model is created in memory. It's accessible from the built in Form help. That means validation errors will be passed correctly to the view.
Here is an example view file.
<?php echo $this->Form->create('Email',array('action'=>array('controller'=>'mycontroller','action'=>'edit'))); ?>
<?php echo $this->Form->input('subject'); ?>
<?php echo $this->Form->input('message',array('type'=>'textarea')); ?>
<?php echo $this->Form->submit(); ?>
The view will now render the validation errors from the Email model using the Form helper. This is because CakePHP class registry has saved a copy of the EMail auto model in memory that the Form helper will access.
If you want to use custom validation rules, then you will have to put the callback methods in the app_model.php file.
These methods tested in CakePHP 1.3

#ThinkingMedia's answer got me going in the right direction, but $model->save($this->data) was returning false for me unfortunately, even when the form was valid. I'm using CakePHP 2.3.9 for reference.
It turned out that even with 'table' => false parameter set, the returned$success of save() was based on a $count > 0 of the rows that were created/updated/modified. In my table-less case, this meant a $count of 0 and $success was false. I ended up blending the referenced answer with this similar question's solution to have validation work properly without a model file:
function edit()
{
$model = ClassRegistry::init(array('class'=>'YourFormName','table'=>false,'type'=>'Model'));
if($this->request-is('post'))
{
$model->validate = array(
'some_field'=>array(
'rule'=>'notEmpty',
'required'=>true
),
'another_field'=>array(
'rule'=>'notEmpty',
'required'=>true
)
);
$model->set($this->request->data)
if($model->validates($this->request->data) && empty($model->validationErrors))
{
// validation was successful, but no data was actually saved
}
}
}

Came across this question since I also had a similar issue. I have a form that needs to collect data and generate a PDF. So there is no data saving involved nor there is a maching model class. The PDF is a user contract and the user will fill the online form and the data filled will be used to generate the PDF which they must print and mail back. But I need to validate whether the fields are not empty, whether email is really an email format, and date inputs are really date inputs.
First I did without a model class then saw this quesion. Now I'm using a dummy model class to have my validations put in there since the code in controller action looks much neat.

Class Validation which is a subclass of Object is used by model class to perform validation against validation rules specified in it.
One can directly instantiate Validation class inside any controller or model and use its methods for performing validation on any data, not only inputs from forms.

I think my first question would be this: if you don't have a model...what are you validating? Typically data collection would be done to populate a model. If you're using an alternative data repository (file, web services, etc.), a model would still be the appropriate way to access and manipulate that data.
In short, in order to better answer this, I think a little more context would be helpful and maybe even necessary.

Related

How to create a whitelist of updatable fields in a CakePHP's model?

I want to create a whitelist of fields that I want to be updatable in CakePHP. I know that I can pass a fieldList array in the call to Model::save(), but this isn't what I'm looking for. What I want is that every model "publish" a list of the valid fields, so if I call the Model::save() method without a fieldList and with data that mustn't be updatable (like the ownerId) this won't be updated.
What can I do to get this behavior? Maybe override the Model::save method in every Model to call at the "original" Model::save with the whitelist? I think this is a good idea, because I don't pollute all the controllers with lots of duplicated whitelists...
Thanks for your help!
Well, thanks you all for your answers, but I was wrong: I don't need to add this functionality.
The problem I was trying to solve was a security problem, I was trying to avoid form tampering (I've discovered the name just now), and as I am a novice CakePHP user, I didn't know that CakePHP already manages this problem.
The answer to my question is very easy: I must use CakePHP's Security Plugin and use the Form Tampering prevention (http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html#form-tampering-prevention).
Can‘t say I’ve ever needed to do this in CakePHP, but CakePHP only saves the fields you pass it.
If you really need to create a white-list and you’re certain you only ever want these fields saving and never any others in your database (although I don’t understand why you have columns for them if you never touch them) then you could emulate this behavior in a model callback method:
<?php
class User extends AppModel {
public function beforeSave($options = array()) {
$whitelist = array('first_name', 'last_name', 'email');
foreach ($this->data[$this->alias] as $field => $value) {
if (!in_array($field, $whitelist)) {
unset($this->data[$this->alias][$field]);
}
}
return true;
}
}
This will just unset any data not in the $whitelist array, but if you really don’t want a column being updated then don’t pass a value for it.

cakePHP model associations lost after model imported into controller

Due to a client's desire to have URLs that defy Cake logic I have decided to use the pages_controller.php copied from cake/libs to organize my app.
Most of the site's functionality occurs from one url making calls with ajax to different controllers so I chose Pages as the home base.
Pages has no model but I still need to access some of the relevant models so I import them.
I have tried all three methods:
$this->loadModel('Inventories');
----
$Inventories =& ClassRegistry::init('Inventories');
----
App::import('Controller', 'Inventories');
$Inventories = new InventoriesController;
The Inventories Model seems to load fine with each but when I find some records:
$(...)->find("all", array(
'conditions' => array('id' => '1'),
'recursive'=>2)
);
I only get results as if recursive had been set to -1 or as if there were no associated models.
This happens no matter what I set recursive to.
When I load the model in another controller I get the appropriate recursive response.
Any ideas how i can get full access to a model from pages_controller.php?
A messy but instant solution would be to simply populate the $uses array in your controller:
var $uses = array('Inventories', 'User'); // or whatever models need to be loaded
This is considered poor practice and could quickly spiral out of control, however. Mostly handy for quick testing.
You could place the functionality in appropriately named controllers, and then route your client's desired URLs to the corresponding controller / action:
http://book.cakephp.org/view/945/Routes-Configuration
A little-known controller method (it's not in the cookbook, only the API) called setAction() might be the solution for you. See:
http://api13.cakephp.org/class/controller#method-ControllersetAction
also in Cake 2.0
http://api20.cakephp.org/class/controller#method-ControllersetAction

Form validation for relational elements

I have a form in CakePHP to save information to multiple models.
My first model is "World", I have no problem for these fields the validation is correct and it's saved correctly to the database.
The second model is "Country", I use something like this:
echo $this->Form->input('Country.0.name');
This is correctly saved to the database, but there is no validation (like stairs for required fields) and no automagic (autodetection of the content type).
The third model is "Region", I use the same code as for the second one but there is no validation, no automagic and no saving...
Can someone help ?
Thank you,
Sébastien
Without seeing the rest of your code, I am guessing that you are trying to save multiple countries at the same time. The model expects the data to come in a specific format:
$this->data['Model']['field'];
What you are passing is:
$this->data['Model'][0]['field'];
The model cannot interpret it. The way to resolve this is build a foreach when you collect the data and send each request independently.
foreach($country as $field) {
$data['Country']['field'] = $field;
// add other fields that are required
if($this->Country->validates($data)) {
$this->Country->create();
$this->Country->save($data);
} else {
// error handling
}
}
Good luck and happy coding!

CakePHP 1.3 controller save() does not save data

I want to add a new user to the database, and do so by making them fill in a form and submitting this. All seems to work fine. Just before my save() call in the controller I return all data and all necessary data is there. It is valid(ated), since no errors are returned.
But then nothing happens. I'm being returned to my form without any errors being shown.
This is my 'save-line':
if($this->Registratie->save($this->data)) {
I'm not making use of any beforeSave() methods.
Using debug($this->validationErrors); shows:
app/controllers/registratie_controller.php (line 45)
Which is the line of code from above.
I've been going through my code over and over. What could the problem be?
When you create a form using the FormHelper it will generate input names like:
<input type='text' name='data[Registratie][first_name]'>
Once the form is submitted cake will push that into the $this->data array with an index of 'Registratie'
You probably need to pass the index to the model when saving
if ($this->Registratie->save( $this->data['Registratie'] ) ) {
I would also do a var_dump($this->data) or print_r($this->data) to make sure your form fields are coming through.
I had the same problem, fixed doing exactly what Jack B Nimble told. Using CakePHP 1.3
Sample:
Model: Contacts
$this->data['contact']

CakePHP: Passing $this->data to the View from Controller

I'm using CakePHP 1.2 and I'm just wondering if there is any side affect on passing the $this->data to the View from the Controller.
Ex:
// inside PostsController, I have this code:
$this->data['Posts'] = $this->Post->find('all');
instead of :
$posts = $this->Post->find('all');
$this->set(compact('posts'));
// inside the /posts/view, I access it like this:
<?php foreach ($this->data['Posts'] as $post) {....};?>
By doing this, I skipped the $this->set() from the controller all together. Does this violate any MVC pattern or any security issue that I might have overlook? I saw that using the Auth Component, $this->data contains the [_Token] array.
Thanks
You need to be aware of the different places that Cake Helpers automagically look for data, since that is were it makes a real difference. The Form Helper will fill in fields automatically based on the contents of $this->data. That's how form data persists when validation fails. OTOH, a <select> elements options array is automatically taken from the pluralized field name,
e.g. $form->select('Model.foo_id') will take its options from $foos if set.
As such, $this->data has its special place and shouldn't be used lightly, just as named variables have their use and shouldn't be ignored. Use both as appropriate. If you don't want to auto-set Form Helper content, set() your variables. IMHO it's also more readable to assign a variable name that hints at the data it contains. All your views operating on $this->data is less clear than one view operating on $foo and another on $bar.
In CakePHP 2.x you should use $this->request->data instead if plain $this->data, otherwise you might end up getting this error:
Indirect modification of overloaded property View::$data has no effect
$controller->data is meant for data posted to the control from view file.
$view->data is for general data.
I would avoid doing it to keep myself sane. besides you are typing more in view.
There is no good reason for setting $this->data directly except when working with forms.
Why break convention - Controller:set is there for a reason. If you want to pass data to the view for display or display logic purposes you should use the function provided instead of trying to co-opt Controller:data for unintended purposes.
Everything is easier from within CakePHP if you follow the rules and do things the expected, correct way.
In cakephp version 2.*, error occurs when you try to set data on $this->data

Resources