Symfony 1.2 added a lot of new and exciting new features to an already great and powerful PHP framework. One of those features is the ability to embed a form into another. So what excatly does this mean?
First consider the model shown in the diagram below:

As shown in the above figure, there is a one-to-one relationship between the company and the contact model.
Here is the schema.yml:
propel:
_attributes:
package: lib.model
defaultIdMethod: native
company:
_attributes: { phpName: Company }
id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true }
name: { type: VARCHAR, size: '255', required: true }
contact_id: { type: INTEGER, size: '11', required: false, foreignTable: contact, foreignReference: id, onDelete: SET NULL, onUpdate: RESTRICT }
_indexes: { company_FI_1: [contact_id] }
contact:
_attributes: { phpName: Contact }
id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true }
first_name: { type: VARCHAR, size: '255', required: true }
last_name: { type: VARCHAR, size: '255', required: true }
company_id: { type: INTEGER, size: '11', required: false, foreignTable: company, foreignReference: id, onDelete: SET NULL, onUpdate: RESTRICT }
_indexes: { contact_FI_1: [company_id] }
Note that both foriegn keys are not required, that is a must for us to be able to embed the Contact form in the Company form.
Now that we have the schema ready, let us build the sql schema, models, forms, filters, and create the tables.
$ php symfony propel:build-sql $ php symfony propel:build-model $ php symfony propel:build-forms $ php symfony propel:build-filters $ php symfony propel:insert-sql --env=dev
Now let us generate a module using symfony’s admin generator and see how the generator will handle this relationship.
$ php symfony propel:generate-admin backend Company
By browsing to the category module we just created above we can see the below figure:

Now if we click on the “New” link we get the following screen:

Notice that the “contact_id” foreign column was interpreted by the admin generator as a drop down list. Of course that is not what we had in mind, we want to be able to add a new “Company” along with it’s “Contact”. Thanks to symfony 1.2 ability to embed forms, this can be done very easily.
First open “CompanyForm.class.php” file and edit the configure method to match the following:
public function configure() {
// get Contact model object
$contact = $this->getObject()->getContact();
// contact object is null
if (is_null($contact)) {
// create a new Contact object
$contact = new Contact();
// set the copmany of the newly created object to the current company
$contact->setCompany($this->getObject());
// set the contact of the current company
$this->getObject()->setContact($contact);
}
// create a new contact form
$contact_form = new ContactForm($contact);
// embed the contact form in the current company form
$this->embedForm('contact', $contact_form);
// remove the contact_id from the form
unset($this['contact_id']);
}
Next, open the ContactForm.class.php and edit as follows:
public function configure() {
unset($this['company_id']);
}
Now modify the Company.php model class to delete the contact while delete the company
public function delete(PropelPDO $con = null) {
$this->getContact()->delete($con);
parent::delete($con);
}
And last but not least, edit the generator.yml to match the following(this step is optional):
generator:
class: sfPropelGenerator
param:
model_class: Company
theme: admin
non_verbose_templates: true
with_show: false
singular: ~
plural: ~
route_prefix: company
with_propel_route: 1
config:
actions: ~
fields:
contact_id: { label: Company Contact }
list:
display: [=name, contact]
filter: ~
form: ~
edit: ~
new: ~
Now go to the add new “Company” screen and you should see the following:

Try adding, editing, and deleting some records now to make sure it’s working.


easyyy
hope keeping us up to date always using this easy explanation
thanks for the article i find it easy to get and very helpfull…
Nice tuto, kudos
great! but now, let’s do the same for a 1 to many relation … : My companies have many contacts, so that would be better for me!
@jukea: The one-to-many relationship will automatically be implmemented by symfony’s admin generator in the form of a select many form elemeny. If you’d like more options, try reading this article
@ahmed : yes, but I’m more looking into something like in-place editing of contacts inside a company, like :
http://redotheoffice.com/?p=42
@jukea: I see, I’ll look into it and update the article ASAP.
thanks for the article i find it easy to get and very help full me …
@Gopal: You’re most welcome, I’m glad that I can help. By he way if you need any help regarding symfony please don’t hesitate in contacting me.
Hi,
This is great and has helped me a lot, however I’m having one issue…
This line:
# // get Contact model object
# $contact = $this->getObject()->getContact();
Modified to call the model object I want, fails with the error Call to undefined method BaseCompany::getContact (I’ve modified names back to suit your example rather than my own code)…
If I just comment it out and the if structure following, leaving everything else, the form displays fine, validates, submits etc, but when you bring it back up, it can’t populate the embedded form with the data to math the parent form, presumably because the above, which I’ve commented out, is what controls this.
So how can you get it to call on a model outside of itself (ie call the Contact model from the Company model) when you’re using $this?? (There is probably just something I am missing / don’t understand
)
Thanks,
JM
Sorry I should add I am using it in a non admin-generated frontend, but I don’t believe for this problem it makes a difference?
@John: I’m not quite sure what the problem is, but I think it has to to with your MySQL tables, are they MYISAM or InnoDB?
@Ahmed El.Hussaini: I had the model wrong, no foreign key from the main table to the ‘child’ table I wanted to embed… I thought I could do it without this (just a link back the other way) but obviously not! Thanks
snip
Note that both foriegn keys are not required, that is a must for us to be able to embed the Contact form in the Company form.
/snip
should that be “foreign keys *are* required”?
Hello !
Very nice tutorial. Hey by the way, can you give any tips on managing many-to-many relations on admin generator ?
I have
Portfolio
Tags
portfolio_tags
and want to manage the tags associated inside my portfolio form.
Thanks
Joao
@Joao: Thanks, try this: http://www.symfony-project.org/blog/2008/10/14/new-in-symfony-1-2-make-your-choice
Hi!!
I have the same problem that jhon:
“Call to undefined method BaseCompany::getContact”
But my tables are: Denuncia and Denunciado, i want to embed Denunciado into Denuncia, so my model have the foreing keys on each table, and the exact error is:
“Call to undefined method BaseDenuncia::getDenunciado”
and my tables are InnoDB..
What could be my problem???
I get error when i add __toString function in lib/model/contac.php
Catchable fatal error: Method Contact::__toString() must return a string value in E:\Web\company\cache\backend\dev\modules\autoCompany\templates\_list_td_tabular.php on line 5
plese help this..
thanks
@ojie43: Open lib/model/Contact.php and override the __toString() function.
ex:
public function __toString() { return $this->getFullName(); }thanks…
now, I can use it to my form..
sory my english not good
Now, I have probel with year range ini my birdday field, I only get year range 2004- 2014..
why I can get more year range ?, example 1950-2000
please help me
thanks before
Great tutorial, This is exactly what I was looking for since last two days. I was not able to save my embedded for.
You solved my problem.
Thanks
Is there a version for users of the doctrine orm available ?
i have troubles implementing this with doctrine …
maybe you could be so kind and describe the differences / modifations neccessary when ussing doctrine. thx a lot!!!!
I have problem i am struggling with for some time now:
Is there a way to save file with filename same as primary key of inserted record?
For example such method works only for updates couse primary id already exists:
public function generateImageFilename(sfValidatedFile $file)
{
return $this->getId().$file->getExtension($file->getOriginalExtension());
}
Do u have any solution for situation when new record is being saved?
This is a great post. I really appreciate the screen shots. It’s this type of functionality that helps illustrate why the forms framework is what it is. Although sometimes I really miss the simplicity of the old forms functionality in 1.0, embedding forms is hard to compete with. For @Norbert, besides a different schema, remember that with doctrine “$contact = $this->getObject()->getContact();” will always return a contact object, even if one does not exist in the database.
Hello,
Your schema example is not correct for postgresql databese.
Correct:
propel:
_attributes:
package: lib.model
defaultIdMethod: native
contact:
_attributes: { phpName: Contact }
id: { type: INTEGER, primaryKey: true, autoIncrement: true, required: true }
first_name: { type: VARCHAR, size: 255, required: true }
last_name: { type: VARCHAR, size: 255, required: true }
company_id: { type: INTEGER, required: false }
# foreign key is creating as an “alter table” on the end of *.sql file
_foreignKeys:
-
foreignTable: company
onDelete: cascade
references:
– { local: company_id, foreign: id }
company:
_attributes: { phpName: Company }
id: { type: INTEGER, primaryKey: true, autoIncrement: true, required: true }
name: { type: VARCHAR, size: 255, required: true }
contact_id: { type: INTEGER, required: false }
_foreignKeys:
-
foreignTable: contact
onDelete: cascade
references:
– { local: contact_id, foreign: id }
Your schema is creating constraints in create table so it gives a lot of conflicts beetween tables.
V
Better solution for:
public function configure() {
unset($this['company_id']);
}
.. is unset company_id directly in CompanyForm.class.php:
// create a new contact form
$contact_form = new ContactForm($contact);
unset($contact_form['company_id']);
Why? When you create standalone module for contact you can show company name or other informations related to.
Am i right?
wonderful post! Thanks a lot!