How to embed forms in symfony 1.2 admin generator

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:
company_contact

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:
company_1

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

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:
company_3

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

Related Posts

Comments So Far » (21 Comments). Add Yours

1. forganna

21 December, 2008

easyyy
hope keeping us up to date always using this easy explanation

2. Hebatollah Moaz

21 December, 2008

thanks for the article i find it easy to get and very helpfull…

3. NiKo

21 December, 2008

Nice tuto, kudos :)

4. jukea

22 December, 2008

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!

5. Ahmed El.Hussaini

22 December, 2008

@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

6. jukea

06 January, 2009

@ahmed : yes, but I’m more looking into something like in-place editing of contacts inside a company, like :

http://redotheoffice.com/?p=42

7. Ahmed El.Hussaini

07 January, 2009

@jukea: I see, I’ll look into it and update the article ASAP.

8. Gopal

23 January, 2009

thanks for the article i find it easy to get and very help full me …

9. Ahmed El.Hussaini

23 January, 2009

@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.

10. John

23 January, 2009

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

11. John

23 January, 2009

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?

12. Ahmed El.Hussaini

23 January, 2009

@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?

13. John

24 January, 2009

@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 :)

14. roland

28 January, 2009

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”?

15. Joao Correia

02 February, 2009

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

17. germana

04 May, 2009

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???

18. ojie43

10 May, 2009

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

19. Ahmed El.Hussaini

10 May, 2009

@ojie43: Open lib/model/Contact.php and override the __toString() function.

ex:

public function __toString() {
  return $this->getFullName();
}

20. ojie43

14 May, 2009

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

21. sudhir

29 June, 2009 (5 days ago)

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

Submit Your Comment

Your email is never published nor shared.

Please use <pre> tags for inserting code