Symfony Forms: Collection - Just Add New Entries / Prevent Modifying Old Entries - php

I got a problem in my project and dont really know how to fix it. Maybe my approach is completely wrong and I should go another direction.
I have two Entities: Article & ArticleComment
In my ArticleType I add the Comments with a CollectionType (main reason for this is that I also want to modify the Article - basically change Article stuff and also be able to add 1 comment):
//ArticleType.php
$builder->add('articleComments', CollectionType::class, ['label' => false, 'entry_type' => AritcleCommentType::class, 'entry_options' => ['label' => false]]);
My ArticleCommentType is just a textarea field:
//ArticleCommentType.php
$builder->add('text', TextareaType::class, ['label' => 'comment', 'required' => false, 'attr' => ['class' => 'textarea-sm']]);
This solution works perfectly for me.
However, whenever I add a comment, my other comments are also rendered in a textarea field and can be edited, which I do not want. I simple want the user to add 1 comment, reload the page and maybe add another comment thats it.
I already tried to prevent rendering the other comments, but this throws an error, since the element is not in the DOM anymore - but still expected.
Any ideas how to fix this? (Hiding the fields is no valid solution for me)
Update:
My controller:
//ArticleController.php
...
/**
* #Route("/article/{id}", name="app_article", requirements={"id"="\d+"})
*/
public function article(Request $request, Article $article)
{
$articleComment = new ArticleComment();
$article->addArticleComment($articleComment);
$form = $this->createForm(ArticleType::class, $article);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($articleComment); //cascade not active
$em->persist($article);
$em->flush();
$this->addFlash('success', 'entry_saved');
return $this->redirectToRoute('app_article', ['id' => $article->getId()]);
}
return $this->render('article/view.html.twig', ['article' => $article, 'form' => $form->createView()]);
}
...
Changed ArticleType:
$builder->add('articleComments', ArticleCommentType::class, ['label' => false, 'data' => new ArticleComment()]); //changed as suggested

As long as you use
$builder
->add('articleComments', CollectionType::class, [
'label' => false,
'entry_type' => AritcleCommentType::class,
'entry_options' => ['label' => false],
'mapped' => false,
]);
you'll render the whole collection.
To me the best fitting solution is to add directly ArticleCommentType with data attribute sette to new ArticleComment.
If you want to display all the comments, you should do directly in the template passing all existing ArticleComment displaying what you need to display.
Only thing you should take care is to manually add (maybe in a form subscriber) the brand new element (if has any value) to existing collection.

Related

categoryChoiceTree in prestashop module configuration page

I'm developing a prestashop module and I'm trying to show a category tree in my backoffice configuration page.
I'm trying to follow this instructions below but I don't know exactly where to add this code.
It should be inside main module's php? or inside a separate .php file and call it from the main one (don't know how to do it either).
As much time I'm spending trying to figure out, how to implement the code in the link above, the more I think I'm losing my time.
I see that "use" files, and this JS, " /admin-dev/themes/new-theme/js/components/form/choice-tree.js " are not in any prestashop folders.
Well, you should invest some time and learn Symfony since this is what you need to build backend modules for Prestashop 1.7.
As a pointer, you need to create a form class extending the CommonAbstractType, add a build form method. e.g. :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->context = Context::getContext();
$parents = [
['id_category' => 2, 'name' => 'Home', 'children' => $this->getSubCategories(1, true, 2)]
];
$builder->add('category', CategoryChoiceTreeType::class, [
'choices_tree' => $parents,
'choice_value' => 'id_category',
'choice_children' => 'children',
'choice_label' => 'name',
'disabled_values' => $disabledCategories,
'label' => 'Choose a category'
])
then add methods for retrieving the data to populate the form fields.
Then use this class in your controller and display the form:
$form = $this->createForm(YourFormForm::class);
Also add a processForm to process data.
As mentioned, this is not a copy/paste situation you need to understand the Symfony workflow.
The only way that I found to "paint" the categorytree in my configuration page is adding this code to the inputs form array:
Can anyone tell me how to retrieve users selection data to my database?
It does not work as any other form field.
array(
'type' => 'categories',
'label' => $this->l('Destination Category'),
'desc' => $this->l('Select ONE Category'),
'name' => 'CATEGORY_CATEGORY_TO',
'tree' => [
// 'selected_categories' => [],
'disabled_categories' => null,
'use_search' => false,
'use_checkbox' => false,
'id' => 'id_category_tree',
],
'required' => true
),
Well, it is SOLVED!!!! Finally it was very simple, but you must get the correct info for you particular case.
#Robertino's answer might be the best implementation, I don't know, but it became impossible to solve for me,
I uses this code below, and called $categoryTree from the form input. This input must be type=> categories_select
Thanks for your time, and for the help of another post from this forum.
$root = Category::getRootCategory();
//Generating the tree
$tree = new HelperTreeCategories('categories_1'); //The string in param is the ID used by the generated tree
$tree->setUseCheckBox(false)
->setAttribute('is_category_filter', $root->id)
->setRootCategory($root->id)
->setSelectedCategories(array((int)Configuration::get('CATEGORY_1'))) //if you wanted to be pre-carged
->setInputName('CATEGORY_1'); //Set the name of input. The option "name" of $fields_form doesn't seem to work with "categories_select" type
$categoryTree = $tree->render();
And the Form:
array(
'type' => 'categories_select',
'label' => $this->l('Category'),
'desc' => $this->l('Select Category '),
'name' => 'CATEGORY_1', //No ho podem treure si no, no passa la variable al configuration
'category_tree' => $categoryTree, //This is the category_tree called in form.tpl
'required' => true

Symfony form CollectionType remove empty value

I'm using Symfony 3.3 and I have a form with a CollectionType like :
$builder->add('links', CollectionType::class, array(
'label' => false,
'entry_type' => LinkType::class,
'entry_options' => ['data_class' => CompanyLink::class],
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'by_reference' => false,
));
I followed the documentation : https://symfony.com/doc/current/reference/forms/types/collection.html
This is what I have in my view :
So, the form displays well data already in my database (one line each time) but it always adds an empty field in addition.
How can I remove this empty field? Because I want to have this line only I click on the button "Ajouter".
Thanks!
I think in your function of rendering form you have setData(),
to render the form without data, you just need to do this sample:
//Your main entity
$mainEntity = new Entity();
//adding the other entity into collection variable of other entity of main entity
$mainEntity->addOtherEntity(new otherEntity());
$form = $this->createForm(
MainEntityForm::class,
$mainEntity
);
hope it helps.
This post is a bit old ihope it's not to late. I found a solution, even if you travel the collectionType field with a twig for.
To avoid having this empty field after your for loop, add
{% do form.your_field.setRendered %}
It worked for me, hope it will do the same for you :)

Symfony 2 Embedding Collections and Entity FieldTypes to Persist a Join Table (1:M M:1 1:M)

I'm struggling through a scenario and may be missing the final piece.
Person has many abilities
Abilities have properties of years, and commercial experience (optional)
An ability has a type (ex: Daily / Monthly, etc.
Person -- 1:M -- Abilities M:1 Person / M:1 Type -- Type 1:M Abilities
Person / Abilities have cascade persist / remove and also orphan removal set.
User Interaction
The user creates their initial profile, they go to the abilities page and click checkboxes which relate to their abilities, a corresponding (optional) checkbox for their commercial experience, and a dropdown for the years of experience.
Intention:
When a user selects their abilities and submit - the corresponding abilities are associated with the user (plus any properties). Then persisted, ignoring any rows in which the abilitytitle is not checked (disabled, etc).
ABILITIES FORM: Updated
$builder
->add('abilitytitle', 'collection', array(
'by_reference' => false,
'type' => new AbilityTitleType(),
'data_class' => 'AppBundle\Entity\AbilityTitle',
))
->add('commercialexperience','collection',array(
'type' => 'checkbox',
'allow_add' => true,
'options' => array(
'label' => 'Commercial Experience',
'required' => false,)
))
->add('experience','collection',array(
'type' => 'choice',
'allow_add' => true,
'options' => array(
'placeholder' => 'Please select',
'choices' =>
array(
'1-2' => '1-2',
'3-4' => '3-4',
),
'required' => false,
)
))
For the form, I have Experience and Commercial Experience printed out manually coinciding with the abilities information. I created an AbilityTitleType and inserted it as a collection, but it seemed to put me off track.
CONTROLLER
public function newUserAction(Request $request)
{
$person= new Person();
$form = $this->createForm(new PersonEntry(),$person);
$form->handleRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($person);
$em->flush();
return $this->redirect($this->generateUrl('qualifications'));
}
return $this->render('AppBile:User:newPerson.html.twig', array(
'form' => $form->createView(),
));
}
This is a generic form flow
AbilityTitleType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('abilityname', 'entity', array(
'class' => 'AppBundle:AbilityTitle',
'property' => 'abilityname',
'multiple' => true,
'expanded' => true,
'required' => false,
'label' => 'Please select claims in which you have experience'
));
}
public function getName()
{
return 'abilitytitle';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBile\Entity\AbilityTitle'
));
}
Issue: Ability Title displays blank in the form, when embedded
My getters and setters are typical to the associations and can be provided as desired, with short cuts on users and abilities.
The problem
I am stuck with the error "The form's view data is expected to be an instance of class... given as ArrayCollection" for my ability titles. I imagine this is due to the multi-line insert as the dummy code below inserts fine. I believe my issue is concerning collections, embedding, and the entityfield type.
Am I looking at a scenario where each row is an entity in itself, and I need to catch the data received by the form, persist each separately?
A collection of embedded entity forms, where the rows may vary by the number of rows abilities could contain? If so, I'm not quite visualizing it!
Thanks for any pointers and for looking! Happy to upload more code if its necessary.
// DUMMY CODE - Inserts Fine
for($i = 0; $i < 5; ++$i) {
$person= $em->find('person', 1);
$person_abilities->setExperience(rand(1,10))
->setCommercialExperience(rand(1,10));
$ability= $this->getDoctrine()
->getRepository('AppBundle:AbilityTitle')
->findOneById(rand(1,3));
}
$potential->addPerson_Abilities($person_abilities);
$ability->addPerson_Abilities($person_abilities);
Thats because you have to set the data_class option.
For Example:
->add('test', 'collection', array(
'type' => new TestFormType(),
'options' => array(
'data_class' => 'TestBundle\Entity\Test',
),
)
);
EDIT: Forgot the other stuff. You may be interested in reading this (scroll down to Doctrine: Cascading Relations and saving the "Inverse" side).

Set default value for entity type in Symfony2

I couldn't figure out how to make a default value for an entity type in symfony2. My code looked like this:
$rewardChoice = $this->createFormBuilder($reward)
->add('reward_name', 'entity', array(
'class' => 'FuelFormBundle:Reward',
'property' => 'reward_name',
'data' => 2,
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('r')
->where('r.active = 1')
->groupBy('r.reward_id')
->orderBy('r.reward_name', 'DESC');
},
))
->getForm();
However you need to hand in the object you are working with to make it work. My answer is below.
I found a lot of different answers on this but they all restructured the way the form was built. This was much easier.
So I found a lot of answers to make this work but all of them seemed to restructure the form to be built in another way however I found that setting the object in works best so I figured I would post my solution incase anyone ran into the issue again.
Here is my code in the controller.
// this is setting up a controller and really isn't important
$dbController = $this->get('database_controller');
// this is getting the user id based on the hash passed by the url from the
// database controller
$user_id = $dbController->getUserIdByHash($hash);
// this is getting the Reward Entity. A lot of times you will see it written as
// $reward = new Reward however I am setting info into reward right away in this case
$reward = $dbController->getRewardByUserId($user_id);
$rewardChoice = $this->createFormBuilder($reward)
->add('reward_name', 'entity', array(
'class' => 'FuelFormBundle:Reward',
'property' => 'reward_name',
// I pass $reward to data to set the default data. Whatever you
// assign to $reward will set the default value.
'data' => $reward,
'query_builder' => function(EntityRepository $er){
return $er->createQueryBuilder('r')
->where('r.active = 1')
->groupBy('r.reward_id')
->orderBy('r.reward_name', 'DESC');
},
))
->getForm();
I hope this make things more clear. I saw a lot of the same question but none with this solution.
The best way to do this is to set reward name before creating the form. For example:
$reward->setRewardName('your_relationship_reference_here');
$rewardChoice = $this->createFormBuilder($reward)
Using the data field can cause problems.
For symfony 4 we can use 'placeholder' => 'Choose an option',
->add('reward_name', EntityType::class, array(
'class' => Reward::class,
'choice_label' => 'name',
'placeholder' => 'Choose an option',
))
Initialize value from the form's target object
$article=2/*Id of item that you wont to set as default*/
$stock->setArtigo($this
->getDoctrine()
->getManager()
->getRepository('AppBundle:Artigo')
->find($article));
$form = $this->createForm('AppBundle\Form\StockType', $stock);

Symfony2 and FormBuilder: How to get the elemetns number added in the builder

I have a formbuilder where I am adding some values from an entity:
$builder->add('affiliation', 'entity', array(
'class' => 'SciForumVersion2Bundle:UserAffiliation',
'multiple' => true,
'expanded' => true,
'query_builder' => function(EntityRepository $er) use ($author,$user) {
return $er->createQueryBuilder('ua')
->where("ua.user_id = {$user->getId()}")
->andWhere("ua.affiliation_id not in ( select pa.affiliation_id FROM SciForumVersion2Bundle:PersonAffiliation pa where pa.person_id = {$author->getPersonId()} )");
},
'required' => true,
));
In my controller, I would like to check if there is something in my form. If there is something, I will display one view, if there is nothing, I will display another view.
Is this possible and if so, how?
Thank you.
Simply try it:
$data = $form->getData()
function getData() documentation Book
If you want to get the current data (just after rendering the form) in your form type you can use the builder supplied in each form type by standard.
It works exactly as a normal form response, so you can use:
$builder->getData();
and use if clauses to add different fields depending on what you want to generate.

Categories