How to implement a checklist through "ManyToMany relationship with extra information"?

时间:2019-03-17 22:27:52

标签: php symfony doctrine symfony4

In a symfony4 project, I have an entity Order that I want to relate to another entity ChecklistItem through a ManyToMany relationship but with extra information. This is straightforward enough to do by creating an intermediate entity OrderChecklist as in this approach.

In my order entry/edit form, I want a full "checklist" to show for every order so I can track when various parts of the order fulfillment are completed. I can't figure out how to implement this.

One option would be to create a full linked checklist containing all checklist items for every order along with a "completed" parameter, but this seems wasteful. Instead, I'd like to show a checkbox of all checklist items and let the user check off items as they get completed and save that in the OrderChecklist entity. This is what I can't figure out to do.

Here's what I have.

Checklist item:

/**
 * @ORM\Entity()
 */
class ChecklistItem
{
    /**
     * @ORM\OneToMany(targetEntity="OrderChecklist", mappedBy="checklistItem")
     */
    private $orderChecklistItems;

    // ... other parameters ...
}

Order:

/**
 * @ORM\Entity()
 */
class Order
{
    /**
     * @ORM\OneToMany(targetEntity="OrderChecklist", mappedBy="order")
     */
    private $orderChecklistItems;

    // ... other parameters ...
}

Linker table:

/**
 * @ORM\Entity()
 */
class OrderChecklist
{
    /**
     * @ORM\ManyToOne(targetEntity="Order", inversedBy="orderChecklistItems")
     */
    private $order;

    /**
     * @ORM\ManyToOne(targetEntity="ChecklistItem", inversedBy="orderChecklistItems")
     */
    private $checklistItem;

    /**
     * @ORM\Column(type="datetime")
     */
    private $completionDate;

    // ... other parameters ...
}

When rendering the order form with a CollectionType, I have this:

class OrderFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('orderChecklistItems', CollectionType::class, [
                'entry_type' => ChecklistItemEmbeddedForm::class,
            ])
            // ... add other parameters ...
            ;
    }
}

with my embeded form:

class ChecklistItemEmbeddedForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('checklistItem', EntityType::class, [
                'class' => ChecklistItem::class,
                'multiple' => true,
                'expanded' => true,
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(['data_class' => OrderChecklist::class]);
    }
}

The form can be rendered from my controller:

class OrderController extends AbstractController
{
    public function edit(Request $request, Order $order)
    {
        $form_order = $this->createForm(OrderFormType::class, $order);

        $form_order->handleRequest($request);
        if ($form_order->isSubmitted() && $form_order->isValid()) {
            $order = $form_order->getData();

            $em = $this->getDoctrine()->getManager();
            $em->persist($order);
            $em->flush();

            return $this->redirectToRoute('order_edit', ['id'=>$order->getId()]);
        }

        return $this->render('order/main.html.twig', [
            'form_order' => $form_order->createView(),
        ]);
    }
}

and then from twig:

{{ form_start(form_order) }}
    {{ form_row(form_order.orderChecklistItems) }}
{{ form_end(form_order) }}

I know that I can use prototypes to offer a single ChecklistItem at a time per the symfony documentation, but that is not the behavior I am after.

It would seem like I'm missing something simple and obvious in my form. What is it?

0 个答案:

没有答案