Archive

Posts Tagged ‘form’

Advanced radio button grouping with jQuery

November 18, 2009 8 comments

I was asked to do something rather interesting with some radio button inputs the other day. I haven’t come across this particular problem before and google didn’t return anything helpful, so I decided to share what I came up with.

First, the scenario

We have a standard form with 4 radio button groups. Each group has 4 radio buttons labeled from “1” to “4”. Now, when the user selects “option 1” in “group 1”, all the remaining “option 1” items need to be disabled. To see an example in action, you can view the demo here.

The problem

When I first started, I thought that it’s simply a matter of giving each “option” (regardless of group) a common class name – then just disabling all other options with the same class name. That works, to a point, but what if the user changes his mind and selects another option ? Now I had to find all previously disabled options, re-enable them and start all over again.

The solution

Essentially what I came up with was a basic nested loop to handle setting and unsetting the relevant “disabled” attributes. To achieve this, first we assign all “option 1” a class of “number1”, “option 2” a class of “number2” and so on.

Next, we run a basic for loop, and go through each “number” class (i.e. number1 to number4). For each class, we call a function. Inside this function is another loop – this time iterating over each radio button assigned the current class name. Using this loop, we remove any “disabled” attributes which may have been assigned previously. We also find out which item in that group is currently selected (if any) – this is so we can run a second loop to disable all those options not currently selected. Confused ? Now might be a good time to go through the code

The code

$(function(){
		// fire our code when a radio button has been selected
		$("input[type=radio]").change(function(){
			var name = $(this).attr("name"); // get the current button's group name
			$("input[name="+name+"]").removeAttr("selected"); // first we deselect "all" radio buttons within the same group
			$(this).attr("selected","selected"); // make sure our currently "changed" button is selected
			for(i=1;i<=4;i++){ // go through the 4 radio classes
				processRadio(".radio"+i);
			}
		});
		/**
			Loops through each item with same "class" name and disables/enables where appropriate
		**/
		function processRadio(class){
			var empty;
			var id = "";
			var buttons = $(class); // read all buttons with the specified class name into a jQuery object
			buttons.each(function(){ // loop through each item
				var me = $(this);
				var isSelected = me.attr("selected"); // bool value, based on whether the "selected" attribute is present or not
				me.removeAttr("disabled"); // clear the disabled attribute if present
				me.siblings("label").removeClass("disabled"); // same with the associated label element
				if (isSelected != empty && isSelected != ""){
					id = $(this).attr("id"); // set our tmp id var to our currently selected item
				}
			});
			// make sure we have an id, otherwise we'll get an error
			if (id != empty && id != ""){
				buttons.each(function(){ // loop through each radio button once again
					if ($(this).attr("id") != id){ // set the disabled attributes if id attribute doesn't match our tmp id var
						$(this).attr("disabled", "disabled").siblings("label").addClass("disabled");
					}
				});
			}
		}

	});

I’ve commented as best I could, which hopefully makes more sense than my rambling above. Once again, there is a working demo available for you to play with. Be sure to have a look at the markup as well, might clear up a few questions.

I strongly suspect that there’s a more efficient method for achieving the same result – so if you have a better suggestion, tweak or link please let me know – I’d greatly appreciate it.

How to use jQuery to select a radio button from parent element

January 9, 2009 4 comments

A friend asked me for some help on doing the following :
1. You have an unordered list – within each list item you have a radio input.
2. When you click on the list item (i.e. the entire container), it must a) select the radio input and b) add a css classname of “selected” to the list item.

You can find a demo here.

First, the html :

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
	<!-- include our jquery scripts -->
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js" type="text/javascript"></script>
    <script src="js/radio.js" type="text/javascript"></script>
    <style type="text/css">
    	li { color: #000; }
    	li.selected { color: #FF0000; }
    </style>
</head>
<body>
	<div>
		<ul>
			<li class="test">Test 1 <input type="radio" name="test" /></li>
			<li class="test">Test 2 <input type="radio" name="test" /></li>
			<li class="test">Test 3 <input type="radio" name="test" /></li>
			<li class="test">Test 4 <input type="radio" name="test" /></li>
			<li class="test">Test 5 <input type="radio" name="test" /></li>
		</ul>
	</div>
</body>
</html>

There is nothing out of the ordinary here – just a simple unordered list of radio inputs.

Next, the js (radio.js) :

$(document).ready(function(){
	$('li').click( function(){
		$('li.selected').removeClass('selected');
		$(this).addClass('selected');
		$(this).children("input[type=radio]").click();
	});
});

1. First – we tell jQuery to execute the following code once the document has finished loading, ensuring that the DOM has been registered successfully.
2. Next we bind a click event to all list item elements on the page – you can of course change this to all list items with a class of “myselection” for example ( $(“li.myselection”).click… )
3. When a list item is clicked – we first need to remove the “selected” class from all other list items.
4. Now we need to add the “selected” class to the list item which fired the event ( $(this) ).
5. Finally, we need to make sure that the radio button inside the list item is “clicked” : we do this by using the .children() selector and executing a click() event on the input.

The important bit is the $(this).children(“input[type=radio]”) selector. What we’re doing here is telling jQuery to find all inputs of type radio (i.e. all radio inputs) inside “this” list item (i.e. the list item which was clicked on). We then use .click() to..as you guessed it… execute a click event on the returned element (in this case the radio input).

And that’s all there is to it. You can read up some more on jQuery selectors here.
I would also highly recommend reading the following article : Improve your jQuery – 25 excellent tips.

Spam proof Contact Form with Zend Framework, ReCaptcha and Akismet / TypePad Antispam

October 3, 2008 7 comments

Update : Download Source code here
Update : View Working Demo

Over the last few weeks I’ve blogged on using the Zend Framework’s Akismet Service to “spam proof” your generic, run of the mill contact form.

Of course, services like Akismet and TypePad Antispam only come into play after the form has been submitted, a “cure” so to speak.

What we really need is some form of robust “prevention”. Stop the spam bots right at the door. There’s nothing more irritating than getting bombarded by 10k submissions every hour on the hour – even if they’re being filtered out by your Akismet / TypePad service.

Which brings us to the now common place CAPTCHA component. Designed to stop automated spam bots in their tracks, its a pretty effective method. Ordinarily, it’s not too difficult to implement a CAPTCHA component to your form, but ReCaptcha and the Zend Framework have now made it child’s play.

ReCaptcha is an online service providing a free CAPTCHA API for you to use. What’s also really nifty is that while your users fill in the CAPTCHA form, they’re also helping develop OCR software used to digitize books and manuscripts.

As an example I’ve gone and written a basic contact form, zipped it up and made it available here. Inside the zip file is all the code necessary for a very basic and hopefully spam proof contact form.

The following Zend Framework components were used :

  • Zend Loader
  • Zend Config
  • Zend View
  • Zend Service ReCaptcha
  • Zend Service Akismet
  • Zend Mail
  • Zend Validate
  • Zend Filter
  • Zend Registry

I’ve also included a simple wrapper for Zend View, since I didn’t feel that it was necessary to go the whole MVC route on this one.

The code used is pretty straightforward and easy to follow with as many comments thrown in there as I could stomach, but if you have any questions please feel free to leave a comment or send a mail.

Before you begin, you’ll need the following:

To use, unzip the file (it already contains the Zend Framework v1.6), edit the config.xml file and then simply customize the view scripts (view/form.php, view/success.php, view/mail.php).

Unfortunately I haven’t had much time to test the example thoroughly as it was written in a bit of a hurry (only had about an hour or so), so if you find or even fix any bugs please let me know and I’ll sort it out as soon as I get an opportunity to do so.

Update: I’ve amended 3 bugs thanks to comments from Mark. Affected files can be downloaded separately here, or you can download the full source again here. I’ve also created a working demo here.

  1. In view/form.php line 23 : “$this->TextArea” has been changed to “$this->Textarea”. TextArea works on my local host, but not when uploaded to my demo server.
  2. In view/form.php line 25-27 : Added a new paragraph containing “$this->captcha” to output the ReCaptcha element. This was not included in the zip file’s form.php for some inexplicable reason – my apologies
  3. In library/FlexiDev/Process.php line 122 : Logic error preventing ReCaptcha from verifying correctly. $recaptcha->verify(…) returns an object and not a boolean value – amended to $result = $recaptcha->verify(…); return $result->isValid();

One last note : the code is provided under the New BSD License, so have fun.

Adding TypePad Anti Spam to your Contact Form with Zend Framework

September 24, 2008 3 comments

About 2 weeks ago, I posted a quick tutorial on how to implement Akismet.com’s anti-spam checks to your contact form using the excellent Zend Framework. I was fortunate enough to have Anil Dash from Six Apart (the creators of TypePad) drop by and post a comment asking me if I’d be interested in giving TypePad’s Antispam service a try.

I’m happy to report that TypePad Antispam is just as effective as Akismet, with the bonus of there being no restrictions on usage (that I could find in any case).

With all of that in mind – I simply copied the current Zend/Service/Akismet.php class – renamed it to Zend/Service/TypePadAntiSpam.php and did a simple search and replace. The two services are so alike in implementation, that’s all it took.

The code posted below makes the following assumptions :

Firstly the TypePad Antispam Class. Simply save this file to the following folder inside your Zend Framework installation : Zend/Service/TypePadAntiSpam.php (should be in the same folder as Akismet.php)

<?php
/**
 * Please note that this is NOT an official Zend Framework package.
 * This is essentially a copy-paste-modification of the original Zend Framework's Service/Akismet.php class to
 * work with the TypePad Anti Spam service. If you find this class useful or find an error etc, please leave a
 * comment at https://calisza.wordpress.com - all feedback is welcome.
 * 
 * All original/offical headers have been left intact. Thanks to all the devs who have made the Zend Framework
 * the wonderful product that it is.
 */

/**
 * Zend Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://framework.zend.com/license/new-bsd
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@zend.com so we can send you a copy immediately.
 *
 * @category   Zend
 * @package    Zend_Service
 * @subpackage TypePadAntiSpam
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */


/**
 * @see Zend_Version
 */
require_once 'Zend/Version.php';

/**   
 * @see Zend_Service_Abstract
 */
require_once 'Zend/Service/Abstract.php';


/**
 * Typepad Anti Spam REST service implementation
 *
 * @uses       Zend_Service_Abstract
 * @category   Zend
 * @package    Zend_Service
 * @subpackage TypePadAntiSpam
 * @copyright  Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com)
 * @license    http://framework.zend.com/license/new-bsd     New BSD License
 */
class Zend_Service_TypePadAntiSpam extends Zend_Service_Abstract
{
    /**
     * TypePadAntiSpam API key
     * @var string
     */
    protected $_apiKey;

    /**
     * Blog URL
     * @var string
     */
    protected $_blogUrl;

    /**
     * Charset used for encoding
     * @var string
     */
    protected $_charset = 'UTF-8';

    /**
     * TCP/IP port to use in requests
     * @var int
     */
    protected $_port = 80;

    /**
     * User Agent string to send in requests
     * @var string
     */
    protected $_userAgent;

    /**
     * Constructor
     *
     * @param string $apiKey TypePadAntiSpam API key
     * @param string $blog Blog URL
     * @return void
     */
    public function __construct($apiKey, $blog)
    {
        $this->setBlogUrl($blog)
             ->setApiKey($apiKey)
             ->setUserAgent('Zend Framework/' . Zend_Version::VERSION . ' | TypePadAntiSpam/1.1');
    }

    /**
     * Retrieve blog URL
     *
     * @return string
     */
    public function getBlogUrl()
    {
        return $this->_blogUrl;
    }

    /**
     * Set blog URL
     *
     * @param string $blogUrl
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception if invalid URL provided
     */
    public function setBlogUrl($blogUrl)
    {
        require_once 'Zend/Uri.php';
        if (!Zend_Uri::check($blogUrl)) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid url provided for blog');
        }

        $this->_blogUrl = $blogUrl;
        return $this;
    }

    /**
     * Retrieve API key
     *
     * @return string
     */
    public function getApiKey()
    {
        return $this->_apiKey;
    }

    /**
     * Set API key
     *
     * @param string $apiKey
     * @return Zend_Service_TypePadAntiSpam
     */
    public function setApiKey($apiKey)
    {
        $this->_apiKey = $apiKey;
        return $this;
    }

    /**
     * Retrieve charset
     *
     * @return string
     */
    public function getCharset()
    {
        return $this->_charset;
    }

    /**
     * Set charset
     *
     * @param string $charset
     * @return Zend_Service_TypePadAntiSpam
     */
    public function setCharset($charset)
    {
        $this->_charset = $charset;
        return $this;
    }

    /**
     * Retrieve TCP/IP port
     *
     * @return int
     */
    public function getPort()
    {
        return $this->_port;
    }

    /**
     * Set TCP/IP port
     *
     * @param int $port
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception if non-integer value provided
     */
    public function setPort($port)
    {
        if (!is_int($port)) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid port');
        }

        $this->_port = $port;
        return $this;
    }

    /**
     * Retrieve User Agent string
     *
     * @return string
     */
    public function getUserAgent()
    {
        return $this->_userAgent;
    }

    /**
     * Set User Agent
     *
     * Should be of form "Some user agent/version | TypePadAntiSpam/version"
     *
     * @param string $userAgent
     * @return Zend_Service_TypePadAntiSpam
     * @throws Zend_Service_Exception with invalid user agent string
     */
    public function setUserAgent($userAgent)
    {
        if (!is_string($userAgent)
            || !preg_match(":^[^\n/]*/[^ ]* \| TypePadAntiSpam/[0-9\.]*$:i", $userAgent))
        {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid User Agent string; must be of format "Application name/version | TypePadAntiSpam/version"');
        }

        $this->_userAgent = $userAgent;
        return $this;
    }

    /**
     * Post a request
     *
     * @param string $host
     * @param string $path
     * @param array  $params
     * @return mixed
     */
    protected function _post($host, $path, array $params)
    {
        $uri    = 'http://' . $host . ':' . $this->getPort() . $path;
        $client = self::getHttpClient();
        $client->setUri($uri);
        $client->setConfig(array(
            'useragent'    => $this->getUserAgent(),
        ));

        $client->setHeaders(array(
            'Host'         => $host,
            'Content-Type' => 'application/x-www-form-urlencoded; charset=' . $this->getCharset()
        ));
        $client->setParameterPost($params);

        $client->setMethod(Zend_Http_Client::POST);
        return $client->request();
    }

    /**
     * Verify an API key

     *
     * @param string $key Optional; API key to verify
     * @param string $blog Optional; blog URL against which to verify key
     * @return boolean
     */
    public function verifyKey($key = null, $blog = null)
    {
        if (null === $key) {
            $key = $this->getApiKey();
        }

        if (null === $blog) {
            $blog = $this->getBlogUrl();
        }

        $response = $this->_post('api.antispam.typepad.com', '/1.1/verify-key', array(
            'key'  => $key,
            'blog' => $blog
        ));

        return ('valid' == $response->getBody());
    }

    /**
     * Perform an API call
     *
     * @param string $path
     * @param array $params
     * @return Zend_Http_Response
     * @throws Zend_Service_Exception if missing user_ip or user_agent fields
     */
    protected function _makeApiCall($path, $params)
    {
        if (empty($params['user_ip']) || empty($params['user_agent'])) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Missing required TypePadAntiSpam fields (user_ip and user_agent are required)');
        }

        if (!isset($params['blog'])) {
            $params['blog'] = $this->getBlogUrl();
        }

        return $this->_post($this->getApiKey() . '.api.antispam.typepad.com', $path, $params);
    }

    /**
     * Check a comment for spam
     *
     * Checks a comment to see if it is spam. $params should be an associative
     * array with one or more of the following keys (unless noted, all keys are
     * optional):
     * - blog: URL of the blog. If not provided, uses value returned by {@link getBlogUrl()}
     * - user_ip (required): IP address of comment submitter
     * - user_agent (required): User Agent used by comment submitter
     * - referrer: contents of HTTP_REFERER header
     * - permalink: location of the entry to which the comment was submitted
     * - comment_type: typically, one of 'blank', 'comment', 'trackback', or 'pingback', but may be any value
     * - comment_author: name submitted with the content
     * - comment_author_email: email submitted with the content
     * - comment_author_url: URL submitted with the content
     * - comment_content: actual content
     *
     * Additionally, TypePadAntiSpam suggests returning the key/value pairs in the
     * $_SERVER array, and these may be included in the $params.
     *
     * This method implements the TypePadAntiSpam comment-check REST method.
     *
     * @param array $params
     * @return boolean
     * @throws Zend_Service_Exception with invalid API key
     */
    public function isSpam($params)
    {
        $response = $this->_makeApiCall('/1.1/comment-check', $params);

        $return = trim($response->getBody());

        if ('invalid' == $return) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid API key');
        }

        if ('true' == $return) {
            return true;
        }

        return false;
    }

    /**
     * Submit spam
     *
     * Takes the same arguments as {@link isSpam()}.
     *
     * Submits known spam content to TypePadAntiSpam to help train it.
     *
     * This method implements TypePadAntiSpam's submit-spam REST method.
     *
     * @param array $params
     * @return void
     * @throws Zend_Service_Exception with invalid API key
     */
    public function submitSpam($params)
    {
        $response = $this->_makeApiCall('/1.1/submit-spam', $params);
        $value    = trim($response->getBody());
        if ('invalid' == $value) {
            require_once 'Zend/Service/Exception.php';
            throw new Zend_Service_Exception('Invalid API key');
        }
    }

    /**
     * Submit ham
     *
     * Takes the same arguments as {@link isSpam()}.
     *
     * Submits a comment that has been falsely categorized as spam by TypePadAntiSpam
     * as a false positive, telling TypePadAntiSpam's filters not to filter such
     * comments as spam in the future.
     *
     * Unlike {@link submitSpam()} and {@link isSpam()}, a valid API key is
     * never necessary; as a result, this method never throws an exception
     * (unless an exception happens with the HTTP client layer).
     *
     * this method implements TypePadAntiSpam's submit-ham REST method.
     *
     * @param array $params
     * @return void
     */
    public function submitHam($params)
    {
        $response = $this->_makeApiCall('/1.1/submit-ham', $params);
    }
}

Next, some code to implement our new class :

<?php
/*
* Basic function to check for spam
* @param items : associative array for containing form field values
* @return boolean : true if spam, false if clean
*/
function spamCheck($items){
 	require_once 'Zend/Service/TypePadAntiSpam.php'; // include the required class file - change path if necessary
 	$url = "http://url.to.my.blog.or.form"; // url associated with API key
	$api = "432dsjk890"; // TypePad Antispam API key
 	$spam = new Zend_Service_TypePadAntiSpam($api, $url ); // create new instance of our TypePadAntiSpam Service class

	if ($spam->verifyKey()){ // make sure the API key if valid before performing check
	 	$params = array(); // check the comments for the isSpam() method in Zend/Service/TypePadAntiSpam.php for more information on available parameters
	 	$params["user_ip"] = $_SERVER['REMOTE_ADDR']; // required by TypePadAntiSpam
	 	$params["user_agent"] = $_SERVER['HTTP_USER_AGENT']; // required by TypePadAntiSpam
	 	$params["referrer"] = $_SERVER[ 'HTTP_REFERER'];
	 	$params["comment_type"] = "comment";
	 	$params["comment_author"] = $items["name"];
	 	$params["comment_author_email"] = $items["email"];
	 	$params["comment_content"] = $items["comments"];

	 	return $spam->isSpam($params); // submits api call and returns true if spam, false if clean

	} else {
 		return false;
 	}
 }

// to make use of our spam check function try the following :
$items = sanitize($_POST); // sanitize is your own built-in function to sanitize user submitted data

// only mail the form contents if not spam
if (!spamCheck($items)){
	// insert code to mail form contents here
}
?>

That should do it. You should now have a robust, easy to use anti-spam solution for your contact forms.

Adding Akismet.com spam checks to your contact form

September 13, 2008 5 comments

I had a request the other day to write a spam filter for a standard contact form. Thanks to wordpress I’ve been made aware of the excellent spam filtering service offered by Akismet.com. What’s even better, is that an Akismet Service class has been added to the Zend Framework – allowing for easy integration into one’s PHP projects.

The code posted below makes the following assumptions :

<?php
/*
* Basic function to check for spam
* @param items : associative array for containing form field values
* @return boolean : true if spam, false if clean
*/
function spamCheck($items){
 	require_once 'Zend/Service/Akismet.php'; // include the required class file - change path if necessary
 	$url = "http://url.to.my.blog.or.form"; // url associated with API key
	$api = "432dsjk890"; // Akismet API key
 	$spam = new Zend_Service_Akismet($api, $url ); // create new instance of our Akismet Service class

	if ($spam->verifyKey()){ // make sure the API key if valid before performing check
	 	$params = array(); // check the comments for the isSpam() method in Zend/Service/Askismet.php for more information on available parameters
	 	$params["user_ip"] = $_SERVER['REMOTE_ADDR']; // required by Akismet
	 	$params["user_agent"] = $_SERVER['HTTP_USER_AGENT']; // required by Akismet
	 	$params["referrer"] = $_SERVER[ 'HTTP_REFERER'];
	 	$params["comment_type"] = "comment";
	 	$params["comment_author"] = $items["name"];
	 	$params["comment_author_email"] = $items["email"];
	 	$params["comment_content"] = $items["comments"];

	 	return $spam->isSpam($params); // submits api call and returns true if spam, false if clean

	} else {
 		return false;
 	}
 }

// to make use of our spam check function try the following :
$items = sanitize($_POST); // sanitize is your own built-in function to sanitize user submitted data

// only mail the form contents if not spam
if (!spamCheck($items)){
	// insert code to mail form contents here
}
?>

This is of course a very basic example, and only touches on the isSpam() method provided by the class. The real power is actually contained in the submitSpam() and submitHam() methods on which I will be posting a tutorial shortly.

Beginners tips for form processing with PHP

September 9, 2008 Leave a comment

I remember when first starting with the whole PHP / Web development scene that processing forms was a real hassle. This was back in the day when php_global_vars was still accepted as the “in” thing and you had to use each form variable as php variable. I only found out about $_GET (or as it was $HTTP_GET_VARS) later on, much to my consternation.

I did however, come up with a workaround at the time, and it’s stuck with me ever since. I fully realise that the PHP superglobals make this method somewhat redundant, but I still find it useful in larger forms, especially when you’re trying to group related form variables.

The basic idea is to add each form variable to an array. In my case this array is usually called “items”. For instance, I have a standard login form with the usual login and password fields. I define each field using the following method : <input type=”text” name=”items[login]” value=”” />  – for the novices out there, notice the “items[login]” bit. What this does is assign the value for “login” to the “items” array. This array is then accessed via the $_GET[“items”] or $_POST[“items”] superglobals. Of course you can have multi-dimensional arrays – for example : <input type=”text” name=”items[user][login]” /> and so on and so forth.

Now the really useful part comes in when you are looking to sanitize only certain parts of your user input (yes I’ve had instances where I purposely didn’t want to sanitize incoming data). For a good tutorial on how to sanitize user input  – try this one . You can also assign input data to a session variable more easily. I.e $_SESSION[“userdata”] = $_GET[“items”][“user”].

Lately, I’ve found this method extremely useful when binding form data to my MVC model data. So lets say we have a form where a user can edit his/her profile. In addition, this form also has space for the user to edit his password. Usually, password and profile information is stored in 2 separate tables. Which means that I would use “user[]” for the password fields (since you need a “confirm password” field aswell) and I would use “profile[]” for the user’s profile data. Then once inside the PHP script, I can then bind $_GET[“user”] to my user model, and $_GET[“profile”] to my userProfile model. The only constraint then is making sure the form field matches a model field.

I admittedly haven’t seen this method being used all that often, and it does make me wonder if it goes against some “best practice” that I’m ignorant of, or perhaps I’m just way ahead of my time 😉

Either way, hopefully it can be of some help to someone out there. I do promise though, that when I’ve improved my blogging and therefore writing skills, I’ll revisit this post and make it more “noob” friendly.