Posted on Jan 19, 2009

How to Embed Forms in Symfony 1.2 Admin Generator Part 3

In the last part of this series of articles we introduced the ability to edit multiple child models in the edit screen of the parent model. In this part we will take it a step further by adding I18N behavior to the child models. To make things a little bit clear before we start coding please take a look at the result we want to achieve below.

embed_i18n

Before starting I strongly recommend reading part one, and two if you haven’t done so already. OK lets get started, we will be modifying the following files:

  • CategoryForm.class.php which represents the parent model
  • SubcategoryI18nForm.class.php which represents the form of the i18n model of the child model
  • Subcategory.class.php which represents the child model

Files need for this article

Schema

  category:
      _attributes: { phpName: Category }
      id: { type: INTEGER, size: '11', primaryKey: true, autoIncrement: true, required: true }
      name: { type: VARCHAR, size: '255', required: true }
      created_at: { type: TIMESTAMP, required: false }
      updated_at: { type: TIMESTAMP, required: false }
  subcategory:
      _attributes: { phpName: Subcategory, isI18N: true, i18nTable: subcategory_i18n }
      id:          { type: integer, required: true, primaryKey: true, autoincrement: true }
      category_id: { type: INTEGER, size: '11', required: true, foreignTable: category, foreignReference: id, onDelete: CASCADE}
      created_at: { type: TIMESTAMP, required: false }
      updated_at: { type: TIMESTAMP, required: false }

  subcategory_i18n:
    _attributes: { phpName: SubcategoryI18n }
    id:          { type: integer, required: true, primaryKey: true, foreignTable: subcategory, foreignReference: id, onDelete: CASCADE }
    culture:     { isCulture: true, type: varchar, size: 7, required: true, primaryKey: true }
    name:        { type: varchar, size: 50 }

SubcategoryI18nForm

public function configure() {
    unset($this['culture'], $this['id']);
}

Subcategory

public function getI18nObject($culture = 'en') {

    $this->setCulture($culture);

    $i18ns = $this->getSubcategoryI18ns();

    if (isset($i18ns[0])) {

        return $i18ns[0];

    }

    return null;

}

CategoryForm

public function configure() {

// remove timestamps
unset($this['created_at'], $this['updated_at']);

	// embed forms only when editing
	if (!$this->isNew()) {

		$user_culture = sfContext::getInstance()->getUser()->getCulture();

		// embed all subcategory forms
		foreach ($this->getObject()->getSubcategorys() as $subcategory) {

			// get the subcategory_i18n model object relative to the current user culture
			$subcategory_i18n_object = $subcategory->getI18nObject($user_culture);

			// create a new subcategory_i18n form for the current subcategory model object
			$subcategory_i18n_form = new SubcategoryI18nForm($subcategory->getI18nObject('en'));

			// get widget schema of subcategory_i18n form
			$subcategory_i18n_form_widget_schema = $subcategory_i18n_form->getWidgetSchema();

			// set the input delete widget
			$subcategory_i18n_form_widget_schema['name'] = new sfWidgetFormInputDelete(array(
			'url' => 'category/deleteSubcategory',      // required
			'model_id' => $subcategory->getId(),        // required
			'confirm' => 'Sure???',                     // optional
			));

			// create a new subcategory form for the current subcategory model object
			$subcategory_form = new SubcategoryForm($subcategory);

			// embed the i18n form
			$subcategory_form->embedForm('subcategory_i18n'.$subcategory_i18n_object->getId(), $subcategory_i18n_form);

			// subcategory form widget schema
			$subcategory_form_widget_schema = $subcategory_form->getWidgetSchema();

			// disable label
			$subcategory_form_widget_schema['subcategory_i18n'.$subcategory_i18n_object->getId()]->setLabel(false);

			// embed the subcategory form in the main category form
			$this->embedForm('subcategory'.$subcategory->getId(), $subcategory_form);

			// set a custom label for the embedded form
			$this->widgetSchema['subcategory'.$subcategory->getId()]->setLabel('Subcategory: '.$subcategory->getName());

		}

		// create a new subcategory form for a new subcategory model object
		$subcategory_form = new SubcategoryForm();

		// create a new subcategory form for a new subcategory_i18n model object
		$subcategory_i18n_form = new SubcategoryI18nForm();

		// embed the subcategory_i18n form in the parent subcategory form
		$subcategory_form->embedForm('subcategory_i18n', $subcategory_i18n_form);

		// subcategory form widget schema
		$subcategory_form_widget_schema = $subcategory_form->getWidgetSchema();

		// disable label
		$subcategory_form_widget_schema['subcategory_i18n']->setLabel(false);

		// embed the subcategory form in the main category form
		$this->embedForm('subcategory', $subcategory_form);

		// set a custom label for the embedded form
		$this->widgetSchema['subcategory']->setLabel('New Subcategory');

	}
}
public function bind(array $taintedValues = null, array $taintedFiles = null) {

		if(!$this->isNew()) {

			$user_culture = sfContext::getInstance()->getUser()->getCulture();

			// remove the embedded new form if the name field was not provided
			if (is_null($taintedValues['subcategory']['subcategory_i18n']['name']) || strlen($taintedValues['subcategory']['subcategory_i18n']['name']) === 0 ) {

				unset($this->embeddedForms['subcategory'], $taintedValues['subcategory']);

				// pass the new form validations
				$this->validatorSchema['subcategory'] = new sfValidatorPass();

			} else {

				// set the category of the new subcategory form object
				$this->embeddedForms['subcategory']->getObject()->setCategory($this->getObject());

				// get subcategory embedded forms
				$subcategory_embedded_forms = $this->embeddedForms['subcategory']->getEmbeddedForms();

				// set subcategory parent of the subcategory_i18n model object
				$subcategory_embedded_forms['subcategory_i18n']->getObject()->setSubcategory($this->embeddedForms['subcategory']->getObject());

				// set the culture of the subcategory_i18n model object
				$subcategory_embedded_forms['subcategory_i18n']->getObject()->setCulture($user_culture);
			}

		}

		// call parent bind method
		parent::bind($taintedValues, $taintedFiles);

	}

As you can see this is a lot of code to copy and paste every time you need the functionality, that’s why I’m currently working on extending the Admin Generator in order to have this functionality available when you generate an admin module by default.

1 Comment