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
))
Related
When symfony collection fields are dynamicly added/removed in DOM with javascript, on save of this collection either validation or persisted collection elem ids get messed up. For example if I have Entity\User.php releation like:
/**
* #var \TestBundle\Entity\UserFollowers
*
* #ORM\OneToMany(targetEntity="TestBundle\Entity\UserFollowers", mappedBy="user", cascade={"persist"}, orphanRemoval=true)
* #Assert\Valid()
*/
protected $user_followers;
and backward Entity\UserFollower.php relation like:
/**
* #ORM\ManyToOne(targetEntity="TestBundle\Entity\User", inversedBy="user_followers")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* #var string
* #ORM\Column(name="name", type="string")
* #Assert\NotBlank()
*/
private $name;
And UserFormType like:
$builder->add('user_followers', CollectionType::class, array(
'entry_type' => UserFollowersType::class,
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__name__',
'by_reference' => false,
'label' => 'User followers'
))
and UserFollowersFormType like:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('name', TextType::class, array(
'attr'=> [
'class' =>'input input-group-field'
],
'label' => false
));
}
when form is handled and user persisted to db either form validation attaches to wrong collection elem or persisted id to db gets wrong, different DOM manipulaiton gets different error, controller is like:
$form->handleRequest($request);
if ($form->isValid()) {
$em->persist($user);
$em->flush();
}
Why is that happening? its like form collection indexes gets messed up....
When you dynamicly add new UserFollowersType form you also have to replace prototype_name __name__ with length of the collection.
On collection init:
var $collection = $(your collection container of forms );
$collection.data('index', $collection.children().length);
var index = $collection.data('index');
//-------on add new form
// Replace '__name__' in the prototype's HTML to
// instead be a number based on how many items we have
var newForm = prototype.replace(prototypeName, index);
// increase the index with one for the next item
$collection.data('index', index + 1);
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;
}
First of all thanks for looking into this.
I'm building a form to add categories to the db table and these categories can have a parent category (self referring). there is a dropdown to select the parent category. I'm using ZF2 and Doctrine 2 to build this form. Everything works fine but the only issue i have is that on the edit page, the parent category dropdown it shows the current category as well. I'd like to know how to exclude it from the dropdown. I'm posting some of my codes below. To keep it simple i removed some unrelated lines and shorten some names.
I defined the self referring relationship on the model
//Category model
use Doctrine\ORM\Mapping as ORM;
Class Category {
/**
*
* #var integer
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
.....
.....
/**
* Parent category if available
* #var self
* #ORM\OneToOne(targetEntity="Category")
* #ORM\JoinColumn(name="parent", referencedColumnName="id", nullable=true)
*/
protected $parent;
On the form I have a dropdown listing all the categories
$parent = new \DoctrineModule\Form\Element\ObjectSelect('parent');
$parent->setOptions(array(
'label' => 'Parent Category',
'class' => '',
'object_manager' => $this->em,
'target_class' => \Data\Entity\Category::class,
'property' => 'name',
'display_empty_item' => true,
'empty_item_label' => '- Select Category -',
'required' => false
))->setAttributes(array(
'class' => 'form-control'
));
On the edit controller i load the form and and bind it to the db entry
public function editAction()
{
//get id from url
$id = $this->params()->fromRoute('id', 0);
$request = $this->getRequest();
//load selected category from database
$category = $this->em->getRepository(\Data\Entity\Category::class)->find($id);
//create form
$form = new Form\Category($this->em);
//bind selected category to form
$form->bind($category);
......
}
Thanks.
You need to pass the category id of the category being edited to the form and set object selects search params to pass the id to the entity repository. You will then need to create a search query in the repository to exclude the category id from being returned in the search results.
You can pass the category id to the form with a simple setter.
protected $categoryId;
public function setCategoryId($categoryId)
{
$this->categoryId = $categoryId;
}
In your form you will need something like
$parent->setOptions(array(
'label' => 'Parent Category',
'class' => '',
'object_manager' => $this->em,
'target_class' => \Data\Entity\Category::class,
'property' => 'name',
'is_method' => true,
'find_method' => array(
'name' => 'findCategories',
'params' => array(
'searchParams' => array('id' => $this->categoryId),
),
),
'display_empty_item' => true,
'empty_item_label' => '- Select Category -',
'required' => false
))->setAttributes(array(
'class' => 'form-control'
));
and in your categories repository
public function findCategories($searchParams)
{
$builder = $this->getEntityManager()->createQueryBuilder();
$builder->select('c')
->from(\Data\Entity\Category::class, 'c')
->where('c.id != ?1')
->setParameter(1, $searchParams['id'])
->orderBy('c.category', 'ASC');
return $builder->getQuery()->getResult(Query::HYDRATE_OBJECT);
}
note the orderBy is optional.
I hope this makes sense.
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
I have created a simple user/role form. The form shows the user's detail correctly and displays all the possible roles, but for some reason it does not pre-select the users' current role. For the relationship between the user and role I had the following in the user entity class:
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users", cascade={"persist","remove"})
* #ORM\JoinTable(name="user_role")
*/
protected $roles;
The formtype class was built using:
$builder->add('firstname')
->add('lastname')
->add('email')
->add('roles');
The database looks like this:
Any hints/assistance would be appreciated.
You need to define your roles fields as entity
http://symfony.com/doc/current/reference/forms/types/entity.html
change this line ->add('roles'); to:
->add('roles', 'entity', array(
'multiple' => true,
'expanded' => true,
'property' => 'name',
'class' => 'Your_Path\Entity\Roles',
));
it should work.
Second option:
you can try to create role type form as mentioned here and then do something like this
$builder->add('roles', 'collection', array('type' => new RoleType()));
its recomended to read this this about mapped option and other as by_reference
Had the same problem in symfony4, adding this did the trick for me:
'choice_value' => function ($choice) {
return $choice;
},