Form setter option Symfony 3 - forms

I'm using Symfony 3 to build a website. I have an Entity (Users) that is in OneToOne relation with itself in order to make couples. (I didn't have others idea on how to do it easily)
The end goal is to create a form to reference the id of the other Users in the couple. So I created an IntegerType field and assign it the id but I can't set it (because there are no setId(...)). So I would know if there is a setter option (can't find in Doc/Tests), and if there isn't how could I achieve this ?
The steps to register a new couple would have been:
Send new id (of the other Users) [FORM]
Fetch the other Users ($userCouple = ...findOne...) [BDD]
If he have $couple == null then $userCouple->setCouple($this) and $this->setCouple($userCouple)
So my Users entity looks like:
<?php
namespace Acme\UserBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\User as BaseUser;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="Acme\UserBundle\Repository\UsersRepository")
*/
class Users extends BaseUser
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToOne(targetEntity="Acme\UserBundle\Entity\Users")
* #ORM\JoinColumn(nullable=true)
*/
protected $couple;
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* Set couple.
*
* #param \Acme\UserBundle\Entity\Users|null $couple
*
* #return Users
*/
public function setCouple(\Acme\UserBundle\Entity\Users $couple = null)
{
$this->couple = $couple;
return $this;
}
/**
* Get couple.
*
* #return \Acme\UserBundle\Entity\Users|null
*/
public function getCouple()
{
return $this->couple;
}
}
And my form looks like :
<?php
namespace Acme\UserBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
class ProfileFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('couple', IntegerType::class, array(
'label' => 'Couple ID',
'property_path' => 'couple.id',
'attr' => array('min' => 0),
));
}
public function getBlockPrefix()
{
return 'acme_user_profile';
}
}

You should solve this using a one-to-one self-referencing relation (see more here). Basically your couple would be replaced by partner which suites best the case:
...
/**
* #OneToOne(targetEntity="User")
* #JoinColumn(name="partner_id", referencedColumnName="id")
*/
protected $partner;
...
Then in the form you could use the EntityType (not sure why you wanted to use IntegerType in the first place) and do something like this:
$builder->add('users', EntityType::class, array(
// query choices from this entity
'class' => 'UserBundle:User',
// use the User.username property as the visible option string
'choice_label' => 'username',
));
Of course you can exclude the user you're editing the profile from the list of users you show as possible partners using query_builder option (passing a custom query) or choices to pass the collection of User entities you want to use (getting them first and filter out current user).

Related

Getting Symfony3 dropdown field, built using EntityType, to default to previous value

I'm struggling with finding the right solution to this problem.
I have a form with a dropdown which I want to generate dynamically via a call to a DB using the EntityType field. Everything works perfectly however I have been unable to get it to default to the previous value upon edit. All other fields in the form (all text) default to the previous value and if I build the field manually using ChoiceType it defaults as expected.
I have tried using the 'data' option to manually set it but it did not work. Short of building this as a ChoiceType and sending the data via the controller are there other things I should attempt?
Here is my form class TerminologyType.php
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\ORM\EntityRepository;
class TerminologyType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// Event listener added to prevent users from editing the unique
// code after it has been created.
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$terminology = $event->getData();
$form = $event->getForm();
// check if the Terminology object is "new"
// This should be considered a new "Terminology" and user must enter a code
if (!$terminology || null === $terminology->getCode()) {
$form->add('code');
}
});
$builder
->add('name')
->add('version')
->add('description')
->add('status', EntityType::class, [
'class' => 'AppBundle:CodeSet',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->where('c.terminologyCode = :code')
->setParameter('code', 'entity_status')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'choice_value' => 'code',
]);
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Terminology',
));
}
}
Controller
<?php
namespace AppBundle\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use AppBundle\Entity\Terminology;
use AppBundle\Form\TerminologyType;
/**
* Terminology controller.
*
* #Route("/admin/terminology")
*/
class TerminologyController extends Controller
{
/**
* Lists all Terminology entities.
*
* #Route("/", name="terminology_index")
* #Method("GET")
*/
....
/**
* Displays a form to edit an existing Terminology entity.
*
* #Route("/{code}/edit", name="terminology_edit")
* #Method({"GET", "POST"})
*/
public function editAction(Request $request, Terminology $terminology)
{
$deleteForm = $this->createDeleteForm($terminology);
$editForm = $this->createForm('AppBundle\Form\TerminologyType', $terminology);
$editForm->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em->persist($terminology);
$em->flush();
return $this->redirectToRoute('terminology_show', array('code' => $terminology->getCode()));
}
return $this->render('terminology/edit.html.twig', array(
'terminology' => $terminology,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Entity
<?php
// src/AppBundle/Entity/Terminology.php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* #ORM\Entity
* #Gedmo\Loggable
* #ORM\Table(name="ts_terminology")
* #Gedmo\SoftDeleteable(fieldName="dateDeleted", timeAware=false)
* #UniqueEntity("code")
*
*/
class Terminology extends TerminologyServices
{
/**
* #ORM\Column(type="string", length=75)
* #ORM\OneToMany(targetEntity="CodeSet", mappedBy="terminologyCode")
* #ORM\Id
*/
protected $code;
/**
* #ORM\Column(type="string", length=75, nullable=true)
* #ORM\OneToOne(targetEntity="CodeSet")
* #ORM\JoinColumn(name="status", referencedColumnName="code")
* #Gedmo\Versioned
*/
protected $status;
I was able to track down the problem. In the entity definition for status I had defined the column type, length, etc in the annotations.
#ORM\Column(type="string", length=75, nullable=true)
By removing this it began defaulting to the last entered value (however this subsequently broke other things). I believe this has to do with EntityType expecting an object but the entity type being defined as a string and only providing that (which explains why ChoiceType option worked).

Same Table, Two entities (or class) in symfony

I'd like to build two class that refer to the same table.
For example:
Table Invoice:
idInvoice, IdClient, idProduct.
I've got one entity that shows general information (idClient and idInvoice) and than I've got an entity that allows me to show a dynamically generated form (id Product).
For every invoice I need one or more product so I should be able to insert different row for different product.
How can I link this different entities on the same table?
When I submit the form I need to extract all data from Product information and all data from Client and then I need to insert on MySQL.
I have got ORDER TABLE and PRODUCT TABLE. They are in a one to many relationship (one order, more product.) I created two entities: Order and Product and these are the classes:
ORDER CLASS
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="ordini")
*/
class Ordine
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idOrdine;
/**
*
* #ORM\OnetoMany(targetEntity="Prodotto", cascade={"persist"},
mappedBy="prodotto")
*
*/
protected $prodotti;
/**
* Get idOrdine
*
* #return integer
*/
public function getIdOrdine()
{
return $this->idOrdine;
}
public function __costruct()
{
$this->prodotti= new ArrayCollection();
}
public function setProdotti(ArrayCollection $prodotti)
{
$this->prodotti = $prodotti;
//return $this;
}
public function addProdotti(Prodotto $prod) {
$this->prodotti[] = $prod;
return $this;
}
public function addProdotto(Prodotto $prodotto) {
$this->prodotti[] = $prodotto;
$prodotto->setOrdine($this);
return $this;
}
public function getProdotti() {
return $this->prodotti;
}
}
PRODUCT CLASS
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="prodotto")
*/
class Prodotto
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $idProdotto;
/**
* #ORM\Column(type="string", length=100, nullable=TRUE)
*
*/
protected $modello;
/**
* #ORM\ManyToOne(targetEntity="Ordine", inversedBy="prodotti")
* #var AppBundle\Entity\Ordine
*/
protected $ordini;
/**
* Get idProdotto
*
* #return integer
*/
public function getIdProdotto()
{
return $this->idProdotto;
}
/**
* Set modello
*
* #param string $modello
* #return Prodotto
*/
public function setModello($modello)
{
$this->modello = $modello;
return $this;
}
/**
* Get modello
*
* #return string
*/
public function getModello()
{
return $this->modello;
}
}
Then I created a Form that allows me to create an order (invoice) and I can dynamically generate Product Field.
I should be on the right way but now it gives me an error:
Expected argument of type "Doctrine\Common\Collections\ArrayCollection",
"array" given
My OrderForm is:
namespace AppBundle\Form\Documentofiscale;
use Symfony\Component\Form\TextField;
use Symfony\Component\Form\Checkbox;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use AppBundle\Entity\Prodotto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class FormDocumentoFiscale extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// $builder->add('idCliente');
//$builder->add('save', 'submit', array('label' => 'INSERISCI
DOCUMENTO'));
$builder->add('prodotti', 'collection', array(
'type' => new FormAggiuntaProdotti(),
'allow_add' => true,
'by_reference' => false,
)
);
$builder->add('save', 'submit', array('label' => 'INSERISCI
DOCUMENTO'));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Ordine',
));
}
public function getName()
{
return 'documento';
}
}
And my dynamically generated Form is:
namespace AppBundle\Form\Documentofiscale;
use Symfony\Component\Form\TextField;
use Symfony\Component\Form\Checkbox;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use AppBundle\Entity\Prodotto;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class FormAggiuntaProdotti extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('modello');
//// $builder->add('idCliente');
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Prodotto',
));
}
public function getName()
{
return 'prodotto';
}
}

In the Symfony2 documentation you can embed a collection of forms to add object, but how do you select from existing objects to add to the collection?

Not sure that was the clearest way of asking my question, but basically I want to try and achieve the below in Symfony2:
It is important to note that I have tried the embed a collection as per the documentation, but I am not looking to create a new object and add to the collection I am trying to select an existing object to add to the collection.
I currently have a product group entity that simply consists of an ID, Name and collection of products:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Product Group
* #ORM\Table(name="productGroups")
* #ORM\HasLifecycleCallbacks
*/
class ProductGroup
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="Product", mappedBy="productGroups")
**/
protected $products;
...
I then have the product entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Product
* #ORM\Table(name="products")
* #ORM\HasLifecycleCallbacks
*/
class Product
{
/**
* #var integer
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var string
* #ORM\Column(name="name", type="string", length=255)
*/
protected $name;
/**
* #var string
* #ORM\Column(name="code", type="string", length=20)
*/
protected $name;
/**
* #ORM\ManyToMany(targetEntity="ProductGroup", mappedBy="products")
**/
protected $productGroups;
...
What I am trying to do is create a form that allows me to create and update a product group.
I want to enter a name for the product group and then select products by ticking a check box against the rows of a table. The idea being that I could use something like datatables to filter the products to make it easier to add.
To start this off I have added a Product Group Type:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductGroupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('products', 'collection', array(
'type' => new ProductType()
));
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\ProductGroup',
));
}
public function getName()
{
return 'product_group';
}
}
I then have the Product Type:
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name');
$builder->add('code');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Product',
));
}
public function getName()
{
return 'product';
}
}
Now I am very confused about how I can have a table of products that have a checkbox that once selected and the form saved are saved against the product group.
Can anyone help or suggest any other way of doing this?
I have attached an image of what I am trying to achieve if that helps.
Thanks in advance.
Use the entity form type:
http://symfony.com/doc/current/reference/forms/types/entity.html
$builder->add('name', 'text')
->add('code', 'text')
->add(
'ProductGroups', 'entity', array(
'class' => 'YourBundle:ProductGroups',
'label' => 'Select ProductTypes',
'placeholder' => 'Select ProductGroups',
'multiple' => true,
'expanded' => true
))
->getForm();
}
The Multiple tag will generate a list of checkboxes that allows you to select multiple productGroups

Gedmo Doctrine Extensions Tree can't create category without a parent

I'm using the Gedmo Doctrine Extensions to handle Categories as a nested set.
I'm building a REST API and I have a route to create a Category.
I want to be able to create root Categories or child Categories.
Here is the entity
<?php
namespace AppBundle\Entity;
use Gedmo\Mapping\Annotation as Gedmo;
use Doctrine\ORM\Mapping as ORM;
/**
* #Gedmo\Tree(type="nested")
* #ORM\Table(name="bo_categories")
* use repository for handy tree functions
* #ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
*/
class Category
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue
*/
private $id;
/**
* #ORM\Column(name="name", type="string", length=64)
*/
private $name;
/**
* #Gedmo\TreeLeft
* #ORM\Column(name="lft", type="integer")
*/
private $lft;
/**
* #Gedmo\TreeLevel
* #ORM\Column(name="lvl", type="integer")
*/
private $lvl;
/**
* #Gedmo\TreeRight
* #ORM\Column(name="rgt", type="integer")
*/
private $rgt;
/**
* #Gedmo\TreeRoot
* #ORM\Column(name="root", type="integer", nullable=true)
*/
private $root;
/**
* #Gedmo\TreeParent
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent")
* #ORM\OrderBy({"lft" = "ASC"})
*/
private $children;
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function setParent(Category $parent = null)
{
$this->parent = $parent;
}
public function getParent()
{
return $this->parent;
}
}
Here is the form
<?php
namespace AppBundle\Form\Type\Classification;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class CategoryFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('name')
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
))
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\Category',
'csrf_protection' => false,
));
}
public function getName()
{
return 'api_category';
}
}
And here is the controller
/**
* #Route("/create")
* #Security("has_role('ROLE_SUPER_ADMIN')")
* #Rest\View
*/
public function postCreateAction(Request $request)
{
$categoryManager = $this->get('app.manager.category');
$category = $categoryManager->createNew();
$form = $this->createForm(new CategoryFormType(), $category);
// $category->setParent(10);
$form->handleRequest($request);
if ($form->isValid()) {
$categoryManager->save($category);
return new Response('', 201);
} else {
return $this->view($form, 400);
}
}
If I want to create a child category, it works fine. But if I want to create a root category without removing the "parent" field in the form I get this error
An exception occurred while executing 'SELECT b0_.id AS id0, b0_.name AS name1, b0_.lft AS lft2, b0_.lvl AS lvl3, b0_.rgt AS rgt4, b0_.root AS root5, b0_.parent_id AS parent_id6 FROM bo_categories b0_ WHERE b0_.id IN (?)' with params [""]:\n\nSQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "",
"class": "Doctrine\DBAL\DBALException
Why do I get this error ? Can't the "parent" value be empty/null in the form ?
There is absolutely no problem parent being NULL. Your current mapping on the other hand, doesn't allow that.
First, you should modify your mapping for $parent
#ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
should allow null values:
#ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE", nullable=true)
Don't forget to run app/console doctrine:schema:update --force when you change your mappings!
Next, your form looks okay, but it miss one property - empty_value. This won't force you to choose parent category.
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
))
should be like this:
->add('parent', 'entity', array(
'class' => 'AppBundle:Category',
'property' => 'id',
'empty_value' => '-- Select parent --'
))
This should add extra option with no value and should be selected by default (when creating new category). Since your field is of type entity you will see all of your categories as well (when you need to select parent).
This should do the trick, give it a try.

Entity field form Symfony2

My goal is to display a social networks form in users dashboard like :
- Facebook : <input>
- Twitter : <input>
- Linkedin : <input>
Social networks are dynamic (manage by administration panel).
I've got currently 3 Entities (Social, UserSocial and User (FOS...)).
In User entity, I just added it :
/**
*#ORM\OneToMany(targetEntity="Application\SocialBundle\Entity\UserSocial", mappedBy="user")
*/
private $userSocials;
My Social entity :
class Social
{
/**
* #var integer
*/
private $id;
/**
* #var string
*/
private $name;
/**
* #var string
*/
private $class;
...
}
My UserSocial entity :
class UserSocial
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*#ORM\ManyToOne(targetEntity="Admin\UserBundle\Entity\User", inversedBy="userSocials")
*/
private $user;
/**
*#ORM\ManyToMany(targetEntity="Admin\SocialBundle\Entity\Social", inversedBy="userSocials")
*/
private $social;
/**
* #var string
*
* #ORM\Column(name="value", type="string", length=255)
*/
// Not really sure about it
private $value;
....
And finally, the UserSocialType, this is where i'm stuck :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('social', 'entity', array(
'class' => 'Admin\SocialBundle\Entity\Social',
'property' => 'name',
'required' => false,
))
->add('value')
;
}
It gave me just a select with all social name in DB and a value input. I would like a value input for each social entity in DB.
Thank you in advance for your help.
May be you do it a little different?
Craete a SocialType:
$builder
->add('social', 'text', array(
'data' => $existingSocial
))
...
;
UserSocialType:
$builder
->add('social', new SocialType(), array(
// pass social to the Social type
...
))
...
;
PS// These are just my general thoughts, here are some wrong lines my snippet, you should not pass an object to the text field, but you can use data transformers to get an object from the text value.. Also I think your UserSocial entity unproperly configured, $social field should receive manyToOne relationship