Best practise for handling form submission in controllers - php

Lets say for example I am creating a an online shop. I have a controller called products and within that controller I have a function called create_product. Create_product calls a view that displays a form where users get to enter new products into the database.
When the user fills in the form to create a product, should I send the action back to the create_product controller and handle it with an IF statement? or offload to another function?
Example
<form method="post" action="www.example.dev/products/create_product/add">
//the above form would post back to the original controller
function create_product()
{
if(uri->segment(3) == "add")
{
//call a model to do all the database stuff
}
load->view->create_product_form;
}
Is this the best way to handle this or should I be passing it off to another function?

Don't cram a ton of stuff in one function using the URI segment to filter it. createProduct() can list the products available for creation (in a CRUD format, I assume), and the submission of the form should ping another controller with the POSTed data. Perhaps insertProduct(), where the data is sanitized and sent to the model for insertion to the database.
Separation of concerns! Keep the functions as separate as possible with good descriptors for the names of the functions.

I (personally) would have a function that set the form parameters and "launch" the view with that form, and another function used to validate and call the model to put the values of that form into the database. I believe that is really up to you, but the code would be cleaner if you divide the controller with several functions depending on what they actually do.

I like the way symfony deals with forms & form submission. It is in one function (action)
simplified code:
executeCreate() {
$this->form = new Form()
if($r->isMethod('POST')) {
//handle submission
bind();
save();
}

Related

PHP MVC Controller Created Over and Over Again

I am new to PHP. I am using a MVC project as an example, and I noticed that each time a submit button is pressed my controller is called. The issue with this is that it creates a new model every time the button is pressed.
To fix this, I used a hidden field to check if the button has already been pressed. If it has, then I do not instantiate a new Model, otherwise I do. Code from controller is below:
//code listed below is in the controller which is called each time button
//has been pressed...
$myModel;//used to access model and its functions from controller
if(isset($_POST['has_started']))
{
//stores some logic that uses $myModel variable
playingGame();
}
else
{
echo "just starting...";
$myModel=new HangManModel();
startGame($myModel);
}
This seems to work, BUT then when playingGame() is called it tells me I cannot use $myModel and its functions because it was not declared, or it is a non-object. How can I fix this? Thanks for the help!
First, there is no mvc pattern I see here.
Secondly, php executes and generates the output in html and javascript and sent to the browser.
At the next postback, php does not store any state for this.
As per my understanding, for storing states you can use one of the following.
1) Option1-->Using session.
//code listed below is in the controller which is called each time button
//has been pressed...
$myModel;//used to access model and its functions from controller
if(!isset($_SESSION)){session_start();}
if(isset($_POST['someuniqueID']))
{
//stores some logic that uses $myModel variable
$someuniqueID = $_POST['someuniqueID'];
$myModel = $_SESSION[$someuniqueID]; //retrieving the session object.
playingGame($myModel); //passing your model to the main function.
}
else
{
echo "just starting...";
$myModel=new HangManModel();
//start session
$_SESSION['someuniqueID'] = $myModel; // do not store very complex model object to avoid server memory problem.
startGame($myModel);
}
2) Option2--> you can serialise that object and send it in hidden field then it will be posted back but this will have bandwidth consumption problem. Each time, the page is posted, it will send your model back and forth.
3) Option3--> If your model is big, Use database and create a temp table where you can store unique id and serialised $myModel object. You will need to clean the unwanted states from database. because unlike session and hidden field it is permanent storage.

Having an "edit" controller to deal with user editing? Does this design make sense?

Here is the flow:
User creates a text based post.
User edits a text based post (an edit page with the post info is displayed)
User submits the changes to the post (a request sent to the post controller)
Now, if I have MULTIPLE types of posts, I have to check in steps 2 and 3 that the user is indeed updating the RIGHT type of post because someone could very well alter the URL to edit a post of type A when it's really of type B. This leads to a lot of redundant code, such as ...
if(user is indeed the editor && the post type is correct) show the edit page
I think it would make a lot of sense to have an EDIT controller that does all the verification needed in the constructor (or maybe a base class?), and then calls the method. Have you encountered similar issues like this - and if not, does this make any design sense?
CodeIgniter is an MVC. That means that your controllers serve as an intermediate between your models (your data), and your view (front-end). "Edit" is an action that you do to objects, like data. Data objects should be organized within a controller, which calls the actual edit functions from the model.
I'm assuming you have a Post controller. At its core, it should have basic CRUD functions, like adding and editing posts. It should look something like this:
class Post extends CI_Controller
{
function __construct()
{
parent::__construct();
}
function index()
{
// List all posts, perhaps?
}
function add()
{
// Add a post
}
function edit($post_id)
{
// Edit a post
}
function view($post_id)
{
// View a post
}
}
That will give you the following pages:
http://example.com/post
http://example.com/post/add
http://example.com/post/view/1
http://example.com/post/edit/1
Checking for user permissions is its own chapter. If you are using a library like Tank Auth, you can check permissions like so:
if ($this->tank_auth->is_logged_in()) {
// Do stuff
}
That should go at the beginning of each function - or in the __construct(), if you want to DRY it up completely.
Good luck.

Please suggestion a better design of this controller class

Here is the code using CodeIgniter:
The problem I encounter:
The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Seems many method, but each one is depends another.
I think it is difficult to track the code.
Please give some suggestions thank you.
*Please reminded that, it is only the controller class. the load view is actually prepare the data for the view, won't render the page. also the doXXX function call model is only use the model method, it won't have any SQL statement. The MVC is separated, but the controller also have the functions related to the view or model, make it quite messy.
class User extends CI_Controller
{
public function register()
{
//check is logged in or not
//if not logged in , show the register page
}
public function show_register_page()
{
//generate the UI needed data , and call the view to render, and will the user will post back a valid_register function
}
public function valid_register()
{
//do all the valid logic, if success,
//do the do_register
//if fail, valid_register_fail
}
public function valid_register_fail()
{
//check is logged in or not
//show the valid register fail page
}
public function show_valid_register_fail_page()
{
//generate the UI needed data , and call the view to render
}
public function do_register()
{
//insert data in the db, the Model will be called
//if something go wrong in db, show the error page
//if everything is success, show the register success
}
public function show_db_error_page()
{
//generate the UI needed data , and call the view to render
}
public function show_register_success()
{
//generate the UI needed data , and call the view to render
}
}
1. The controller will have some functions call view, and it
separated, but it is still very close with the logic itself, if the
controller change to return in JSON or XML to display result, it seems
very trouble.
Depends on how you organized your code and what you actually pass into the view (template). If that's well structured, you can have one view for HTML, one for XML and one for json, where-as json normally just encodes the view variable's (see json_encodeDocs).
2. Seems many method, but each one is depends another.
Well, just don't do it :) The names look like you wanted to "code that into". Keep it apart. Make those function actually actions that a user performs:
register - that action handles the registration process
Make a login controller out of it that handles anything you need:
login - the login action
lost_password - the lost password action
register - the registration action
activate - the registration activation action
Everything else does not belong in there. There is no need for an action to display some page - the controller itself can decide which view to pick.
Next to that you don't need to display database errors. CI takes care of that. Just put only in what's needed and keep things simple. That should help you to reduce the number of methods and the code therein as well.
3. I think it is difficult to track the code.
Sure. Too many functions with not really speaking names. Keep things simple. It's not easy, but give naming and reducing the overall logic some love.

multi model forms in yii

How to create a multi-model form in Yii? I searched the entire documentation of Yii, but got no interesting results. Can some one give me some direction or thoughts about that? Any help will be appreciable.
In my expirience i got this solution to work and quickly understandable
You have two models for data you wish collect. Let's say Person and Vehicle.
Step 1 : Set up controller for entering form
In your controller create model objects:
public function actionCreate() {
$Person = new Person;
$Vehicle = new Vehicle;
//.. see step nr.3
$this->render('create',array(
'Person'=>$Person,
'Vehicle'=>$Vehicle)
);
}
Step 2 : Write your view file
//..define form
echo CHtml::activeTextField($Person,'name');
echo CHtml::activeTextField($Person,'address');
// other fields..
echo CHtml::activeTextField($Vehicle,'type');
echo CHtml::activeTextField($Vehicle,'number');
//..enter other fields and end form
put some labels and design in your view ;)
Step 3 : Write controller on $_POST action
and now go back to your controller and write funcionality for POST action
if (isset($_POST['Person']) && isset($_POST['Vehicle'])) {
$Person = $_POST['Person']; //dont forget to sanitize values
$Vehicle = $_POST['Vehicle']; //dont forget to sanitize values
/*
Do $Person->save() and $Vehicle->save() separately
OR
use Transaction module to save both (or save none on error)
http://www.yiiframework.com/doc/guide/1.1/en/database.dao#using-transactions
*/
}
else {
Yii::app()->user->setFlash('error','You must enter both data for Person and Vehicle');
// or just skip `else` block and put some form error box in the view file
}
You can find some examples in these two Yii wiki articles:
Yii 1.1: How to use a single form to collect data for two or more models?
Yii 1.1: How to use single form to collect data for two or more models (CActiveForm and Ajax Validation edition).
You don`t need a multi-model. The right use of the MVC pattern requires a Model that reflects your UI.
To solve it, you'll have to use a CFormModel instead of an ActiveRecord to pass the data from View to Controller. Then inside your Controller you`ll parse the model, the CFormModel one, and use the ActiveRecord classes (more than one) to save in database.
Forms Overview and Form Model chapters in Yii Definitive Guide contains some details and samples.
Another suggestions -
Also we can use Wizard Behavior, It's an extension that simplifies the handling of multi-step forms. In which we can use multi model forms for registration process flow or others.
Demo - http://wizard-behavior.pbm-webdev.co.uk/

Processing Zend_Form dynamically generated elements

I need to create a form where the elements (texbox, select, ..) will be dynamically inserted. Right now I have created a empty Form file with just a hidden element and them in my controller I go inserting elements according to certain conditions.
My form file:
class Form_Questions extends Zend_Form {
public function __construct() {
parent::__construct($options);
$this->setName('Questions');
// Hidden Label for error output
$hiddenlabel = new Zend_Form_Element_Hidden('hiddenlabel');
$hiddenlabel->addDecorator(new Form_Decorator_HiddenLabel());
$this->addElements( array($hiddenlabel) );
}
}
In the controller I have something like:
...
$form = new Form_Questions();
$request = $this->getRequest();
if ($request->isPost())
{
$formData = $request->getPost();
if ($form->isValid($request->getPost()))
{
die(var_dump($form->getValues()));
}
}
else
{
//... add textbox, checkbox, ...
// add final submit button
$btn_submit = new Zend_Form_Element_Submit('submit');
$btn_submit->setAttrib('id', 'submitbutton');
$form->addElement($btn_submit);
$this->view->form = $form;
}
The form displays fine but the validation is giving me big trouble. My var_dump() only shows the hidden element that is staticly defined in the Form file. It does not save the dinamic elements so altought I can get them reading what's coming via POST, I can not do something like
$form->getValue('question1');
It behaves like if Zend uses the Form file to store the values when the submit happend, but since the elements are created dinamically they do not persist (either their values) after the post so I can not process them using the standar getValue() way.
I would appreciate any ideas on how to make them "live" til after the post so I can read them as in a normal form.
The form which you are calling isValid() and getValues() methods on is actually your "empty" form - you have instantiated it only a few lines up and haven't added any elements to it at that point.
Remember that POST only sends an array of fieldName => fieldValue type, it doesn't actually send a Zend_Form object.
It is difficult to suggest a new solution without knowing what you are trying to achieve. It is generally better to add all possible elements to your Zend_Form right away, and then only use the ones you need in the view scripts, i.e. echo $this->form->myField;. This will allow isValid() to process all the elements of the form.
It sounds like the form is dynamic in the sense that the questions come from a db, not in then sense that the user modifies the form itself to add new questions.
Assuming this is the case, then I wouldn't add the question fields in the controller. Rather, I'd pass the questions to the form in the constructor and then add the question fields and the validators in the form's init() method. Then in the controller, just standard isPost() and isValid() processing after that.
Or, if you are saying that the questions to be added to the form are somehow a consequence of the hidden label posted, then perhaps you need two forms and two actions: one for the hidden field form and another for the questions.
Ok, the simplest solution I came up with - to my case and considering the really of the code I am currently playing with was to load all the questions I need from the database using a method from my Model (something like fetchQuestions()), them in my controller I go throught the recordset and create the form elements according to the current question of the recordset.
The elements are stacked in an array that is passed to my Form constructor. In the form constructor I read the array and generate all the dynamic elements. I them just echoed the form to the view.
I have not seem why it would be a bad idea to override the Form constructor as I also could not use any of the set/get methods to pass this to my form.

Categories