Product and Sizes symfony2 doctrine example - php

I'm new to symfony2 and I'm building my first online store with it. I have products and I want to add product sizes, one product can have many sizes and one size can have many products. For example: two products cat have 'M' size.
class Product {
...
/**
* #ORM\ManyToMany(targetEntity="Size", inversedBy="products", cascade={"persist", "merge"})
* #ORM\JoinTable(name="sizes")
*/
private $sizes;
}
//in another file
class Size {
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="sizes")
*/
protected $products;
}
ProductController.php
...
->add('sizes', CollectionType::class, [
'entry_type' => SizeType::class,
'label' => 'Sizes',
'allow_add' => true,
])
...
SizeType.php
public function buildForm(FormBuilderInterface $builder, array $options) {
$repo = $this->em->getRepository('AppBundle:Size');
$q = $repo->createQueryBuilder('c')
->getQuery();
$sizes = $q->getResult();
$builder->add('name', EntityType::class, array(
'class' => 'AppBundle:Size',
'choice_label' => 'name',
));
}
Right now I'm getting
Catchable Fatal Error: Object of class AppBundle\Entity\Size could not be converted to string I can fix if I implement __toString() but I don't know if this is the right thing to do, and if I do this, when editing the product, the dropdown doesn't select the right size.
My question is, is this the right way to implement product - sizes function to online store?

Try with this code:
$builder->add('name', EntityType::class, array(
'class' => 'AppBundle:Size',
'choice_label' => 'name',
'property' => 'needed_property_name' //just write the needed property name there
));

So I've figured out better way to do it, 'entity' type with multiple => true
->add('sizes', 'entity', [
'class' => Size::class,
'label' => 'Размери',
'choice_label' => 'name',
'multiple' => true,
'expanded' => false,
//'allow_add' => true,
])
This way multiple sizes can be selected, with bootstrap-multiselect I've made it good looking and perfectly works for me now.
I'd love to hear if there is a better way.

Product annotations looks wrong. The JoinTable is a lookup table for many-to-many relation:
The convention is to name it after linked tables: products_sizes in your case:
class Product {
...
/**
* #ORM\ManyToMany(targetEntity="Size", inversedBy="products", cascade={"persist", "merge"})
* #ORM\JoinTable(name="products_sizes")
*/
private $sizes;
}

Related

symfony OneToMany Form

I am using symfony to build the functions as below:
I have Product and ShippingWays.
One Product could have more ShippingWays and One ShippingWay only match one product.
ProductEntity:
/**
* #ORM\OneToMany(targetEntity="ShippingWay",mappedBy="product")
*/
private $shippingWays;
ShippingWay Entity:
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="shippingWays")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
**/
private $product;
Then I build both the ProductType and ShippingWayType.
ProductType
->add('shippingWays', EntityType::class, array(
'label' => ' Shipping Ways',
'translation_domain' => 'forms',
'class' => 'CoreBundle:ShippingWay',
'choice_label' => 'name',
'multiple' => true,
'required' => false,
))
ProductController
/**
* #Route("/admin/product/new", name="admin_product_new")
* #Template()
*/
public function newAction(Request $request)
{
$product = new Product();
$shippingWay= new ShippingWay();
$form = $this->createForm(ProductType::class, $product);
$shippingForm = $this->createForm(ShippingWayType::class, $shippingWay);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($product );
$em->flush();
return $this->redirect($this->generateUrl('admin_product'));
}
}
return(array('form' => $form->createView(),'users'=>$users,'shippingForm '=>$shippingForm ->createView()));
}
I'd like to add/edit the Shipping Way in the product page.
If there is no suitable shipping ways then I have to create a new one in the product page.
Something like this :
enter image description here
Currently I have 2 problems:
How to manage the Shipping way with Form as usual in Product page? Is there a general way to handle the forms under OneToMany & ManyToOne relationships?
I select Shipping way A when I add Product X, Then I could select Shipping Way A as before when I add product Y. I found that procuct_id with Shipping Way A always be Null.
Could someone give me some suggestions and references?
Thank you very much.
For your first problem : In your ProductType, you should not use EntityType for ShippingWay as it just display the list of existing entries related to ShippingWay entities.
If you want to add/edit, it would be better to use CollectionType, as specified here: How to Embed a Collection on a Symfony Form
In your case, your ProductType would be:
->add('shippingWays', CollectionType::class, array(
'label' => ' Shipping Ways',
'translation_domain' => 'forms',
'entry_type => 'CoreBundle:ShippingWay',
'choice_label' => 'name',
'allow_add' => true,
'allow_delete' => true
))

Multiple ChoiceType, array expected

I am trying to make a multiple-select ChoiceType in my Symfony form. However, I am getting the following error:
Unable to transform value for property path "[organisations]":
Expected an array.
Note: If I change the name of the component from organisations to anything else, it's rendering the form correctly.
As far as I can see, organisations is an array:
/**
* #var Organisation[]
* #ORM\ManyToMany(targetEntity="Booking\Entity\Organisation", inversedBy="users")
* #ORM\JoinTable(name="users_organisations")
*/
protected $organisations;
Here's the form:
$organisations = $doctrine->getRepository('Booking\Entity\Organisation')->findAll();
$builder
->add('organisations', ChoiceType::class, array(
'choices' => $organisations,
'choice_label' => function($organisation, $key, $index) {
return $organisation->getName();
},
'multiple' => true
))
What am I doing wrong?
Use EntityType instead.
$builder
->add('organisations', EntityType::class, array(
'choices' => $organisations,
'choice_label' => function($organisation, $key, $index) {
return $organisation->getName();
},
'multiple' => true
))
I posted this just because people (like me) don't usually read all the comments. Merit goes to Jakub.

Selected null-value for 1:N Doctrine relation in EntityType-field

I got incidents which have a reference to a risk.
I am using Symfony 2.6 and Doctrine ORM entities with the following rather symbolic entities:
class Incident
{
private $id;
private $name;
private $date;
/**
* #ORM\ManyToOne(targetEntity="Risk")
* #ORM\JoinColumn(name="risk_id")
*/
private $risk;
}
class Risk
{
private $id;
private $level;
private $name;
}
The risks-Table has data like:
id level name
1 0 none
2 2 low
3 1 very low
4 5 moderate
5 10 high
For the incidents I got a form-type like this:
class IncidentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
[...]
->add('risk', 'entity', [
'class' => 'Model:Risk',
'property' => 'name',
'empty_data' => null,
'empty_value' => 'not assessed',
'expanded' => true,
'required' => [true|false](see below),
])
[...]
}
So my incidents risk can be 'not assessed'. That is when there is no risk set (=null). To keep my model clean I would not want to have a row with a level of -1 and a name of 'not assessed'.
My form works as expected, missing one tiny detail: when the risk is null, my list of radiobuttons has no value selected. This happens regardless of the value of the 'required'-option.
For a null-value I get:
( ) not assessed
( ) none
( ) very low
[...]
I would want:
(x) not assessed
( ) none
( ) very low
[...]
I guess that behaviour is just fine for a (not expanded) drop-down field.
Is there a way to have the 'placeholder'/'empty_value'-option of my radiobuttons selected when the data-value is null?
For your risk field you should do:
class IncidentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
[...]
->add('risk', 'entity', [
'class' => 'Model:Risk',
'property' => 'name',
'empty_data' => null,
'empty_value' => 'not assessed',
'expanded' => true,
'required' => true
])
[...]
}
}
I just checked that and it works, the first value is selected when the risk is empty and when using the above code. Note I added 'empty_data' => null, 'empty_value' => 'not assessed', and removed placeholder.
that works but is not a good solution... what would happens if your first value is dynamic?

Symfony Entities in a collection

I couldn't find any similar questions so here goes:
I have a Many-To-Many relationship between my (order)Flow Entity and my Product Entity, but it doesn't select an <option> in the <select>.
All the Entities has been setup by Symfony itself so the #ORM\ManyToMany etc. should be ok.
My controller has the following line:
$form = $this->createForm(new \FDM\BestilBundle\Type\FlowsType(), $flow)->add('submit', 'submit');
The $flow is populated correctly from the db.
The FlowsType file has amoung many fields the following code:
->add('product', 'collection', [
'type' => new ProductDropdownType(),
'allow_add' => TRUE,
'allow_delete' => TRUE,
'prototype' => TRUE,
'by_reference' => FALSE,
]
)
All fields in the FlowsType are filled out correctly, except the one below.
The ProductDropdownType has the following code:
$builder->add('name', 'entity', [
'class' => 'FDMBestilBundle:Flows',
'property' => 'name',
'by_reference' => false
]);
The number of rows is correct - if I have three rows in my many-to-many sql table it shows three rows. They just aren't selected.
If I change the ProductDropdownType to:
$builder->add('name', 'text');
The data is showing just fine - so the db and everyting is working. Except when I want a collection of <select>...
The relations are the following in the Entities:
In the Flow Entity:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="FDM\BestilBundle\Entity\Product", inversedBy="flow")
* #ORM\JoinTable(name="productsinflows",
* joinColumns={
* #ORM\JoinColumn(name="flow", referencedColumnName="flowId")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="product", referencedColumnName="productId")
* }
* )
*/
private $product;
In the Product Entity:
/**
* #var \Doctrine\Common\Collections\Collection
*
* #ORM\ManyToMany(targetEntity="FDM\BestilBundle\Entity\Flows", mappedBy="product")
*/
private $flow;
Can someone please help me - I'm stuck!
You can use query builder in your form, this is an exemple :
In the query builder, create the SQL query with DQL
->add('espece', 'entity', array(
'class' => 'ADMapecheBundle:Espece',
'property' => 'nom',
'multiple' => false,
'query_builder' => function(EspeceRepository $er) use ($user) {
return $er->createQueryBuilder('p')
->where("p.user = :user")
->orderBy('p.nom', 'ASC')
->setParameter('user', $user);
I hope to help you, friendly

Doctrine 2: Understand how entities works with doctrine 2

I have 3 Entities: Person, Affiliation and PersonAffiliation.
In my form, I will display each affiliation as a checked checkbox.
Nowm when the user uncecks the checkbox and click submit, this affiliation should be removed from the PersonAffiliation table.
The problem is that when I submit without unchecking, my data are duplicated.
Example: aff1 and aff2. When both checked and submit, I will then get aff1 aff1 aff2 aff2.
The same, If I uncheck aff2, I will then have aff1 aff1.
The error is probably somewhere in using the doctrine:
Here is how I am managing that:
Entity Persom
#ORM\OneToMany(targetEntity="PersonAffiliation", mappedBy="person", cascade={"persist", "remove"})
protected $affiliations;
Entity Affiliation:
#ORM\OneToMany(targetEntity="PersonAffiliation", mappedBy="affiliation")
protected $person_affiliations;
Entity PersonAffiliation
#ORM\ManyToOne(targetEntity="Person", inversedBy="affiliations")
#ORM\JoinColumn(name="person_id", referencedColumnName="id")
protected $person;
#ORM\ManyToOne(targetEntity="Affiliation", inversedBy="person_affiliations")
#ORM\JoinColumn(name="affiliation_id", referencedColumnName="id")
protected $affiliation;
An idea on how to resolve that?
Thank you.
EDIT:
Cotroller part:
foreach( $enquiry->getAffiliations() as $aff )
{
$pAff = new PersonAffiliation();
$pAff->setPersonId( $person->getId() );
$pAff->setAffiliationId( $aff->getAffiliation()->getId() );
$pAff->setPerson( $person );
$pAff->setAffiliation( $aff->getAffiliation() );
$em->persist($pAff);
$em->flush();
}
Form Part:
public function buildForm(FormBuilder $builder, array $options)
{
$person = $this->person;
$user = $this->user;
$builder->add('firstname', 'text');
$builder->add('middlename', 'text', array('required'=>false));
$builder->add('lastname', 'text');
$builder->add('sex', 'choice', array( 'choices' => array('m' => 'Male', 'f' => 'Female'),
'required' => true,
'multiple' => false,
'expanded' => true));
$builder->add('email', 'text', array('required'=>false));
if( $this->addAffiliations ) {
$builder->add('affiliations', 'entity', array(
'label' => 'Athor\'s affiliations',
'class' => 'SciForumVersion2Bundle:PersonAffiliation',
'query_builder' => function($em) use ($person, $user){
return $em->createQueryBuilder('pa')
->where('pa.person_id = :pid')
->setParameter('pid', $person->getId());
},
'property' => 'affiliation',
'multiple' => true,
'expanded' => true,
));
}
}
In the Person entity :
/**
* #ORM\ManyToMany(targetEntity="Affiliation", inversedBy="people")
* #ORM\JoinTable(name="PersonAffiliation")
*/
protected $affiliations;
And in the Affiliation entity :
/**
* #ORM\ManyToMany(targetEntity="Person", mappedBy="affiliations")
*/
protected $people;
You made a $em->flush() on each iteration of the foreach. It should be done after the end of foreach isnt'it ?
Moreover, you may use a ManyToMany relation.

Categories