PhpRiot
Become Zend Certified

Prepare for the ZCE exam using our quizzes (web or iPad/iPhone). More info...


When you're ready get 7.5% off your exam voucher using voucher CJQNOV23 at the Zend Store

Creating Multi-Step Forms And Wizards In PHP

ZervWizard Example

Here is a full example of a class that extends the ZervWizard class. In the next step, we will write the corresponding code that uses the code.

There will be various comments and notes scattered throughout the code to explain it in a top-down approach, so read carefully!

The example is of processing a shopping cart checkout section. It includes 3 steps: 1 to get the user’s shipping details, 1 to get the billing details, and the final step is to confirm the user’s details. Once they have confirmed the details, we will process the data.

To simplify this example, we are process the credit card as the very final thing we do. This means if it fails there’s no mechanism to go back and ask for a new number. The example would be significantly longer to handle all this functionality correctly.

Also please note that the ZervWizard is quite functional and works well, but there may be some limitations with it that will hopefully be overcome in the future. It is certainly a good starting point for what we’re trying to achieve.

The CheckoutWizard class

Listing 7 listing-7.php
<?php
    require_once('ZervWizard.class.php');
 
    class CheckoutWizard extends ZervWizard
    {
        function CheckoutWizard()
        {
            // start the session and initialize the wizard
            session_start();
            parent::ZervWizard($_SESSION, __CLASS__);
 
 
            // create the steps, we're only making a simple 3 step form
            $this->addStep('userdetails', 'Enter your shipping details');
            $this->addStep('billingdetails', 'Enter your billing details');
            $this->addStep('confirm', 'Confirm your details');
        }
 
        // here we prepare the user details step. all we really need to
        // do for this step is generate the list of countries.
 
        function prepare_userdetails()
        {
            $this->loadCountries();
        }
 
        // now we process the first step. we've simplified things, so we're
        // only collecting, name, email address and country
 
        function process_userdetails(&$form)
        {
            $name = $this->coalesce($form['name']);
            if (strlen($name) > 0)
                $this->setValue('name', $name);
            else
                $this->addError('name', 'Please enter your name name');
 
            $email = $this->coalesce($form['email']);
            if ($this->isValidEmail($email))
                $this->setValue('email', $email);
            else
                $this->addError('email', 'Please enter a valid email address');
 
            $country = $this->coalesce($form['country']);
            $this->loadCountries();
            if (array_key_exists($country, $this->countries))
                $this->setValue('country', $country);
            else
                $this->addError('country', 'Please select your country');
 
            return !$this->isError();
        }
 
        // next, prepare the billing details step. again, not much to do here. here
        // we'll generate a list of the different types of credit cards
 
        function prepare_billingdetails()
        {
            $this->ccTypes = array('VISA', 'MASTERCARD', 'AMEX');
        }
 
        // the next thing we do is process the billing details step. this involves
        // validating the credit card details. we're going to use the name accepted
        // in the first step as the credit card name just to simplify things.
        // additionally, we're not going to bother with the expiry date, once again
        // just to simplify things.
 
        function process_billingdetails(&$form)
        {
            // load the cc types so we can validate the selected value
            $this->prepare_billingdetails();
            $cc_type = $this->coalesce($form['cc_type']);
 
            if (in_array($cc_type, $this->ccTypes))
                $this->setValue('cc_type', $cc_type);
            else
                $this->addError('cc_type', 'Please select a valid credit card type');
 
            $cc_number = $this->coalesce($form['cc_number']);
 
            if (strlen($cc_number) > 0 && $this->validLuhn($cc_number))
                $this->setValue('cc_number', $cc_number);
            else
                $this->addError('cc_number', 'The specified credit card number is invalid');
 
            return !$this->isError();
        }
 
        // the final step is the confirmation step. there's nothing to prepare here
        // as we're just asking for final acceptance from the user
 
        function process_confirm(&$form)
        {
            $confirm = (bool) $this->coalesce($form['confirm'], true);
 
            return $confirm;
        }
 
 
        function completeCallback()
        {
            // finally, all form data is valid and the user has confirmed they
            // want to proceed. Now we do all the final processing here. Because
            // we don't really have a credit card gateway for this example we
            // just simulate it
 
            $this->processCreditCard($this->getValue('name'),
                                     $this->getValue('cc_type'),
                                     $this->getValue('cc_number'));
 
 
            $this->sendUserConfirmationEmail($this->getValue('email'));
        }
 
 
        function processCreditCard($name, $type, $number)
        {
            // communicate with CC gateway here
        }
 
        function sendUserConfirmationEmail($email)
        {
            // create and a send an email
        }
 
 
 
        /**
         * Miscellaneous utility functions
         */
 
 
        function isValidEmail($email)
        {
            return preg_match('/^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*$/i', $email);
        }
 
        function loadCountries()
        {
            $this->countries = array('au' => 'Australia',
                                     'de' => 'Germany',
                                     'fr' => 'France',
                                     'uk' => 'United Kingdom',
                                     'us' => 'United States');
        }
 
        // a method to validate a credit card number. we're not actually validating
        // it against the type of credit card, just to simplify the example. to do
        // this, it's just a matter of checking the number prefix and the length
        // of the number (depending on the card)
        function validLuhn($number)
        {
            $len = strlen($number);
            $dbl = '';
            for ($i = $len - 2; $i >= 0; $i -= 2) {
                $dbl .= ((int) $number{$i}) * 2;
            }
 
            $dbllen = strlen($dbl);
            $dbltotal = 0;
            for ($i = 0; $i < $dbllen; $i++) {
                $dbltotal += (int) $dbl{$i};
            }
 
            $total = $dbltotal;
            for ($i = $len - 1; $i >= 0; $i -= 2) {
                $total += $number{$i};
            }
            return $total % 10 == 0;
        }
    }
?>

In This Article


Additional Files