Created
July 15, 2011 16:14
-
-
Save Ocramius/1084994 to your computer and use it in GitHub Desktop.
A sample integration of Doctrine 2 and Zend_Form
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Deneb\Form; | |
/** | |
* Manages scalar fields of an entity | |
*/ | |
class Entity extends ListDecorated { | |
const DEFAULT_DISPLAY_GROUP = 'default'; | |
/** | |
* @var \Deneb\Entity | |
*/ | |
protected $_entity; | |
/** | |
* @var \Doctrine\ORM\EntityManager | |
*/ | |
protected $_em; | |
/** | |
* @var \Doctrine\Common\Annotations\Reader | |
*/ | |
protected $_annotationReader; | |
/** | |
* @var \Zend_Acl | |
*/ | |
protected $_acl; | |
/** | |
* @var \Zend_Acl_Role_Interface | |
*/ | |
protected $_role; | |
/** | |
* Weather to display/manage associations or not | |
* @var bool | |
*/ | |
protected $_manageAssociations; | |
/** | |
* | |
* @var \Deneb\EntityRepository\Lock | |
*/ | |
protected $_locksRepository; | |
public function __construct( | |
\Deneb\Entity $entity, | |
\Doctrine\ORM\EntityManager $em, | |
\Deneb\Annotation\Reader $annotationReader, | |
\Zend_Acl $acl, | |
\Zend_Acl_Role_Interface $role, | |
$manageAssociations = false | |
) { | |
$this->_entity = $entity; | |
$this->_em = $em; | |
$this->_annotationReader = $annotationReader; | |
$this->_acl = $acl; | |
$this->_role = $role; | |
$this->_manageAssociations = $manageAssociations; | |
$this->_locksRepository = $em->getRepository('Deneb\\Entity\\Lock'); | |
$metadata = $this | |
->_em | |
->getMetadataFactory() | |
->getMetadataFor(\get_class($this->_entity)); | |
//$fieldsWrapper = new \Zend_Form_SubForm(); | |
//$fieldsWrapper->setLegend($this->_entity); | |
//$this->setLegend($this->_entity); | |
$displayGroupsElements = array(); | |
$lockable = $this->_locksRepository->isLockable($this->_entity); | |
foreach($metadata->getColumnNames() as $columnName) { | |
$fieldName = $metadata->getFieldName($columnName); | |
/** Allowed ACL privileges are "read" and "write" */ | |
$resource = $metadata->name . ':property:' . $fieldName; | |
$read = $this->_acl->isAllowed($this->_role, $resource, 'read'); | |
$write = $this->_acl->isAllowed($this->_role, $resource, 'write'); | |
if($read || $write) { | |
$typeOfColumn = $metadata->getTypeOfColumn($columnName); | |
switch($typeOfColumn) { | |
case \Doctrine\DBAL\Types\Type::BIGINT: | |
case \Doctrine\DBAL\Types\Type::INTEGER: | |
case \Doctrine\DBAL\Types\Type::SMALLINT: | |
$element = new \Deneb\Form\Element\Int($fieldName); | |
break; | |
case \Doctrine\DBAL\Types\Type::BOOLEAN: | |
$element = new \Zend_Form_Element_Checkbox($fieldName); | |
$element->addFilter(new \Zend_Filter_Boolean()); | |
break; | |
case \Doctrine\DBAL\Types\Type::DATE: | |
$element = new \Deneb\Form\Element\Date( | |
$fieldName, | |
array('format' => \Zend_Date::DATE_LONG) | |
); | |
break; | |
case \Doctrine\DBAL\Types\Type::DATETIME: | |
case \Doctrine\DBAL\Types\Type::DATETIMETZ: | |
$element = new \Deneb\Form\Element\Date( | |
$fieldName, | |
array('format' => \Zend_Date::DATETIME_FULL) | |
); | |
break; | |
case \Doctrine\DBAL\Types\Type::DECIMAL: | |
$details = $metadata->getFieldMapping($fieldName); | |
$element = new \Deneb\Form\Element\Decimal( | |
$fieldName/*, | |
array( | |
'precision' => $details['precision'], | |
'scale' => $details['scale'] | |
)*/ | |
); | |
break; | |
case \Doctrine\DBAL\Types\Type::OBJECT: | |
//Just do not display object data type in forms | |
continue 2; | |
case \Doctrine\DBAL\Types\Type::TARRAY: | |
throw new \Zend_Form_Exception('Unsupported data type \'' . $metadata->getTypeOfColumn($columnName) . '\''); | |
break; | |
case \Doctrine\DBAL\Types\Type::STRING: | |
$element = new \Zend_Form_Element_Text($fieldName); | |
$fieldMapping = $metadata->getFieldMapping($fieldName); | |
$element->addValidator(new \Zend_Validate_StringLength(array('max' => $fieldMapping['length']))); | |
break; | |
case \Doctrine\DBAL\Types\Type::TEXT: | |
$element = new \Zend_Form_Element_Textarea($fieldName); | |
break; | |
case \Doctrine\DBAL\Types\Type::TIME: | |
$element = new \Deneb\Form\Element\Date( | |
$fieldName, | |
array('format' => \Zend_Date::TIME_FULL) | |
); | |
break; | |
default: | |
$type = \Doctrine\DBAL\Types\Type::getType($typeOfColumn); | |
if($type instanceof \Deneb\Mapping\Type\Enum) { | |
$element = new \Deneb\Form\Element\Enum( | |
$fieldName, | |
array('enumType' => $type) | |
); | |
}else if($type instanceof \Deneb\Mapping\Type\Document\File) { | |
$element = new \Deneb\Form\Element\File($fieldName); | |
}else if($type instanceof \Deneb\Mapping\Type\RichText) { | |
$element = new \Deneb\Form\Element\RichText($fieldName); | |
}else{ | |
throw new \Zend_Form_Exception('Unsupported data type \'' . $typeOfColumn . '\''); | |
} | |
break; | |
} | |
$fieldMapping = $metadata->getFieldMapping($fieldName); | |
if($lockable) { | |
$locked = !$this | |
->_locksRepository | |
->isAllowed( | |
$this->_role, | |
$this->_entity, | |
$fieldName, | |
'lock' | |
); | |
if($locked) { | |
$element->setAttrib('disabled', 'disabled'); | |
$element->setIgnore(true); | |
} | |
$ownlock = $this | |
->_locksRepository | |
->findLock($this->_role, $this->_entity, $fieldName, 'lock'); | |
if($ownlock !== null) { | |
/** @todo this should be encapsulated within the form element class. Will require future refactoring */ | |
$class = $element->getAttrib('class'); | |
$element->setAttrib('class', ($class ? $class . ' ' : '') . 'locked'); | |
} | |
if($this->_acl->isAllowed($this->_role, $resource, 'lock')) { | |
$element->setAttrib( | |
'data-lock-action', | |
\Zend_Controller_Action_HelperBroker::getStaticHelper('url') | |
->url(array( | |
'controller' => 'lock', | |
'action' => 'toggle', | |
'entity' => $metadata->name, | |
'identifier' => \reset($metadata->getIdentifierValues($this->_entity)), | |
'field' => $fieldName, | |
'privilege' => 'lock' | |
)) | |
); | |
} | |
} | |
if(!($element instanceof \Zend_Form_Element_Checkbox)) { | |
if($write) { | |
if( | |
( | |
!$read | |
&& ($this->_entity->$fieldName === null) | |
) || !$fieldMapping['nullable'] | |
) { | |
$element->setRequired(); | |
} | |
if($fieldMapping['unique']) { | |
$element->setRequired(); | |
$element->addValidator(new \Deneb\Validate\Entity\Field\Unique($this->_em, $this->_entity, $fieldName)); | |
} | |
}else{ | |
$element->setAttrib('disabled', 'disabled'); | |
$element->setIgnore(true); | |
} | |
} | |
if(!$element->isRequired()) { | |
if($element instanceof \Deneb\Form\Element\Enum) { | |
$element->setMultiOptions(array(-1 => '') + $element->getMultiOptions()); | |
} | |
}else{ | |
/** @todo this should be encapsulated within the form element class. Will require future refactoring */ | |
$class = $element->getAttrib('class'); | |
$element->setAttrib('class', ($class ? $class . ' ' : '') . 'required'); | |
} | |
if($read) { | |
$element->setValue($this->_entity->$fieldName); | |
} | |
$reflectionProperty = $metadata->getReflectionProperty($fieldName); | |
if($filter = $this->_annotationReader->getPropertyAnnotation($reflectionProperty, 'Deneb\\Annotation\\Filter')) { | |
$element->addFilter($filter); | |
} | |
if($validator = $this->_annotationReader->getPropertyAnnotation($reflectionProperty, 'Deneb\\Annotation\\Validate')) { | |
$element->addValidator($validator); | |
} | |
$element->setLabel($resource); | |
if($displayGroupAnnotation = $this->_annotationReader->getPropertyAnnotation($reflectionProperty, 'Deneb\\Annotation\\DisplayGroup')) { | |
$displayGroupName = (string) $displayGroupAnnotation; | |
}else{ | |
$displayGroupName = self::DEFAULT_DISPLAY_GROUP; | |
} | |
if(!isset($displayGroupsElements[$displayGroupName])) { | |
$displayGroupsElements[$displayGroupName] = array(); | |
} | |
if($displayPriority = $this->_annotationReader->getPropertyAnnotation($reflectionProperty, 'Deneb\\Annotation\\DisplayPriority')) { | |
$element->setOrder($displayPriority->value); | |
} | |
$displayGroupsElements[$displayGroupName][] = $element; | |
} | |
} | |
foreach($displayGroupsElements as $name => $elements) { | |
$this->addDisplayGroup( | |
$elements, | |
$name, | |
array( | |
//'legend' => $metadata->name . ':displaygroup:' . $name | |
) | |
); | |
} | |
//$this->addSubForm($fieldsWrapper, 'fields'); | |
parent::__construct(); | |
} | |
/** | |
* Assign data to the entity. Doesn't cause \Doctrine\ORM\EntityManager#flush() | |
* | |
* @warningThis works only for valid fields! | |
* | |
* @return \Deneb\Entity the persisted entity | |
*/ | |
public function persistData() { | |
$fieldsWrapper = $this; | |
$metadata = $this->_em->getMetadataFactory()->getMetadataFor(get_class($this->_entity)); | |
foreach($metadata->getColumnNames() as $columnName) { | |
$fieldName = $metadata->getFieldName($columnName); | |
$resource = $metadata->name . ':property:' . $fieldName; | |
$read = $this->_acl->isAllowed($this->_role, $resource, 'read'); | |
$write = $this->_acl->isAllowed($this->_role, $resource, 'write'); | |
$element = $fieldsWrapper->getElement($fieldName); | |
if($write && $element && $element->isValid($this->getValue($fieldName))) { | |
if($element instanceof \Deneb\Form\Element\Date) { | |
$value = $element->getDateTime(); | |
}else if($element instanceof \Deneb\Form\Element\Decimal) { | |
$value = $element->getDecimal(); | |
}else if($element instanceof \Deneb\Form\Element\Int) { | |
$value = $element->getInt(); | |
}else if($element instanceof \Deneb\Form\Element\Enum) { | |
$value = $element->getEnum(); | |
}else{ | |
$value = $element->getValue(); | |
} | |
//Not allowing writeable only fields to be replaced by empty values | |
//(i.e.: operator does not set password, which means overwriting password with null!) | |
if($read || ($value !== null)) { | |
$this->_entity->$fieldName = $value; | |
} | |
} | |
} | |
foreach($metadata->getColumnNames() as $columnName) { | |
$fieldName = $metadata->getFieldName($columnName); | |
$resource = $metadata->name . ':property:' . $fieldName; | |
$read = $this->_acl->isAllowed($this->_role, $resource, 'read'); | |
$element = $fieldsWrapper->getElement($fieldName); | |
if($read && $element) { | |
$element->setValue($this->_entity->$fieldName); | |
} | |
} | |
return $this->_entity; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment