Symfony2: How to add FormType - doctrine-orm

I have an Entity Product and I have an Entity File.
The relationship between those 2 is normally a ManyToMany Relation. But I need also a field Value "sorting" and "description", so I must declare my own JoinTable ProductHasFile. Now I want in my Form, that I can have a Product bute include the File FormTpye an not the ProductHasFile FormType.
Product Entity:
/**
* #ORM\Entity
* #ORM\Table(name="Product")
*/
class Product
{
/**
* #ORM\OneToMany(targetEntity="ProductHasFile", mappedBy="product", cascade={"persist"}))
*/
private $productHasFiles;
....
File Entity:
/**
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class File
{
/**
* #ORM\OneToMany(targetEntity="ProductHasFile", mappedBy="file")
*/
private $productHasFiles;
...
And my own generated Entity ProductHasFile:
/**
* #ORM\Entity
* #ORM\Table(name="ProductHasFile")
*/
class ProductHasFile
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $sorting;
/**
* #ORM\Column(type="string", nullable=true)
*/
private $description;
/**
* #ORM\ManyToOne(targetEntity="Product", inversedBy="productHasFiles")
* #ORM\JoinColumn(name="product_id", referencedColumnName="id")
*/
private $product;
/**
* #ORM\ManyToOne(targetEntity="File", inversedBy="productHasFiles")
* #ORM\JoinColumn(name="file_id", referencedColumnName="id")
*/
private $file;
When I now make my Formtype for Product:
class ProductType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
->add('productHasFiles', CollectionType::class, array(
'entry_type' => ProductHasFileType::class,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
,
)
)
and my FormType for ProductHasFileType:
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/**
* #var $entityManager EntityManager
*/
$fileRepository = $options['fileRepository'];
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', EntityType::class, array(
'class' => 'AppBundle\Entity\File',
'label' => 'file',
))
;
I get only a dropdwon for the File Entity. But I want to have my full File FormType which has the Uploadfield and other things, it looks like this:
class FileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextType::class, array(
'label' => 'description',
))
->add('type', TextType::class, array(
'label' => 'type',
))
->add('file', \Symfony\Component\Form\Extension\Core\Type\FileType::class, array(
'label' => 'file',
))
;
}
Does anybody has a solution for this?

As explained in Symfony doc about how to embed forms this is as easy as
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
/**
* #var $entityManager EntityManager
*/
$fileRepository = $options['fileRepository'];
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', FileType::class) //where FileType is your own FormType
;
}
}

In ProductHasFileType you're adding your file like this:
->add('file', EntityType::class, array(
'class' => 'AppBundle\Entity\File',
'label' => 'file',
))
so you are adding your file as entity type wich logically renders a dropdown of all file entities.
What you need to do is pass your own FileType.To make sure you are using your own file type and not the default symfony one don't forget to add the correct use statement.
result:
use MyBundle\Form\Type\FileType;
class ProductHasFileType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('sorting', TextType::class)
->add('description', TextType::class)
->add('file', FileType::class)
;

Related

Symfony 3.3 and doctrine 2

I have two entities Article and Image related by a ManytoMany relations using a third entity called ArticlesHaveImages.
I don't use classical ManytoMany relation of Doctrine because I have a third field in ArticlesHaveImages called sequence.
class Article
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
...
/**
* One Article has Many articleHaveImage.
* #ORM\OneToMany(targetEntity="ArticlesHaveImages", mappedBy="article")
*/
private $images;
class Image
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
...
...
/**
* One Image has Many articleHaveImage.
* #ORM\OneToMany(targetEntity="ArticlesHaveImages", mappedBy="image")
*/
private $articles;
public function __construct() {
$this->articles = new ArrayCollection();
}
class ArticlesHaveImages
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* Many ArticlesHaveImages have One Article.
* #ORM\ManyToOne(targetEntity="Article", inversedBy="images")
* #ORM\JoinColumn(name="article_id", referencedColumnName="id")
*/
private $article;
/**
* Many ArticlesHaveImages have One Image.
* #ORM\ManyToOne(targetEntity="Image", inversedBy="articles")
* #ORM\JoinColumn(name="image_id", referencedColumnName="id")
*/
private $image;
/**
* #var int
*
* #ORM\Column(name="sequence", type="integer")
*/
private $sequence;
How do I create a form out of this ?
I want a multiple select option which all images name.
I tried :
class ArticleType extends AbstractType
{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$images = $options['images'];
$builder
->add('translations', CollectionType::class, array(
'entry_type' => ArticleTranslationType::class,
'entry_options' => array('label' => false)))
->add('images', ChoiceType::class, array(
"required" => false,
"choices" => $images,
'multiple' => true,
But I get :
Could not determine access type for property "images" in class
"AppBundle\Entity\Article".
BIG THANKS !

upload not running with VichUploaderBundle

I would like to use a VichUploaderBundle for upload files in my symfony 3.2.3 project (PHP 5.6). I try lot of thing but nothing upload run. But the persistance layer run perfeclty with my database.
config.yml
knp_gaufrette:
stream_wrapper: ~
adapters:
fileupload_adapter:
local:
directory: %kernel.root_dir%/../web/
create: true
filesystems:
fileupload_fs:
adapter: fileupload_adapter
# VichUploaderBundle Configuration
vich_uploader:
db_driver: orm
twig: true
storage: gaufrette
mappings:
tgmedia_file:
uri_prefix: web
upload_destination: fileupload_fs
namer: vich_uploader.namer_origname
Entity
namespace MediaBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use TweedeGolf\MediaBundle\Model\AbstractFile;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
/**
* #ORM\Entity
* #Vich\Uploadable
* #ORM\HasLifecycleCallbacks
* #ORM\Table
*/
class FileUpload
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* NOTE: This is not a mapped field of entity metadata, just a simple property.
*
* #Vich\UploadableField(mapping="tgmedia_file", fileNameProperty="imageName")
*
* #var File
*/
protected $imageFile;
/**
* #ORM\Column(type="string", length=255)
*
* #var string
*/
private $imageName;
/**
* #ORM\Column(type="datetime")
*
* #var \DateTime
*/
protected $updatedAt;
/**
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* #return File|null
*/
public function getImageFile()
{
return $this->imageFile;
}
/**
* If manually uploading a file (i.e. not using Symfony Form) ensure an instance
* of 'UploadedFile' is injected into this setter to trigger the update. If this
* bundle's configuration parameter 'inject_on_load' is set to 'true' this setter
* must be able to accept an instance of 'File' as the bundle will inject one here
* during Doctrine hydration.
*
* #param File|\Symfony\Component\HttpFoundation\File\UploadedFile $image
*
* #return Product
*/
public function setImageFile(File $image = null)
{
$this->imageFile = $image;
if ($image) {
// It is required that at least one field changes if you are using doctrine
// otherwise the event listeners won't be called and the file is lost
$this->updatedAt = new \DateTimeImmutable();
}
return $this;
}
/**
* #param string $imageName
*
* #return FileUpload
*/
public function setImageName($imageName)
{
$this->imageName = $imageName;
return $this;
}
/**
* #return string|null
*/
public function getImageName()
{
return $this->imageName;
}
/**
* Set updatedAt
*
* #param \DateTime $updatedAt
*
* #return FileUpload
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* #return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}
The formType
<?php
namespace MediaBundle\Form;
use MediaBundle\Entity\File;
use MediaBundle\Entity\FileUpload;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Vich\UploaderBundle\Form\Type\VichFileType;
use Vich\UploaderBundle\Form\Type\VichImageType;
class FileUploadType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('imageName')
->add('imageFile', VichImageType::class, [
'required' => false,
'allow_delete' => true,
'download_link' => true,
'mapped' => false,
'data_class' => null
])
->add('submit', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => FileUpload::class,
'csrf_protection' => false,
));
}
}
Controller
<?php
namespace MediaBundle\Controller;
use MediaBundle\Entity\File;
use MediaBundle\Entity\FileUpload;
use MediaBundle\Entity\Product;
use MediaBundle\Form\FileType;
use MediaBundle\Form\FileUploadType;
use MediaBundle\Form\ProductType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class MainController extends Controller
{
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$fileUploaded = new FileUpload();
$form = $this->createForm(FileUploadType::class, $fileUploaded, array(
'action' => $this->generateUrl('media_main')
));
$form->handleRequest($request);
if ($form->isSubmitted())
{
$fileUploaded->setUpdatedAt(new \DateTime());
$fileUploaded->setImageFile($form->get('imageFile')->getData());
$em->persist($fileUploaded);
$em->flush();
}
return $this->render('MediaBundle:main:index.html.twig', array(
'form' => $form->createView()
));
}
}
Where is my error ? What wrong ?
uri_prefix: /project/web/fileupload_fs
upload_destination: '%kernel.root_dir%/../web/fileupload_fs'
uri_prefix begins from www folder.
and change controller like that
if ($form->isSubmitted() && $form->isValid())
{
$em->persist($fileUploaded);
$em->flush();
}

Symfony 3: UniqueEntity(errorPath) not working

I have an entity called ClassSubject and you can see it bellow,
/**
* ClassSubject
*
* #ORM\Table(name="class_subject")
* #ORM\Entity(repositoryClass="PIE10Bundle\Repository\ClassSubjectRepository")
*
* #UniqueEntity( fields={"class", "subjects"},
* errorPath="subjects",
* message="This subject is already added"
* )
*/
class ClassSubject
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Classes")
* #ORM\JoinColumn(name="class_id", referencedColumnName="id")
*/
private $class;
/**
* #var int
*
* #ORM\ManyToOne(targetEntity="Subject")
* #ORM\JoinColumn(name="subject_id", referencedColumnName="id")
*/
private $subjects;
as you can see, I want to make this Unique Entity so I am following this guide.
Further I have added the followings to the top of the entity file,
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
After all when I submit data it still adds duplicating rows to the table. For an example,
if the table exists with a row like, class=clsA and subjects=subA and if I try to insert with same values, it inserts data with no validation/message "This subject is already added"
My controller is like below,
public function addTeacherSubjectAction( $id, Request $request )
{
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('PIE10Bundle:Users')->find($id);
$subjects = $em->getRepository('PIE10Bundle:Subject')->findAll();
$form = $this->createForm(ClassSubjectType::class,$subjects);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid() )
{
$allSubjects = $form['subject']->getData();
foreach( $allSubjects as $subject )
{
$subject_repo = $em->getRepository('PIE10Bundle:Subject')->find($subject->getId());
$teacherSubject = new TeacherSubjects();
$teacherSubject->setTeachers($user);
$teacherSubject->setSubjects($subject_repo);
$em->persist($teacherSubject);
$em->flush();
}
$this->addFlash('notice',
'Subjects added');
return $this->redirectToRoute('user_teachers');
}
return $this->render( 'PIE10Bundle:Form:layout_commom_form.html.twig',
array( 'title' => 'Add Subjects',
'form' => $form->createView() )
);
}
and the ClassSubjectType is like below,
class ClassSubjectType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('subject',
EntityType::class,
array('class' => 'PIE10Bundle:Subject',
'expanded' => true,
'multiple' => true,
'attr' => array( 'class' => 'form-control',
'style' => 'margin:5px 0;')));
$builder->add('Add Classes',
SubmitType::class,
array('attr' => array('class' => 'btn btn-primary',
'style' => 'margin:15px 0;')) );
}
}
So I need to know what is wrong with my code. Thanks in advance.

Doctrine2 ORM OneToMany or ManyToOne not working

I would like to have the relationship between 2 tables, with OneToMany and ManyToOne relationships.
Survey
id
title
Questions:
id
survey_id
So i am looking to list the surveys and their respective questions, so how can i achieve that.
Here is my code,
Survey Entity:
<?php
namespace Survey\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
/**
* Description of Survey
*
* #author Mubarak
*/
/**
* #ORM\Entity
* #ORM\Table(name="surveys")
*/
class Survey extends BaseEntity{
public function __construct() {
$this->questions = new ArrayCollection();
}
/**
* #ORM\Column(name="title", type="string")
* #var string
*/
protected $title;
/**
* #ORM\Column(name="description", type="string")
* #var string
*/
protected $description;
/**
* #ORM\OneToMany(targetEntity="Survey\Entity\Questions", mappedBy="surveys")
*/
private $questions;
public function getTitle() {
return $this->title;
}
public function setTitle($title) {
$this->title = $title;
}
public function getDescription() {
return $this->description;
}
public function setDescription($description) {
$this->description = $description;
return $this;
}
public function getQuestions() {
return $this->questions;
}
public function setQuestions(ArrayCollection $questions) {
$this->questions = $questions;
}
public function __toString() {
return __CLASS__ . ": [id: {$this->id}, name: {$this->name}]";
}
}
Below is the Questions Entity:
<?php
namespace Survey\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Library\Entity\BaseEntity;
/**
* Description of Survey Questions
*
* #author Mubarak
*/
/**
* #ORM\Entity
* #ORM\Table(name="survey_questions")
*/
class Questions extends BaseEntity{
/**
* #ORM\Column(name="question", type="string")
* #var string
*/
protected $question;
/**
* #ORM\ManyToOne(targetEntity="Survey\Entity\Survey", inversedBy="questions")
* #ORM\JoinColumn(name="survey_id", referencedColumnName="id")
*/
private $surveys;
public function getQuestion() {
return $this->question;
}
public function setQuestion($question) {
$this->question = $question;
}
public function getSurveys() {
return $this->surveys;
}
public function setSurveys(ArrayCollection $surveys) {
$this->surveys = $surveys;
}
public function __toString() {
return __CLASS__ . ": [id: {$this->id}, name: {$this->name}]";
}
}
What is the mistake i am doing here, this is my code to get the surveys and questions:
$surveysInterface = $this->surveyService->getAllSurveys();
foreach($surveysInterface as $survey){
$surveysArray[] = array(
'id' => $survey->getId(),
'title' => $survey->getTitle(),
'description' => $survey->getDescription(),
'isActive' => $survey->getActive(),
'questions' => array(
'question' => $survey->getQuestions()->getQuestion()
)
);
}
'questions' => array(
'question' => $survey->getQuestions()->getQuestion()
)
This part look wrong since getQuestions() function return array of Questions entities. Suggest to change it to
'questions' => $survey->getQuestions()

ZF2 doctrine OneToMany annotation form

I have following entities:
User
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="Users\Entity\Repository\UsersRepository")
* #Annotation\Name("user")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ClassMethods")
*/
class User
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100, nullable=false)
* #Annotation\Filter({"name":"StringTrim"})
* #Annotation\Validator({"name":"StringLength", "options":{"min":2, "max":100}})
* #Annotation\Attributes({"type":"text","class":"form-control"})
* #Annotation\Options({"label":"Full name:"})
*/
private $name;
/**
* #var User\Entity\UserItem
*
* #ORM\OneToMany(targetEntity="User\Entity\UserItem", mappedBy="user")
* #Annotation\Exclude()
*/
private $items;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Annotation\Exclude()
*/
private $id;
public function __construct()
{
$this->vessels = new ArrayCollection();
}
public function setName($name)
{
$this->name = $name;
return $this;
}
public function getName()
{
return $this->name;
}
public function getId()
{
return $this->id;
}
public function getItems()
{
return $this->items
}
public function setItems($items)
{
$this->itemr = $items;
return $this;
}
Item
/**
* Items
* #ORM\Entity
* #ORM\Table(name="items")
* #ORM\Entity(repositoryClass="User\Entity\Repository\ItemsRepository")
* #Annotation\Name("item")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ClassMethods")
*/
class Item
{
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=100, nullable=false)
* #Annotation\Filter({"name":"StringTrim"})
* #Annotation\Validator({"name":"StringLength", "options":{"min":2, "max":100}})
* #Annotation\Attributes({"type":"text","class":"form-control"})
* #Annotation\Options({"label":"Name:"})
*/
private $name;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Annotation\Exclude()
*/
private $id;
And finally entity keepeing relation between those two
UserItem
/**
* UserItem
*
* #ORM\Table(name="users_items")
* #ORM\Entity(repositoryClass="User\Entity\Repository\UserItemsRepository")
* #Annotation\Name("user_item")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ClassMethods")
*/
class UserItem{
/**
* #var Users\Entity\User
*
* #ORM\ManyToOne(targetEntity="Users\Entity\User", inversedBy="items")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
* #Annotation\Exclude()
*/
private $user;
/**
* #var User\Entity\Item
*
* #ORM\ManyToOne(targetEntity="User\Entity\Item", inversedBy="users")
* #ORM\JoinColumn(name="item_id", referencedColumnName="id")
* #Annotation\Type("DoctrineModule\Form\Element\ObjectSelect")
*/
private $item;
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #Annotation\Exclude()
*/
private $id;
With this entities i can create and edit User and Item. Form are generated from annotations.
My question is how can i generate form for assigning items to user. Preferably as select field(s) with multiple selection. And after submitting doctrine should save the relations.
I wrote this form to represent items to select:
class ItemsForm extends Form
{
public function __construct($entityManager, $name = null)
{
parent::__construct('items');
$this->setAttribute('method', 'post');
$this->add(array(
'type' => 'DoctrineModule\Form\Element\ObjectSelect',
'name' => 'items',
'attributes' => array(
'id' => 'selectItems',
'multiple' => true,
'data-placeholder' => 'Items'
),
'options' => array(
'object_manager' => $entityManager,
'target_class' => 'User\Entity\Item',
'property' => 'name',
),
)
);
}
}
And now i do not know how to preselect items user has already assigned.
In select field in value you must have id from items. After submit you can do something like that
foreach($post['items'] as $itemId)
{
$userItem = new UserItem();
$userItem->user = $this->getEntityManager()->getReference('User', $userId);
$userItem->item = $this->getEntityManager()->getReference('Item', $itemId);
$this->getEntityManager()->save($userItem);
}