1

我目前正在从事一个涉及 Zend Framework 2 和 Doctrine 的项目。

我正在实现 Zend\Form 并使用 DoctrineORMModule\Stdlib\Hydrator\DoctrineEntity 从/向数据库提取和水合数据。

在我读过的大部分教程中,当您需要在水合对象之前转换特定值(在我的示例中是日期时间值字符串,当使用教义)。然而,尽管我尽最大努力实现这一点,但似乎只调用了我的水合策略中的 extract() 方法,而不是 hydrate() 方法。曾经!

为了提供一个代码示例,这就是我正在做的 - 为了简洁起见,我缩短了代码的某些方面;

// 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;
}
}
4

1 回答 1

2

我也发现了这种行为。到目前为止,我仍然不知道为什么没有调用我的自定义策略。

策略应用在hydrateValue()extractValue()函数中,因此有必要通过hydrate()extract()函数调用这些函数,以便应用策略或自定义策略。

我的问题在 DoctrineModule\Stdlib\Hydrator\DoctrineObject中的hydraByReference()函数中很明显。

似乎$this->hydraValue(...)仅称为该字段具有“关联”。我还不知道这些“关联”是什么,或者我做错了什么让它们不存在于我的数据中。

当我将它与extractByReference()函数进行比较时,我注意到它总是调用$this->extractValue()并且不需要任何“关联”。

在我的应用程序中,我已经实现了一个自定义Hydrator类。这意味着当我创建表单时,会自动应用 Hydrator 和 Strategies。

  • 在 Form 的init()函数中,我分配了自定义 Hydrator。
  • 在我的自定义 Hydrator 的__construct()中,我添加了策略和自定义策略。


所以我需要做的就是在我的自定义 Hydrator 中覆盖hydrateByReference(...)来解决问题。示例如下。

笔记:

  1. 如果您使用“按值”水合,您可能还需要覆盖hydrateByValue(...) 。
  2. 我的解决方案可能会破坏“关联”的功能。


我的自定义补水器课程:

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;
    }

}
于 2013-11-01T00:41:47.663 回答