This post is to share the php/zend framework code I used to create an rsvp for my wedding site. I’m not going into all the details since that would take too long ;p but all the code is available if you want to use it. This was created using the zend framework version 1.7.

So I’m getting married in two month and for our wedding my fiancee and I decided to create a website for our guests. The site includes information such as location, time, links to registries, maps, and a section to rsvp. The site was made by my fiancee in html and css. When she was done, I ported it over to zend framework and started creating the rsvp section which I’ll describe next. You can view the finished wedding site here:

RSVP Page

http://www.joeyrivera.com/wedding

The objectives of the rsvp are the following:

  • Allow guest to enter their code
    • each invitation that goes out has a code in it specific to the ‘family’.
  • Validate code from database and load information about that family
    • return the family name and how many seats they have available. Because of seating limitations, we only allow x amounts of seats per family. Ex: A friend could have 2 seats available in case they are coming with a guest.
  • Let guest select if they attending, if so, select how many guests are coming with an optional comments field
  • Submit and save their rsvp in the database and email us with the details

The database structure is very simple, only one table that looks like:

DROP TABLE IF EXISTS `family`;
CREATE TABLE  `family` (
`family_id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`alias` VARCHAR(100) NOT NULL,
`code` SMALLINT(5) UNSIGNED NOT NULL DEFAULT ‘0’,
`attending` TINYINT(1) DEFAULT NULL,
`available_seats` TINYINT(3) UNSIGNED NOT NULL,
`confirmed_seats` TINYINT(3) UNSIGNED DEFAULT NULL,
`comments` VARCHAR(255) DEFAULT NULL,
`last_access` DATETIME DEFAULT NULL,
PRIMARY KEY  (`family_id`),
UNIQUE KEY `Index_2` USING BTREE (`code`)
) ENGINE=INNODB AUTO_INCREMENT=80 DEFAULT CHARSET=latin1;

The fields are:

  • Alias is the ‘description or name’ for the family such as Ashley and Joey Rivera or The Rivera’s.
  • Code is the unique code we send out with each invitation.
  • Attending is a boolean that is updated when the guest rsvps.
  • Available seats is the number we set to limit how many people can come from that family.
  • Confirmed seats is the number the guest sets when rsvping.
  • Comments is optional
  • Last access tracks the last time that row was updated

I made code unique because it should be, we don’t want to accidentally assign two codes to the same family. We don’t want to use the family id as the code because we want the code a bit harder to guess if someone feels like fooling around with the system – we are hoping our family members and friends won’t purposely try to break the system though :p.

Here is the code:

<?php

class RsvpController extends Zend_Controller_Action
{
        protected $_redirector = null;

        public function init()
        {
                $this->_redirector = $this->_helper->getHelper(‘Redirector’);
        }

        public function indexAction()
        {
                $this->view->form = $this->rsvpForm();
        }

        public function submit1Action()
        {
                // get and filter code
                $filters = array(
                        ‘*’ => ‘StringTrim’
                );
                $validators = array(
                        ‘Digits’
                );
                $input = new Zend_Filter_Input($filters, $validators, $this->getRequest()->getParams());
                // verify code was passed
                if(!$input->isValid(‘code’))
                {
                        // go back with error message
                        $this->_redirect(‘/rsvp’);
                }
                $code = $input->code;
                // get family from code
                $family = new Family();
                $family_row = $family->fetchRow(‘code = ‘ . $code . );
                // if family doesn’t exist, leave
                if(empty($family_row))
                {
                        $this->_redirect(‘/rsvp’);
                }
                // track row in session
                $session = new Zend_Session_Namespace(‘family’);
                $session->code = $code;
                $session->row = $family_row;
                // all is good, give view data
                $this->view->family = $family_row;
                $this->view->form = $this->familyForm($family_row);
        }

        public function submit2Action()
        {
                // validate session
                $session = new Zend_Session_Namespace(‘family’);
                if(!isset($session->row))
                {
                        $this->_redirect(‘/’);
                        return;
                }
                $form = $this->familyForm($session->row);
                // get vars to check
                $attending = $this->getRequest()->getParam(‘attending’);
                $seats = $this->getRequest()->getParam(‘seats’);
                // make sure data is good
                // if attending but seats is 0 then ask them to select how many seats
                // if the seats is greater than available seats error out
                if(($attending &amp;&amp; !$seats) || ($attending > $session->row->available_seats))
                {
                        // assumed by passing error on element would trigger this but doesn’t seem too
                        $form->markAsError();
                        $seats_element = $form->getElement(‘seats’);
                        $seats_element->addError(‘Please select how many people are coming.’);
                }
                //else if
                // if not attending but selected seats, ask them about it
                if(!$attending &amp;&amp; $seats)
                {
                        $form->markAsError();
                        $attending_element = $form->getElement(‘attending’);
                        $attending_element->addError(‘You selected you are not coming but set the number
                                of seat to a number greater than 0. Did you mean to say you are attending?’
);
                }
                // if the form has an error, it can still be valid so need to check for both
                if($form->isErrors() || !$form->isValid($_POST))
                {
                        $form->populate($_POST); // for some reason have to manually call this or values don’t show
                        $this->view->form = $form;
                        $this->view->family = $session->row;
                        $this->render(‘index’);
                        return;
                }
                // update family
                $family = new Family();
                $family->update(array(
                        ‘attending’ => $form->getValue(‘attending’) ,
                        ‘confirmed_seats’ => $form->getValue(‘seats’) ,
                        ‘comments’ => $form->getValue(‘comments’) ,
                        ‘last_access’ => date(‘Y-m-d H:i:s’)
                ), ‘code = ‘ . $session->code . );

                // email
                $mail = new Zend_Mail();
                $mail->setBodyText(‘New RSVP submitted.’);
                $mail->setBodyHtml(‘<p>New RSVP submitted:</p>
                                                        <p>’
. $session->row->alias . ‘</p>
                                                        <p>Attending: ‘
. $form->getValue(‘attending’) . ‘</p>
                                                        <p>Seats: ‘
. $form->getValue(‘seats’) . ‘</p>
                                                        <p>Comments: ‘
. $form->getValue(‘comments’) . ‘ </p>’);
                $mail->setFrom(‘joey@joeyrivera.com’, ‘JoeyRivera.com’);
                $mail->addTo(‘joey1.rivera@gmail.com’, ‘Joey Rivera’);
                $mail->addTo(‘person@person.com’, ‘Ashley Fleishel’);
                $mail->setSubject(‘Wedding New RSVP’);
                $mail->send();

                $this->_redirect(‘/rsvp/done’);
        }

        public function doneAction()
        {
                // unset session
                $session = new Zend_Session_Namespace(‘family’);
                $session->unsetAll();
        }

        private function rsvpForm()
        {
                $form = new Zend_Form();
                $form->setAction(‘/rsvp/submit1’)->setMethod(‘post’)->setAttrib(‘id’, ‘rsvp’);
                $form->addElement(‘text’, ‘code’, array(
                        ‘label’ => ‘RSVP Code (found on your invitation)’ ,
                        ‘validators’ => array(
                        ‘digits’
                ) , ‘required’ => true
                ));
                $form->addElement(‘submit’, ‘submit’);
                return $form;
        }

        private function familyForm($family)
        {
                $form = new Zend_Form();
                $form->setAction(‘/rsvp/submit2’)->setMethod(‘post’)->setAttrib(‘id’, ‘rsvp’);
                $form->addElement(‘radio’, ‘attending’, array(
                        ‘label’ => ‘Are you attending?’ ,
                        ‘multiOptions’ => array(1 => ‘Yes’ , 0 => ‘No’) ,
                        ‘validators’ => array(
                        ‘Digits’
                ) , ‘required’ => true
                ));
                // create array of seats
                $seats = array();
                for($x = 0, $max = $family->available_seats; $x <= $max; $x++)
                {
                        $seats[$x] = $x;
                }
                // limit number to available seats from row session
                $form->addElement(‘select’, ‘seats’, array(
                        ‘label’ => ‘How many will be attending?’ ,
                        ‘multiOptions’ => $seats ,
                        ‘required’ => true
                ));
                $form->addElement(‘textarea’, ‘comments’, array(
                        ‘label’ => ‘Comments’ , //’validators’ => array(
                //      ‘Alnum’
                //),
                ‘required’ => false , ‘width’ => 250
                ));
                $form->addElement(‘submit’, ‘submit’);
                return $form;
        }
}

I’ll summarize what I’m doing with the code. I have 4 actions in my RsvpController:

index

  • This action display the rsvpForm in the view which is a input box for the guests to type in their family code.

submit1 (I know, name sucks but I was going for something quick ;p)

  • This action will validate the code submitted by the index action.
  • It then creates an instance of family using a zend db table model and loads the data from the database for the family that matches that code.
  • If it’s a real family, a session var is created with the family data and the form familyForm is displayed. Else, the user is taken back to the index action.

submit2

  • When the familyForm is submitted in submit1, this action validates that there is a family session var.
  • Then it does a few validations more such as making sure that the family seats is a number greater than 0 if they are attending. Or didn’t choose a number of seats greater than 0 if they are not attending (in case they accidentally hit not coming but did want to).
  • Then it validates the submitted form – I put it at the end because it seemed as if it would be less code that way. You can read the comments to see why I did what I did, I had some issues with the way I was expecting the form zend framework class to behave.
  • If all is well, the family row in the database is updated and an email is sent to myself and fiancee.

done

  • Displays a done message and the rsvp process is complete!

That’s about it. A couple quick thoughts or comments about this. The form could be made more secure but like I said before, for this project I think it’s fine. Also, you’ll see a bunch of css in the views, it’s because I was trying to port the site over quickly and that was the quickest way. If you have any specific questions feel free to ask. Hope you find this useful. Below are some screen shots of the final product and a zip file of the code including the model, the rsvp controller, and the views in the application folder:

RSVP Files

index:

submit1:

done: