1

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?

1 Answer 1

3

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);
Sign up to request clarification or add additional context in comments.

9 Comments

+1 just because you did read my long question.. thank you, will try right now. It makes sense what you are writing
You will probably need a separate widget for OrderPositionType. Because right now it's only will be possible to set a quantity and to delete position, while you probably will need only to show some information about Product. If you need a help with this, let me know
Thank you Dmitry! Problem is the last line ..buildForm.. cause buildForm is an undefined Method. If I change to ..createFormBuilder.. I get a "Catchable Fatal Error: Argument 2 passed to createFormBuilder() must be array but object given".. can you help?
Sorry, it's createForm(). I made it by memory and did a mistake
that kind a works, but I'm not getting an input field for quantity it says "Order Positions" but with no <input> field
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.