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?
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);
Related
I'm new on Symfony 6 and i've got problem with 2 of my entities.
App\Entity\Mission :
The mappings App\Entity\Mission#idtagmissionassign and App\Entity\Tag#missionstag are inconsistent with each other.
App\Entity\Tag :
The association App\Entity\Tag#missionstag refers to the inverse side field App\Entity\Mission#tags which does not exist.
This is the relation betewen the 2 entities :
DB
This is Mission entity
<?php
namespace App\Entity;
use App\Repository\MissionRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: MissionRepository::class)]
#[ORM\Table(name: 'mission')]
class Mission
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 255)]
private ?string $titlemission = null;
#[ORM\Column(length: 255)]
private ?string $descriptionmission = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $onsetdate = null;
#[ORM\Column(type: Types::DATETIME_MUTABLE)]
private ?\DateTimeInterface $deadline = null;
#[ORM\Column]
private ?int $budgetmission = null;
/*#[ORM\Column(length: 255)]
private ?string $codeapemission = null;*/
#[ORM\Column(length: 255)]
private ?string $prioritymission = null;
#[ORM\ManyToMany(targetEntity: Tag::class, inversedBy: 'missionstag',cascade: ['persist'])]
#[ORM\JoinTable(name:'mission_tag')]
private Collection $idtagmissionassign;
#[ORM\ManyToOne(inversedBy: 'missions')]
#[ORM\JoinColumn(nullable: false)]
private ?User $iduser = null;
#[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'missionsassign')]
private Collection $idmissionassign;
#[ORM\Column(length: 100)]
private ?string $remote = null;
public function __construct()
{
$this->idtagmissionassign = new ArrayCollection();
$this->idmissionassign = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTitlemission(): ?string
{
return $this->titlemission;
}
public function setTitlemission(string $titlemission): self
{
$this->titlemission = $titlemission;
return $this;
}
public function getDescriptionmission(): ?string
{
return $this->descriptionmission;
}
public function setDescriptionmission(string $descriptionmission): self
{
$this->descriptionmission = $descriptionmission;
return $this;
}
public function getOnsetdate(): ?\DateTimeInterface
{
return $this->onsetdate;
}
public function setOnsetdate(\DateTimeInterface $onsetdate): self
{
$this->onsetdate = $onsetdate;
return $this;
}
public function getDeadline(): ?\DateTimeInterface
{
return $this->deadline;
}
public function setDeadline(\DateTimeInterface $deadline): self
{
$this->deadline = $deadline;
return $this;
}
public function getBudgetmission(): ?int
{
return $this->budgetmission;
}
public function setBudgetmission(int $budgetmission): self
{
$this->budgetmission = $budgetmission;
return $this;
}
/*public function getCodeapemission(): ?string
{
return $this->codeapemission;
}
public function setCodeapemission(string $codeapemission): self
{
$this->codeapemission = $codeapemission;
return $this;
}*/
public function getPrioritymission(): ?string
{
return $this->prioritymission;
}
public function setPrioritymission(string $prioritymission): self
{
$this->prioritymission = $prioritymission;
return $this;
}
/**
* #return Collection<int, tag>
*/
public function getIdtagmissionassign(): Collection
{
return $this->idtagmissionassign;
}
public function addIdtagmissionassign(tag $idtagmissionassign): self
{
if (!$this->idtagmissionassign->contains($idtagmissionassign)) {
$this->idtagmissionassign->add($idtagmissionassign);
}
return $this;
}
public function removeIdtagmissionassign(tag $idtagmissionassign): self
{
$this->idtagmissionassign->removeElement($idtagmissionassign);
return $this;
}
public function getIduser(): ?user
{
return $this->iduser;
}
public function setIduser(?user $iduser): self
{
$this->iduser = $iduser;
return $this;
}
/**
* #return Collection<int, user>
*/
public function getIdmissionassign(): Collection
{
return $this->idmissionassign;
}
public function addIdmissionassign(user $idmissionassign): self
{
if (!$this->idmissionassign->contains($idmissionassign)) {
$this->idmissionassign->add($idmissionassign);
}
return $this;
}
public function removeIdmissionassign(user $idmissionassign): self
{
$this->idmissionassign->removeElement($idmissionassign);
return $this;
}
public function getRemote(): ?string
{
return $this->remote;
}
public function setRemote(string $remote): self
{
$this->remote = $remote;
return $this;
}
public function __toString(){
return $this->titlemission;
}
}
This is Tag entity :
<?php
namespace App\Entity;
use App\Repository\TagRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity(repositoryClass: TagRepository::class)]
class Tag
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 100)]
#[Assert\NotBlank]
#[Assert\Length(min:2)]
#[Assert\Valid]
private ?string $nomtag = null;
#[ORM\ManyToMany(targetEntity: Mission::class, mappedBy: 'tags')]
#[ORM\JoinTable(name:'mission_tag')]
private Collection $missionstag;
public function __construct()
{
$this->missionstag = new ArrayCollection();
}
// #[ORM\ManyToMany(targetEntity: Mission::class, inversedBy:"$idtagmissionassign")]
// private $genusScientists;
public function getId(): ?int
{
return $this->id;
}
public function getNomtag(): ?string
{
return $this->nomtag;
}
public function setNomtag(string $nomtag): self
{
$this->nomtag = $nomtag;
return $this;
}
/**
* #return Collection<int, Mission>
*/
public function getMissions(): Collection
{
return $this->missionstag;
}
public function addMission(Mission $mission): self
{
if (!$this->missionstag->contains($mission)) {
$this->missionstag->add($mission);
$mission->addTag($this);
}
return $this;
}
public function removeMission(Mission $mission): self
{
if ($this->missionstag->removeElement($mission)) {
$mission->removeTag($this);
}
return $this;
}
public function __toString(): string
{
return $this->nomtag;
}
}
Do you have an idea to help me to solve this problem ?
Many thanks
Change the mappedby and inversedby with no effect.
Trying some stuff follow internet guideline with no success.
Tag's property missionstag claims, that it is mapped by Missions's property "tags", but there is no such property, instead you have idtagmissionassign. (Side note: Your property names are bad, because they look like database columns, but you're using an ORM.
The first question you would have to ask yourself: will you need additional columns on your many-to-many table. If the answer is yes, ORM\ManyToMany isn't even the right fit. If the answer is no, it fits.
The case where it fits:
Mission should have the following property + attributes:
#[ORM\ManyToMany(targetEntity: Tag::class, inversedBy: 'missions', cascade: ['persist'])]
#[ORM\JoinTable(name: 'mission_tag')]
private Collection $tags;
and the Tag should have the following property + attributes:
#[ORM\ManyToMany(targetEntity: Mission::class, mappedBy: 'tags', cascade: ['persist'])]
#[ORM\JoinTable(name: 'mission_tag')] // <- is this the actual table name?
private Collection $missions;
the mappedBy and inversedBy must match the property name on the other entity, respectively.
If your mission_tag table doesn't have columns mission_id and tag_id, you might have to explicitly name the columns via the JoinColumn and InverseJoinColumn Attibutes.
For the case, where it doesn't fit, you would have to have an actual entity MissionTags with OneToMany on Mission/Tag that both reference it, and reversed ManyToOnes on MissionTags. But that wasn't what you asked.
BC\InventoryBundle\Entity\ProductRecipe:
type: entity
table: ProductRecipe
repositoryClass: BC\InventoryBundle\Entity\ProductRecipeRepository
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
amount: // Previously "ammount"
type: decimal
presision: 10
scale: 2
manyToOne:
products:
targetEntity: Product
// "Products" is named correctly but recipe is singular
// so for the sake of uniformity
inversedBy: recipes
joinColumn:
name: product_id
referencedColumnName: id
recipes:
targetEntity: Recipe
// Previously "Recipes", incorrect entity name
inversedBy: products
joinColumn:
name: recipe_id
referencedColumnName: id
I have two entities - one is Product, second - Order. I've created controllers to navigate between page of ProductController that lists the whole ProductRepository to the page of OrderController, which should:
take a Product ID to show its properties on the Order page (name and price)
have a form with quantity and "customer's" email to be added to the Order
calculate the price x quantity and add to the Order
I can't figure how to access the passed project's properties. I am able to access it via addProduct method of the Order entity, but cannot reach them to show them in twig nor manipulate them on variables as described above.
I'm sure it's a simple stuff but I am not sure even how to ask google the right way on this.
we click on a product from ProductRepository with
<a href="{{ path('app_order', {id: product.id}) }}">
and we go to OrderController
OrderController:
<?php
namespace App\Controller;
class OrderController extends AbstractController
{
#[Route('/order', name: 'app_order')]
public function addToOrder(ManagerRegistry $doctrine, Request $request, EntityManagerInterface $em): Response
{
$orderedProductId = (int) ($request->query->get('id'));
$pPrice = 1;
$order = new Order();
$order->addProduct($doctrine->getRepository(Product::class)->find($orderedProductId));
$form = $this->createFormBuilder($order)
->add('clientEmail', TextType::class)
->add('quantity', IntegerType::class)
->add('makeOrder', SubmitType::class, ['label' => 'Make Order'])
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//$data = $form->getData();
$order->setStatus("Pending");
$order->setTotalSum(1);
$em->persist($order);
$em->flush();
}
return $this->render('order/index.html.twig', [
'form'=> $form->createView(),
'order'=> $order
]);
}
}
The entities look like this:
Product
<?php
namespace App\Entity;
use App\Repository\ProductRepository;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: ProductRepository::class)]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string')]
private $name;
#[ORM\Column(type: 'integer')]
private $price;
#[ORM\Column(type: 'boolean')]
private $visible;
#[ORM\ManyToOne(targetEntity: Order::class, inversedBy: 'product')]
private $orderIds;
public function getId(): ?int
{
return $this->id;
}
public function getPrice(): ?int
{
return $this->price;
}
public function setPrice(int $price): self
{
$this->price = $price;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
public function isVisible(): ?bool
{
return $this->visible;
}
public function setVisible(bool $visible): self
{
$this->visible = $visible;
return $this;
}
public function getOrderIds(): ?Order
{
return $this->orderIds;
}
public function setOrderIds(?Order $orderIds): self
{
$this->orderIds = $orderIds;
return $this;
}
}
Order:
<?php
namespace App\Entity;
use App\Repository\OrderRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: OrderRepository::class)]
#[ORM\Table(name: '`order`')]
class Order
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'integer')]
private $totalSum;
#[ORM\Column(type: 'string', length: 255)]
private $status;
#[ORM\Column(type: 'integer')]
private $quantity;
#[ORM\OneToMany(mappedBy: 'orderIds', targetEntity: Product::class)]
private $product;
#[ORM\Column(type: 'string', length: 255)]
private $clientEmail;
public function __construct()
{
$this->product = new ArrayCollection();
}
public function getId(): ?int
{
return $this->id;
}
public function getTotalSum(): ?int
{
return $this->totalSum;
}
public function setTotalSum(int $totalSum): self
{
$this->totalSum = $totalSum;
return $this;
}
public function getStatus(): ?string
{
return $this->status;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function getQuantity(): ?int
{
return $this->quantity;
}
public function setQuantity(int $quantity): self
{
$this->quantity = $quantity;
return $this;
}
/**
* #return Collection<int, Product>
*/
public function getProduct(): Collection
{
return $this->product;
}
public function addProduct(Product $product): self
{
if (!$this->product->contains($product)) {
$this->product[] = $product;
$product->setOrderIds($this);
}
return $this;
}
public function removeProduct(Product $product): self
{
if ($this->product->removeElement($product)) {
// set the owning side to null (unless already changed)
if ($product->getOrderIds() === $this) {
$product->setOrderIds(null);
}
}
return $this;
}
public function getClientEmail(): ?string
{
return $this->clientEmail;
}
public function setClientEmail(string $clientEmail): self
{
$this->clientEmail = $clientEmail;
return $this;
}
}
OK I got this.
Changed a property taken by OrderController from EntityManager to ProductRepository, then
$product= $productRepository->find($orderedProductId);
also: this is helpful: Getting only ID from entity relations without fetching whole object in Doctrine
solved!
I've manage to create a form embedded in another form but I think I'm not doing something right. Here's my code
Category
class Category
{
private $id;
private $name;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="category")
*/
private $subcategorues;
public function __construct()
{
$this->subcategorues = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function addSubcategorue(\AppBundle\Entity\Category $subcategorues)
{
$this->subcategorues[] = $subcategorues;
return $this;
}
public function removeSubcategorue(\AppBundle\Entity\Category $subcategorues)
{
$this->subcategorues->removeElement($subcategorues);
}
public function getSubcategorues()
{
return $this->subcategorues;
}
}
Subcategory
class Subcategory
{
private $id;
private $name;
/**
* #ORM\ManyToOne(targetEntity="Category", inversedBy="subcategories")
* #ORM\JoinColumn(name="category_id", referencedColumnName="id")
*/
private $category;
/**
* #return mixed
*/
public function getCategory()
{
return $this->category;
}
/**
* #param mixed $category
*/
public function setCategory($category)
{
$this->category = $category;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
}
CategoryType
.......
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'entity', [
'class' => 'AppBundle\Entity\Category',
'choice_label' => 'name'
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Category'
]);
}
......
SubcategoryType
$builder
->add('category', new CategoryType(), [
'label' => false
])
->add('name', 'text')
->add('save', 'submit')
;
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => 'AppBundle\Entity\Subcategory'
]);
}
DefaultController
public function indexAction(Request $request)
{
$subcategory = new Subcategory();
$form = $this->createForm(new SubcategoryType(), $subcategory);
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$subcategory->setCategory($subcategory->getCategory()->getName());
$em->persist($subcategory);
$em->flush();
return new Response(sprintf('ID %d', $subcategory->getId()));
}
return $this->render('AppBundle::layout.html.twig', [
'form' => $form->createView(),
]);
}
Please notice this line of code $subcategory->setCategory($subcategory->getCategory()->getName());
I need that line in order to save the entity to the database otherwise I get an error. So my question is is there a way to skip this line of code and pass category object on the fly to subcategory->category property instead of doing that manually?
//EDIT
Here's the output of dump($form->getData());
DefaultController.php on line 33:
Subcategory {#467 ▼
-id: null
-name: "Uncharted"
-category: Category {#588 ▼
-id: null
-name: Category {#685 ▼
-id: 2
-name: "Games"
-subcategorues: PersistentCollection {#686 ▶}
}
-subcategorues: ArrayCollection {#660 ▶}
}
}
Your CategoryType is not correctly mapped compared to your Category entity. Actually, in your case, you don't need to have a sub-form CategoryType with a name field, since you have a category field in SubCategory which is a relationship towards Category.
Just replace:
->add('category', new CategoryType(), [
'label' => false
])
by:
->add('category', 'entity', [
'class' => 'AppBundle\Entity\Category',
'choice_label' => 'name'
]);
Could your try smth like this (for Category entity class):
public function addSubcategorue(\AppBundle\Entity\Category $subcategorues)
{
if ($this->subcategorues->contains($subcategorues)) {
$this->subcategorues->add($subcategorues);
$subcategorues->setCategory($this);
}
return $this;
}
I have 3 entities where the mapping is causing some issues.
The two problems im facing are:
When i use doctrine command line schema update, it removes the column "item_type" in the item_type_field table. It looks like doctrine is not seeing this column in the YML file, while it is there.
In the Symfony application the mapping between TypeField and FieldType is not working. When i dump the FieldType of a field it returns null.
Am i missing something?
Entities and mappings:
class ItemType
{
protected $id;
protected $title;
protected $description;
protected $fields;
public function __construct(){
$this->fields = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getTitle()
{
return $this->title;
}
public function setDescription($description)
{
$this->description = $description;
return $this;
}
public function getDescription()
{
return $this->description;
}
public function addField(\Aveqcms\CoreBundle\Entity\TypeField $field)
{
$field->setItemType($this);
$this->fields[] = $field;
return $this;
}
public function removeField(\Aveqcms\CoreBundle\Entity\TypeField $field)
{
$this->fields->removeElement($field);
}
public function getFields()
{
return $this->fields;
}
}
class TypeField
{
private $id;
private $item_type;
private $field_type;
public function getId()
{
return $this->id;
}
public function setItemType($itemType)
{
$this->item_type = $itemType;
return $this;
}
public function getItemType()
{
return $this->item_type;
}
public function setFieldType($fieldType)
{
$this->field_type = $fieldType;
return $this;
}
public function getFieldType()
{
return $this->field_type;
}
}
class FieldType
{
private $id;
private $title;
private $fields;
public function __construct()
{
$this->fields = new ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setTitle($title)
{
$this->title = $title;
return $this;
}
public function getTitle()
{
return $this->title;
}
}
Mapping files:
Acme\CoreBundle\Entity\ItemType:
type: entity
table: item_type
id:
id:
type: integer
generator: { strategy: AUTO }
fields:
title:
type: string
length: 255
description:
type: string
length: 255
oneToMany:
fields:
targetEntity: TypeField
mappedBy: item_type
cascade: [persist]
Acme\CoreBundle\Entity\TypeField:
type: entity
table: item_type_field
id:
id:
type: integer
generator: { strategy: AUTO }
manyToOne:
field_type:
targetEntity: FieldType
inversedBy: fields
joinColumn:
name: field_type
referencedColumnName: id
manyToOne:
item_type:
targetEntity: ItemType
inversedBy: fields
joinColumn:
name: item_type
referencedColumnName: id
Acme\CoreBundle\Entity\FieldType:
type: entity
table: field_type
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
title:
type: text
oneToMany:
fields:
targetEntity: TypeField
mappedBy: field_type
In TypeField you have manyToOne repeated twice. The second one overrides the first one whcih is why doctrine is not seeing it.
Acme\CoreBundle\Entity\TypeField:
type: entity
table: item_type_field
id:
id:
type: integer
generator: { strategy: AUTO }
manyToOne:
field_type:
targetEntity: FieldType
inversedBy: fields
joinColumn:
name: field_type
referencedColumnName: id
#manyToOne: *** Get rid of this ***
item_type:
targetEntity: ItemType
inversedBy: fields
joinColumn:
name: item_type
referencedColumnName: id
This may or may not fix all your issues. And it certainly does not address the issue of your very confusing naming conventions.
Ok so I currently have set up a one-to-many-to-one relation over 3 tables:
Recipes-ProductRecipe-Products
###Recipes.yml###
oneToMany:
products:
targetEntity: ProductRecipe
mappedBy: recipes
cascade: ["all"]
###ProductRecipe.yml###
manyToOne:
products:
targetEntity: Products
inversedBy: recipes
recipes:
targetEntity: Recipes
inversedBy: products
###Products.yml###
oneToMany:
recipes:
targetEntity: ProductRecipe
mappedBy: products
cascade: ["all"]
I have set up these entities using the built in command (doctrine:generate:entities) so I have all my setter/getters/_construct/_tostring in place.
Now I have all this set up (which works fine on it's own) I want the user to be able to add new products to a recipe from the recipe form. So I have made a collection of forms in the recipe form.
class RecipesType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('recipename')
->add('recipedesc')
->add('recipeyeild')
->add('recipecost');
$builder->add('products', 'collection', array(
'type' => new ProductRecipeType(),
'allow_add' => true,));
}
Which connects to this form:
class ProductRecipeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('ammount')
->add('products')
;
}
And in my Recipes Controller I have this:
public function newAction()
{
$entity = new Recipes();
$pr = new ProductRecipe();
$entity->getProducts()->add($pr);
$form = $this->createCreateForm($entity);
So far so good, the forms are created fine and persist to the database fine. However in my Many table the Recipe_ID field is not been filled with the recipe being created at that time. How do I fill that field on persist? Otherwise a user is creating a recipe and adding products to the recipe, but then when persisted I just have a new recipe with no products and a collection of product ammounts not connected to a recipe.
Sorry if I've written this up in a confusing way... It's because I'm confused myself.
EDIT
Just to help a little I'm adding my controllers code trimmed down a little:
class Recipes
{
private $id;
private $recipename;
private $recipedesc;
private $recipeyeild;
private $recipecost;
public function __construct()
{
$this->products = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function addProduct(\BC\InventoryBundle\Entity\ProductRecipe $pr)//<-this was $products
{
//$this->products[] = $products; <-Original code now changed to below as suggested.
$this->products->add($pr);
$pr->setRecipes($this);
return $this;
}
public function removeProduct(\BC\InventoryBundle\Entity\ProductRecipe $products)
{
$this->products->removeElement($products);
}
public function getProducts()
{
return $this->products;
}
And my ProductRecipe entity:
class ProductRecipe
{
private $id;
private $ammount;
private $products;
private $recipes;
public function getId()
{
return $this->id;
}
public function setAmmount($ammount)
{
$this->ammount = $ammount;
return $this;
}
public function getAmmount()
{
return $this->ammount;
}
public function setProducts(\BC\InventoryBundle\Entity\Products $products = null)
{
$this->products = $products;
return $this;
}
public function getProducts()
{
return $this->products;
}
public function setRecipes(\BC\InventoryBundle\Entity\Recipes $recipes = null)
{
$this->recipes = $recipes;
return $this;
}
public function getRecipes()
{
return $this->recipes;
}
Edit 2
So after Cerad's suggestion I've got these...
//Recipes entity
public function addProduct(\BC\InventoryBundle\Entity\ProductRecipe $products)
{
$this->products->add($products);
$products->setRecipes($this);
return $this;
}
//Recipes Controller
public function newAction()
{
$entity = new Recipes();
$products = new ProductRecipe();
$entity->getProducts()->add($products);
$entity->addProduct($products);
$form = $this->createCreateForm($entity);
return $this->render('BCInventoryBundle:Recipes:new.html.twig', array(
'entity' => $entity,
'form' => $form->createView(),
));
}
Still to no avail...
Edit 3
This Now works my issue was the majority of what I had in newAction should have been in the createAction. Works perfectly. Thank you Cerad.
This is a common problem. You are never calling the ProductRecipe::setRecipe() method. Probably not calling setProduct either.
class Recipe
{
public function addProduct($pr)
{
$this->products->add($pr);
$pr->setRecipe($this); // *** This is what you need to add
In your controller:
$entity->addProduct($pr);
And while not directly relevant, consider changing Products and Recipes to Product and Recipe.