It's the first time I'm using an PHP framework and I have a question regarding this design pattern.
Let's say I'm trying to build an php app that has a contact us page (which will ask for the name, email address comment etc and once submitted it will simply use the mail function to send an email to the admin). I know what my view is gonna be, I will also use a controller to render the view, but what should be my model? I'm not going to load anything from the database or save anything to the database. So in this case should I skip the model?
I’d say that, even with a contact form, a model has its use.
A contact form handles enquiries, a contact form has fields, and a contact form usually requires some form of validation. This can be wrapped up in an Enquiry model.
In fact, this is exactly what I do with CakePHP applications. I have an Enquiry model that defines the fields and data validation, and saves any enquiries to the database (for archiving purposes) and has an afterSave callback method that sends me the enquiry details via email. I’m sure you could do something analogous in CodeIgniter.
the gritty details go in the model. even a simple contact form has lots of gritty specific details: names of fields, specific validation rules, etc.
the controller is the boss - validate this form! it does not say how to validate it, and it doesn't need the details of each field in the form. if the form validates - the controller calls the next view. if it does not validate - then the controller shows the form again and maybe passes specific error messages.
the validation details for the contact form - put that in a model.
the emailing of the form: the content of the email, taking the form values and putting it into the email, who the email goes to, the details of actually sending the email - that is all work for a model.
what does this gain us? when your client calls in a year and says - change the text in the email - you won't be hunting through a bunch of controller code. you will look at your model names and know exactly where to go. separation of concerns is not just for building - its for the inevitable changes that happen over time.
EDIT
yes the business logic should be in the model. so like for a contact form the business rules might be - we ask for name and address - and we require the email address and phone number. the validation rules then fulfill the business rules.
then six months later your client calls and says - well people don't like us requiring the phone number - just make the email required. you have put those validation rules in your model - therefore it is only that specific model which needs to change. the controller does not change at all! and since you separated the form validation rules from the sending of the email into separate models - if you make a mistake when changing the validation rule - its much less code to look through and debug.
the other way to look at is - how specific and 'granular' is the method? the more specific it is, the further from the controller it should be. the controller methods should rarely change because of changes to business rules.
so when i said the controller orders "validate this form" - i just meant that the controller is calling the validation method that is in the model, and then its getting back true or false, no matter what changes happen to the form over time.
Models are optional in Codeigniter. If you don't need one then don't use one.
Models are optionally available for those who want to use a more traditional MVC approach.
Documentation
In your particular case you have no need of a model so don't worry about making one.
OP here, just want to do a quick update on this.
I have looked at CodeIgniter's documentation, and surprisingly found out that the form validation code is inside the controller:
http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#validationrules
which IMO is not a good idea.
Fortunately, there is a way to clean up the controller, which requires you to create a form validation config file inside the config folder:
http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#savingtoconfig
since I don't want to reinvent the wheel, I guess I will just create the config file without creating the model
Related
I have an object (a person) which is being edited (and validated) over several forms:
personal data form
first name
last name
file input for photo upload
account data form
email
publishing form
can publish only if all required fields have been filled
What I need is to have validations that I can use per-form and also for the whole person, e.g the errors would be:
personal data form - “please fill your first name”, …
whole person - “please fill your personal data”, …
If I define validations in Entity, there might still be some rules that are form-only (file upload, password repeat); it seems kinda wrong anyway because I might have different rules for web form and for API. Then again if I define validations in Form classes, then the rules are scattered around, also how can I initiate validation without form submission (which seems a hackish thing to do anyway).
Most elegant solution seems to me a separate validator which I can divide into validation groups and call it per-form and for whole object. How to do that using compound validator so that I can use other validators?
Or is there another way? Thank you.
Symfony verson is 6.
I just wonder how to build up a signup form like this with cakephp 2.x:
A member can have multiple workingtimes and multiple vacationtimes.
There should be one large signup form with one submit button and 3 visually separated areas: memberdetails, workingtimes, vacationtimes.
During signup a member should enter his personal data inside memberdetails and add arbitrary workingtimes as well as arbitrary vacationtimes in the designated areas before hitting the submit button. All the entered times should be listed tabular inside the form before submitting the whole form. At some point the memberdetails, workingtimes and vacationtimes look good and the user submits the form(I know that this is only the ideal situation and there are missing some functions).
My first approach was including the forms for adding workingtimes and vacationtimes inside the members form and send serialized form data via Ajax-POST to the other controller actions (Workingtime->add, Vacationtime->add) and also load the whole related data with something like a crud index function via Ajax and inject the response into the signup form without reloading it. This seems to keep things like validation, security component or the view layout simple, because most of the work will be done by cakephp and not with javascript, but I think it only works if I have allready a database ID for the member - after this I can store workingtimes and vacationtimes. Like allready said, I would prefer one form with one submit button and be able to cancel the whole registration even if there where allready added workingtimes or vacationtimes.
So what is the cake way to achive an integrated form like this? Is it useful to start a transaction and create a dummy member when the form is loading? And then use the ID of this dummy record to store the related workingtimes and vacationtimes? And when hitting the submit button the dummy member is updated to store the entered personal data and everything can be commited? Or should the entered related model data only be validated without saving and then just cache these data for a final transaction block?
Any other ideas?
I think the best approach for this is to manage states. Allow user to enter the basic data, change state, and then you can ask for more information but you will have already a user id to associate and match info. If the user comes back later it will have the current state already.
With Cake, a Component would help you to go from one step to another. Each data you enter should be validated.
How would a customer be able to login with both their primary email address they signed up with as well as a Secondary email address customer attribute field? (I’ve created a customer attribute text field secondary_email).
Assuming it has something to do with customerEntity and would be similair to what people have been doing to get usernames to work: http://www.magentocommerce.com/magento-connect/Sylvain_Raye/extension/7928/diglin_username
or
http://www.magentocommerce.com/boards/viewthread/195573/P15/
I just need for customers to have 1 single password, but be able to use an alternate email address specified within their account if they want.
Thank you!
Magento is no different from almost all other PHP-based frameworks in that it has a serial flow of execution. Thinking from a request flow standpoint, an entry point to suss out your requirement would be the class which handles the login form POST. You can see this in the rendered source in your browser: action="https://demo.magentocommerce.com/customer/account/loginPost/".
The above URI resolves to the method Mage_Customer_AccountController::loginPostAction(). In there one finds typical login logic for a login controller action: is user logged in? is the user posting in login data? is the the login data valid? and so forth. This quickly points to the customer session model, Mage_Customer_Model_Session, particularly to the authenticate() method. In this method is a call to Mage_Customer_Model_Customer->loadByEmail(), which gets us to Mage_Customer_Model_Entity_Model->loadByEmail()`.
At this point, we know that we can rewrite the resource model and change its loadByEmail() to handle lookup of a secondary email method (messy & obtrusive). We could also rewrite and change Mage_Customer_Model_Session->authenticate(), providing some pre-processing to first load the customer record by secondary email, then extract the main email and allow things to proceed as normal.
//rewritten authenticate method
public function authenticate($username,$password) {
$customer = Mage::getResourceModel('customer/customer_collection')
->addAttributeToFilter('secondary_email',$username)
->getFirstItem();
//check we found customer record by secondary email
if ($customer->getId()) {
parent::authenticate($login,$customer->getEmail());
}
else {
parent::authenticate($username,$password)
}
}
I've not really looked into the above snippet, nor would I vouch for its security, but hopefully this demonstrates the process by which one can answer these types of questions using awareness of the framework. This may not be a bad starting point; with something similar in the configured class rewrite plus a setup script to add the secondary_email attribute, this should be quick to implement.
A note worth mentioning:
It's also possible to accomplish this by observing the runtime-constructed controller_action_predispatch_customer_account_loginpost event (see Mage_Core_Controller_Varien_Action::preDispatch()). While it is generally advisable to use the event observe system to effect functional rewrites when possible, this would be quite unintuitive and the messiest option of all.
All my previous projects have had this workflow on Contact pages
User submits form
Controller gets $_POST details
Controller validates details (and sets error messages if necessary)
Controller sends email
Controller redirects to thanks page
Is this the standard workflow?
I used to validate everything in controllers, and then did some more reading and they recommended against it. Therefore, should I send the $_POST details to a helper type object and let it do all the work (validation/sending)?
In controller we should only check validation. The main validation should be on model before operations with DB.
The controller file need to check & validate the user input data.
After getting & accumulating all the data, it needs to transfer the data to the Model file for checking with the database (if needed) & then need to do some other works from here (like setting sessions / cookies, or sending mails, or firing hooks, ...). However, the control must come back to the same controller method, as all the previous model functionalities must be fired by a method call, from the same controller method.
The proper view method must be called now, and then the output must be rendered to the console.
Hope it helps.
Validation is typically performed in the Model, not in the Controller.
This is because data structures are usually defined in the Model and it is best to compare the acquired data immediately before manipulation (i.e. inserting into a database, etc.).
I am in a bit of a dilemma about how best to handle the following situation. I have a long registration process on a site, where there are around 10 form sections to fill in. Some of these forms relate specifically to the user and their own personal data, while most of them relate to the user's pets - my current set up handles user specific forms in a User_Controller (e.g via methods like user/profile, user/household etc), and similarly the pet related forms are handled in a Pet_Controller (e.g pet/health). Whether or not all of these methods should be combined into a single Registration_Controller, I'm not sure - I'm open to any advice on that.
Anyway, my main issue is that I want to generate a progress bar which shows how far along in the registration process each user is. As the urls in each form section can potentially be mapping to different controllers, I'm trying to find a clean way to extract which stage a person is at in the overall process. I could just use the query string to pass a stage parameter with each request, e.g user/profile?stage=1. Another way to do it potentially is to use routing - e.g the urls for each section of the form could be set up to be registration/stage/1, registration/stage/2 - then i could just map these urls to the appropriate controller/method behind the scenes.
If this makes any sense at all, does anyone have any advice for me?
I think creating a SignupController is a fine idea. The initial user registration is a distinct process, and ought to be separate from general profile management tasks.
If you've been a good developer and keeping your controllers thin and your models fat, you ought to be able to avoid any code duplication. If you find yourself duplicating, it's probably a good idea to think about refactoring.
As a concrete example, consider your user's email address. Let's say you're pretty strict, and any time a user changes their email address, they have to do a little confirmation-email dance. During signup, you'll want the user to return to the signup process after they click their confirmation link. When an existing user is changing their email address, you'll want them to land somewhere else (like their profile). It's likely that you'll want different content in the body of the confirmation email is each case. Trying to make /user/profile handle both cases is going to start creating a bunch of complexity where the action needs to figure out context and behave accordingly.
The better solution is to decide that signup is it's own mode of interaction, distinct from general profile management. Therefore, it gets its own controller, which shares model and view resources with other controllers.
That's my take, anyway.