我目前正在开展涉及Zend Framework 2和Doctrine的项目。
我正在实现一个Zend \ Form并使用DoctrineORMModule \ Stdlib \ Hydrator \ DoctrineEntity从数据库中提取和水合数据。
在我读过的大部分教程中,当你需要在保湿对象之前转换特定值时,理想的是实现Zend \ Stdlib \ Hydrator \ Strategy \ StrategyInterface(在我的例子中是一个日期时间值字符串,这是使用Doctrine时的一个问题)。然而,尽管我尽最大努力实现这一点,但似乎只调用了我的水合策略中的extract()方法,而不是水合()方法。 EVER!
为了提供一个代码示例,这就是我正在做的事情 - 为了简洁,我缩短了代码的某些方面;
// Service
public function getProposerForm() {
// get required classes from service manager
$proposerEntity = $this->getServiceManager()->get('tourers_entity_proposer');
$entityManager = $this->getServiceManager()->get('Doctrine\ORM\EntityManager');
$formManager = $this->getServiceManager()->get('FormElementManager');
$proposerFieldset = $formManager->get('tourers_form_proposer_fieldset');
$proposerForm = $formManager->get('tourers_form_proposal');
$proposerFieldset->setUseAsBaseFieldset(true);
$proposerForm->add($proposerFieldset);
$proposerForm->get('submit')->setValue('Continue');
$proposerForm->bind($proposerEntity);
return $proposerForm;
}
// Controller
public function proposerAction() {
// grab the form from the form service
$formService = $this->getServiceLocator()->get('tourers_service_forms');
$form = $formService->getProposerForm();
if (true === $this->getRequest()->isPost()) {
$form->setData($this->getRequest()->getPost());
if (true === $form->isValid()) {
$proposerEntity = $form->getData();
$encryptedPolicyId = $formService->saveProposerForm($proposerEntity, $policyId);
return $this->redirect()->toRoute('tourers/proposal/caravan',array('policyid' => $encryptedPolicyId));
} else {
$errors = $form->getMessages();
var_dump($errors);
}
}
// view
return new ViewModel(array(
'form' => $form
,'policyid' => $policyId
)
);
}
// Form
class ProposerFieldset extends Fieldset implements InputFilterProviderInterface, ObjectManagerAwareInterface
{
/**
* @var Doctrine\ORM\EntityManager
*/
private $objectManager;
/**
* @return Zend\Form\Fieldset
*/
public function init()
{
// set name
parent::__construct('Proposer');
// set the hydrator to the domain object
$hydrator = new DoctrineEntity($this->objectManager,true);
$hydrator->addStrategy('proposerDateOfBirth',new DateStrategy);
$this->setHydrator($hydrator);
// other form elements below here including proposerDateOfBirth
$minDate = date('dd\/mm\/yyyy',strtotime('-100 years'));
$maxDate = date('dd\/mm\/yyyy',strtotime('-16 years'));
$this->add(array(
'name' => 'proposerDateOfBirth'
,'type' => 'Zend\Form\Element\Date'
,'attributes' => array(
'class' => 'form-control'
,'id' => 'proposerDateOfBirth'
,'placeholder' => 'dd/mm/yyyy'
,'min' => $minDate
,'max' => $maxDate
,'data-date-format' => 'dd/mm/yyyy'
),
'options' => array(
'label' => 'Date of Birth',
)
));
}
}
// Hydrator Strategy
namespace Tourers\Hydrator\Strategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;
class DateStrategy implements StrategyInterface {
/**
* (non-PHPdoc)
* @see Zend\Stdlib\Hydrator\Strategy.StrategyInterface::extract()
*/
public function extract($value) {
var_dump($value . ' extracted'); // GETS CALLED
return $value;
}
/**
* (non-PHPdoc)
* @see Zend\Stdlib\Hydrator\Strategy.StrategyInterface::hydrate()
*/
public function hydrate($value) {
var_dump($value . ' hydrated'); // NEVER CALLED
return $value;
}
}
答案 0 :(得分:2)
我也发现了这种行为。至于为什么我的自定义策略没有被调用,我仍然感到很遗憾。
策略应用于 hydrateValue()和 extractValue()函数中,因此 hydrate()<需要调用这些函数/ strong>和 extract()函数,以便应用策略或自定义策略。
我的问题在DoctrineModule \ Stdlib \ Hydrator \ DoctrineObject中的 hydrateByReference()函数中很明显。
似乎 $ this-&gt; hydrateValue(...)仅被称为字段“ associations ”。我还不知道这些“ association ”是什么,或者我错误地认为它们不存在于我的数据中。
当我将它与 extractByReference()函数进行比较时,我注意到它总是调用 $ this-&gt; extractValue(),并且不需要任何“协会”。
在我的应用程序中,我已经实现了一个自定义的 Hydrator 类。这意味着当我创建表单时,会自动应用Hydrator和Strategies。
所以我需要做的就是在我的Custom Hydrator中覆盖 hydrateByReference(...)来解决问题。示例如下。
注意:
My Custom Hydrator课程:
class maintenance
extends DoctrineHydrator
{
/**
* Constructor
*
* @param ObjectManager $objectManager The ObjectManager to use
*/
public function __construct($objectManager)
{
/*
* Just call the parent class.
*/
parent::__construct($objectManager,
'Maintenance\Entity\maintenance', // The FQCN of the hydrated/extracted object
false // If set to true, hydrator will always use entity's public API
);
/*
* Now set up our strategies, and attach them
* to the appropriate fields.
*/
$this->addStrategy('purchasedate', new TIGDateStrategy());
$this->addStrategy('maintexpirydate', new TIGDateStrategy());
}
/**
* SF Modification, to ensure we call this->hydrateValue on
* all values before doing anything else with the data.
* This way, we convert the data first, before trying to
* store it in a DoctrineEntity.
*
* Hydrate the object using a by-reference logic (this means that values are modified directly without
* using the public API, in this case setters, and hence override any logic that could be done in those
* setters)
*
* @param array $data
* @param object $object
* @return object
*/
protected function hydrateByReference(array $data, $object)
{
$object = $this->tryConvertArrayToObject($data, $object);
$metadata = $this->metadata;
$refl = $metadata->getReflectionClass();
foreach ($data as $field => $value) {
// Ignore unknown fields
if (!$refl->hasProperty($field)) {
continue;
}
// SF Mod
$value = $this->hydrateValue($field, $value);
// End SF Mod
$value = $this->handleTypeConversions($value, $metadata->getTypeOfField($field));
$reflProperty = $refl->getProperty($field);
$reflProperty->setAccessible(true);
if ($metadata->hasAssociation($field)) {
$target = $metadata->getAssociationTargetClass($field);
if ($metadata->isSingleValuedAssociation($field)) {
$value = $this->toOne($target, $this->hydrateValue($field, $value));
$reflProperty->setValue($object, $value);
} elseif ($metadata->isCollectionValuedAssociation($field)) {
$this->toMany($object, $field, $target, $value);
}
} else {
$reflProperty->setValue($object, $value);
}
}
return $object;
}
}