Assert does not works in my Form - symfony

I have a strange problem using a form in Symfony 3.
And my Assert does not works in my entity file.
In "the developer tools" it show the phrase but in the view all is in blank.
<?php
namespace TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
/**
* User
*
* #ORM\Table(name="users")
* #UniqueEntity("name")
* #UniqueEntity("email")
* #ORM\HasLifecycleCallbacks()
*/
class User implements UserInterface
{
/**
* #Assert\NotBlank()
* #Assert\NotBlank(message="Please enter your name.")
*/
private $name;
/**
* #Assert\NotBlank()
* #ORM\Column(type="string", length=50, nullable=false)
*/
private $sername;
/**
* #var string
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #var string
*/
private $password;
/**
* #var integer
*/
private $role;
/**
* #var \DateTime
*/
private $created;
/**
* #var integer
*/
private $id;
/**
* Set name
*
* #param string $name
*
* #return User
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set sername
*
* #param string $sername
*
* #return User
*/
public function setSername($sername)
{
$this->sername = $sername;
return $this;
}
/**
* Get sername
*
* #return string
*/
public function getSername()
{
return $this->sername;
}
/**
* Set email
*
* #param string $email
*
* #return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* #return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* #param string $password
*
* #return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set role
*
* #param integer $role
*
* #return User
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role
*
* #return integer
*/
public function getRole()
{
return $this->role;
}
/**
* Set created
*
* #param \DateTime $created
*
* #return User
*/
public function setCreated($created)
{
$this->created = $created;
return $this;
}
/**
* Get created
*
* #return \DateTime
*/
public function getCreated()
{
return $this->created;
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
public function getRoles()
{
return array($this->role);
}
public function getSalt()
{
return null;
}
public function eraseCredentials()
{
}
/**
* Get username
*
* #return string
*/
public function getUsername()
{
return null;
}
}
My form Action ( I put required in false to use the symfony valid option):
public function addAction(Request $request){
$user = new User();
$form = $this->createFormBuilder($user)
->add('Name', TextType::class,array('required'=> false,'empty_data' => null))
->add('SerName', TextType::class,array('required'=> false,'empty_data' => null))
->add('role', TextType::class,array('required'=> false,'empty_data' => null))
->add('email', EmailType::class,array('required'=> false,'empty_data' => null))
->add('password', RepeatedType::class, array(
'type' => PasswordType::class,
'first_name' => 'pass',
'second_name' => 'confirm',
'required' => false,
))
->add('save', SubmitType::class, array('label' => 'Create Post'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
//$user = new Users();
// $form->getData() holds the submitted values
// but, the original `$task` variable has also been updated
$password = $form->get('password')->getData();
$encoder = $this->container->get('security.password_encoder');
$encoded = $encoder->encodePassword($user,$password);
$user->setPassword($encoded);
$user = $form->getData();
// ... perform some action, such as saving the task to the database
// for example, if Task is a Doctrine entity, save it!
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
return $this->redirectToRoute('test_homepage');
}
//return $this->render('UserBundle:Default:add.html.twig');
return $this->render("TestBundle:Default:add.html.twig",array("form"=>$form->createView()));
}
EDIT (I PUT VIEW FILE):
My view:
{%extends "principal.html.twig"%}
{% block body %}
{{parent()}}
<h1> {% trans %}New User{% endtrans %}</h1>
{{ form_start(form,{'attr':{'novalidate':'novalidate','role':'form'}}) }}
{{ form_label(form.Name,'Username', {'label_attr': {'class': 'foo'}}) }}
{{ form_widget(form.Name,{'attr':{'class':'form-control'}}) }}
{{form_errors(form.Name)}}
{{ form_label(form.SerName,'Last name', {'label_attr': {'class': 'foo'}}) }}
{{ form_errors(form.SerName) }}
{{ form_widget(form.SerName,{'attr':{'class':'form-control'}}) }}
{{ form_label(form.role,'Role', {'label_attr': {'class': 'foo'}}) }}
{{ form_errors(form.role) }}
{{ form_widget(form.role,{'attr':{'class':'form-control'}}) }}
{{ form_label(form.email,'Email', {'label_attr': {'class': 'foo'}}) }}
{{ form_widget(form.email, {'attr':{'class':'form-control'}}) }}
{{ form_errors(form.email) }}
{{ form_label(form.password.pass, "Password", {'attr':{'class':'form-control'}}) }}
{{ form_errors(form.password) }}
{{ form_widget(form.password.pass, {'attr':{'class':'form-control'}}) }}
{{ form_label(form.password.confirm, "Confirm" , {'attr':{'class':'form-control'}}) }}
{{ form_widget(form.password.confirm, {'attr':{'class':'form-control'}}) }}
{{ form_widget(form.save, { 'label': 'Create User','attr':{'class':'btn btn-primary'}}) }}
{{ form_end(form) }}
{% endblock %}

You should read the doc about form rendering
In your twig template, you have to display the errors by using
{{ form_errors(form.age) }}
Or the whole row (label , error message, inpout) with
{{ form_row(form.age) }}
Which is almost equal to
<div>
{{ form_label(form.age) }}
{{ form_errors(form.age) }}
{{ form_widget(form.age) }}
</div>
In your case, it means you have to add form_errors this way:
{{ form_label(form.Name,'Username', {'label_attr': {'class': 'foo'}}) }}
{{ form_widget(form.Name,{'attr':{'class':'form-control'}}) }}
{{ form_errors(form.Name) }}

Related

Syfmony: upload files with dropzone

I'm developing a king of simple CMS, with Symfony 4.1.
Regarding my question, we have 2 entities:
Post Entity:
<?php
namespace App\Entity;
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="App\Repository\PostRepository")
*/
class Post extends BaseEntity
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="text")
*/
private $content;
/**
* #ORM\Column(type="boolean")
*/
private $status;
/**
* #ORM\ManyToMany(targetEntity="App\Entity\Category", inversedBy="posts")
*/
private $categories;
/**
* #ORM\OneToMany(targetEntity="App\Entity\Picture", mappedBy="post", orphanRemoval=true, cascade={"persist"})
*/
private $pictures;
/**
* #Assert\All({#Assert\Image(mimeTypes="image/jpeg")})
*
*/
private $pictureFiles;
/**
* Post constructor.
*/
public function __construct()
{
$this->categories = new ArrayCollection();
$this->pictures = new ArrayCollection();
}
/**
* #return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #return null|string
*/
public function getContent(): ?string
{
return $this->content;
}
/**
* #param string $content
* #return Post
*/
public function setContent(string $content): self
{
$this->content = $content;
return $this;
}
/**
* #return bool|null
*/
public function getStatus(): ?bool
{
return $this->status;
}
/**
* #param bool $status
* #return Post
*/
public function setStatus(bool $status): self
{
$this->status = $status;
return $this;
}
/**
* #return Collection|Category[]
*/
public function getCategories(): Collection
{
return $this->categories;
}
/**
* #param Category $category
* #return Post
*/
public function addCategory(Category $category): self
{
if (!$this->categories->contains($category)) {
$this->categories[] = $category;
}
return $this;
}
/**
* #param Category $category
* #return Post
*/
public function removeCategory(Category $category): self
{
if ($this->categories->contains($category)) {
$this->categories->removeElement($category);
}
return $this;
}
/**
* #return Collection|Picture[]
*/
public function getPictures(): Collection
{
return $this->pictures;
}
/**
* #param Picture $picture
* #return Post
*/
public function addPicture(Picture $picture): self
{
if (!$this->pictures->contains($picture)) {
$this->pictures[] = $picture;
$picture->setPost($this);
}
return $this;
}
/**
* #param Picture $picture
* #return Post
*/
public function removePicture(Picture $picture): self
{
if ($this->pictures->contains($picture)) {
$this->pictures->removeElement($picture);
if ($picture->getPost() === $this) {
$picture->setPost(null);
}
}
return $this;
}
/**
* #return mixed
*/
public function getPictureFiles()
{
return $this->pictureFiles;
}
/**
* #param $pictureFiles
* #return Post
*/
public function setPictureFiles($pictureFiles): self
{
foreach ($pictureFiles as $pictureFile) {
/** #var Picture $picture */
$picture = new Picture();
$picture->setImageFile($pictureFile);
$this->addPicture($picture);
}
$this->pictureFiles = $pictureFiles;
return $this;
}
}
Picture Entity:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\HttpFoundation\File\File;
/**
* #ORM\Entity(repositoryClass="App\Repository\PictureRepository")
*/
class Picture
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #var File|null
* #Assert\Image(mimeTypes="image/jpeg")
*/
private $imageFile;
/**
* #ORM\Column(type="string", length=255)
*/
private $filename;
/**
* #ORM\ManyToOne(targetEntity="App\Entity\Post", inversedBy="pictures")
* #ORM\JoinColumn(nullable=false)
*/
private $post;
/**
* #return int|null
*/
public function getId(): ?int
{
return $this->id;
}
/**
* #return File|null
*/
public function getImageFile(): ? File
{
return $this->imageFile;
}
/**
* #param File|null $imageFile
* #return Picture
*/
public function setImageFile(? File $imageFile): self
{
$this->imageFile = $imageFile;
return $this;
}
/**
* #return string|null
*/
public function getFilename(): ?string
{
return $this->filename;
}
/**
* #param string $filename
* #return Picture
*/
public function setFilename(string $filename): self
{
$this->filename = $filename;
return $this;
}
/**
* #return Post|null
*/
public function getPost(): ?Post
{
return $this->post;
}
/**
* #param Post|null $post
* #return Picture
*/
public function setPost(?Post $post): self
{
$this->post = $post;
return $this;
}
}
So for adding a Post, I have a PostType:
<?php
namespace App\Form;
use App\Entity\Category;
use App\Entity\Post;
use FOS\CKEditorBundle\Form\Type\CKEditorType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
/**
* Class PostType
* #package App\Form
*/
class PostType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('content', CKEditorType::class)
->add('categories', EntityType::class,
[
'class' => Category::class,
'required' => true,
'choice_label' => 'name',
'multiple' => true,
]
)
->add('pictureFiles', FileType::class,
[
'required' => false,
'multiple' => true,
'label' => 'Add files...',
'attr' =>
[
'action' => '%kernel.project_dir%/public/media/posts'
]
]
)
->add('status')
;
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Post::class,
]);
}
}
The view corresponding to that form:
{% form_theme form '/admin/form/switch_btn_layout.html.twig' %}
{{ form_start(form) }}
{{ form_errors(form) }}
<div class="form-row">
<div class="col-md-6">
{{ form_row(form.name) }}
{{ form_row(form.categories) }}
{{ form_row(form.status) }}
</div>
<div class="col-md-6 dropzone" id="postDropzone">
{{ form_row(form.pictureFiles, {'attr': {'class': 'dropzone'}} ) }}
<div class="dropzone-previews" style="border: 1px solid red"></div>
</div>
</div>
<div class="form-group">
{{ form_row(form.content) }}
</div>
<div class="form-group">
{{ form_row(form.status) }}
</div>
{{ form_rest(form) }}
<button class="btn btn-success btn-lg btn-block" id="postSubmit">
{{ button_label|default('Save') }}
</button>
{{ form_end(form) }}
As you can see, the "input" for files as the dropzone css class.
Indeed, my project include the oneup_uploader bundle, for dropzone.
Here the configuration for oneup_uploader:
oneup_uploader:
mappings:
# This is a mapping example, remove it and create your own mappings.
post_image:
frontend: dropzone
namer: oneup_uploader.namer.uniqid
storage:
directory: '%kernel.project_dir%/public/media/posts'
And my script for Dropzone:
Dropzone.autoDiscover = false;
var postDropzone = new Dropzone('.dropzone', {
url: '%kernel.project_dir%/public/media/posts',
// url: 'file/post',
maxFiles: 10,
addRemoveLinks: true,
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
});
postDropzone.on("addedfile", function (file) {
file.previewElement.addEventListener("click", function () {
postDropzone.removeFile(file);
})
});
The issue for me is:
no file is save in the folder
the Post entity is save in my DB, but nothing is save for Pictures.
I also tried to not use OneUploaderBundle, and use VichUploader: the saving part in DB is perfect, but I can't link it to dropzone.
Some help guys ?
Thanks a lot !
Might be useful for new visitors.
You can use a library that extends Symfony Form and adds a new type DropzneType.
1.Install the library
composer require emrdev/symfony-dropzone
This way you will have a new form type DropzoneType
2.Use the type in your form like this
public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options)
{
// userFiles is OneToMany
$builder->add('userFiles', DropzoneType::class, [
'class' => File::class,
'maxFiles' => 6,
'uploadHandler'=>'uploadHandler', // route name
'removeHandler'=> 'removeHandler'// route name
]);
}
Change the uploadHandler and removeHandler options to your endpoints
3.Route uploadHandler/removeHandler might look something like this
/**
* #Route("/uploadhandler", name="uploadHandler")
*/
public function uploadhandler(Request $request, ImageUploader $uploader) {
$doc = $uploader->upload($request->files->get('file'));
$file = new File();
$file->setSrc($doc['src']);
...
$this->getDoctrine()->getManager()->persist($file);
$this->getDoctrine()->getManager()->flush();
return new JsonResponse($file);
}
/**
* #Route("/removeHandler/{id}", name="removeHandler")
*/
public function removeHandler(Request $request,File $file = null) {
$this->getDoctrine()->getManager()->remove($file);
$this->getDoctrine()->getManager()->flush();
return new JsonResponse(true);
}
note that uploadhandler should return a File object
you should pass upload url instead of upload directory
generate url in twig - {{ oneup_uploader_endpoint('post_image') }}
var postDropzone = new Dropzone('.dropzone', {
url: '{{ oneup_uploader_endpoint('post_image') }}',
// url: '%kernel.project_dir%/public/media/posts',
// url: 'file/post',
maxFiles: 10,
addRemoveLinks: true,
autoProcessQueue: false,
uploadMultiple: true,
parallelUploads: 100,
});

Translations not working with Symfony 3.3, KNP Doctrine Behaviors & Sonata Admin

Could someone explain me what i am doing wrong. I have an entity with classes Actualite and ActualiteTranslation.
I created ActualiteAdmin class with sonanta:admin:generate.
Everything is working as expected. I debug fields with dump($actualite) in the controller and i see that each fields are well displayed :
array:1 [▼
0 => Actualite {#742 ▼
-id: 8
-date: DateTime {#806 ▶}
-image: null
#translations: PersistentCollection {#709 ▼
-snapshot: array:1 [ …1]
-owner: Actualite {#742}
-association: array:16 [ …16]
-em: EntityManager {#789 …11}
-backRefFieldName: "translatable"
-typeClass: ClassMetadata {#738 …}
-isDirty: false
#collection: ArrayCollection {#662 ▼
-elements: array:1 [▼
"fr" => ActualiteTranslation {#705 ▼
#actucategorie: "categorie"
#alt: "image"
#titre: "Page Facebook"
#article: "mon article"
#id: 4
#locale: "fr"
#translatable: Actualite {#742}
#slug: "page-facebook"
}
]
}
#initialized: true
}
#newTranslations: null
#currentLocale: "fr"
#defaultLocale: "fr"
}
]
I have thir error in Twig Template when using {{actualite.translations.titre}}:
Key "translations" for array with keys "0" does not exist.
This is the code I use :
namespace BDN\ActualitesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* Actualite
*
* #ORM\Table(name="bdn_actualite")
* #ORM\Entity(repositoryClass="BDN\ActualitesBundle\Repository\ActualiteRepository")
*
*/
class Actualite
{
use ORMBehaviors\Translatable\Translatable;
/**
* #param $method
* #param $args
*
* #return mixed
*/
public function __call($method, $args)
{
if (!method_exists(self::getTranslationEntityClass(), $method)) {
$method = 'get' . ucfirst($method);
}
return $this->proxyCurrentLocaleTranslation($method, $args);
}
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var \DateTime
*
* #ORM\Column(name="date", type="datetime")
*/
private $date;
/**
* #var string
*
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set date
*
* #param \DateTime $date
*
* #return null
*/
public function setDate($date)
{
$this->date = $date;
return $this;
}
/**
* Get date
*
* #return \DateTime
*/
public function getDate()
{
return $this->date;
}
/**
* Set image
*
* #param string $image
*
* #return null
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
}
namespace BDN\ActualitesBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Knp\DoctrineBehaviors\Model as ORMBehaviors;
/**
* ActualiteTranslation
*
* #ORM\Entity
* #ORM\Table(name="bdn_actualite_translation")
*
*/
class ActualiteTranslation
{
use ORMBehaviors\Translatable\Translation;
use ORMBehaviors\Sluggable\Sluggable;
/**
* #var string
*
* #ORM\Column(name="actucategorie", type="string", length=50)
*/
protected $actucategorie;
/**
* #var string
*
* #ORM\Column(name="alt", type="string", length=255)
*/
protected $alt;
/**
* #var string
*
* #ORM\Column(name="titre", type="string", length=255, unique=true)
*/
protected $titre;
public function getSluggableFields()
{
return ['titre'];
}
/**
* #var string
*
* #ORM\Column(name="article", type="text")
*/
protected $article;
/**
* Set actucategorie
*
* #param string $actucategorie
*
* #return ActualiteTranslation
*/
public function setActucategorie($actucategorie)
{
$this->actucategorie = $actucategorie;
return $this;
}
/**
* Get actucategorie
*
* #return string
*/
public function getActucategorie()
{
return $this->actucategorie;
}
/**
* Set alt
*
* #param string $alt
*
* #return ActualiteTranslation
*/
public function setAlt($alt)
{
$this->alt = $alt;
return $this;
}
/**
* Get alt
*
* #return string
*/
public function getAlt()
{
return $this->alt;
}
/**
* Set titre
*
* #param string $titre
*
* #return ActualiteTranslation
*/
public function setTitre($titre)
{
$this->titre = $titre;
return $this;
}
/**
* Get titre
*
* #return string
*/
public function getTitre()
{
return $this->titre;
}
/**
* Set article
*
* #param string $article
*
* #return ActualiteTranslation
*/
public function setArticle($article)
{
$this->article = $article;
return $this;
}
/**
* Get article
*
* #return string
*/
public function getArticle()
{
return $this->article;
}
}
<!-- begin snippet: js hide: false console: true babel: false -->
<?php
namespace BDN\ActualitesBundle\Admin;
use Sonata\AdminBundle\Admin\AbstractAdmin;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use A2lix\TranslationFormBundle\Form\Type\TranslationsType;
class ActualiteAdmin extends AbstractAdmin
{
/**
* #param DatagridMapper $datagridMapper
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('id')
->add('date')
->add('image')
;
}
/**
* #param ListMapper $listMapper
*/
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('id')
->add('date')
->add('image')
->add('_action', null, array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
),
))
;
}
/**
* #param FormMapper $formMapper
*/
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('date')
->add('image')
->add("translations", TranslationsType::class, array(
"fields" => array(
"actucategorie" => [],
"alt" => [],
"titre" => [],
"slug" => ["display" => false,],
"article" => [ "field_type" => "text"],
),
))
;
}
/**
* #param ShowMapper $showMapper
*/
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('id')
->add('date')
->add('image')
->add("translations", TranslationsType::class, array(
"required_locales" => ['fr'],
"fields" => array(
"actucategorie" => [],
"alt" => [],
"titre" => [],
"article" => [ "field_type" => "text"],
),
))
;
}
}
{% extends "::base.html.twig" %}
{% block page %}
<section class="row jumbotron">
<div class="col-lg-12">
<div class="row">
<div class="col-xs-9">
<h3>{{ actualite.getTitre() }}</h3>
<p class="categorie">Le {{ actualite.date | date('d/m/Y') }} | Catégorie : {{ actualite.translate.actucategorie }}</p>
</div>
{% if actualite.image is not null %}
<div class="col-xs-3 hidden-xs">
<img src="{{ asset('actualite.image') }}" class="img-thumbnail img-shop" alt="{{ actualite.alt }}">
</div>
{% endif %}
</div>
<div class="row content-actualite">
<div class="col-lg-12">
{{ actualite.translate.article }}
</div>
</div>
</div>
</section>
{% endblock %}
Any help would be very appreciated!
Denis

Symfony and Database

I just make my 2 entity, one is Article and one is Commento, that are comments.
I want a comment form in every single article, so I created the 2 entity, the action in the controller, and the template.
ARTICLE ENTITY
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* Article
*
* #ORM\Table()
* #ORM\HasLifecycleCallbacks
* #ORM\Entity
*/
class Article
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="titolo", type="string", length=255)
*/
private $titolo;
/**
* #var string
*
* #ORM\Column(name="autore", type="string", length=255)
*/
private $autore;
/**
* #var string
*
* #ORM\Column(name="testo", type="text")
*/
private $testo;
/**
* #var string
*
* #ORM\Column(name="categoria", type="string", length=100)
*/
private $categoria;
/**
* #var string $image
* #Assert\File( maxSize = "1024k", mimeTypesMessage = "Perfavore inserisci un'immagine valida!")
* #ORM\Column(name="image", type="string", length=255, nullable=true)
*/
private $image;
/**
* #var date
*
* #ORM\Column(name="data", type="date")
*/
public $data;
/**
* #ORM\OneToMany(targetEntity="Commento", mappedBy="commento")
*/
protected $commento;
public function __construct()
{
$this->commento = new ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set titolo
*
* #param string $titolo
*
* #return Article
*/
public function setTitolo($titolo)
{
$this->titolo = $titolo;
return $this;
}
/**
* Get titolo
*
* #return string
*/
public function getTitolo()
{
return $this->titolo;
}
/**
* Set autore
*
* #param string $autore
*
* #return Article
*/
public function setAutore($autore)
{
$this->autore = $autore;
return $this;
}
/**
* Get autore
*
* #return string
*/
public function getAutore()
{
return $this->autore;
}
/**
* Set testo
*
* #param string $testo
*
* #return Article
*/
public function setTesto($testo)
{
$this->testo = $testo;
return $this;
}
/**
* Get testo
*
* #return string
*/
public function getTesto()
{
return $this->testo;
}
/**
* Set image
*
* #param string $image
*/
public function setImage($image)
{
$this->image = $image;
}
/**
* Get image
*
* #return string
*/
public function getImage()
{
return $this->image;
}
public function getFullImagePath() {
return null === $this->image ? null : $this->getUploadRootDir(). $this->image;
}
protected function getUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
return $this->getTmpUploadRootDir().$this->getId()."/";
}
protected function getTmpUploadRootDir() {
// the absolute directory path where uploaded documents should be saved
return __DIR__ . '/../../../web/imgArticoli/';
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function uploadImage() {
// the file property can be empty if the field is not required
if (null === $this->image) {
return;
}
if(!$this->id){
$this->image->move($this->getTmpUploadRootDir(), $this->image->getClientOriginalName());
}else{
$this->image->move($this->getUploadRootDir(), $this->image->getClientOriginalName());
}
$this->setImage($this->image->getClientOriginalName());
}
/**
* #ORM\PostPersist()
*/
public function moveImage()
{
if (null === $this->image) {
return;
}
if(!is_dir($this->getUploadRootDir())){
mkdir($this->getUploadRootDir());
}
copy($this->getTmpUploadRootDir().$this->image, $this->getFullImagePath());
unlink($this->getTmpUploadRootDir().$this->image);
}
/**
* Set data
*
* #param \DateTime $data
*
* #return Article
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get data
*
* #return \DateTime
*/
public function getData()
{
return $this->data;
}
/**
* Set categoria
*
* #param string $categoria
*
* #return Article
*/
public function setCategoria($categoria)
{
$this->categoria = $categoria;
return $this;
}
/**
* Get categoria
*
* #return string
*/
public function getCategoria()
{
return $this->categoria;
}
/**
* Add commento
*
* #param \AppBundle\Entity\Commento $commento
*
* #return Article
*/
public function addCommento(\AppBundle\Entity\Commento $commento)
{
$this->commento[] = $commento;
return $this;
}
/**
* Remove commento
*
* #param \AppBundle\Entity\Commento $commento
*/
public function removeCommento(\AppBundle\Entity\Commento $commento)
{
$this->commento->removeElement($commento);
}
/**
* Get commento
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getCommento()
{
return $this->commento;
}
}
COMMENTO ENTITY
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Commento
*
* #ORM\Table()
* #ORM\HasLifecycleCallbacks
* #ORM\Entity
*/
class Commento
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nome", type="string", length=255)
*/
private $nome;
/**
* #var string
*
* #ORM\Column(name="testo", type="text")
*/
private $testo;
/**
* #var datetime
*
* #ORM\Column(name="data", type="datetime")
*/
public $data;
/**
*#ORM\ManyToOne(targetEntity="Article",inversedBy="commenti")
* #ORM\JoinColumn(name="article_id",referencedColumnName="id")
*/
protected $article;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nome
*
* #param string $nome
*
* #return Commento
*/
public function setNome($nome)
{
$this->nome = $nome;
return $this;
}
/**
* Get nome
*
* #return string
*/
public function getNome()
{
return $this->nome;
}
/**
* Set testo
*
* #param string $testo
*
* #return Commento
*/
public function setTesto($testo)
{
$this->testo = $testo;
return $this;
}
/**
* Get testo
*
* #return string
*/
public function getTesto()
{
return $this->testo;
}
/**
* Set data
*
* #param \DateTime $data
*
* #return Commento
*/
public function setData($data)
{
$this->data = $data;
return $this;
}
/**
* Get data
*
* #return \DateTime
*/
public function getData()
{
return $this->data;
}
/**
* Set article
*
* #param \AppBundle\Entity\Article $article
* #return Commento
*/
public function setArticle(\AppBundle\Entity\Article $article=null)
{
$this->article = $article;
return $this;
}
/**
* Get article
*
* #return \AppBundle\Entity\Article
*/
public function getArticle()
{
return $this->article;
}
}
ACTIONS IN THE CONTROLLER
public function singlearticleAction(Request $request, $id)
{
//Codice di singlearticle
$art = $this->getDoctrine()->getEntityManager();
$article = $art->getRepository('AppBundle:Article')->find($id);
if (!$article)
{
throw $this->createNotFoundException('Non riesco a trovare questo articolo!');
}
//Fin qui
$commento = new Commento();
$commento->setData(new \DateTime('now'));
$form = $this->createForm(new CommentoType($article), $commento);
$request = $this->getRequest();
$form->handleRequest($request);
if ($form->isValid())
{
if ($article)
{
$commento->setArticle($article);
}
$em = $this->getDoctrine()->getManager();
try
{
$em->persist($commento);
$em->flush();
return $this->redirect('singlearticle');
} catch (\Exception $e)
{
$form->addError(new FormError('errore nel database'));
}
}
return $this->render('default/singlearticle.html.twig', array(
'article' => $article,
'commento' => $commento,
'form' => $form->createView()));
}
public function inserisci_commentoAction(Request $request)/* ROTTA "inserisci_commento" */
{
$commento = new Commento();
$commento->setData(new \DateTime('now'));
$form = $this->createForm(new CommentoType($commento), $commento);
$request = $this->getRequest();
$form->handleRequest($request);
if ($form->isValid())
{
$em = $this->getDoctrine()->getManager();
try
{
$em->persist($commento);
$em->flush();
return $this->redirect('singlearticle');
} catch (\Exception $e)
{
$form->addError(new FormError('errore nel database'));
}
}
return $this->render('default/singlearticle.html.twig', array(
'commento' => $commento,
'form' => $form->createView()));
}
AND THE TEMPLATE
{% extends 'base.html.twig' %}
{% block body %}{% endblock %}
{% block maincontent %}
<div class="maincontent-title">{{article.titolo}} di {{article.autore}}</div>
<br>
<div class="tipologia">Categoria: {{article.categoria}}</div>
<div class="link" style="float:right;">Modifica</div>
<div class="link" style="float:right;padding-right: 25px;">Elimina</div>
<div class="rigaseparatrice"></div>
<br>
<article>
<p>
{%if article.image is not empty%}
<img class="imgarticolo" src="{{ asset('imgArticoli/' ~ article.id ~'/' ~ article.image)}}"/>
{%endif%}
<div class="titolo"></div>
<br>
<div class="testoarticolo">{{article.testo}}</div>
<br><br>
<br><br>
<div class="form-commento">
INSERISCI UN COMMENTO!
<form action="{{ path('inserisci_commento',{id:commento.id}) }}" method="post" {{ form_enctype(form) }} >
{{ form_errors(form) }}
{{ form_row(form.nome) }}
<br>
{{ form_row(form.testo) }}
<br>
{{ form_rest(form) }}
<input id="bottone" type="submit" value="INSERISCI" />
</form>
</div>
</p>
</article>
{% endblock %}
So, the problem is that when you insert a comment in the form, and I submit the form, I get an error, even if the data are now saved in the db.
Only one data (article_id) that is a Commento's parameter, is NULL, and I don't know why.
This is the error:
No route found for "GET /singlearticle" (from
"http://local/Sito/web/app_dev.php/singlearticle/1")
You need a refactor to the two actions from controller:
public function singlearticleAction($id)
{
//Codice di singlearticle
$art = $this->getDoctrine()->getEntityManager();
$article = $art->getRepository('AppBundle:Article')->find($id);
if (!$article){
throw $this->createNotFoundException('Non riesco a trovare questo articolo!');
}
//Fin qui
$form = $this->createForm(new CommentoType($article), new Commento());
return $this->render('default/singlearticle.html.twig', array(
'article' => $article,
'form' => $form->createView())
);
}
public function inserisci_commentoAction(Request $request, $articleId)
{
//Codice di singlearticle
$em = $this->getDoctrine()->getEntityManager();
$article = $em->getRepository('AppBundle:Article')->find($articleId);
if (!$article) {
throw $this->createNotFoundException('Non riesco a trovare questo articolo!');
}
$commento = new Commento();
$form = $this->createForm(new CommentoType($commento), $commento);
$form->handleRequest($request);
if ($form->isValid())
{
try
{
$commento->setData(new \DateTime('now'));
$commento->setArticle($article);
$em->persist($commento);
$em->flush();
// add a success message to session flashbag
} catch (\Exception $e)
{
// add a error message to session flashbag
}
}
return $this->redirect($this->generateUrl('singlearticle', array('id'=> $articleId)));
}
The route definition for inserisci_commento needs to be changed to:
inserisci_commento:
path: /singlearticle/{articleId}/inserisci_commento
defaults: {_controller: AppBundle:Default:inserisci_commento}
And in twig replace {{ path('inserisci_commento',{id:commento.id}) }} with {{ path('inserisci_commento',{articleId: article.id}) }}
Hope this helps
The problem is in your redirect statement. You need to pass in the article id.
return $this->redirect('singlearticle',array('id' => $article->getId());
whats definetly wrong is the mappedBy attribute in article class
/**
* #ORM\OneToMany(targetEntity="Commento", mappedBy="commento")
*/
protected $commento;
must be
/**
* #ORM\OneToMany(targetEntity="Commento", mappedBy="article")
*/
protected $commento;
and you have a typo in inversedBy attribute in comment class
/**
*#ORM\ManyToOne(targetEntity="Article",inversedBy="commenti")
* #ORM\JoinColumn(name="article_id",referencedColumnName="id")
*/
protected $article;
must be
/**
*#ORM\ManyToOne(targetEntity="Article",inversedBy="commento")
* #ORM\JoinColumn(name="article_id",referencedColumnName="id")
*/
protected $article;

Sonata admin bundle, manipulate objects

I have 2 entities with one to many relationship project and prototype And I've been looking for a way to list the prototypes that belong to a project in the show action .
here is my project entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Projet
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\ProjetRepository")
*/
class Projet
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #var \DateTime
*
* #ORM\Column(name="dateCreation", type="date")
*/
private $dateCreation;
/**
* #ORM\OneToMany(targetEntity="AppBundle\Entity\Prototype", mappedBy="projet",cascade={"persist"} , orphanRemoval=true)
* #ORM\OrderBy({"id"="ASC"})
*/
protected $prototypes;
public function __construct()
{
$this->prototypes = new \Doctrine\Common\Collections\ArrayCollection();
$this->dateCreation = new \DateTime("now");
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set nom
*
* #param string $nom
* #return Projet
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
public function __toString()
{
return $this->getNom();
}
/**
* Set description
*
* #param string $description
* #return Projet
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set dateCreation
*
* #param \DateTime $dateCreation
* #return Projet
*/
public function setDateCreation($dateCreation)
{
$this->dateCreation = $dateCreation;
return $this;
}
/**
* Get dateCreation
*
* #return \DateTime
*/
public function getDateCreation()
{
return $this->dateCreation;
}
public function setPrototypes($prototypes)
{
if (count($prototypes) > 0) {
foreach ($prototypes as $i) {
$this->addPrototypes($i);
}
}
return $this;
}
/**
* Add prototypes
*
* #param \AppBundle\Entity\Prototype $prototypes
* #return Projet
*/
public function addPrototypes(\AppBundle\Entity\Prototype $prototypes)
{
$this->prototypes[]= $prototypes;
return $this;
}
public function addPrototype(\AppBundle\Entity\Prototype $prototype)
{
$prototype->setProjet($this);
$this->prototypes->add($prototype);
}
/**
* Remove prototypes
*
* #param \AppBunble\Entity\Prototype $prototypes
*/
public function removePrototypes(\AppBundle\Entity\Prototype $prototypes)
{
$this->prototypes->removeElement($prototypes);
}
/**
* Get prototypes
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getPrototypes()
{
return $this->prototypes;
}
}
and here is my prototype entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Prototype
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="AppBundle\Entity\PrototypeRepository")
*/
class Prototype
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="nom", type="string", length=255)
*/
private $nom;
/**
* #var string
*
* #ORM\Column(name="description", type="string", length=255)
*/
private $description;
/**
* #var \DateTime
*
* #ORM\Column(name="dateCreation", type="date")
*/
private $dateCreation;
/**
* #ORM\ManyToOne(targetEntity="AppBundle\Entity\Projet", inversedBy="prototypes")
* #ORM\joinColumn(name="projet_id", referencedColumnName="id")
*/
private $projet;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Get nom
*
* #return string
*/
public function getNom()
{
return $this->nom;
}
public function __toString()
{
return $this->getNom();
}
/**
* Set nom
*
* #param string $nom
* #return Prototype
*/
public function setNom($nom)
{
$this->nom = $nom;
return $this;
}
/**
* Set description
*
* #param string $description
* #return Prototype
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set dateCreation
*
* #param \DateTime $dateCreation
* #return Prototype
*/
public function setDateCreation($dateCreation)
{
$this->dateCreation = $dateCreation;
return $this;
}
/**
* Get dateCreation
*
* #return \DateTime
*/
public function getDateCreation()
{
return $this->dateCreation;
}
/**
* Set projet
*
* #param \AppBundle\Entity\Projet $projet
* #return Prototype
*/
public function setProjet(\AppBundle\Entity\Projet $projet = null)
{
$this->projet = $projet;
return $this;
}
/**
* Get projet
*
* #return \AppBundle\Entity\Projet
*/
public function getProjet()
{
return $this->projet;
}
}
In my projetAdmin I can show in the ShowAction the prototypes :
here is projetAdmin :
<?php
namespace AppBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Sonata\AdminBundle\Route\RouteCollection;
class ProjetAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('nom', 'text', array('label' => 'Nom'))
->add('description','text',array('label'=>'Description'))
->add('dateCreation', 'date', array('label' => 'Date de création'))
;
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('nom')
->add('dateCreation')
;
}
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('nom')
->add('description')
->add('dateCreation')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
)
)
)
;
}
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('prototypes')
;
}
}
I get the prototypes ( names )
But I would like to "list" the prototypes that belong to the project like the list view ( name , description of the prototype...)
How can I do that ?
One way is to define the template for your prototypes field in showMapper
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper ->add('prototypes',null, array('template' => 'NamespaceYourBundle::Admin/prototypes.html.twig'));
}
Create Admin folder in your bundle's resources folder and create prototypes.html.twig file ,extend your twig template with sonata's base_show_field.html.twig template and in {% block field %} define your own markup looping through all related prototypes
{% extends 'SonataAdminBundle:CRUD:base_show_field.html.twig' %}
{% block field %}
{% spaceless %}
{% if object.getPrototypes() is not empty %}
<table class="table table-bordered table-striped">
<thead>
<tr class="sonata-ba-list-field-header">
<th class="sonata-ba-list-field-header-text">Nom</th>
<th class="sonata-ba-list-field-header-text">Description</th>
...
...
...
</tr>
</thead>
<tbody>
{% for prototype in object.getPrototypes() %}
<tr>
<td class="sonata-ba-list-field sonata-ba-list-field-text">{{ prototype.getNom() }}</td>
<td class="sonata-ba-list-field sonata-ba-list-field-text">{{ prototype.getDescription() }}</td>
...
...
...
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% endspaceless %}
{% endblock %}
I found a way to resolve the problem but still I feel it's not the better way
I created a controller and overrided the showAction
<?php
namespace AppBundle\Controller;
use Sonata\AdminBundle\Route\RouteCollection;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class CRUDController extends Controller
{
public function showAction($id = null)
{
return $this->redirect($this->generateUrl('admin_app_prototype_list', array(
'filter[projet__id][value]'=>$id
)));
}
}
Then I get a list of prototypes that belong to a project.

Symfony2 Doctrine2 trouble with optional one to many relation

I have a problem with Doctrine2 and two relationed entities.
There is a user-entity that can (not must) have one or a collection of social-entity referenced which contains a social network link.
I do not control Doctrine and I'm still learning relationship.
I want to add a user with/without adding social network link.
After several researches and testing, I am still unable to find a solution.
Here is my user-entity
<?php
//...
/**
* User
*
* #ORM\Table(name="admin_users")
* #ORM\Entity(repositoryClass="UserRepository")
* #ORM\HasLifecycleCallbacks()
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="username", type="string", length=25, unique=true)
*/
private $username;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=40)
*/
private $password;
/**
*
* #var string
*
* #Assert\NotBlank
*/
private $plainPassword;
//...
/**
* #var ArrayCollection
*
* #ORM\OneToMany(targetEntity="Social", mappedBy="user", cascade={"persist","remove"})
* #ORM\JoinColumn(nullable=true)
*/
private $socials;
public function __construct()
{
$this->socials = new ArrayCollection();
}
//Some getters setters
/**
* Add socials
*
* #param Social $socials
* #return User
*/
public function addSocials(Social $socials)
{
$this->socials[] = $socials;
$socials->setUser($this);
return $this;
}
/**
* Remove socials
*
* #param Social $socials
*/
public function removeSocials(Social $socials)
{
$this->socials->removeElement($socials);
}
/**
* Get socials
*
* #return Collection
*/
public function getSocials()
{
return $this->socials;
}
}
Here is the social-entity
<?php
/**
* Social
*
* #ORM\Table(name="admin_social")
* #ORM\Entity(repositoryClass="SocialRepository")
*/
class Social
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=20, nullable=true)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="url", type="string", length=255, nullable=true)
*/
private $url;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="socials", cascade={"persist","remove"})
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=true)
*/
private $user;
//getters setters
/**
* Set user
*
* #param User $user
* #return Social
*/
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return User
*/
public function getUser()
{
return $this->user;
}
}
The userType code looks like this:
$builder
->add('username', 'text', array(
'attr'=> array('class' => 'span6',),
'label_attr' => array('class' => 'control-label'),
)
)
// ....
->add('sociaux', 'collection', array('type' => new SocialType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,))
;
Finally the controller code :
public function addAction()
{
$user = new User;
// Create the form
$form = $this->createForm( new UserType, $user );
// Gets the request
$request = $this->getRequest();
// Checks if the request have type POST
if ( $request->getMethod() == 'POST' ) {
// Links the request and the form
$form->bind( $request );
// Checks if all input values are correct
if ( $form->isValid() ) {
// Save user object in database
$em = $this->getDoctrine()->getManager();
// Persist entity user
$em->persist( $user );
$em->flush();
//...
}
}
//...
}
When I try to add a user without social-entity I have no error, but in the database I have in social table a row with null values. Please help.
UPDATE
In user-entity I added this :
if( !( $socials->getName() === null && $socials->getUrl() === null ) )
{
$this->socials[] = $socials;
$socials->setUser($this);
}
Now there is no row inserted in social table, but when I try editing the user, I have two collection field (duplicated).
See the screenshot
Here my template file (Twig) :
<div class="widget-body">
{{ form_start(form, { 'action': path('acme_admin_edit_user', {'id': userId}), 'attr': {'class': 'form-horizontal'} }) }}
<div class="control-group">
{{ form_errors(form.username) }}
{{ form_label(form.username) }}
<div class="controls">
{{ form_widget(form.username) }}
</div>
</div>
<!-- ... -->
<div id="acme_adminbundle_useredittype_socials" data-prototype="{{ form_row(form.socials.vars.prototype) | escape }}">
{% for social in form.socials %}
<div>
<label class="required text-primary lead">Lien n°{{ loop.index }}</label>
<div id="acme_adminbundle_useredittype_socials_{{ loop.index0 }}">
<div class="control-group">
{{ form_errors(social.name) }}
{{ form_label(social.name) }}
<div class="controls">
{{ form_widget(social.name) }}
</div>
</div>
<div class="control-group">
{{ form_errors(social.url) }}
{{ form_label(social.url) }}
<div class="controls">
{{ form_widget(social.url) }}
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="txt-center well">
<input type="submit" class="auto-margin btn btn-primary btn-large" />
</div>
{{ form_end(form) }}
</div>
Try removing:
#ORM\JoinColumn(nullable=true)
from your User class. #JoinColumn should be defined only on one side of relationship and since Social entity contains name and referencedColumnName it is unnecessary inside the User.

Resources