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

Simple 1:n embedRelation problem Symfony

  • SOLVED

Hi,

I am using symfony 1.3 and doctrine 1.2

I have a simple situation where one contact has many phone numbers. Contacts are one object/table, phone numbers are another object/table.

I want to have a form where I create a contact along with a single phone number. However, for some reason the "phone" form never displays.

I must be doing something simple wrong, but I can't figure out out.

Here is the schema snippet:

schema.yml

Contact:
columns:
id: { type: integer(4), primary: true, autoincrement: true }
first_name: { type: string(50)}
last_name: { type: string(50)}

Phone:
columns:
id: { type: integer(5), primary: true, autoincrement: true }
contact_id: { type: integer(4) }
number: { type: string(20) }
type: { type: enum, values: [Home, Work, Cell, Fax] }
relations:
Contact: { foreignAlias: Phone }


What I want is for the form to look like:

<blockquote>First name: (input firstname)
Last name: (input lastname)
Phone number: (input number) (dropdown phone number type)
</blockquote>

<strong>Code snippets:</strong>

<em>lib/form/doctrine/PhoneEmbedForm.class.php</em>

<?php

/**
* Phone Embed Form
*
* @package form
* @subpackage PhoneEmbed
* @version 0.1
*/
class PhoneEmbedForm extends BasePhoneForm
{
public function configure()
{
$this->useFields(array('number', 'type'));
}

}


<em>lib/form/doctrine/Contact.class.php</em>

<?php

/**
* Contact form.
*
* @package form
* @subpackage Contact
* @version SVN: $Id: sfDoctrineFormTemplate.php 6174 2007-11-27 06:22:40Z fabien $
*/
class ContactForm extends BaseContactForm
{
public function configure()
{
$this->embedRelation('Phone', 'PhoneEmbedForm');

$this->useFields(array('first_name', 'last_name', 'Phone'));
}
}


<em>apps/myapp/modules/contact/templates/_form.php</em>

<?php echo $form?>



The contact part of the form displays, and it says "Phone" in text but with no input boxes. How do I get it to show the phone boxes AND associate it with the contact upon save? Thank you.

Answerer must submit actual code for this specific case and not just link to a manual (however links to add clarity are appreciated).

Answers (4)

2010-10-19

Joshua Estes answers:

http://www.symfony-project.org/more-with-symfony/1_4/en/06-Advanced-Forms


webguy comments:

I specifically asked please don't directly link to docs with no notes. I have read that page and am still having issues.

So, a few things:

1) The phone number is a doctrine object, so according to that page embedRelation should work (am i wrong?)

2) I have tried the embedForm approach, however it doesn't properly associate the phone number and contact (however it creates both in the database

lib/form/doctrine/ContactForm.class.php
<?php
/**
* Contact form.
*/
class ContactForm extends BaseContactForm
{
public function configure()
{
$phoneForm = new PhoneEmbedForm(new Phone());
$this->embedForm('Phone', $phoneForm);
$this->useFields(array('first_name', 'last_name', 'Phone'));
}
}


lib/form/doctrine/PhoneEmbedForm.class.php

<?php

/**
* Phone Embed Form
*
* @package form
* @subpackage PhoneEmbed
* @version 0.1
*/
class PhoneEmbedForm extends PhoneForm
{
public function configure()
{
$this->useFields(array('number', 'type'));
}

}


Joshua Estes comments:

schema.yml

Contact:
columns:
first_name: { type: string(50)}
last_name: { type: string(50)}
relations:
Phone:
foreign: contact_id
local: id

Phone:
columns:
contact_id: { type: integer(4) }
number: { type: string(20) }
type: { type: enum, values: [Home, Work, Cell, Fax] }


ContactForm.class.php

class ContactForm extends BaseContactForm
{
public function configure()
{
$this->embedRelation('Phone');
}
}


PhoneForm.class.php

class PhoneForm extends BasePhoneForm
{
public function configure()
{
unset($this['contact_id']);
}
}


That should get you started

2010-10-19

Pawel Dawczak answers:

Hi,

first thing is a little change in schema:

Contact:
columns:
id: { type: integer(4), primary: true, autoincrement: true }
first_name: { type: string(50)}
last_name: { type: string(50)}



Phone:
columns:
id: { type: integer(5), primary: true, autoincrement: true }
contact_id: { type: integer(4) }
number: { type: string(20) }
type: { type: enum, values: [Home, Work, Cell, Fax] }
relations:
Contact: { foreignAlias: Phone, <strong>foreignType: one</strong> }


PhoneForm.class.php should be:


class PhoneForm extends BasePhoneForm
{
public function configure()
{
$this->useFields(array('number', 'type'));
}
}


and ContactForm.class.php:


class ContactForm extends BaseContactForm
{
public function configure()
{
$this->useFields(array('first_name', 'last_name'));

// embdding form
$phoneForm = new PhoneForm($this->getObject()->getPhone());
$this->embedForm('Phone', $phoneForm);
}
}


If you use <blockquote>$this->embedRelation('Phone', 'PhoneEmbedForm');</blockquote> Doctrine fetches Phone objects related with Contact object only for editing, so you can't create new Phone objects.

In case I showed above you are embedding a Phone form initialized with related Phone object <blockquote>$this->getObject()->getPhone()</blockquote> If there is no related Phone object, then you can see a blank form for creating new object.


webguy comments:

That works, but the schema complicates things a bit now.

I have a page where I list contacts and all of their phone numbers...

For example:
<blockquote>
Contact 1:
- Phone1
- Phone2
Contact 2:
- Phone3
Contact3:
- Phone 4
- Phone 5
</blockquote>

I used to be able to do a

<?php foreach ($contact->getPhone() as $i => $phone): ?>
<?php echo $phone->getNumber(); ?>
<?php endforeach; ?>


but now because of the schema updates, that doesnt work...


Pawel Dawczak comments:

Hi, sorry for my mistake. I gave you solution if you want to have relation 1:1. But this is for relation 1:N.

<em>config/schema.yml</em>

Contact:
columns:
id: { type: integer(4), primary: true, autoincrement: true }
first_name: { type: string(50)}
last_name: { type: string(50)}

Phone:
columns:
id: { type: integer(5), primary: true, autoincrement: true }
contact_id: { type: integer(4) }
number: { type: string(20) }
type: { type: enum, values: [Home, Work, Cell, Fax] }
relations:
Contact: { foreignAlias: PhoneNumbers }


Now you can fetch all related objects with:

foreach ($contact->getPhoneNumbers() as $number)
{
echo $number->getNumber();
}


<em>lib/form/doctrine/PhoneForm.class.php</em>

class PhoneForm extends BasePhoneForm
{
public function configure()
{
$this->useFields(array('contact_id', 'number', 'type'));

$this->widgetSchema['contact_id'] = new sfWidgetFormInputHidden();
}
}


<em>lib/form/doctrine/ContactForm.class.php</em>

class ContactForm extends BaseContactForm
{
public function configure()
{
$this->useFields(array('first_name', 'last_name'));

// if object is new, it has no ID that we need to set in new embedded form
if (!$this->getObject()->isNew())
{
// adds blank form for adding new related values
$newPhoneForm = new PhoneForm();
$newPhoneForm->setDefault('contact_id', $this->getObject()->getId());
$this->embedForm('new', $newPhoneForm);

// adds forms for all related objects
$this->embedRelation('PhoneNumbers');
}
}

protected function doBind(array $values)
{
// you need to check if blank form is filled
if ('' === trim($values['new']['number']))
{
unset($values['new'], $this['new']);
}

parent::doBind($values);
}
}


It is based on [[LINK href="http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine"]][[/LINK]]. You can find there a solution, how to add a check box, to delete embedded values.


Pawel Dawczak comments:

Sorry, I missed a link to article: [[LINK href="http://prendreuncafe.com/blog/post/2009/11/29/Embedding-Relations-in-Forms-with-Symfony-1.3-and-Doctrine"]]link[[/LINK]]

2010-10-20

Ivan Rey answers:

Just do both things in your form. Do a embedForm to allow the user to add new phone numbers, and do embedRelation to allow the user to edit current relations.

In ContactForm.class.php


class ContactForm extends BaseContactForm
{
public function configure()
{

$this->embedRelation('Phone'); //This allows the user to edit current relations
$phone = new Phone();
$phone->setContact($this->getForm()->getObject());
$phonesForm = new PhoneForm($phone);
$this->embedForm($phonesForm); //This allows new phones to be added
}
}


2010-10-20

saki answers:

Instead of this:

$phone->setContact($this->getForm()->getObject());

Use this:

$phone->Contact = $this->getObject();

I hope this helps...