1

getInputFilterSpecification()与 fileprg 控制器插件一起使用时,为字段集读取两次。

基本上我有一个带有单个字段集的简单表单:

表格:

class CreateEditReward extends ProvideEventsForm
{
    public function __construct($name = null, $options = array())
    {
        parent::__construct($name ?: 'Reward', $options);

        $this->add(array(
            'name' => 'submit',
            'type' => 'submit'
        ));

        $this->add(array(
            'name' => 'cancel',
            'type' => 'button'
        ));
    }

    public function init()
    {
        parent::init();

        $this->add(array(
            'name' => 'reward',
            'type' => 'MyAchievement\Form\RewardFieldset',
            'options' => array(
                'use_as_base_fieldset' => true
            )
        ));

        if (($listener = $this->get('reward')) instanceof ListenerAggregateInterface) {
            /** @var $listener ListenerAggregateInterface */
            $this->getEventManager()->attachAggregate($listener);
        }
    }
}

该表单扩展了最基本的 EventsCapable 实现,与问题无关。

字段集:

class RewardFieldset extends Fieldset implements
    InputFilterProviderInterface,
    ListenerAggregateInterface
{
    /**
     * @var RewardService
     */
    protected $rewardService;

    /**
     * @var WebPathResolver
     */
    protected $webPathResolver;

    protected $listeners = array();

    public function __construct(RewardService $rewardService, WebPathResolver $webPathResolver, $name = null, $options = array())
    {
        parent::__construct($name ?: 'Reward', $options);

        $this->rewardService = $rewardService;
        $this->webPathResolver = $webPathResolver;

        //...
        $this->add(array(
            'name' => 'thumb',
            'type' => 'file',
            'options' => array(
                'label' => 'Thumb'
            ),
            'attributes' => array(
                'id' => 'thumb',
                'data-rel' => null
            )
        ));
        // .. more elts
    }

    /**
     * Should return an array specification compatible with
     * {@link Zend\InputFilter\Factory::createInputFilter()}.
     *
     * @return array
     */
    public function getInputFilterSpecification()
    {
        return array(
            // ... other elts input filters
            'thumb' => array(
                'required' => false,
               'filters' => array(
                   array(
                       'name' => 'fileRenameUpload',
                       'options' => array(
                           'target' => './data/upload',
                           'overwrite' => true,
                           'randomize' => true,
                           'use_upload_name' => true
                       )
                   )
               ),
                'validators' => array(
                    array(
                        'name' => 'fileMimeType',
                        'options' => array(
                            'image',
                            'enableHeaderCheck' => true
                        )
                    )
                )
            )
        );
    }

    public function addThumbPreview(FormEvent $e)
    {
        /** @var $object Reward */
        if (! ($object = $e->getObject()) instanceof Reward) {
            return;
        }

        if ($object->getThumb()) {
            $this->get('thumb')
                ->setAttribute('data-rel', $this->webPathResolver->getPath($object, $object->getThumb()));
        }
    }

    /**
     * Attach one or more listeners
     *
     * Implementors may add an optional $priority argument; the Eve\ntManager
     * implementation will pass this to the aggregate.
     *
     * @param EventManagerInterface $events
     *
     * @return void
     */
    public function attach(EventManagerInterface $events)
    {
        $this->listeners[] = $events->attach(FormEvent::EVENT_POST_BIND, array($this, 'addThumbPreview'));
    }

    /**
     * Detach all previously attached listeners
     *
     * @param EventManagerInterface $events
     *
     * @return void
     */
    public function detach(EventManagerInterface $events)
    {
        foreach ($this->listeners as $i => $listener) {
            if ($events->detach($listener)) {
                unset($this->listeners[$i]);
            }
        }
    }
}

我的控制器的编辑操作:

public function editAction()
    {
        /** @var $request Request */
        $request = $this->getRequest();
        $back = $request->getQuery('back', $this->url()->fromRoute('admin/reward'));

        /** @var $reward Reward */
        if (null === $reward = $this->rewardService->fetchById($id = $this->params()->fromRoute('id'))) {
            return $this->redirect()->toUrl($back);
        }

        $form = $this->getCreateEditForm();
        $form->bind($reward);

        if (($prg = $this->fileprg($form)) instanceof ResponseInterface) {
            return $prg;
        }

        if (is_array($prg)) {
            try {
                if ($form->isValid()) {
                    $this->rewardService->updateReward($reward);

                    $this->flashMessenger()->addSuccessMessage('Changes saved');
                    $this->redirect()->toRoute('admin/reward',
                        array('action' => 'edit', 'id' => $id),
                        array('query' => array('back' => $back)));
                } else {
                    /** @var $rewardFieldset FieldsetInterface */
                    $rewardFieldset = $form->get('reward');
                    /** @var $thumbElt ElementInterface */
                    $thumbElt = $rewardFieldset->get('thumb');

                    if (! $thumbElt->getMessages()) {
                        $reward->setThumb($thumbElt->getValue());
                        $this->rewardService->updateReward($reward);
                    }
                }
            } catch (\Exception $e) {
                $this->flashMessenger()->addErrorMessage('Changes could not be saved');
                return $this->redirect()->toUrl($back);
            }
        }

        return new ViewModel(array(
            'form' => $form,
            'reward' => $reward,
            'back' => $back
        ));
    }

我的表单模块conf:

/**
 * Expected to return \Zend\ServiceManager\Config object or array to
 * seed such an object.
 *
 * @return array|\Zend\ServiceManager\Config
 */
public function getFormElementConfig()
{
    return array(
        'invokables' => array(
            'MyAchievement\Form\CreateEditReward' => 'MyAchievement\Form\CreateEditReward',
        ),
        'factories' => array(
            'MyAchievement\Form\RewardFieldset' => function (FormElementManager $fm) {
                /** @var $rewardService RewardService */
                $rewardService = $fm->getServiceLocator()->get('MyAchievement\Service\Reward');
                /** @var $webPathResolver WebPathResolver */
                $webPathResolver = $fm->getServiceLocator()->get('Application\Service\WebPathResolver');

                $fieldset = new RewardFieldset($rewardService, $webPathResolver);
                $fieldset->setHydrator(new ClassMethods());
                return $fieldset;
            }
        )
    );
}

问题是 getInputFilterSpecifications() 被调用了两次,因此每次我尝试用我的表单上传图片时,我都会得到

Zend\Filter\Exception\RuntimeException: File './data/upload/my-thumb_5249d42bd582b.jpg' could not be renamed. An error occurred while processing the file.

那是因为我的过滤器在同一个缩略图上运行了两次,第一次成功(我什至可以看到文件上传到临时目录)。谢谢。

4

0 回答 0