以Silex形式验证

时间:2016-01-18 14:44:30

标签: php forms validation symfony silex

我遇到了一个问题,我无法自己解决嵌入式表格问题。

我将Silex 1.3与作曲家一起使用,composer.json文件将在下面复制。我没有使用Doctrine,而且我制作了自己的DAO,因此我没有使用注释。

我认为我的问题来自我的验证或我的数据映射。

背景信息:我尝试使用以下对象:

  • Region(如欧洲,北美等),
  • Country(法国,加拿大等)属于Region(因此将其作为属性)
  • State(Ile de France,Quebec),属于Country(因此将其作为属性)

我的目标是使用我称之为SelectType的内容,它基本上是一种允许我逐步选择某个对象的表单,而不必直接选择一个巨大的列表。 表单与对象具有相同的逻辑,我有:

  • RegionType,允许我编辑或添加Region
  • RegionSelectType,允许我选择现有的Region
  • CountryType,使用RegionSelectType
  • CountrySelectType,其中RegionSelectType允许我选择Region,然后选择Country中的Region
  • StateType,使用CountrySelectType
  • 在短期内,我将基于相同的原则StateSelectType

当我尝试通过ajax或手动提交表单(StateType)时,$form->isSumbitted()&&$form->isValid()会返回trueRegion已填充,但没有{填充{1}}(这是显而易见的,因为我没有选择它)。

我的表格有问题吗?

我注意到当我没有使用Country时,一切都很顺利,但是当我为每个表单手动填充表单选项时(导致很多代码被重新调整)。表格已经过适当的验证。

感谢您的时间和帮助!

Composer.json:

SelectType

执行表单管理的StateController:

{
    "require": {
        "silex/silex": "~1.3",
        "doctrine/dbal": "2.5.*",
        "symfony/security": "2.7.*",
        "twig/twig": "1.21.*",
        "symfony/twig-bridge": "2.7.*",
        "symfony/form": "2.7.*",
        "symfony/translation": "2.7.*",
        "symfony/config": "2.7.*",
        "jasongrimes/silex-simpleuser": "*",
        "twig/extensions": "1.3.*",
        "symfony/validator": "2.*",
        "phpoffice/phpexcel": "1.*",
        "symfony/monolog-bridge": "*"
    },
    "require-dev": {
        "phpunit/phpunit": "*",
        "symfony/browser-kit": "*",
        "symfony/css-selector": "*",
        "silex/web-profiler": "*"
    },
    "autoload":{
        "psr-4":{"Easytrip2\\": "src"}
    },
    "autoload-dev":{
        "psr-4":{"Easytrip2\\": "tests"}
    }
}

public function stateAddAction(Request $request, Application $app) { $formView = null; if ($app ['security.authorization_checker']->isGranted ( 'ROLE_ADMIN' ) and $app ['security.authorization_checker']->isGranted ( 'ROLE_ADMIN' )) { // A user is fully authenticated : he can add comments $new = new State (); $form = $app ['form.factory']->create ( new StateType ( $app ), $new ); $form->handleRequest ( $request ); //this returns true, event if the country is not filled. if ($form->isSubmitted () && $form->isValid ()) { if ($app ['dao.state']->save ( $new )) { $app ['session']->getFlashBag ()->add ( 'success', 'Succesfully added.' ); return $app->redirect ( $app ['url_generator']->generate ( 'state' ) ); } else { $app ['session']->getFlashBag ()->add ( 'error', 'Error in SQL ! Not added...' ); } } $formView = $form->createView (); return $app ['twig']->render ( 'form.html.twig', array ( 'title' => 'Add state', 'scripts_ids' => StateType::getRefNames (), 'form' => $formView ) ); } else { $app ['session']->getFlashBag ()->add ( 'error', 'Don\'t have the rights...' ); return $app->redirect ( $app ['url_generator']->generate ( 'home' ) ); } } ,基本上是注入应用程序以便能够使用DAO:

AbstractEasytrip2Type

<?php namespace Easytrip2\Form; use Silex\Application; use Symfony\Component\Form\AbstractType; abstract class AbstractEasytrip2Type extends AbstractType { /** * * @var Application */ protected $app; public function __construct(Application $app/*, $data*/) { $this->app = $app; } public static function getRefNames() { return null; } }

RegionSelectType

RegionSelectDataMapper:

<?php

namespace Easytrip2\Form\Select;

use Easytrip2\Form\AbstractEasytrip2Type;
use Easytrip2\Form\Select\DataMapper\RegionSelectDataMapper;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class RegionSelectType extends AbstractEasytrip2Type {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $obj = $this->app ['dao.region']->findAll ();
        $builder->add ( 'choice', 'choice', array (
                'choices' => $obj,
                'choices_as_values' => true,
                'choice_label' => function ($value) {
                    // if nothing exists, then an empty label is generated.
                    return is_null ( $value ) ? "" : $value->getName ();
                },
                'choice_value' => function ($value) {
                    // here i only have int unsigned in database, so -1 is safe. This is probably used for comparison for selecting the stored object between the list and the stored object.
                    return is_null ( $value ) ? - 1 : $value->getId ();
                },
                'placeholder' => 'Select a region',
                'label' => 'Region'
        ) );
        $builder->setDataMapper ( new RegionSelectDataMapper () );
    }
    /**
     *
     * {@inheritDoc}
     *
     * @see \Symfony\Component\Form\AbstractType::setDefaultOptions()
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver) {
        $resolver->setDefaults ( array (
                'data_class' => 'Easytrip2\Domain\Region',
                'cascade_validation' => true
        ) );
    }
    public function getName() {
        return 'region';
    }
    public static function getRefNames() {
        return array ();
    }
}

CountrySelectType:

<?php

namespace Easytrip2\Form\Select\DataMapper;

use Symfony\Component\Form\DataMapperInterface;

class RegionSelectDataMapper implements DataMapperInterface {
    public function mapDataToForms($data, $forms) {
        $forms = iterator_to_array ( $forms );
        $forms ['choice']->setData ( $data );
    }
    public function mapFormsToData($forms, &$data) {
        $forms = iterator_to_array ( $forms );
        $data = $forms ['choice']->getData ();
    }
}

CountrySelectDataMapper:

<?php

namespace Easytrip2\Form\Select;

use Easytrip2\Domain\Region;
use Easytrip2\Form\AbstractEasytrip2Type;
use Easytrip2\Form\Select\DataMapper\CountrySelectDataMapper;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class CountrySelectType extends AbstractEasytrip2Type {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        $builder->add ( 'region', new RegionSelectType ( $this->app ), array (
                'label' => false,
                'cascade_validation' => true
        ) );
        $builder->addEventListener ( FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $this->modifyFormFromRegion ( $event->getForm (), $event->getData () ? $event->getData ()->getRegion () : null );
        } );
        $builder->get ( 'region' )->addEventListener ( FormEvents::POST_SUBMIT, function (FormEvent $event) {
            $this->modifyFormFromRegion ( $event->getForm ()->getParent (), $event->getForm ()->getData () );
        } );
        $builder->setDataMapper ( new CountrySelectDataMapper () );
    }
    public function modifyFormFromRegion(FormInterface $builder, Region $data = null) {
        $obj = array ();
        if (! is_null ( $data )) {
            $obj = $this->app ['dao.country']->findByRegionId ( $data->getId () );
        } else {
            // change this if you do not want the country to be filled with all countries.
            // $obj = $this->app ['dao.country']->findAll ();
            $obj = array ();
        }
        $builder->add ( 'choice', 'choice', array (
                'choices' => $obj,
                'choices_as_values' => true,
                'choice_label' => function ($value) {
                    // if nothing exists, then an empty label is generated.
                    return is_null ( $value ) ? "" : $value->getName ();
                },
                'choice_value' => function ($value) {
                    // here i only have int unsigned in database, so -1 is safe. This is probably used for comparison for selecting the stored object between the list and the stored object.
                    return is_null ( $value ) ? - 1 : $value->getId ();
                },
                'placeholder' => 'Select a country',
                'label' => 'Country',
                'required' => true,
                'data_class' => 'Easytrip2\Domain\Country',
                'cascade_validation' => true
        ) );
    }
    /**
     *
     * {@inheritDoc}
     *
     * @see \Symfony\Component\Form\AbstractType::setDefaultOptions()
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver) {
        $resolver->setDefaults ( array (
                'data_class' => 'Easytrip2\Domain\Country',
                'cascade_validation' => true
        ) );
    }
    function getName() {
        return 'country';
    }
    public static function getRefNames() {
        $ret = array (
                'in' => 'country_region_choice',
                'out' => 'country_choice'
        );
        return array (
                $ret
        );
    }
}

StateType:

<?php

namespace Easytrip2\Form\Select\DataMapper;

use Symfony\Component\Form\DataMapperInterface;

class CountrySelectDataMapper implements DataMapperInterface {
    public function mapDataToForms($data, $forms) {
        $forms = iterator_to_array ( $forms );
        $forms ['choice']->setData ( $data );
        if (isset ( $forms ['region'] )) {
            if ($data) {
                $forms ['region']->setData ( $data->getRegion () );
            }
        }
    }
    public function mapFormsToData($forms, &$data) {
        $forms = iterator_to_array ( $forms );
        $data = $forms ['choice']->getData ();
    //  $data->getRegion() === $forms['']
    }
}

1 个答案:

答案 0 :(得分:1)

我设法在我的课程中使用loadValidatorMetadata并使用一些解决方法。 这样,Silex进行验证,甚至将其发送给浏览器以基本验证数据。

我也简化了很多地图制作者。

如果您对此项目有任何疑问,请随时问我。

编辑:因为,我有:

  • 更改了DataMappers表单的select
  • 添加了cascade_validation并使用data_class
  • 小心地将setDefaultOptions选项填充到我的表单中
  • 使用loadValidatorMetadata,我不确定它是否有所作为(也许它可以检查特定选择是否有效,由cascade_validation触发?

自从我修复它以来已经有很多时间了,所以我可能会在这里忘记一些事情。

但基本上,我理解的是我的验证没有正确进行,验证实体中的数据,将正确的数据映射到实体和级联验证以确保属性得到验证也是必要的。