caramba
caramba

Reputation: 22490

Create form based on Entity from array collection

Trying to create a simple web shop with symfony and doctrine. Problem is: I don't know how to create the order form which should have the ability to select/set the quantity of each product. I think I need 3 Entities Product, Order and OrderPosition.

Where Product has a price and title. Order knows which User made an Order and when but relevant in this case is an Order has OrderPositions and the OrderPositions have the property quantity. So the OrderPosition knows which Product has been ordered how many times.

I'm not sure if the mapping/relation of those entities is correct so I will show them here:

Product:

class Product
{
    private $id;
    private $price;
    private $title;
    private $orderPositions;

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

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

    public function setPrice($price)
    {
        $this->price = $price;

        return $this;
    }

    public function getPrice()
    {
        return $this->price;
    }

    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    public function getTitle()
    {
        return $this->title;
    }

    public function getOrderPositions()
    {
        return $this->orderPositions;
    }

    public function addOrderPosition(OrderPosition $orderPosition)
    {
        $this->orderPositions[] = $orderPosition;
        if ($orderPosition->getProduct() !== $this) {
            $orderPosition->setProduct($this);
        }
        return $this;
    }
}

Order:

class Order
{
    private $id;
    private $orderPositions;

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

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

    public function getOrderPositions()
    {
        return $this->orderPositions;
    }

    public function addOrderPosition(OrderPosition $orderPosition)
    {
        $this->orderPositions[] = $orderPosition;
        if ($orderPosition->getOrder() !== $this) {
            $orderPosition->setOrder($this);
        }
        return $this;
    }
}

OderPosition:

class OrderPosition
{
    private $id;
    private $quantity;
    private $order;
    private $product;

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

    public function getQuantity()
    {
        return $this->quantity;
    }

    public function setQuantity($quantity)
    {
        $this->quantity = $quantity;
    }

    public function getOrder()
    {
        return $this->order;
    }

    public function setOrder(Order $order)
    {
        $this->order = $order;
        if ($order->getOrderPositions() !== $this) {
            $order->addOrderPosition($this);
        }
        return $this;
    }

    public function getProduct()
    {
        return $this->product;
    }

    public function setProduct(Product $product)
    {
        $this->product = $product;
        if ($product->getOrderPositions() !== $this) {
            $product->addOrderPosition($this);
        }
        return $this;
    }
}

The mapping files:

Product:

MyBundle\Entity\Product:
    type: entity
    table: product
    repositoryClass: MyBundle\Repository\ProductRepository
    oneToMany:
        orderPositions:
            targetEntity: OrderPosition
            mappedBy: product
            cascade: [ "persist" ]
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        price:
            type: float
        title:
            type: string
            length: 255
            column: title

    lifecycleCallbacks: {  }

Order:

MyBundle\Entity\Order:
    repositoryClass: MyBundle\Repository\OrderRepository
    type: entity
    table: order
    oneToMany:
        orderPositions:
            targetEntity: OrderPosition
            mappedBy: order
            cascade: [ "persist" ]
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO

    lifecycleCallbacks: {  }

OrderPosition:

MyBundle\Entity\OrderPosition:
    repositoryClass: MyBundle\Repository\OrderPositionRepository
    type: entity
    table: order_position
    manyToOne:
        order:
            targetEntity: Order
            inversedBy: orderPositions
            joinColumn:
                name: order_id
                referencedColumnName: id
        product:
            targetEntity: Product
            inversedBy: orderPositions
            joinColumn:
                name: product_id
                referencedColumnName: id
    id:
        id:
            type: integer
            id: true
            generator:
                strategy: AUTO
    fields:
        quantity:
            type: integer
    lifecycleCallbacks: {  }

And the controller which should create the form looks like:

$order = new Order();

$form = $this->createFormBuilder($order)
        ->add('quantity', OrderPositionType::class)
        ->add('save', SubmitType::class, array('label' => 'Order'))
        ->getForm();

and last the OrderPositionType

class OrderPositionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('quantity', IntegerType::class);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'MyBundle\Entity\OrderPosition'
        ));
    }
}

Now, the Question is: How can I get the Order form to create a quantity input field for every Product?

Upvotes: 1

Views: 1261

Answers (1)

Dmitry Malyshenko
Dmitry Malyshenko

Reputation: 3051

You define types like this:

OrderPositionType:

class OrderPositionType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('quantity');
    }
}

OrderType:

class OrderType extends AbstractType {

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('orderPositions', CollectionType::class, [
            'entry_type' => OrderPositionType::class,
            'allow_add' => false,
        ]);

        // Other fields then...
    }
}

and your controller:

    // $products = list of products user has selected. You already have it
    $order = new Order();

    foreach ($products as $product) {
        $orderPosition = new OrderPosition($order);
        $orderPosition->setProduct($product);
        $orderPosition->setQuantity(1); // default quantity, just my guess
    }

    $form = $this->createForm(OrderType::class, $order);

Upvotes: 3

Related Questions