Using Uploadify in Frontend Forms

30 Aug

Using Uploadify in Frontend Forms

Since its release last week, Uploadify fever has really caught on. I couldn’t be happier to see that! I have a lot of plans for Uploadify, including integrating it with the DataObjectManager module, and continuing to refine the authentication and Amazon S3 features.

But for today, I thought it would be worthwhile to give a tutorial on integrating Uploadify into the front end. Uploadify fields are just as easy to use on the frontend as they are in the backend. No extra configuration is needed. A different template is automatically used for the frontend, and administrative features such as folder selection and file importing are removed.

In this example, we will create a job application form. We will collect a single resume and several letters of recommendation. The resumes will be limited to PDFs and recommendations will be limited to .doc or .docx. The form will store the application in the database for an administrator to view later.

Step 1: Create the Job Application object

The form will save into an object so that it can be stored in the database.

// JobApplication.php //

class JobApplication extends DataObject
{
	static $db = array (
		'FirstName' => 'Text',
		'LastName' => 'Text',
		'Email' => 'Text',
		'Position' => "Enum('Sales, Marketing, Logistics')"
	);

	static $has_one = array (
		'Resume' => 'File'
	);

	static $has_many = array (
		'Recommendations' => 'RecommendationFile'
	);
}

Because we have a $has_many relationship for files, we need to create a File subclass to create the $has_one back to JobApplication. This is not necessary for Resume, because there is only one of those allowed.

// RecommendationFile.php //

class RecommendationFile extends File
{
	static $has_one = array (
		'JobApplication' => 'JobApplication',
		'StaffMemberPage' => 'StaffMemberPage'
	);
}

Now it is necessary to run a /dev/build, and make sure the database gets updated.

Step 2: Build the form

We’ll create a new page type to display the form.

// JobApplicationPage.php //

class JobApplicationPage extends Page
{

}

class JobApplicationPage_Controller extends Page_Controller
{
	public function ApplyForm() {
		$form = new Form (
			$this,
			"ApplyForm",
			new FieldSet (
				new TextField('FirstName', _t('JobApplication.FIRSTNAME','First name')),
				new TextField('LastName', _t('JobApplication.LASTNAME','Last name')),
				new EmailField('Email', _t('JobApplication.EMAIL','Email')),
				new DropdownField('Position', _t('JobApplication.POSITION','Position applying for'), singleton('JobApplication')->dbObject('Position')->enumValues()),
				$resume = new FileUploadField('Resume', _t('JobApplication.RESUME','Upload a resume')),
				$recommendations = new MultipleFileUploadField('Recommendations', _t('JobApplication.RECOMMENDATIONS','Upload several letters of recommendation'))
			),
			new FieldSet (
				new FormAction('doApply', _t('JobApplication.APPLYNOW','Apply now'))
			),
			new RequiredFields('FirstName','LastName','Email','Position')
		);
		$resume->setFileTypes(array(
			'pdf'
		));
		$recommendations->setFileTypes(array(
			'doc',
			'docx'
		));

		return $form;
	}

}

Here we have a pretty straightforward form, but the important thing to notice is that all of the field names are consistent with those of the JobApplication object, e.g. “FirstName, “Position”, etc. This is because the form will save into the JobApplication dataobject. If we were not doing that, this consistency would not be necessary.

Notice that the form action “doApply” is specified in the actions FieldSet, but it has not been defined. That will be the next step.

Step 3: Handle the form

Create the following action in JobApplicationPage_Controller class.

// JobApplicationPage.php //

	public function doApply($data, $form) {
		$form->saveInto($application = new JobApplication());
		$application->write();
		return array (
			'Application' => $application
		);

	}

This really simple handler is all we need to save a form whose fields are already in alignment with the JobApplication dataobject. If we were not saving the data to a the database, we might do something like this:

public function doApply($data, $form) {
	$from = $data['Email'];
	$to = "administrator@mysite.com";
	$subject = "New job application";
	$body = "Someone submitted a job application. The files are attached.";
	$email = new Email($from, $to, $subject, $body);
	if(isset($data['ResumeID'])) {
		if($file = DataObject::get_by_id("File", (int) $data['ResumeID'])) {
			$email->attachFile($file->getFullPath(), $file->Name);
		}
	}
	if(isset($data['Recommendations']) && is_array($data['Recommendations'])) {
		foreach($data['Recommendations'] as $id) {
			if($file = DataObject::get_by_id("File", (int) $id)) {
				$email->attachFile($file->getFullPath(), $file->Name);
			}
		}
	}
	$email->send();
        $form->sessionMessage("Thank you for submitting your application","good");
        return Director::redirectBack();
}

Notice that the single file is sent as [RelationshipName]ID, but the multiple file relation is sent as an array of IDs under the name [Relationship].

Let’s look again at the first doApply() function. The return value is an array containing the application object.

return array (
       'Application' => $application
);

The field “Application” will be available on the template. Let’s examine how we can handle that.

// JobApplicationPage.ss //

	

$Title

Thank you for applying. You entered the following information:
$FirstName $LastName
$Email
$Position

Resume

Resume: $Resume.Name download

Recommendations

$Name download
$ApplyForm

If the application is available, we enter a control and show the components, and prove the integrity of the data by offering a download link.

If the application isn’t available, we simply invoke $ApplyForm to show the form.

That’s it! If you have any questions, please feel free to post a comment. Good luck!

14 Responses to “Using Uploadify in Frontend Forms”

  1. Lamin Barrow 31. Aug, 2010 at 9:47 am #

    Sweet stuff Uncle Cheese. We are all excited about about uploadify just as you are. Your modules have made Silverstripe more juicy. Thanks a lot. :)

  2. nick 05. Nov, 2010 at 3:36 am #

    Hiya UC,
    hey…is there some code missing from that last snippet (JobApplicationPage.ss)? You refer to using a control with the ‘Application’ filed, but don’t have it in the code sample…

    Cheers
    nj

  3. nick 05. Nov, 2010 at 4:20 am #

    Also, another quick question on this…
    I’m getting uploadify errors with code similar to yours above, and have tracked it down to a hidden field in the form:

    new HiddenField ($name = “jobvacancynumber”, $title = “jobvacancynumber”,$value = $this->CurrentJob->VacancyNumber)

    it outputs fine to the page, but then when I try to upload a file I get the following:

    500 Notice: “Trying to get property of non-object” at line 91 of /usr/local/www/vhosts/tdh.org.nz/httpdocs/mysite/code/JobApplicationForm.php (where line 91 is the hiddenfield above)

    any ideas??

  4. nick 05. Nov, 2010 at 3:31 pm #

    hi again:) delete that last one….stupid mistake was causing the glitch! I found it using charles – dunno if your familiar with it – web debugging proxy developed by a guy here in NZ – essential! http://www.charlesproxy.com/

  5. Marijn Kampf 10. Nov, 2010 at 12:41 pm #

    I’ve tried this, and it doesn’t work for me on the frontend. Trying with this and my own example the files are uploaded into the assets folder, but there is no ImageID variable available in $data variable.

    Using SS 2.4.2. / Uploadify 511.

    Debug (JobApplicationPage_Controller->doApply() in line 39 of JobApplicationPage.php)

    * url =

    /abflags/new-jobapplicationpage/ApplyForm

    * FirstName =

    Test

    * LastName =

    test

    * Email =

    test@example.com

    * Position =

    Sales

    * FolderID =

    1

    * SecurityID =

    12850

    * action_doApply =

    Apply now

    Debug (JobApplicationPage_Controller->doApply() in line 40 of JobApplicationPage.php)
    Form

    * TextFieldTextField (FirstName: First name : ) = Test
    * TextFieldTextField (LastName: Last name : ) = test
    * EmailFieldEmailField (Email: Email : ) = test@example.com
    * DropdownFieldDropdownField (Position: Position applying for : ) = Sales
    * FileUploadFieldFileUploadField (Resume: Upload a resume : ) =
    * MultipleFileUploadFieldMultipleFileUploadField (Recommendations: Upload several letters of recommendation : ) =
    * HiddenFieldHiddenField (SecurityID: : ) = 12850

    Validator

    * FirstName
    * LastName
    * Email
    * Position

    • Carsten 26. Jan, 2011 at 7:24 am #

      i have got exactely the same problem. was anybody able to solve this?

      Greetings, Carsten.

      • Tim 08. Apr, 2011 at 11:57 am #

        Same problem here.

        • Marijn Kampf 23. Jun, 2011 at 9:26 am #

          Not 100% sure whether it’s the same issue as this post was a while back, but I found that two references to separate jQuery include files causes Uploadify to fail without error. This can simply be resolved by ensuring jQuery is only included once.

  6. dominic 12. Jan, 2011 at 2:38 am #

    I’m running the demos linked from this page
    http://dataobjectmanager.carlinowebdesign.com/job-application-page/
    http://dataobjectmanager.carlinowebdesign.com/job-application-page-2/

    and in each case
    – js error
    $ is not a function $(‘#Form_ApplyForm’).validate();
    when the page loads

    – first name field is bounded and shaded red as though there was an error

    – no upload behaviour in the file upload parts, ie nothing uploads

    – no multiple behaviour in the multiple upload field

    I’m running FF3 on ubuntu linux

  7. dominic 12. Jan, 2011 at 3:05 am #

    I wonder if there’s something wrong with the demo site (or the demo site user =] ) because it works much better on my local machine with the downloaded code. I’m getting “IO error” on upload but that’s probably my fault, I’ve not tried any setup at all yet.

  8. Lukin 24. Feb, 2011 at 2:40 pm #

    Hi Is there a way to doApply via Ajax.submit?

    I tried it that way:

    jQuery(“#Form_ApplyForm”).submit(function(){
    jQuery(‘#Form_ApplyForm’).load(
    ‘domain/doApply’,{values: jQuery(this).serialize()}…

    but don’t know how to get the form-object for saving it into my dataobject

    I know doApply needs two values

  9. Lukin 09. Mar, 2011 at 2:58 pm #

    Found a bug,..
    when deleting a file while it is uploading, doesn’t delete the file but all the others which have already been uploaded

  10. Dave 21. Apr, 2011 at 2:13 pm #

    Not sure if this is a bug or not, but I’m running SS 2.4.5 and when I put FileUploadField on a front end form and have it upload automatically (not on submit) the file gets uploaded but the file name does not show up under “Attached Files” is just says “No File Attached”.

    Is there a way to have the file show up after it uploads.

    Also, if I set the uploadOnSubmit and the form doesn’t validate the file gets uploaded still, is there a way to not have that upload unless the form is valid?

  11. Bernardo Martinez 19. May, 2011 at 2:48 pm #

    I just copy the files that you bring in this post, and i get this error “there has been an error, error loading page”, i actually have a question LOL, the package “uploadify_frontend.zip” has a “templates” folder, where do i have to put this folder…tnks, i hope you can help me.