Last active
December 27, 2023 19:32
-
-
Save codeliner/b02871398c2ac44009f6 to your computer and use it in GitHub Desktop.
DoctrineModule\Form\ObjectSelect fix to define custom value property
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 | |
$element = array( | |
'name' => 'task', | |
'type' => 'Application\Form\Element\ObjectSelect', //Point to extended ObjectSelect which provides custom proxy impl. | |
'options' => array( | |
'label' => 'task', | |
'object_manager' => $this->getObjectManager(), | |
'target_class' => 'Application\Entity\Task', | |
'property' => 'name', | |
'find_method' => 'findBy', | |
'value' => 'getForeignEntityId', //Define entity property or method to populate options values | |
'value_is_method' => true, //Flag to indicate whether "value" should be treated as method or property | |
), | |
'attributes' => array( | |
'multiple' => false, | |
'required' => false, | |
'type' => 'select', | |
) | |
); |
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 Application\Form\Element; | |
use Application\Form\Element\ObjectSelect\Proxy; | |
use DoctrineModule\Form\Element\ObjectSelect as DoctrineObjectSelect; | |
/** | |
* Class ObjectSelect | |
* | |
* Extends doctrines ObjectSelect to provide own Proxy implementation | |
* | |
* @package Application\Form\Element | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class ObjectSelect extends DoctrineObjectSelect | |
{ | |
/** | |
* Provide own Proxy implementation to fix bug that only identifier can be loaded as option value | |
*/ | |
public function init() | |
{ | |
$this->proxy = new Proxy(); | |
} | |
} |
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 Application\Form\Element\ObjectSelect; | |
use DoctrineModule\Form\Element\Proxy as DoctrineProxy; | |
use RuntimeException; | |
/** | |
* Class Proxy | |
* | |
* Extends doctrines Form\Element\Proxy to fix bug that only identifier can be loaded as option value | |
* | |
* @package Application\Form\Element\ObjectSelect | |
* @author Alexander Miertsch <[email protected]> | |
*/ | |
class Proxy extends DoctrineProxy | |
{ | |
protected $value = false; | |
protected $valueIsMethod = false; | |
public function setOptions($options) | |
{ | |
if (isset($options['value'])) { | |
$this->value = $options['value']; | |
unset($options['value']); | |
} | |
if (isset($options['value_is_method'])) { | |
$this->valueIsMethod = $options['value_is_method']; | |
unset($options['value_is_method']); | |
} | |
parent::setOptions($options); | |
} | |
/** | |
* Load value options | |
* | |
* @throws RuntimeException | |
* @return void | |
*/ | |
protected function loadValueOptions() | |
{ | |
if (!($om = $this->objectManager)) { | |
throw new RuntimeException('No object manager was set'); | |
} | |
if (!($targetClass = $this->targetClass)) { | |
throw new RuntimeException('No target class was set'); | |
} | |
$metadata = $om->getClassMetadata($targetClass); | |
$identifier = $metadata->getIdentifierFieldNames(); | |
$objects = $this->getObjects(); | |
$options = array(); | |
if ($this->displayEmptyItem || empty($objects)) { | |
$options[''] = $this->getEmptyItemLabel(); | |
} | |
if (!empty($objects)) { | |
foreach ($objects as $key => $object) { | |
if (null !== ($generatedLabel = $this->generateLabel($object))) { | |
$label = $generatedLabel; | |
} elseif ($property = $this->property) { | |
if ($this->isMethod == false && !$metadata->hasField($property)) { | |
throw new RuntimeException( | |
sprintf( | |
'Property "%s" could not be found in object "%s"', | |
$property, | |
$targetClass | |
) | |
); | |
} | |
$getter = 'get' . ucfirst($property); | |
if (!is_callable(array($object, $getter))) { | |
throw new RuntimeException( | |
sprintf('Method "%s::%s" is not callable', $this->targetClass, $getter) | |
); | |
} | |
$label = $object->{$getter}(); | |
} else { | |
if (!is_callable(array($object, '__toString'))) { | |
throw new RuntimeException( | |
sprintf( | |
'%s must have a "__toString()" method defined if you have not set a property' | |
. ' or method to use.', | |
$targetClass | |
) | |
); | |
} | |
$label = (string) $object; | |
} | |
if ($valueProperty = $this->value) { | |
if ($this->valueIsMethod == false && !$metadata->hasField($valueProperty)) { | |
throw new RuntimeException( | |
sprintf( | |
'Property "%s" could not be found in object "%s"', | |
$valueProperty, | |
$targetClass | |
) | |
); | |
} | |
$getter = ($this->valueIsMethod)? $valueProperty : 'get' . ucfirst($valueProperty); | |
if (!is_callable(array($object, $getter))) { | |
throw new RuntimeException( | |
sprintf('Method "%s::%s" is not callable', $this->targetClass, $getter) | |
); | |
} | |
$value = $object->{$getter}(); | |
} else { | |
if (count($identifier) > 1) { | |
$value = $key; | |
} else { | |
$value = current($metadata->getIdentifierValues($object)); | |
} | |
} | |
$options[] = array('label' => $label, 'value' => $value); | |
} | |
} | |
$this->valueOptions = $options; | |
} | |
} |
Excellent!
In doctrine 3.0 change:
public function setOptions($options) to public function setOptions(array $options): void
and
protected function loadValueOptions() to protected function loadValueOptions(): void
Code is a bit out dated now, but still very helpful! Thank you.
I'm surprised doctrine still doesn't have this functionality at the end of 2023.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you - very useful and saved me the trouble of writing this.