Symfony2 - 在事件之间传递数据

时间:2014-01-31 05:11:43

标签: php oop symfony symfony-forms symfony-2.3

我需要什么

我必须构建一个允许输入非常简单的工资单的模块。表示此类的是PayStubDetail,它们是POPO和持久性的。此外,Detail必须与Fee s进行平衡,而Tariff s又是属于Detail类的类。因此,Fee包含有关正在支付的Fee的信息。

为了使事情稍微复杂一点,用户必须付费的EducationLevel直接取决于Enrolment Student所拥有的Person属于PayStub,它也属于DetailFee

我拥有什么

Tariff有:

  • 支付号码
  • 付款日期
  • 付款方式
  • 观察

PayStubType有:

  • 检查,如果有的话
  • 付费金额
  • 奖学金百分比

class PayStubType extends AbstractType { //I need to pass the years and the DAOs because I need to use them later public function __construct(School $c, DAOPerson $dao, $years = array(), DAOFee $dc) { $this->c = $c; $this->dao = $dao; $this->years = $years; $this->dc = $dc; } public function buildForm(FormBuilderInterface $builder, array $options) { $sch = $this->c; $std = $this->dao->getEnroledStudents($sch)[0]; $builder->add('student', 'entity', array( 'label' => 'Student', 'class' => 'System\SchoolsBundle\Person\Person', 'mapped' => false, 'query_builder' => $this->dao->getEnroledStudents($sch, true) ))->add('payDate', 'datetime', array( 'label' => 'Payment Date', 'widget' => 'single_text', 'input' => 'datetime', 'format' => 'dd/MM/yyyy' ))->add('payMethod', 'choice', array( 'label' => 'Payment Method', 'choices' => EPaymentMethod::$arrPayMethods ))->add('stubNumber', 'text', array( 'label' => 'Pay Stub No.', 'required' => false ))->add('observation', 'textarea', array( 'label' => 'Observations', 'max_length' => 1024, 'required' => false )); $years = $this->years; $dc = $this->dc; $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($sch, $std, $years, $dc) { $form = $event->getForm(); $data = $event->getData(); $stdRole = $std->getInfoContainer()->getRole('STUDENT'); $form->add('details', 'collection', array( 'type' => new DetailType($sch, $std, $years, $dc), 'label' => false, 'allow_add' => true, 'by_reference' => false )); }); public function getName() { return 'paystubtype'; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'System\SchoolsBundle\Payments\PayStub' )); } private $c; private $dao; private $years; private $dc; } 有:

  • 费用数目
  • 价格

属于DetailType类:

  • 总金额
  • 教育程度

问题

当我构建表单时,整个问题就出现了。

class DetailType extends AbstractType { public function __construct(School $c, Student $al, $years = array(), DAOFee $dc) { $this->c = $c; $this->al = $al; $this->years = array_reverse($years, true); $this->dc = $dc; } public function buildForm(FormBuilderInterface $builder, array $options) { $sch = $this->c; $list = array(); //List of scholarship percentages for ($i=0; $i<=100; $i++) { $list[(string)($i/100)] = $i."%"; } $pref = min($cole->getSchoolYear(), array_values($this->years)[0]); $builder->add('ct', 'choice', array( 'label' => false, 'mapped' => false, 'choices' => Fee::$arrFees //A list of possible fees. The only possible values are the enrolment price and one fee per school month. Read after the code for a longer explanation about how this works. ))->add('year', 'choice', array( 'mapped' => false, 'label' => false, 'choices' => $this->years, //Years that have tariffs registered 'preferred_choices' => array($pref) //The minimum between the current school year and the last year where tariffs were registered ))->add('cheque', 'entity', array( 'label' => false, 'class' => 'System\SchoolsBundle\Payments\Cheque', 'property' => 'numberAndBank', 'required' => false, 'empty_value' => 'Select Cheque', 'query_builder' => function(EntityRepository $er) use ($sch) { return $er->createQueryBuilder('u') ->where('u.school = ?1') ->orderBy('u.number') ->setParameter(1, $sch); } ))->add('amount', 'text', array( 'label' => false, ))->add('scholarshipPerc', 'choice', array( 'label' => false, 'choices' => $list )); // From here on, it gets blurry. Read below for more. } public function getName() { return 'detailtype'; } public function setDefaultOptions(OptionsResolverInterface $resolver) { $resolver->setDefaults(array( 'data_class' => 'System\SchoolsBundle\Payments\Detail' )); } private $c; private $al; private $years; private $dc; }

PRE_SUBMIT

$builder->addEventListener(FormEvents::PRE_SUBMIT, function(FormEvent $event) use ($sch, $al) { $form = $event->getForm(); $data = $event->getData(); if (array_key_exists('ct', $data) == true && array_key_exists('year', $data) == true) { $et = $al->getEnrolmentBySchool($sch, $data['year'])->getEducationLevel(); $year = $data['year']; $feeNumber = $data['ct']; $form->add('fee', 'entity', array( 'label' => false, 'class' => 'System\SchoolsBundle\Payments\Fee', 'property' => 'feeName', 'query_builder' => function(EntityRepository $er) use ($et, $year, $feeNumber) { return $er->createQueryBuilder('u') ->innerJoin('u.tariff', 'a') ->innerJoin('a.edType', 'et') ->where('et = ?1') ->andWhere('a.year = ?2') ->andWhere('u.feeNumber = ?3') ->orderBy('u.feeNumber') ->setParameter(1, $et) ->setParameter(2, $year) ->setParameter(3, $feeNumber); } )); } });

POST_SUBMIT

关于费用选择字段,我没有使用实体类型,因为它涉及一个AJAX查询,每当我更新学生或年份时,我将不得不重新加载集合类型的原型,加载新的这一年的费用,并添加极其复杂的事件,我不确定这些事件是否正常。所以我决定使用通用列表并稍后进行处理。

但事情变得复杂了。我正在玩这些事件并决定{{1}}事件能够捕获年份和费用,所以我只是查询它并将其添加到对象中。但是,事件不处理映射的数据类型,所以我必须将这个新信息传递给另一个事件,我决定这样做:

{{1}}

我以为我可以稍后收到数据,手工映射,然后丢弃它(即从表单中删除它)。但是,{{1}}事件无法正确接收对象,无论是形式(空值)还是对象(同一问题)。

我是否正确处理此问题?有没有更简洁的方法来做到这一点,还是我可以这样解决这个问题?先谢谢你。

1 个答案:

答案 0 :(得分:0)

我想提出几点建议:
第一个是关于代码,我认为你的Form中的逻辑开始有点复杂,最好避免在同一个类中有太多代码行,所以我想根据SRP考虑一个重构(单一责任原则) )。

第二件事是我无法确切知道你的方法是否正确 我可以假设它是,也许使用PRE_SUBMIT可以促进事情,但我想更加关注你的架构。

我的意思是,我们需要了解有关费用问题的更多信息 如果您认为获取该值将消耗时间并且还可能减慢表单的提交过程(必须尽可能快), 可能值得考虑一些替代方案:

  • 将表单拆分为2个子表单(示例2页,步骤1和步骤2);
  • 重组模型数据,例如创建另一个表,或者更好的是可以快速获取值的视图或Api;
  • 稍后使用cron作业计算值。

希望这种观点有所帮助。