I have to create a multilingual table so i choose this schema:
Article ( id, name_translation_fk)
Translation ( id )
Translation_Text (id, language, translation_fk, text)
Now i need to add names in different languages for an article that allready exists.
Doctrine gives me this error:
Catchable Fatal Error: Argument 1 passed to Doctrine\Common\Collections\ArrayCollection::__construct() must be an array, object given, called in */vendor/doctrine/lib/Doctrine/ORM/UnitOfWork.php on line 416 and defined in */vendor/doctrine-common/lib/Doctrine/Common/Collections/ArrayCollection.php line 46
I have no clue what the problem could be since all the entities are well declared.I think the issue is somewhere in the Form Class.
I have posted below my entities, forms and views implicated.
Article
class Article
{
/**
* #ORM\ManyToOne(targetEntity="Translation", inversedBy="article_name", cascade= {"persist"})
* #ORM\JoinColumn(name ="name", referencedColumnName="id")
*/
protected $name;
}
Translation
class Translation
{
/**
* #ORM\OneToMany(targetEntity="Translation_Text", mappedBy="translation", cascade={"persist"})
*/
public $translation_text;
/**
* #ORM\OneToMany(targetEntity="Article", mappedBy="name", cascade={"persist"})
*/
protected $article_name;
public function __construct()
{
$this->translation_text = new ArrayCollection();
$this->article_name = new ArrayCollection();
}
}
Translation_Text
class Translation_Text
{
/**
* #ORM\ManyToOne(targetEntity="Language", inversedBy="translation_text")
* #ORM\JoinColumn(name ="language_id", referencedColumnName="id")
*/
protected $language;
/**
* #ORM\ManyToOne(targetEntity="Translation", inversedBy="translation_text")
* #ORM\JoinColumn(name ="translation_id", referencedColumnName="id")
*/
protected $translation;
}
The form
class TranslationTextType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('text','text');
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Translation_Text',
);
}
public function getName()
{
return 'translation_text';
}
}
class TranslationType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('translation_text',new TranslationTextType());
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Translation',
);
}
public function getName()
{
return 'translation';
}
}
class ArticleTranslationForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name',new TranslationType());
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Udo\WebserviceBundle\Entity\Article',
);
}
public function getName()
{
return 'article';
}
}
The controller
$article = $em->getRepository('Udo\WebserviceBundle\Entity\Article')->find($id);
$form = $this->createForm(new ArticleTranslationForm(),$article);
The form view
<form action="{{path('article_translate', { 'id': entity.id }) }}" method="post" {{ form_enctype(form) }}>
{{form_row(form.name.translation_text.text)}}
{{form_rest(form)}}
<input type="submit" />
</form>
Since it is a ont-to-many relationship, you should use collection:
$builder->add('translation_text', 'collection', array('type' => new TranslationTextType()));
instead of:
$builder->add('translation_text',new TranslationTextType());
Related
I have the collection model:
class EntityList {
/**
* #var Entity[]
* #Assert\Count(min=1)
* #Assert\Valid()
*/
private $entityList;
// ...
}
The single model:
class Entity {
/**
* #var int
* #Assert\NotBlank()
*/
private $id;
/**
* #var string
* #Assert\NotBlank()
*/
private $name;
// ...
}
The collection's form:
class EntityListType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('entity', CollectionType::class, [
'entry_type' => EntityType::class,
'allow_add' => true,
'property_path' => 'entityList',
]);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefault('data_class', EntityList::class);
}
public function getBlockPrefix() {
return null;
}
}
And the entity's form:
class EntityType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('id', NumberType::class)
->add('name', TextType::class)
;
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefault('data_class', Entity::class);
}
}
The collection's form using in a controller:
$form = $this->createForm(EntityListType::class);
$form->handleRequest($request);
I send the package with no required title (it's an API):
curl -X POST ...
-d entity[0][id]=10
And that accept it! The method isValid returns true. But if form's data check manually with ValidationComponent as:
if ($form->isValid()) { // it's valid, okay
echo $this->get('validator')->validate($form->getData()); // it's still not valid!
}
I see correct errors ('title is required')! Why?
P.S. Symfony 3.2, stable.
P.P.S. I was found temporarily solution now. It's use $form->submit($request->request->all()); instead of $form->handleRequest($request);. But I don't understand this feature.
I have a problem with Symfony2, I want to make a tag system but I can not manage to do I tried a lot but there are a lot of errors.
please can you help me and thank you in advance
the last error display :
Catchable Fatal Error: Argument 1 passed to Project\RmBundle\Entity\Posts::setTags() must be an instance of Project\RmBundle\Entity\Tags, string given, called in C:\wamp\www\Test\vendor\symfony\symfony\src\Symfony\Component\PropertyAccess\PropertyAccessor.php on line 438 and defined in C:\wamp\www\Test\src\Project\RmBundle\Entity\Posts.php line 390
Posts.php
class Posts{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var ArrayCollection $tags
*
* #ORM\ManyToMany(targetEntity="Project\RmBundle\Entity\Tags", inversedBy="posts")
*/
private $tags;
/**
* Constructor
*/
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add tags
*
* #param \Portfolio\GeneralBundle\Entity\Tags $tags
* #return Article
*/
public function addTag(\Project\RmBundle\Entity\Tags $tags)
{
$this->tags[] = $tags;
return $this;
}
/**
* Remove tags
*
* #param \Project\RmBundle\Entity\Tags $tags
*/
public function removeTag(\Project\RmBundle\Entity\Tags $tags)
{
$this->tags->removeElement($tags);
}
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
/**
* Set tags
*
* #param \Project\RmBundle\Entity\Tags $tags
* #return Tags
*/
public function setTags(\Project\RmBundle\Entity\Tags $tags)
{
$this->tags[] = $tags;
return $this;
}}
and I have 2 FormType
PostsType.php
class PostsType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tags', 'text');
}}
TagsType.php
class TagsType extends AbstractType{
private $om;
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new StringToTagsTransformer($this->om);
$builder->addModelTransformer($transformer);
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Project\RmBundle\Entity\Tags'
));
}
public function getName()
{
return 'tags';}}
and i create a transform for that
class StringToTagsTransformer implements DataTransformerInterface{
private $om;
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function reverseTransform($ftags)
{
$tags = new ArrayCollection();
$tag = strtok($ftags, ",");
while($tag !== false) {
$itag = new Tags();
$itag->setName($tag);
if(!$tags->contains($itag))
$tags[] = $itag;
$tag = strtok(",");
}
return $tags;
}
public function transform($tags)
{
$ftags = "";
if($tags != null) {
foreach($tags as $tag)
$ftags = $ftags.','.$tag->getName();
}
return $ftags;}}
and finally my controller
PostsController.php
public function addBlogAction(Request $request){
$em = $this->getDoctrine()->getManager();
$posts = new Posts();
$form = $this->createForm(new PostsType, $posts, array(
'action' => $this->generateUrl('project_add_post'),
'method' => 'POST'
));
$em->getRepository('ProjectRmBundle:Tags')->filter($posts->getTags());
if('POST' == $request->getMethod()){
$form->handleRequest($request);
if ($form->isValid()) {
foreach($posts->getTags() as $tag){
$em->persist($tag);
}
}
$em->persist($posts);
$em->flush();
return $this->redirect($this->generateUrl('project_posts'));
}
}
return $this->render('ProjectRmBundle:Posts:add.html.twig', array('form'=>$form->createView())); }
In your PostsType you've defined the tags field as having type text. You want it to use your TagsType, so you should do this:
class PostsType extends AbstractType{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('tags', 'tags');
}}
That is assuming you've declared it as a service.
Update:
You need to put the text field in TagsType. You would do something like this:
class TagsType extends AbstractType{
...
public function buildForm(FormBuilderInterface $builder, array $options)
{
...
$builder
->add('name', 'text');
}
...
A continuation of this question:
I'm trying to embed a collection of forms as described in the official docs here. Unfortunately, I'm getting the following exception:
The form's view data is expected to be an instance of class Acme\SiteBundle\Entity\BlogPost, but is a(n) array. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) array to an instance of Acme\SiteBundle\Entity\BlogPost.
Which is odd, as I believe I followed the official docs to a 't':
BlogPostType:
class BlogPostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// static text fields/attrs (title, body, etc)
$builder->add('comments', 'collection', array('type' => new CommentType()));
}
public function getName()
{
return 'blogpost';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Acme\SiteBundle\Entity\BlogPost'));
}
}
CommentType:
class CommentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('commentId', 'hidden');
$builder->add('commentBody','text',array('label' => 'Comment:','attr'=>array('size'=>80,'class'=>'form-item-input form-type-texfield')));
}
public function getName()
{
return 'comment';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array('data_class' => 'Acme\SiteBundle\Entity\Comment'));
}
}
Relationship as defined in BlogPost:
/**
* #var \Acme\SiteBundle\Entity\Comment.php
*
* #ORM\OneToMany(targetEntity="Comment", mappedBy="blogpost",
cascade={"all"},orphanRemoval=true)
* #ORM\OrderBy({"commentId" = "ASC"})
*/
private $comments;
And in Comment:
/**
* #var BlogPost
*
* #ORM\ManyToOne(targetEntity="BlogPost", inversedBy="comments")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="blog_id", referencedColumnName="id")
* })
*/
private $blogPost;
So, I'm not sure where I've gone wrong. Any ideas?
Turns out I was an idiot (big surprise there) and was passing the form builder an array. Old code that used to work was breaking it now. Whee!
As the documentation around this topic is somewhat thin, I got to a dead end.
I have two models: Job and JobAttribute.
A Job has many JobAttributes and a JobAttribute has one Job:
class Job {
/**
* #ORM\OneToMany(targetEntity="JobAttribute", mappedBy="job_attributes")
*
* #var ArrayCollection
*/
private $attributes;
}
class JobAttribute {
/**
* #ORM\Column(name="type", type="string", length=50)
*
* #var string
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity="Job", inversedBy="jobs")
*/
private $job;
Now,I have the following FormClass:
class JobType extends AbstractType {
public function buildForm(FormBuilder $f, array $options) {
$f->add('name', 'text');
$f->add('attributes', 'collection', array('type' => new JobAttributeType()));
}
public function getName() {
return 'job';
}
}
class JobAttributeType extends AbstractType {
public function buildForm(FormBuilder $f, array $options) {
$attribute = $options['data'];
$f->add('value', $attribute->getType());
}
public function getDefaultOptions(array $options) {
return array('data_class' => 'JWF\WorkflowBundle\Entity\JobAttribute');
}
public function getName() {
return 'job_attribute';
}
}
Yes, indeed, the type property of JobAttribute contains a Form field type, eg. text.
So, as I call a FormBuilder on JobType in my Controller, $options['data'] is correctly populated with a Job-Object within JobType.
But the nested JobAttributeType's $options['data'] doesn't point to an JobAttribute object. It's NULL.
What's the problem? Where is the association lost? Why is $options['data'] = NULL in nested forms?
Is there a workaround in order to get dynamic field types (out of Doctrine) in a nested form?
Thanks in advance!
You cannot rely on $options['data'] when you build the form, as the data can (and will) be changed anytime after building. You should use event listeners instead.
$formFactory = $builder->getFormFactory();
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formFactory) {
$form = $event->getForm();
$data = $event->getData();
if ($data instanceof JobAttribute) {
$form->add($formFactory->createNamed('value', $data->getType());
}
});
The documentation for this can be found in the cookbook.
I want to add a collection of entities in Symfony 2.1.0-dev bug I got:
Neither property "sitterDegrees" nor method "getSitterDegrees()" nor method "isSitterDegrees()" exists in class "xxx\Entity\Degrees"
It happen because I have an entity in DegreesFormType.php and at this line
$this->form->bindRequest($request); in my handler.
I want to add multiple "degrees" on "sitter" entity (but degrees are a choice not like http://symfony.com/doc/master/cookbook/form/form_collections.html)
Did I forget something?
Entities
A simple ManyToMany between Sitter and Degrees
Sitter
class Sitter
{
//some properties
/**
* #var xxx\Entity\Degrees
* #ORM\ManyToMany(targetEntity="xxx\Entity\Degrees", orphanRemoval=true, inversedBy="sitters",cascade={"persist"})
* #ORM\JoinTable(name="sitter_degrees_relationships",
* joinColumns={
* #ORM\JoinColumn(name="sitter_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="degrees_id", referencedColumnName="id")
* }
* )
*/
private $sitterDegrees;
public function getSitterDegrees()
{
return $this->sitterDegrees;
}
public function setSitterDegrees(ArrayCollection $sitterDegrees)
{
foreach ($sitterDegrees as $sitterDegree) {
$sitterDegree->addSitter($this);
}
$this->sitterDegrees = $sitterDegrees;
}
public function addSitterDegree(xxx\Entity\Degrees $sitterDegrees)
{
$this->sitterDegrees[] = $sitterDegrees;
return $this;
}
public function removeSitterDegree(xxx\Entity\Degrees $sitterDegrees)
{
$this->sitterDegrees->removeElement($sitterDegrees);
}
}
Degrees
class Degrees
{
public function __toString(){return $this->name;}
private $id;
private $name;
/**
* #var xxx\Entity\Sitter
* #ORM\ManyToMany(targetEntity="xxx\Entity\Sitter", mappedBy="sitterDegrees")
*/
private $sitters;
public function __construct()
{
$this->sitters = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getId()
{
return $this->id;
}
public function setName($name)
{
$this->name = $name;
}
public function getName()
{
return $this->name;
}
public function getSitters()
{
return $this->sitters;
}
public function addSitter(xxx\Entity\Sitter $sitter)
{
if (!$this->sitters->contains($sitter)) {
$this->sitters->add($sitter);
}
}
public function removeSitter(xxx\Entity\Sitter $sitters)
{
$this->sitters->removeElement($sitters);
}
}
FormType
VerifFormType.php is my main form, it embed DegreesFormType.
VerifFormType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
//->add('some_properties')
->add('sitterDegrees', 'collection', array(
'type' => new DegreesFormType(),
'by_reference' => false,
'allow_add' => true,
'allow_delete' => true,
)
);
}
DegreesFormType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('sitterDegrees', 'entity', array(
'class' => 'xxx:Degrees'
));
}
Controller
public function indexAction()
{
$user = $this->get('security.context')->getToken()->getUser();
$sitter = $user->sitter;
$formHandler = $this->get('xxx.form.handler');
$form = $formHandler->getForm();
$form->setData($sitter);
if ($formHandler->process()) {
//ok
}
//fail
}
Handler
public function process()
{
$request = $this->container->get('request');
if ('POST' == $request->getMethod()) {
$this->form->bindRequest($request);//Fail at this line
if ($this->form->isValid()) {
return $this->onSuccess();
}
}
return false;
}
public function onSuccess()
{
$sitter = $this->form->getData();
$this->form->bindRequest($this->container->get('request'));
$sitter->setContainer($this->container);
$this->container->get('xxx.manager')->persistSitter($sitter);
return true;
}
index.html.twig
With some javascript like in the cookbook http://symfony.com/doc/master/cookbook/form/form_collections.html
<ul class="degrees" data-prototype="{{ form_widget(form.sitterDegrees.getVar('prototype')) | e }}">
{% for sitterDegree in form.sitterDegrees %}
<li>{{ form_row(sitterDegree) }}</li>
{% endfor %}
</ul>
The error comes from DegreesFormType: The sitterDegrees field maps to a setterDegrees property in your Degrees class. However, this class doesn't have such property.
There is a similar problem in VerifFormType: The sitterDegrees field maps to a setterDegrees property in your Sitter class. However, this class doesn't have such property.