In CakePHP 3.x is it acceptable to add validation rules within a controller?
I've read http://book.cakephp.org/3.0/en/core-libraries/validation.html but it doesn't actually say where you (can / should) add your methods.
I understand that typically these go in src/Model/Table/ModelName.php. However I'm trying to validate a form which is not tied to a particular database table and doesn't need a corresponding model.
I'm familar with Cake 2.x where I would typically do this in the controller, or possibly add a model with $useTable = false. But in this case the simplest method seems to add the rules directly in the controller, but I wasn't sure whether this is bad practice. If the rules don't go in the controller where should they be put?
Context - this is a form where the user is doing a search. It requires some input and I'm trying to validate 3 fields: email, quantity and a postcode. Cake's validator has inbuilt features to do the first two but in the case of postcode I'll need to add a custom method.
Any advice appreciated.
In CakePHP 3.x is it acceptable to add validation rules within a controller?
Technically possible, but I would consider it as bad practice.
I understand that typically these go in src/Model/Table/ModelName.php. However I'm trying to validate a form which is not tied to a particular database table and doesn't need a corresponding model.
There is a whole section called "Modelless Forms" in the book that covers that use case.
Related
I am validating with Cake 3 but can't get it working probably.
As the docs says there are two stages of validating:
Before you save your data you will probably want to ensure the data is correct and consistent. In CakePHP we have two stages of validation:
Before request data is converted into entities, validation rules around data types and formatting can be applied.
Before data is saved, domain or application rules can be applied. These rules help ensure that your application’s data remains consistent.
So, if I understand this right, at first validation rules are used when I pass data via newEntity and patchEntity.
After that, the application rules are used when using save or delete.
However, when I am passing data (an array) via newEntity the application rules are never used (buildRules is never called). When using newEntity without passing data, application rules are used!
So, my first question, is it right that not both rules are runned, only one (OR validation rules, OR application rules?). I would expect that first validation rules would be called to check the input, and before saving, ALSO the application rules would be called to check if the entity is valid to the applicaton.
Second question, how should I validate with my API? The actions pass their data via the newEntity method, but I want to check if (for example) the category_id belongs to the same user. Thats typical an application rule I guess?
Thank you very much ;)
Quoting CakePHP documentation:
Validation objects are intended primarily for validating user input, i.e. forms and any other posted request data.
Basically, validation is done when you use newEntity or patchEntity to check that the incoming data is consistent:
You don't have a random string where you should have a number
The user email is of correct format
Standard and confirmation passwords are equals
etc.
Validation is not done when you set field manually:
$user->email = 'not a valid email' ; // no validation check
Basically, validation rules are meant to tell the user « Hey, you did something wrong! ».
Application rules on the other end are always checked when you call save or delete, these may be used for:
Checking uniqueness of a field
Checking that a foreign key exist - There is an Group that correspond to your group_id
etc.
Your first assumption is somehow false because in the following scenario, both validation and application rules are checked:
$article = $this->Articles->newEntity($this->request->data);
$this->Articles->save($article) ;
This part of the documentation explain the difference between the two layers of validation.
Concerning your second question, you should not check that a user has the right to do something in your model, this should be done by your controller, see CakePHP book for more details.
Our ui automation team is asking for a better way to select elements for their automated tests. My thinking is that we can inject a dedicated attribute (say "ui-auto") for each testable element. This attribute would have a value which is:
unique
persistent (doesn't change across sessions or page loads so as to not break the tests)
predictable (follows some naming convention depending on action type, location, etc.)
My questions are:
Is this a good idea? better ideas are welcome.
Are there existing conventions for this?
What the best way to implement
this?
I should mention that we are using angular and I thought that
using some kind of directive and/or service would help automate
this.
I should also say that I don't want to use the "id" attribute b/c I'd like to have separation between development concerns (ids may be used for javascript), and qa concerns (selection of elements for automated tests)
In our implementation we add to the DOM element a data-awt attribute, the value consists of a context (page and mode) type and unique string. As we use the EXTJS library our type is the xtype and the unique string is components name or text property. The context is developer controlled by placing a unique property on the upper most parent and all children use this as their context.
In practice we end up with data-awt values like devicesListing-button-edit, deviceDetails-displayfield-name, deviceDetailsEditWindow-textfield-name.
We found that relying on css, id, or other attributes aren't reliable and predictable since we don't want to rewrite our tests whenever there is some UI change. Now the test only needs updating if an existing element changes its name (for example the PM says the name field should now use the 'customer' data from the DTO).
You can also use the class of the element and provide a unique identifier prefixed with something like "auto_" or "t_".
The agreement exists that if anyone changes the class name with that prefix, tests will break.
#o4ohel I agree that not using ids is better as devs also depend on them and they need to change sometimes. Identifiers for automation should be isolated. It's nice to have that separation.
Basically, I've implemented the HABTM successfully in CakePHP, but the trouble is, I don't understand why it works.
The thing I hate about the CakePHP cookbook is that is tells you what to do but make very little effort to explain the underlying segments of their code.
Essentially, my data model is like this.
Task HABTM Question
I don't understand this code fragment.
$this->set('questions', $this->Task->Question->find('list'))
In particular, what is $this->Task->Question supposed to accomplish?
Also how is the above code link to this code fragment in the view?
echo $this->Form->input('Question');
One thing that is very peculiar is that with the above code fragment, I get a multiple select option.
However, if I change the code to this,
echo $this->Form->input('question');
I get a single select drop down list.
I scoured the entire documentation and still cannot find a satisfactory explanation to my doubts.
Would really appreciate if anyone can clarify this issue for me.
1. Model chaining
When a model has an association to another model (like in your example an HABTM one) then you can call methods of the associated model by chaining it to the current model. This is explained early in Associations and an example of exactly how it works is given at the end of the first section.
When you are someplace in your TasksController normally you would expect that only your Task model would be available. Instead any association described in the Task model is chained to that model in the form of $this->Model1->Model2.
So $this->set('questions', $this->Task->Question->find('list')) means:
From current model Task that you know about, access the associated model Question and then call its find('list') method. Then $this->set the results to the view as variable questions.
2. FormHelper Conventions
When you use a CamelCased single name for field input, like in $this->Form->input('Question'); you are saying to FormHelper that the data contained in the questions variable come from a model named Question with a HABTM association, therefore they should be handled as a multiple select (as HABTM points to such an association).
With a field name of model_id, like in this example question_id, you're asking for a single select (select a single id of the connected model).
With anything else, FormHelper looks at the field definition and takes the decision itself, but of course your can override any default behavior you want using options.
This is explained in detail and I'm surprised you missed both. CakePHP has one of the best documentations available, almost everything you need is there.
I'm using CakePHP2.3 and my app has many associations between models. It's very common that a controller action will involve manipulating data from another model. So I start to write a method in the model class to keep the controllers skinny... But in these situations, I'm never sure which model the method should go in?
Here's an example. Say I have two models: Book and Author. Author hasMany Book. In the /books/add view I might want to show a drop-down list of popular authors for the user to select as associated with that book. So I need to write a method in one of the two models. Should I...
A. Write a method in the Author model class and call that method from inside the BooksController::add() action...
$this->Author->get_popular_authors()
B. Write a method in the Book model class that instantiates the other model and uses it's find functions... Ex:
//Inside Book::get_popular_authors()
$Author = new Author();
$populars = $Author->find('all', $options);
return $populars;
I think my question is the same as asking "what is the best practice for writing model methods that primarily deal with associations between another model?" How best to decide which model that method should belong to? Thanks in advance.
PS: I'm not interested in hearing whether you thinking CakePHP sucks or isn't "true" MVC. This question is about MVC design pattern, not framework(s).
IMHO the function should be in the model that most closely matches the data you're trying to retrieve. Models are the "data layer".
So if you're fetching "popular authors", the function should be in the Author model, and so on.
Sometimes a function won't fit any model "cleanly", so you just pick one and continue. There are much more productive design decisions to concern yourself with. :)
BTW, in Cake, related models can be accessed without fetching "other" the model object. So if Book is related to Author:
//BooksController
$this->Book->Author->get_popular_authors();
//Book Model
$this->Author->get_popular_authors();
ref: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html#relationship-types
Follow the coding standards: get_popular_authors() this should be camel cased getPopularAuthors().
My guess is further that you want to display a list of popular authors. I would implement this using an element and cache that element and fetching the data in that element using requestAction() to fetch the data from the Authors controller (the action calls the model method).
This way the code is in the "right" place, your element is cached (performance bonus) and reuseable within any place.
That brings me back to
"what is the best practice for writing model methods that primarily
deal with associations between another model?"
In theory you can stuff your code into any model and call it through the assocs. I would say common sense applies here: Your method should be implement in the model/controller it matches the most. Is it user related? User model/controller. Is it a book that belongs to an user? Book model/controller.
I would always try to keep the coupling low and put the code into a specific domain. See also separation of concerns.
I think the key point to answer your question is defined by your specifications: "... popular authors for the user to select as associated with that book.".
That, in addition to the fact that you fetch all the authors, makes me ask:
What is the criteria that you will use to determine which authors are popular?
I doubt it, but if that depends on the current book being added, or some previous fields the user entered, there's some sense in adopting solution B and write the logic inside the Book model.
More likely solution A is the correct one because your case needs the code to find popular authors only in the add action of the Book controller. It is a "feature" of the add action only and so it should be coded inside the Author model to retrieve the list and called by the add action when preparing the "empty" form to pass the list to the view.
Furthermore, it would make sense to write some similar code inside the Book model if you wanted, e.g., to display all the other books from the same author.
In this case you seem to want popular authors (those with more books ?), so this clearly is an "extra feature" of the Author model (That you could even code as a custom find method).
In any case, as stated by others as well, there's no need to re-load the Author model as it is automatically loaded via its association with Books.
Look out for Premature Optimization. Just build your project till it works. You can always optimize your code or mvc patterns after you do a review of your code. And most important after your project is done most of the time you will see a more clear or better way to do it faster/smarter and better than you did before.
You can't and never will build a perfect mvc or project in one time. You need to find yourself a way of working you like or prefer and in time you'll learn how to improve your coding.
See for more information about Premature Optimization
I just realized when doing basic CakePHP stuff, that there is quite bad security issue, which many don't necessarily notice. I'll just take this basic function that I think many users use while doing CakePHP driven apps.
function edit() {
if(!empty($this->data)) {
if($this->User->save($this->data)) {
}
}
}
Lets assume user has privileges to use this action. This action could be editing user information, which may have like city and number and ofcourse username. Lets assume that we want to have a form that allows us to edit just the city and number but not the username. Well what if someone just inserts that username field into that form with firebug for example? Then submits the form. Now the edit would just grab all the post information, including the username field and its value and edit them straight away. So you can change your username in this case even though there werent a field for it.
This can go even further, if someone would use saveAll(), which allows you to validate and save multiple models in one shot. If you could guess from form fields the models to use, you could easily go to other models and tables aswell and alter those information.
Now that you understand my concerns, my question is what would be the best or atleast near the best method to avoid this?
I know I could just grab the data I want from $this->data to other variable and then pass that to the save or saveAll, but because there are many forms and ajax requests, that would be quite a lot of work. But is it the only way to go or are there better ways?
Should I make or is there a behavior which could stop this? Like checking what variables some action in some controller can get from post?
After couple of days research I found that this is not really a "security hole", but rather beginners mistake.
There are two ways avoiding this type of form tampering: Security component ( http://book.cakephp.org/view/1296/Security-Component ) which automatically gets CSRF and form tampering protection by creating one-time hashes for form fields.
The other way is to give the third parameter to save() function. The save actually gets 3 parameters: data, validate, fieldlist. The fieldlist parameter acts like whitelist of fields that are allowed to be saved.
I first reported this problem as a bug to cakephp which it then wasn't but this euromark guy replied to me that he had done nice documenting about the actual problem and how to do secure saves and I really think it was quite good reading. So if you have the same problems, please see this page: http://www.dereuromark.de/2010/09/21/saving-model-data-and-security/