I'm new to CakePHP, and am trying to do some blog-like exercise, then I ran into some problems.
See have a Model called Post, then under the PostsController I generated a view action for checking a single blog post. What I want is to allow the users to be able to add comment to the post in the Posts/view page instead of being redirected to a new Comments/add page. To do that I need to tell my CommentsController which post the user is commenting on. So wrote this in my /app/View/Posts/view.ctp:
<?php
echo $this->Form->create('Comment', array('controller' => 'comments', 'action' => 'add');
echo $this->Form->input('content', array('row' => '3'));
// this is the line I'm not sure about
echo $this->Form->input('post_id', array('default' => $post['Post']['id'], 'type' => 'hidden'));
echo $this->Form->end('Submit');
?>
Now this solution will send the value of $post['Post']['id'] to the add action in CommentsController in the form of $this->request->data['post_id'], but call me anal, I worry that whether this is the correct, or "professional" way to do this, since one can easily make the hidden field visible by altering some attributes with "inspect element' inside of any modern browser, leaving not necessarily potential security vulnerabilities, but something I personally don't feel comfortable with. So please, if anyone's worked with CakePHP before, share some experience with me on that.
First you can shorten that line to:
$this->Form->hidden('post_id', array('value' => $post['Post']['id']));
To prevent form tampering use the security component. It will:
Restricting which HTTP methods your application accepts.
CSRF protection.
Form tampering protection
Requiring that SSL be used.
Limiting cross controller communication.
Also I would validate that any data that is processed is valid. So you might want to check that the post exists and is public as well for example to prevent people can add comments to a non-public post. The same concept applies for everything: Never trust any input, not user nor API. Always validate the data and circumstances.
Related
I'm using CakePHP 2.3.8 and the email rule validates that the email has a valid host name or so. This is great, I like this feature, but it does not make things easy when testing on a local server, specially if it's offline.
Is there anyway to disable this very quick? A config variable maybe?
EDIT: Not going model by model changing the rule variable, but a global config variable.
You just need to remove second arguments from rule-- Like you defined as
'rule' => array('email', true)
This should be as-
'rule' => array('email')
after edit on question
You can do this by dynamic validation. But the easy approach will be unset email validation thus you are on development environment..
AppModel.php
public function beforeValidate(){
if$this->validate['email'](){
unset($this->validate['email']);
}
// now, If you really need to check email add dynamic rule for email field here.
}
In cakephp [2.2] I have baked everything and my "people" view is quite busy with relations and phones and addresses and other related data. I do want all of that information visible in the people view, though not quite in the baked layout.
How should I handle those portions of the related data? I'm not sure if I should use elements or extended views or plugins or what, I'm kinda new to this and the documentation wasn't clear to me (at my level) which should be used when. The baked code seemed to be a monolithic approach, so I didn't get much help looking there.
Once the user chooses to edit a phone number (for instance) from the listing on the person view, it takes them to the phone edit view and then returns them to the phone listing (index view) and not the person view that they were on. How do I get them back to the person view instead?
The blog example they provide is nice, but is there a "reference" application somewhere for cakephp that demonstrates best practices on a wide variety of their features? I couldn't find one, or anything more than just a simple app example.
Thanks, I appreciate the guidance.
This is a rather broad question, but I'm going to try and answer it. I'm not sure how advanced you're programming knowledge is, so forgive me if I'm rehashing things you already know. First, this article was a great help when I started to use the framework for the first time as it explains what code should go where and why. It's the closest I've seen to a "reference application", which would actually be a great learning tool. You could try and have a look at some of the higher profile Cake applications, like Croogo (a Cake-based CMS). But the codebase is bound to be a little bit complex.
Personally I would use elements when you want to actually reuse them in different views. The problem however, is feeding the element its data. There's a method called requestAction, but even the manual states that this should be used with moderation and in combination with caching. The problem is that using a lot of requestAction calls in different elements litters your Controllers with methods and doesn't adhere to the "Skinny Controllers, Fat Models" mantra.
I would put most of the related data calls in their respective Models and call those Model methods from the Controller and feed them to the View. So let's say you want the 10 latest PhoneNumbers and related Users.
You would have a method in your PhoneNumber model which returns an array of users and their phonenumbers. Use the Containable behaviour to limit the number of related models which are returned. The code below is an example, so the practical implementation might vary:
public function getRecentPhoneNumbers($limit=10) {
$phoneNumbers = array();
$phoneNumbers = $this->find('all', array(
'limit' => $limit,
'contain' => array('User'),
'order' => 'PhoneNumber.id DESC'
));
return $phoneNumbers;
}
If the PhoneNumber and User model are properly related you would be able to call getRecentPhoneNumbers() from the User model:
$this->PhoneNumber->getRecentPhoneNumbers(10)
Or from the Users Controller:
$this->User->PhoneNumber->getRecentPhoneNumbers(10)
Say you have an element which shows a list of those 10 numbers and it accepts a variable called $recentPhonenumbers, you then set the variable in the relevant UsersController method with the returned array from the getRecentPhoneNumbers call:
$this->set('recentPhonenumbers', $this->User->PhoneNumber->getRecentPhoneNumbers(10));
This will make it available to the View that contains the element.
The extended views are relatively new (from Cake 2.1 and onwards) and I haven't used them, but seem a great way to create conditional markup.
As for the second question, redirecting the user to the person view, rather than the index view. This is a matter of adjusting the redirect (see the manual for more details) in the edit() method of the Controller. Standard baked edit() methods accept an $id parameter you can use this to redirect to the view() (which probably also accepts an $id paramater).
So the redirect probably looks something like this:
$this->redirect(array('controller' => 'users', 'action' => 'index'));
Change it to:
$this->redirect(array('controller' => 'users', 'action' => 'view', $id));
Is there any way to set a layout to my messages in my model code?
Here is my model:
var $validate = array(
'email' => array(
'rule' => array('email', true),
'message' => 'Please supply a valid email address.'
)
);
This can be done with CakePHP. However, you have your concerns mixed up. The Model is there for data gathering, manipulation and massaging. The layout of the data is under the responsibilities of the View. In CakePHP specifically the Form Helper.
For more info about how to specify your own layout for a data validation message check out:
http://book.cakephp.org/view/1639/options-inputDefaults
They provide a pretty great code sample on exactly how to do this.
I also highly suggest you read through the whole book. It will prove invaluable.
Edit: Answer after clarification from comment
You would create an element and put it in app/views/elements. Should name the file using normal Cake conventions. Let's go with flash_error.
You would set this up to be your HTML that you want displayed. To make sure your message is displayed simply add this bit of PHP wherever is appropriate
<?php echo $message; ?>
That's step 1.
Step 2 is in your $this->setFlash() call pass the appropriate parameters. So your new calls would look like this with the element we named above:
$this->setFlash($message, 'flash_error');
Now your setFlash messages will use the layout defined in step 1. Wanna different layout? Just create a new element and pass the new element name.
The setFlash() method has 2 more parameters that come in handy (particularly if you want to have multiple flash() messages on the same page). Another link to the book:
http://book.cakephp.org/view/1313/setFlash
I am looking for an insight into the best approach to create an administrator section in CakePHP. I've looked at plugins like BrowniePHP as well as others, but I am not entirely satisfied with using plugins. So I am trying to create my own which will encompass the things I need. I;ve looked at some tutorials, but cant find the right answer.
I am currently creating a vast application, which is about 10% done, but I now feel the need to have an admin section before moving on.
Basically I would like a section where I can add new articles, approve comments, deny user access, etc. This section should only be accessible by an administrator.
Also, this administrator section must be able to save to any other model.
I am still learning CakePHP and any detailed instruction would be appreciated.
to create an admin-section the first thing you have to do is to manually edit the core.php within /app/config and write the setting Routing.prefixes. This line should be around line 88 somewhere and you just have to uncomment it.
In case you can't find it, it should look like this:
Configure::write('Routing.prefixes', array('admin'));
So now you can write your admin-functions within your controllers like this:
function admin_edit($id = null) {
//your admin function
}
You don't need access to every model since your writing these function within your controllers like every other "normal" action.
You just have to connect a route to handle the admin-actions:
Router::connect('/admin/:controller/:action/*', array('admin' => true, 'prefix' => 'admin', 'controller' => 'pages'));
// 'admin' => true is a variable for you so you can check if it's an admin-action which is requested
// 'prefix' => 'admin' means that you can write function with this prefix like above
You can then access these actions via the url http://yourapp.com/admin/controller/action
If you now use the Auth-Component you can write methods for checking if a user is allowed to access these methods.
For further information please read these manual-entrys:
Prefix-Routing
Authentication (Auth-Component)
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.