Ask your Symfony questions! Pay money and get answers fast! (more info)

Autocomplete and symfony forms Symfony

  • SOLVED

I'm using symfony 1.3/doctrine 1.2. I have a situation where users in my system can send out email messages to multiple recipients, and messages are saved to the db. There are three relevant tables:

- Messages
- Recipients (to/cc) in another.
- Contacts/address book table

I implemented an autocomplete function (jquery autocomplete 1.1, not jquery ui autocomplete 1.8) that is working about 95% about how I want it to work, using the sfFormExtra plugin sfWidgetFormJQueryAutocompleter. Basically, I want it to work just like gmail's autocomplete - you can choose from your address book or type in some other address.

There is nothing that I need to do with setting hidden IDs using the autocomplete - whatever text is input to the to field will be split on commas, each field set as a recipient, and then saved that way.

<strong>Here are my issues:</strong>
- By default, the value of the field is "Array"
- I cannot set a default "to" value on the form using the normal style of $this->setDefaults(array('to_list' => $this->getOption("default_to")));

I haven't gotten to actually saving the message in the db either yet, so if there are any issues there, please consider. Thanks.

Do I need to use a different widget since my use case is not comprised only of items from the database? I'm familiar with http://snippets.symfony-project.org/snippet/336 , but like I said sometimes I need to be able to just type in an address so that doesn't exactly fit the scenario.


Abbreviated schema:

Contact:
columns:
name: { type: string(150) }
email: { type: string(150) }

Email:
columns:
subject: { type: string(150), notnull: true }
body: { type: clob(65532), notnull: true }
relations:
To: { class: Recipients, local: id, foreign: email_id, refClass: To }

Recipient:
columns:
email_id: { type: integer }
type: { type: string(5) }
recipient: { type: string(200) }
relations:
Email: { local: email_id, foreign: id }

To:
inheritance:
extends: Recipient
type: column_aggregation
keyField: type
keyValue: to

Cc:
inheritance:
extends: Recipient
type: column_aggregation
keyField: type
keyValue: cc



lib/form/doctrine/EmailForm.php

class EmailForm extends BaseEmailForm
{
public function configure()
{
sfProjectConfiguration::getActive()->loadHelpers(array('Url'));
$this->widgetSchema['to_list'] = new sfWidgetFormChoice(return array(
'choices' => NULL,
'renderer_class' => 'sfWidgetFormJQueryAutocompleter',
'renderer_options' => array(
'url' => sfContext::getInstance()->getController()->genUrl('@address_autocomplete'),
'config'=>
'{
width: 320,
max: 10,
highlight: false,
scroll: true,
scrollHeight: 150,
minChars: 2,
multiple: true,
multipleSeparator: ",",
parse: function(data) {
var parsed = [];
for (var i = 0; i < data.length; i++) {
parsed[parsed.length] = {
data: data[i],
value: data[i].Contact.email,
result: "\"" + data[i].Contact.first_name + " " + data[i].Contact.last_name + "\" <"+data[i].Contact.email+">"
};
}
return parsed;
},
formatItem: function(row) {
return row.Contact.last_name + ", " + row.Contact.first_name +" - "+ row.Contact.Account.name +" <br/>" + row.Contact.email;
}
}'
),
));

Answers (1)

2011-03-25

Jakub Zalas answers:


Firstly, you need to add a validator for every field. It's one of the changes in the configure() method. Second is setting defaults.

Next you can overload the doUpdateObject() to create Recepients out of values taken from your autocomplete field.

There's something wrong with your model definition. Doctrine didn't generate all the needed setters. Only setRecepient() is present in my case. It requires passing Doctrine_Collection and that's what I did in updateRecepients() method. Once you clean this up you might want to change this to something more common (see http://www.doctrine-project.org/projects/orm/1.2/docs/manual/working-with-models/en).

I simply split recipients by commas and directly put it into the database. You might also consider to clean the input a bit before putting those values into the database. I guess best way is implementing custom validator and replacing sfValidatorString I used.


class EmailForm extends BaseEmailForm
{
public function configure()
{
sfProjectConfiguration::getActive()->loadHelpers(array('Url'));
$this->setDefaults(array('to_list' => $this->getOption('default_to')));
$this->validatorSchema['to_list'] = new sfValidatorString();
$this->widgetSchema['to_list'] = new sfWidgetFormChoice(array(
'choices' => NULL,
'renderer_class' => 'sfWidgetFormJQueryAutocompleter',
'renderer_options' => array( // [....] ),
));
}

protected function doUpdateObject($values)
{
parent::doUpdateObject($values);

$this->updateRecepients($values);
}

protected function updateRecepients($values)
{
$email = $this->getObject();
$toCollection = new Doctrine_Collection('To');

$toList = explode(',', $values['to_list']);

foreach ($toList as $recipient)
{
if (!empty($recipient))
{
$to = new To();
$to->setRecipient($recipient);
$to->setEmail($this->getObject());
$toCollection->add($to);
}
}

$email->setRecipient($toCollection);
}
}


Here's an example action I used to test the solution (index) and a mock of autocomplete action (auto). Notice how I passed default_to value.


public function executeIndex(sfWebRequest $request)
{
$this->form = new EmailForm(null, array('default_to' => 'my default'));

if ($request->isMethod('post'))
{
$parameters = array_merge(
$request->getParameter($this->form->getName()),
$request->getParameter('autocomplete_email', array())
);

if ($this->form->bindAndSave($parameters))
{
print_r($this->form->getValues());
die('saved!');
}
}

public function executeAuto(sfWebRequest $request)
{
$this->getResponse()->setContent(json_encode(array(
array('Contact' => array('first_name' => 'name1', 'last_name' => 'last1', 'email' => '[email protected]', 'Accou
array('Contact' => array('first_name' => 'name2', 'last_name' => 'last2', 'email' => '[email protected]', 'Accou
)));
return sfView::NONE;
}
}