Contact form on each search result

On my search results page each result has a contact form, with fields: name, phone. (So basically the search results page can have for example 10 forms on the same page.)

The form is set to POST to itself, so the user gets taken back to the same results page. Now when I submit the form on one of the results how can I make it identify which form was submitted (and output confirmation/error message on that result only)? Is there an easy way of doing this in Yii?

Ideally I want to be using the active field types so that I can retain the values if there are any errors.

Can anyone provide a sample solution to this problem?

Add a hidden field with the search result’s id in the form. Then after a failed validation, pass the validated form back to the view and you could do something like this:




foreach($results as $r) {

  $form = $r->id === $validatedForm->resultId ? $validatedForm : new ContactForm;

  // display your active input fields for $form

}



If you do this, all other contact forms will be on an empty model and have no errors, only the one that was posted will be marked in red.

Cheers for that Sander. I’ll have a go at that but I’m havin some difficulty understand what needs to go where. Could you provide some sample view/controller code so I can get a better idea?

This is what I have at the moment:

View:




<div class="callback">

<?php if(Yii::app()->user->hasFlash('contact')): ?>

<?php echo Yii::app()->user->getFlash('contact'); ?>

<?php else: ?>


<?php echo CHtml::beginForm(); ?>

<?php echo CHtml::errorSummary($contact, 'ERROR: ') ?>


<fieldset>

<?php echo CHtml::activeLabel($contact, 'name'); ?> 

<?php echo CHtml::activeTextField($contact, 'name', array('class'=>'text')); ?> 

<?php echo CHtml::activeLabel($contact, 'phone'); ?> 

<?php echo CHtml::activeTextField($contact, 'phone', array('class'=>'text')); ?> 

<?php echo CHtml::imageButton(bu('images/btn_contact.jpg'), array('class'=>'submit')); ?> 

</fieldset>

		

<?php echo CHtml::endForm(); ?>

<?php endif; ?>

</div>



Controller:




$contact=new ContactForm;

if(isset($_POST['ContactForm']))

{

	$contact->attributes=$_POST['ContactForm'];

	if($contact->validate())

	{

		$to=Yii::app()->params['adminEmail'];

		$subject="Website Enquiry";

		$body="Name: ".$_POST['ContactForm']['name'] . "\n\nPhone: ".$_POST['ContactForm']['phone'];

		$headers=Yii::app()->params['adminFrom'];

				

		@mail($to,$subject,$body,$headers);

		Yii::app()->user->setFlash('contact','Thank you. We will be in touch soon.');

	}

}



Okay, the controller would look something like this:




function actionResults() {

	$results = SomeClass()->findAll();

	$form = new ContactForm;

	if(isset($_POST['ContactForm'])) {

		$form->attributes = $_POST['ContactForm'];

		$form->validate();

	}

	$this->render('results', array('results'=>$results, 'validatedForm'=>$form));

}



And in the view you do something like this:




<?php foreach($results as $result): ?>

	<?php $form = $result->id === $validatedForm->id ? $validatedForm : new ContactForm; ?>

	<?= CHtml::errorSummary($form); ?>

	<?= CHtml::beginForm(); ?>

		<?= CHtml::activeHiddenField($form, 'id', array('value'=>$result->id)); ?>

		<?= CHtml::activeLabelEx($form, 'attribute'); ?>

		<?= CHtml::activeTextField($form, 'attribute'); ?><br />

	<?= CHtml::submitButton('Send'); ?>

	<?= CHtml::endForm(); ?>

<?php endforeach; ?>



The idea is that the view acts on an empty ContactForm except when a ContactForm was previously submitted and it’s id matches the one of the current result, in which case the validated ContactForm is used to generate the appropriate input fields and errorSummary().

Does that help?

Hi Sander. Thanks for that, I’ve got it almost working now apart from the flash message. Any idea how to make that display on the correct result? (refer to my code above to see this)

Also have you got any idea how I can get unique IDs on my input fields/labels and then set these attribute labels in the Model?

You could change my line


<?php $form = $result->id === $validatedForm->id ? $validatedForm : new ContactForm; ?>

merge it into something like this:


<?php if($result->id === $validatedForm->id): ?>

	<?php $form = $validatedForm; ?>

	<?= CHtml::errorSummary($form); ?>

	<?php if(Yii::app()->user->hasFlash('contact')): ?>

		<?= Yii::app()->user->getFlash('contact'); ?>

	<?php endif; ?>

<?php else: ?>

	<?php $form = new ContactForm; ?>

<?php endif; ?>



Then you display it at the position of the correct search result.

Not really sure what you mean here, but try reading http://www.yiiframework.com/doc/guide/form.table I think it’ll get you started.

Cheers Sander. I did it like this:

Controller:


Yii::app()->user->setFlash($contact->id,'Thank you. We will be in touch soon.');

View:


<?php if(isset($contact->id) && $contact->id == $model->id && Yii::app()->user->hasFlash($contact->id)): ?>

<?php echo Yii::app()->user->getFlash($contact->id); ?>

But I think your method is better!

I mean the ID attribute of the input field / FOR attribute of the label. Obviously these need to be unique for each result. I can of course set these manually by appending $model->id to the end but then I don’t think I can get the attribute labels from the Model.

OK I think I understand what you mean :) You can override the element id with the htmlOptions argument in the CHtml functions:




<?= CHtml::activeLabelEx($form, 'attribute', array('for'=>'somethinguniquewith' . $result->id)); ?>

<?= CHtml::activeTextField($form, 'attribute', array('id'=>'somethinguniquewith' . $result->id)); ?>



The element’s ‘name’ attribute will be unchanged so your $_POST[‘ContactForm’] input stays the same.

But I think attributeLabels() matches on the ID of the label.