Author: francois
Date: 2010-05-14 17:14:34 +0200 (Fri, 14 May 2010)
New Revision: 29467
Added:
plugins/sfPropel15Plugin/trunk/lib/widget/sfWidgetFormDelete.class.php
Modified:
plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropel.class.php
plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropelCollection.class.php
Log:
[sfPropelPlugin] Added the ability to delete related objects in a collection
form
Modified: plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropel.class.php
===================================================================
--- plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropel.class.php
2010-05-14 13:16:18 UTC (rev 29466)
+++ plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropel.class.php
2010-05-14 15:14:34 UTC (rev 29467)
@@ -20,10 +20,25 @@
*/
abstract class sfFormPropel extends sfFormObject
{
+ /**
+ * List of fields that cannot be changed by the user, but still take a
default value
+ * @var array
+ */
protected $fixedValues = array();
+
+ /**
+ * List of forms that can be added by the user
+ * @var array[sfForm]
+ */
protected $optionalForms = array();
/**
+ * Name of the field used for deletion.
+ * @var string
+ */
+ protected $deleteField;
+
+ /**
* Constructor.
*
* @param mixed A object used to initialize default values
@@ -143,17 +158,114 @@
}
}
}
+
+ /**
+ * Adds a widget to the form, and declare this widget as the delete control.
+ * If the bound widget value is true, then the related object will be deleted
+ *
+ * @param string $name The field name
+ * @param sfWidgetForm $widget The widget
+ *
+ * @return sfPropelForm The current form instance
+ */
+ public function setDeleteWidget($name, $widget)
+ {
+ $this->setWidget($name, $widget);
+ $this->setValidator($name, new sfValidatorPass(array('required' =>
false)));
+ $this->setDeleteField($name);
+
+ return $this;
+ }
/**
+ * Updates and saves the current object.
* @see sfFormObject
+ *
+ * If you want to add some logic before saving or save other associated
+ * objects, this is the method to override.
+ *
+ * @param mixed $con An optional connection object
*/
+ protected function doSave($con = null)
+ {
+ if (null === $con)
+ {
+ $con = $this->getConnection();
+ }
+
+ $this->updateObject();
+
+ // this is Propel specific
+ if(!$this->getObject()->isDeleted())
+ {
+ $this->getObject()->save($con);
+ }
+
+ // embedded forms
+ $this->saveEmbeddedForms($con);
+ }
+
+ /**
+ * Updates the values of the object with the cleaned up values.
+ *
+ * If you want to add some logic before updating or update other associated
+ * objects, this is the method to override.
+ * @see sfFormObject
+ *
+ * @param array $values An array of values
+ */
protected function doUpdateObject($values)
{
+ if ($this->hasDeleteField())
+ {
+ if (isset($values[$this->getDeleteField()]) &&
$values[$this->getDeleteField()])
+ {
+ $this->getObject()->delete();
+ return;
+ }
+ }
$values = array_merge($values, $this->getFixedValues());
$this->getObject()->fromArray($values, BasePeer::TYPE_FIELDNAME);
}
/**
+ * Saves embedded form objects.
+ * @see sfFormObject
+ *
+ * @param mixed $con An optional connection object
+ * @param array $forms An array of forms
+ */
+ public function saveEmbeddedForms($con = null, $forms = null)
+ {
+ if (null === $con)
+ {
+ $con = $this->getConnection();
+ }
+
+ if (null === $forms)
+ {
+ $forms = $this->embeddedForms;
+ }
+
+ foreach ($forms as $form)
+ {
+ if ($form instanceof sfFormObject)
+ {
+ $form->saveEmbeddedForms($con);
+ // this is Propel specific
+ if(!$form->getObject()->isDeleted())
+ {
+ $form->getObject()->save($con);
+ }
+ }
+ else
+ {
+ $this->saveEmbeddedForms($con, $form->getEmbeddedForms());
+ }
+ }
+ }
+
+ /**
* Processes cleaned up values with user defined methods.
*
* To process a value before it is used by the updateObject() method,
@@ -322,6 +434,11 @@
}
}
+ /**
+ * Get the name of the Peer class of the form's model, e.g. 'AuthorPeer'
+ *
+ * @return string A Peer class name
+ */
public function getPeer()
{
return constant(get_class($this->getObject()).'::PEER');
@@ -371,7 +488,7 @@
/**
* Overrides sfForm::mergeForm() to also merge embedded forms
- * Allows autosave of marged collections
+ * Allows autosave of merged collections
*
* @param sfForm $form The sfForm instance to merge with current form
*
@@ -387,18 +504,27 @@
}
/**
- * Merge Relation form into this form
+ * Merge a Collection form based on a Relation into this form.
+ * Available options:
+ * - add_empty: Whether to allow the user to add new objects to the
collection. Defaults to true
+ * Additional options are passed to sfFromPropel::getRelationForm()
+ *
+ * @param string $relationName The name of a relation of the current Model,
e.g. 'Book'
+ * @param array $options An array of options
+ *
+ * @return sfPropelForm The current form instance
*/
public function mergeRelation($relationName, $options = array())
{
$options = array_merge(array(
- 'add_empty' => false,
+ 'add_empty' => true,
), $options);
$relationForm = $this->getRelationForm($relationName, $options);
if ($options['add_empty'])
{
+ unset($options['add_empty']);
$emptyForm = $this->getEmptyRelatedForm($relationName, $options);
$emptyName = 'new' . $relationName;
$relationForm->embedOptionalForm($emptyName, $emptyForm);
@@ -406,20 +532,36 @@
}
$this->mergeForm($relationForm);
+
+ return $this;
}
-
+
+ /**
+ * Embed a Collection form based on a Relation into this form.
+ * Available options:
+ * - title: The title of the colleciton form once embedded. Defaults to the
relation name.
+ * - decorator: The decorator for the sfWidgetFormSchemaDecorator
+ * - add_empty: Whether to allow the user to add new objects to the
collection. Defaults to true
+ * Additional options are passed to sfFromPropel::getRelationForm()
+ *
+ * @param string $relationName The name of a relation of the current Model,
e.g. 'Book'
+ * @param array $options An array of options
+ *
+ * @return sfPropelForm The current form instance
+ */
public function embedRelation($relationName, $options = array())
{
$options = array_merge(array(
'title' => $relationName,
'decorator' => null,
- 'add_empty' => false,
+ 'add_empty' => true,
), $options);
$relationForm = $this->getRelationForm($relationName, $options);
if ($options['add_empty'])
{
+ unset($options['add_empty']);
$emptyForm = $this->getEmptyRelatedForm($relationName, $options);
$emptyName = 'new' . $relationName;
$relationForm->embedOptionalForm($emptyName, $emptyForm);
@@ -427,21 +569,34 @@
}
$this->embedForm($options['title'], $relationForm, $options['decorator']);
+
+ return $this;
}
+ /**
+ * Get a Collection form based on a Relation of the current form's model.
+ * Available options:
+ * - hide_on_new: If true, returns null for new objects. Defaults to false.
+ * - collection_form_class: class of the collection form to return.
Defaults to sfFormPropelCollection.
+ * Additional options are passed to sfFormPropelCollection::__construct()
+ *
+ * @param string $relationName The name of a relation of the current Model,
e.g. 'Book'
+ * @param array $options An array of options
+ *
+ * @return sfFormPropelCollection A form collection instance
+ */
public function getRelationForm($relationName, $options = array())
{
$options = array_merge(array(
+ 'hide_on_new' => false,
'collection_form_class' => 'sfFormPropelCollection',
- 'embedded_form_class' => null,
- 'item_pattern' => '%index%',
- 'hide_on_new' => false,
), $options);
if ($this->getObject()->isNew() && $options['hide_on_new'])
{
return;
}
+ unset($options['hide_on_new']);
// compute relation elements
$relationMap = $this->getRelationMap($relationName);
@@ -458,20 +613,29 @@
// create the relation form
$collectionFormClass = $options['collection_form_class'];
- $collectionForm = new $collectionFormClass($collection, array(
- 'embedded_form_class' => $options['embedded_form_class'],
- 'item_pattern' => $options['item_pattern'],
- 'remove_fields' => $relationFields,
- ));
+ unset($options['collection_form_class']);
+ $collectionForm = new $collectionFormClass($collection, $options);
+
return $collectionForm;
}
+ /**
+ * Get an empty Propel form based on a Relation of the current form's model.
+ * Available options:
+ * - embedded_form_class: The class of the form to return
+ * - empty_label: The label of the empty form
+ *
+ * @param string $relationName The name of a relation of the current Model,
e.g. 'Book'
+ * @param array $options An array of options
+ *
+ * @return sfFormPropel A Propel form instance
+ */
public function getEmptyRelatedForm($relationName, $options = array())
{
$options = array_merge(array(
- 'embedded_form_class' => null,
- 'empty_label' => null,
+ 'embedded_form_class' => null,
+ 'empty_label' => null,
), $options);
// compute relation elements
@@ -530,7 +694,22 @@
{
return $this->fixedValues;
}
+
+ public function setDeleteField($fieldName)
+ {
+ $this->deleteField = $fieldName;
+ }
+ public function hasDeleteField()
+ {
+ return null !== $this->deleteField;
+ }
+
+ public function getDeleteField()
+ {
+ return $this->deleteField;
+ }
+
public function __clone()
{
$this->object = clone $this->object;
Modified:
plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropelCollection.class.php
===================================================================
--- plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropelCollection.class.php
2010-05-14 13:16:18 UTC (rev 29466)
+++ plugins/sfPropel15Plugin/trunk/lib/form/sfFormPropelCollection.class.php
2010-05-14 15:14:34 UTC (rev 29467)
@@ -1,18 +1,49 @@
<?php
-
+/**
+ * sfFormPropelCollection represents a form based on a collection of Propel
objects.
+ *
+ * @package symfony
+ * @subpackage form
+ * @author Francois Zaninotto
+ */
class sfFormPropelCollection extends sfForm
{
protected $model;
protected $collection;
protected $isEmpty = false;
+ /**
+ * Form constructor.
+ *
+ * Available options:
+ * - item_pattern: The pattern used to name each embedded form. Defaults to
'%index%'.
+ * - add_delete: Whether to add a delete widget for each object. Defaults
to true.
+ * - delete_name: Name of the delete widget. Defaults to 'delete'.
+ * - delete_widget: Optional delete widget object. If left null, uses a
sfWidgetFormDelete instance.
+ * - alert_text: The text of the Javascript alert to show
+ * - hide_parent: Whether to hide the parent form when clicking the
checkbox
+ * - parent_level: The number of times parentNode must be called to reach
the parent to hide.
+ * Recommended values: 6 for embedded form, 7 for merged
form
+ * - remove_fields: The list of fields to remove from the embedded object
forms
+ *
+ * @param PropelCollection $collection A collection of Propel objects
+ * used to initialize default values
+ * @param array $options An array of options
+ * @param string $CSRFSecret A CSRF secret (false to disable CSRF
protection, null to use the global CSRF secret)
+ *
+ * @see sfForm
+ */
public function __construct($collection = null, $options = array(),
$CSRFSecret = null)
{
$options = array_merge(array(
- 'item_pattern' => '%index%',
+ 'item_pattern' => '%index%',
+ 'add_delete' => true,
+ 'delete_name' => 'delete',
+ 'delete_widget' => null,
'remove_fields' => array(),
), $options);
+
if (!$collection)
{
$this->model = $options['model'];
@@ -35,6 +66,9 @@
parent::__construct(array(), $options, $CSRFSecret);
}
+ /**
+ * Configures the current form.
+ */
public function configure()
{
$formClass = $this->getFormClass();
@@ -46,27 +80,70 @@
{
unset($form[$field]);
}
+ if ($this->getOption('add_delete'))
+ {
+ if (!($deleteWidget = $this->options['delete_widget']))
+ {
+ $options = array();
+ if ($alertText = $this->getOption('alert_text', false))
+ {
+ $options['alert_text'] = $alertText;
+ }
+ if ($hideParent = $this->getOption('hide_parent', false))
+ {
+ $options['hide_parent'] = $hideParent;
+ }
+ if ($parentLevel = $this->getOption('parent_level', false))
+ {
+ $options['parent_level'] = $parentLevel;
+ }
+ $deleteWidget = new sfWidgetFormDelete($options);
+ }
+ $form->setDeleteWidget($this->getOption('delete_name'), $deleteWidget);
+ }
$name = strtr($this->getOption('item_pattern'), array('%index%' => $i,
'%model%' => $this->getModel()));
$this->embedForm($name, $form);
$i++;
}
}
+ /**
+ * Getter for the internal collection object
+ *
+ * @return PropelCollection
+ */
public function getCollection()
{
return $this->collection;
}
+ /**
+ * Getter for the name of the Propel model used by the collection
+ *
+ * @return string
+ */
public function getModel()
{
return $this->model;
}
+ /**
+ * Check whether the embedded colleciton is empty
+ *
+ * @return boolean
+ */
public function isEmpty()
{
return $this->isEmpty;
}
+ /**
+ * Getter for the embedded form class.
+ * Uses the embedded_form_class option if available,
+ * or falls back to the default form for the model.
+ *
+ * @return string
+ */
public function getFormClass()
{
if (!$class = $this->getOption('embedded_form_class', false))
Added: plugins/sfPropel15Plugin/trunk/lib/widget/sfWidgetFormDelete.class.php
===================================================================
--- plugins/sfPropel15Plugin/trunk/lib/widget/sfWidgetFormDelete.class.php
(rev 0)
+++ plugins/sfPropel15Plugin/trunk/lib/widget/sfWidgetFormDelete.class.php
2010-05-14 15:14:34 UTC (rev 29467)
@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * This file is part of the symfony package.
+ * (c) Fabien Potencier <[email protected]>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+/**
+ * sfWidgetFormDelete represents a delete widget for an embedded object form
+ *
+ * @package symfony
+ * @subpackage widget
+ * @author Francois Zaninotto
+ */
+class sfWidgetFormDelete extends sfWidgetFormInputCheckbox
+{
+ /**
+ * Constructor.
+ *
+ * Available options:
+ *
+ * - alert_text: The text of the Javascript alert to show
+ * - hide_parent: Whether to hide the parent form when clicking the checkbox
+ * - parent_level: The number of times parentNode must be called to reach
the parent to hide.
+ * Recommended values: 6 for embedded form, 7 for merged
form
+ *
+ * @param array $options An array of options
+ * @param array $attributes An array of default HTML attributes
+ *
+ * @see sfWidgetFormInput
+ */
+ public function __construct($options = array(), $attributes = array())
+ {
+ parent::__construct($options, $attributes);
+
+ if ($this->getOption('hide_parent'))
+ {
+ $hideParentCode = 'this' . str_repeat('.parentNode',
$this->getOption('parent_level')) . '.style.display="none";';
+ }
+ else
+ {
+ $hideParentCode = '';
+ }
+ if ($this->getOption('alert_text'))
+ {
+ $this->setAttribute('onclick', sprintf('if(confirm("%s")) { %s } else
return false;', $this->translate($this->getOption('alert_text')),
$hideParentCode));
+ }
+ else
+ {
+ $this->setAttribute('onclick', $hideParentCode));
+ }
+ }
+
+ protected function configure($options = array(), $attributes = array())
+ {
+ parent::configure($options, $attributes);
+
+ $this->addOption('alert_text', 'Are you sure you want to delete this
item?\nThe deletion will be complete once the form is saved.');
+ $this->addOption('hide_parent', true);
+ $this->addOption('parent_level', 6);
+ }
+}
\ No newline at end of file
Property changes on:
plugins/sfPropel15Plugin/trunk/lib/widget/sfWidgetFormDelete.class.php
___________________________________________________________________
Added: svn:executable
+ *
Added: svn:keywords
+ "Id Rev Revision Author"
Added: svn:eol-style
+ native
--
You received this message because you are subscribed to the Google Groups
"symfony SVN" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/symfony-svn?hl=en.