Symfony仍然无法验证收集表格

问题描述 投票:1回答:1

我使用的是SYMFONY 5,并已建立一个收集表单,在该表单中,使用户能够创建服务并向该服务添加许多子服务。一切正常,用户可以添加/编辑/显示/删除服务以及子服务。

Screen-Shot with one service and two assigned sub-services where the second one is not valid because the language is not equal to the one of the service

现在,我想验证是否添加了新的子服务并提交了表单,子服务项的语言必须是该服务项之一。否则,数据库将不会更新,并且用户将收到错误消息(请参见屏幕快照)。这也可以正常工作,但有一个例外:我无法将错误消息粘贴到失败的子服务上!它出现在每个子服务上。

这里是我的实体服务的定义:

namespace App\Entity;

    use Symfony\Component\Validator\Constraints as Assert;
    use Doctrine\Common\Collections\ArrayCollection;
    use Doctrine\Common\Collections\Collection;
    use Doctrine\ORM\Mapping as ORM;

    /**
     * @ORM\Entity(repositoryClass="App\Repository\ServicesRepository")
     */
    class Services
    {
/**
 * @ORM\Id()
 * @ORM\Column(type="integer")
 */
private $id;

/**
 * @ORM\Column(type="string", length=3)
 */
private $sprache;
/**
 * @ORM\Column(type="integer", nullable=true)
 */
private $transid;

/**
 * @ORM\Column(type="string", length=200)
 */
private $header;

/**
 * @ORM\Column(type="text", nullable=true)
 */
private $body;

/**
 * @ORM\OneToMany(targetEntity="App\Entity\SubServices", mappedBy="services",cascade={"persist"})
 * @Assert\Valid()
 */
private $subServices;

public function __construct()
{
    $this->subServices = new ArrayCollection();
}

public function getId(): ?int
{
    return $this->id;
}
public function setId(int $id): self
{
    $this->id = $id;

    return $this;
}
public function getTransId(): ?int
{
    return $this->transid;
}
public function setTransId(int $transid): self
{
    $this->transid = $transid;

    return $this;
}
public function getSprache(): ?string
{
    return $this->sprache;
}

public function setSprache(string $sprache): self
{
    $this->sprache = $sprache;

    return $this;
}

public function getHeader(): ?string
{
    return $this->header;
}

public function setHeader(string $header): self
{
    $this->header = $header;

    return $this;
}

public function getBody(): ?string
{
    return $this->body;
}

public function setBody(?string $body): self
{
    $this->body = $body;

    return $this;
}

/**
 * @return Collection|SubServices[]
 */
public function getSubServices(): Collection
{
    return $this->subServices;
}

public function addSubService(SubServices $subService): self
{
    if (!$this->subServices->contains($subService)) {
        $this->subServices[] = $subService;
        $subService->setServices($this);
    }

    return $this;
}

public function removeSubService(SubServices $subService): self
{
    if ($this->subServices->contains($subService)) {
        $this->subServices->removeElement($subService);
        // set the owning side to null (unless already changed)
        if ($subService->getServices() === $this) {
            $subService->setServices(null);
        }
    }

    return $this;
}
    }

您可以看到,在服务实体中,我为子服务放置了@Assert \ Valid()。

这里是子服务实体的定义:

<?php

    namespace App\Entity;

    use App\Validator\SubServiceSprache;
     use Doctrine\ORM\Mapping as ORM;

    /**
     * @ORM\Entity(repositoryClass="App\Repository\SubServicesRepository")
     * @SubServiceSprache()
     */
     class SubServices
      {
        /**
         * @ORM\Id()
         * @ORM\Column(type="integer")
          */
         private $id;

/**
 * @ORM\Column(type="integer", nullable=true)
 */
private $transsubid;

/**
 * @ORM\Column(type="string", length=3)
 */
private $sprache;

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\Services", inversedBy="subServices")
 */
private $services;


/**
 * @ORM\Column(type="string", length=200)
 */
private $header;

/**
 * @ORM\Column(type="text", nullable=true)
 */
private $body;

public function getId(): ?int
{
    return $this->id;
}

public function setId(int $id)
{
    $this->id = $id;
}
public function getTransSubId(): ?int
{
    return $this->transsubid;
}
public function setTransSubId(int $transsubid): self
{
    $this->transsubid = $transsubid;

    return $this;
}
public function getsprache(): ?string
{
    return $this->sprache;
}


public function setsprache(string $sprache): self
{
    $this->sprache = $sprache;
    return $this;
}
public function getServices(): ?Services
{
    return $this->services;
}

public function setServices(?Services $services): self
{
    $this->services = $services;

    return $this;
}

public function getHeader(): ?string
{
    return $this->header;
}

public function setHeader(string $header): self
{
    $this->header = $header;

    return $this;
}

public function getBody(): ?string
{
    return $this->body;
}

public function setBody(?string $body): self
{
    $this->body = $body;

    return $this;
}
    }

您可以看到,对于整个子类服务,我已将验证@SubServiceSprache()。

这里是验证器SubServiceSprache的定义:

    <?php

     namespace App\Validator;


     use Symfony\Component\Validator\Constraint;

     /**
      * @Annotation
      */
    class SubServiceSprache extends Constraint
     {
        public function validatedBy()
         {
             return \get_class($this).'Validator';
         }


public function getTargets()
{
    //PROPERTY_CONSTRAINT wenn zB. EMAIL geprüft werden soll
    //CLASS_CONSTRAINT wenn ganze Entity geprüft werden soll
    // jeweils das Objekt (EMAIL od. ganzes Klassenobjekt wird übergeben
    return self::CLASS_CONSTRAINT;
}
    }

这里是SubServiceSpracheValidator中的验证逻辑:

<?php


    namespace App\Validator;

    use App\Entity\Services;
    use App\Entity\SubServices;
    use Symfony\Component\Validator\ConstraintValidator;
    use Doctrine\ORM\EntityManagerInterface;
    use Symfony\Component\Validator\Constraint;
    use Symfony\Contracts\Translation\TranslatorInterface;



    class SubServiceSpracheValidator extends ConstraintValidator
    {

       private $em;
       private $subservice;
       private $translator;



       public function __construct(EntityManagerInterface $em, TranslatorInterface $translator)
       {
          $this->em = $em;
          $this->translator = $translator;
          $this->subservice = new SubServices();
       }

      public function validate($object, Constraint $constraint)
      {
          // Ist die Sprache des SubService die des Service?

          if ($object instanceof SubServices) {


             if($object->getServices()->getSprache() != $object->getsprache()){
                // Message Translation
                  $message = $this->translator->trans('subservice_sprachcheck',
                                                        ['subsprache' =>  object->getsprache(),'servsprache' => $object->getServices()->getsprache()]
           );
           // Assign message
           $this->context->buildViolation($message)
               ->atPath('sprache')
               ->addViolation();
       }
   }
       }
    }

这里是服务的表单类的摘要:

          ->add('subservices', CollectionType::class,
            array('entry_type' => SubservicesFormType::class,
                  'label' => false,
                  'entry_options' => array('label' => false),
                  'allow_add' => true,
                  'allow_delete' => true,
                  'by_reference' => false,
                  'error_bubbling' => false,
            ))
        ->add('save', SubmitType::class,
            array('label' => 'Sichern',
                'attr' => array('class' => 'buttonsave')
            ))
    ;
}
public function configureOptions(OptionsResolver $resolver)
{
    $resolver->setDefaults([
        'data_class' => Services::class,
        'error_bubbling' => false,
        //'newid' => false,
    ]);
 }

这里是子服务:

class SubservicesFormType extends AbstractType
    {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
    $builder
        ->add('sprache', LanguageType::class,
            array('label' => 'Sprache',
                'disabled' => false,
                'attr' => array('class' => 'form-control'),
                'choice_loader' => NULL,
                'choices' => ['DEUTSCH' => 'de', 'ENGLISCH' => 'en'],
                'choice_translation_domain' => true,
            ))
        ->add('header', TextType::class,
            array('label' => 'Überschrift',
                  'attr' => array('class' => 'form-control')))
        ->add('body', TextareaType::class,
            array('label' => 'Beschreibung',
                  'attr' => array('class' => 'form-control')))
    ;
   }
   public function configureOptions(OptionsResolver $resolver)
   {
    $resolver->setDefaults([
        'data_class' => SubServices::class,
        'validation_groups' => ['Default'],
    ]);

}

}

最后是我的树枝文件:

{% extends 'base.html.twig' %}
{% import _self as formMacros %}
{% block title %}UB Mollekopf{% endblock %}
{% block stylesheets %}
<link rel="stylesheet" href="{{ absolute_url('/css/ub_styles.css') }}"  type="text/css" media="all">
<link rel="stylesheet" href="{{ absolute_url('css/font-awesome.css') }}">
{% endblock %}
{% macro printSubserviceRow(SubservicesFormType) %}
   <td class="subserviceformsprache">{{ form_widget(SubservicesFormType.sprache) }}</td>
   <td class="subserviceformheader">{{ form_widget(SubservicesFormType.header) }}</td>
   <td class="subserviceformbody">{{ form_widget(SubservicesFormType.body) }}</td>
   <td class="subserviceformaction"></td>
{% endmacro %}
{% block body %}
  <div class="tableserviceedit">
   {{ form_start(form) }}
    <div class="tableheadereditservice">
        <table id="editserviceheader">
            <tr style="white-space: nowrap">
                <th style="width: 100%; padding-left: 0.5em">{% trans %}Ändern Service{% endtrans %}  {{ form_widget(form.id) }}</th>
            </tr>
        </table>
    </div>
    <div class="tablebodyeditservice">
        <table id="editservicesingleheader">
            <tr>
                <th style="width: 3.5em;">{% trans %}Sprache{% endtrans %}</th>
                <th style="width: 12em">{% trans %}Überschrift{% endtrans %}</th>
                <th style="width: 15em">{% trans %}Beschreibung{% endtrans %}</th>
            </tr>

            <tr class="editserviceheader">
                <td class="serviceformsprache">
                    {{ form_errors(form.sprache) }}
                    {{ form_widget(form.sprache) }}
                </td>
                <td class="serviceformheader">
                    {{ form_errors(form.header) }}
                    {{ form_widget(form.header) }}
                </td>
                <td class="serviceformbody">
                    {{ form_errors(form.body) }}
                    {{ form_widget(form.body) }}
                </td>
            </tr>

        </table>
        <div class="tablebodysubservices">
        <table id="subservices">
            <thead>
                <tr>
                    <th style="width: 6em;">{% trans %}Sprache{% endtrans %}</th>
                    <th style="width: 22.2em">{% trans %}Überschrift{% endtrans %}</th>
                    <th style="width: 15em">{% trans %}Beschreibung{% endtrans %}</th>
                </tr>
            </thead>

              <tbody id="collector" data-prototype="{{ formMacros.printSubserviceRow(form.subservices.vars.prototype)|e('html_attr') }}">
                {% for subservice in form.subservices %}
                <tr>
                  <td colspan="4">
                    <span style="color:red" > {{ form_errors(form) }}</span>
                  </td>
                </tr>
                <tr>
                  <td class="subserviceformsprache">
                     {{ form_widget(subservice.sprache) }}
                  </td>
                  <td class="subserviceformheader">
                    {{ form_widget(subservice.header) }}
                  </td>
                  <td class="subserviceformbody">
                    {{ form_widget(subservice.body) }}
                  </td>
                  <td class="subserviceformaction"></td>
                </tr>
                {% endfor %}

              </tbody>
            </table>
            </div>
        <div class="tablefooter" id="fussbereichnewservice">
            <div class="btnfooter">{{ form_widget(form.save) }} <button type="" class="buttonabort"><a href="{{path('services_maintain', { _locale: locale }) }}" style="color: white">{% trans %}Abbruch{% endtrans %}</a></button></div>
        </div>
         {{ form_end(form) }}
    {#</div>#}
{{ include('inc/navbar_bottom.html.twig') }}
{% endblock %}

{% block javascripts %}
    <script src="{{ absolute_url('/js/main.js') }}"></script>
{%  if locale == 'en' %}
   <script src="{{ absolute_url('/js/subservicesen.js') }}"></script>
{%  else %}
   <script src="{{ absolute_url('/js/subservices.js') }}"></script>
 {% endif %}
{% endblock %}

在树枝模板文件中,我尝试了服务器功能:如果我编写了{{form_errors(form)}},则错误消息将出现在每个子服务上;如果我编写了{{form_errors(form.sprache)}},则将不会出现错误消息。

有人有解决这个问题的想法吗?

forms symfony validation collections
1个回答
0
投票

如果尝试,会发生什么?>

{% for subservice in form.subservices %}
    {{ form_errors(subservice ) }}

    ...
%}

© www.soinside.com 2019 - 2024. All rights reserved.