【问题标题】:How to setup Symfony form for many to many relationship with add only, no edit or delete如何为多对多关系设置 Symfony 表单,仅添加,不编辑或删除
【发布时间】:2019-04-26 17:36:55
【问题描述】:

我在用户和课程这两个实体之间设置了多对多关系。我有一个连接表 UserCourse 用于具有开始日期和到期日期的额外字段的关系。如果我在 Doctrine 中设置为 ManyToMany 关系,我可以创建一个允许从用户添加和删除课程的 EntityTypeform,但是,我无法设置 UserCourse 到期时间,该到期时间需要使用创建 UserCourse 记录的日期 + a来自 Course 实体的 subScriptionExpiry 术语值(类似于“+1 年”的字符串)。我尝试通过 PrePersist,但更新集合时没有触发。

/**
 * User
 *
 * @ORM\Table(name="user")
 */
class User
{
    /**
     * @ORM\ManyToMany(targetEntity="Course", inversedBy="users")
     * @JoinTable(name="course_user",
     *      joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@JoinColumn(name="course_id", referencedColumnName="id")}
     *      )
     */
    private $courses;
/**
 * Course
 *
 * @ORM\Table(name="course")
 */
class Course

    /**
     * @ORM\ManyToMany(targetEntity="User", mappedBy="users")
     */
    private $users;

    /**
     * @var string|null
     *
     * @ORM\Column(name="subscription_expiry", type="string", length=20, nullable=true)
     */
    private $subscriptionExpiry;
/**
 * CourseUser
 *
 * @ORM\Table(name="course_user")
 * @ORM\HasLifecycleCallbacks()
 */
class CourseUser
{
    /**
     * @var DateTime
     *
     * @Gedmo\Timestampable(on="create")
     * @ORM\Column(name="created_at", type="datetime", nullable=false, options={"default": "CURRENT_TIMESTAMP"})
     */
    private $createdAt;

    /**
     * @var DateTime|null
     *
     * @ORM\Column(name="expiry", type="datetime", nullable=true)
     */
    private $expiry;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->createdAt = new DateTime();
    }

    /**
     * @ORM\PrePersist
      */
    public function updateExpiry()
    {
        $this->expiry = new DateTime($this->getCourse()->getSubscriptionExpiry());
    }
// UserController Create form
$form = $this->createForm(UserFormType::class, $user);
...
        return $this->render('user/editb.html.twig', [
            'userForm' => $form->createView()
        ]);

// UserFormType
   public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(...) // some user fields
            ->add('courses', EntityType::class, [
                'class' => Course::class,
                'multiple' => true,
                'expanded' => true,
                'choice_label' => 'name',
                'label' => 'Courses',
                'query_builder' => function(CourseRepository $repo) {
                    return $repo->createIsActiveQueryBuilder();
                }
            ])
        ;
{{ form_start(userForm) }}
...
{{ form_row(userForm.courses) }}
<button type="submit" class="btn btn-primary" formnovalidate>Save</button>

{{ form_end(userForm) }}

如果我将其设置为 OneToMany/ManyToOne,我可以创建一个 CollectionType 嵌入式表单,但我需要禁止编辑现有的 UserCourse 记录,并且 CollectionType 将 UserCourse 记录的 Course 字段显示为可以更改课程的选择字段.

/**
 * User
 *
 * @ORM\Table(name="user")
 */
class User
{
    /**
     * @var Collection
     *
     *  @ORM\OneToMany(targetEntity="CourseUser", mappedBy="user", fetch="EXTRA_LAZY", orphanRemoval=true, cascade={"persist"})
     */
    private $userCourses;
/**
 * Course
 *
 * @ORM\Table(name="course")
 */
class Course

    /**
     * @var Collection
     *
     * @ORM\OneToMany(targetEntity="CourseUser", mappedBy="course", fetch="EXTRA_LAZY", orphanRemoval=true, cascade={"persist"})
     */
    private $courseUsers;
// UserFormType
   public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add(...) // some user fields
            ->add('userCourses',CollectionType::class, [
                'entry_type' => UserCourseEmbeddedForm::class,
                'allow_delete' => true,
                'allow_add' => true,
                'by_reference' => false,
                'label' => false
            ])
        ;
// UserCourseEmbeddedForm
        $builder
            ->add('course', EntityType::class, [
                'class' => Course::class,
                'choice_label' => 'name',
                'label' => false,
                'query_builder' => function(CourseRepository $repo) {
                    return $repo->createIsActiveQueryBuilder();
                }
            ])
{{ form_start(userForm) }}
...
<h3>Courses</h3>
<div class="row js-user-course-wrapper"
     data-prototype="{{ form_widget(userForm.userCourses.vars.prototype)|e('html_attr') }}"
     data-index="{{ userForm.userCourses|length }}"
>
    {% for userCourseForm in userForm.userCourses %}
        <div class="col-xs-4 js-user-course-item">
            {{ form_errors(userCourseForm) }}
            {{ form_row(userCourseForm.course) }}
     </div>
    {% endfor %}
    <a href="#" class="js-user-course-add">
        <span class="fa fa-plus-circle"></span>
        Add Another Course
    </a>
</div>

<button type="submit" class="btn btn-primary" formnovalidate>Save</button>

{{ form_end(userForm) }}

添加课程的代码在另一个模板中,通过表单定义原型创建一个空表单。

有没有办法解决这些问题?还是一种更好的方法来制作数据模型或表单以使这项工作更加无缝?

【问题讨论】:

    标签: forms doctrine-orm symfony4


    【解决方案1】:

    使用后一种方法(ManyToOne/OneToMany),使用 css 将下拉框标记为禁用(在嵌入式表单中,使用 'attr' =&gt; ['disabled' =&gt; true],然后使用 javascript 启用新呈现的 select 项目。您可能还想要在您的 UserFormType 中标记'allowDelete' =&gt; false,否则您可能需要在提交表单时重新启用所有select 项。

    【讨论】:

    • 如果你这样做,难道不能只使用检查元素启用下拉菜单吗?
    • “使用 javascript 启用新呈现的选择项”是什么意思?您指的是“添加另一门课程”表格吗?
    • @CharlotteMoller - 当您单击“添加另一个课程”时,您可能会运行一些 JavaScript 代码,该代码会创建一个带有课程选项的新 select 元素。由于您将根据我的建议将这些标记为 disabled,因此您必须删除新渲染元素的 disabled 属性。
    • @LiamRoels - 我不知道你提到的“检查元素”是什么?
    • @ehymel 如果有人只想更改他正在使用的浏览器中的 html/css 并启用下拉菜单以选择他想要的内容。然后他可以在表单中添加一些您不想看到提交的值。我猜这是一个安全问题
    猜你喜欢
    • 2019-12-20
    • 1970-01-01
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    • 2016-03-20
    • 2013-03-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多