I have the entity Property with manyToMany to Feature that is oneToMany of FeatureType. The multi-choice drop-down is being implemented in the Property create form.
PropertyRoomAdmin.php
protected function configureFormFields(FormMapper $formMapper)
{
$property = $this->getProperty();
$featuresChoices = $this->getRepository('TestBundle:Feature')->getChoicesWithCategoryGroup();
$formMapper
->add('features', 'entity', array(
'label' => '設備(こだわり条件?)',
'class' => 'TestBundle:Feature',
'choices' => $featuresChoices,
'property' => 'name'
))
;
}
FeatureRepository.php
public function getChoicesWithCategoryGroup()
{
$choices = array();
$qb = $this->createQueryBuilder('p');
$qb->select('p, c');
$qb->LeftJoin('p.featureCategory', 'c');
$qb->orderBy('c.name', 'DESC')->addOrderBy('p.name', 'DESC');
$products = $qb->getQuery()->execute();
$choices = [];
foreach ($products as $product) {
$choices[$product->getFeatureCategory()->getName()][] = $product;
}
return $choices;
}
Neither the property "features" nor one of the methods "addFeatur()"/"removeFeatur()", "addFeature()"/"removeFeature()",
"setFeatures()", "features()", "__set()" or "__call()" exist and have
public access in class "TestBundle\Entity\PropertyRoom".
I tried 'multiple' => true code got error
->add('features', 'entity', array(
'label' => '設備(こだわり条件?)',
'class' => 'EstateBundle:Feature',
'choices' => $featuresChoices,
'property' => 'name',
'multiple'=> true,
'required' => false
))
ERROR - Failed to create object: EstateBundle\Entity\PropertyRoom
Context: {"exception":"Object(Sonata\AdminBundle\Exception\ModelManagerException)","previous_exception_message":"An exception occurred while executing 'INSERT INTO property_room_features (property_room_id, feature_id) VALUES (?, ?)' with params [168, 50]:\n\nSQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '168-50' for key 'PRIMARY'"}
Where you setup your options array:
foreach ($products as $product) {
$choices[$product->getFeatureCategory()->getName()][] = $product;
}
Change it to something like this:
foreach ($products as $product) {
$choices[$product->getFeatureCategory()->getName()][$product->getName()] = $product->getId();
}
Related
How get all values parent_id from DB?
$category = $this->getSubject();
protected function configureFormFields(FormMapper $formMapper)
{
$fieldOptions = array(); //how get all value `parent_id` from DB
$formMapper->add('parent_id', ChoiceType::class, array(
'expanded' => true,
'multiple' => false,
'choices' => $fieldOptions,
'data' => $category->parent_id
));
}
If "parent" is an entity (Category, perhaps), you might want to look at EntityType. Otherwise, your code needs a lot more context. In what class or file is that snippet located?
Answer:
class CategoryAdmin extends AbstractAdmin
{
$category = $this->getSubject();
protected function configureFormFields(FormMapper $formMapper)
{
$em = $this->modelManager->getEntityManager(Category::class);
$fieldOptions = $em->getRepository(Category::class)->getChoiceParentId();
$formMapper->add('parent_id', ChoiceType::class, array(
'multiple' => false,
'choices' => array_flip($fieldOptions),
'data' => $category->parent_id
));
}
}
class CategoryRepository extends ServiceEntityRepository
{
public function getChoiceParentId()
{
$categories = $this->createQueryBuilder('c')
->select('c.id, c.name')
->getQuery()
->getResult();
$choice_parent_id = [0 => 'Empty'];
foreach ($categories as $category) {
$choice_parent_id[$category['id']] = $category['name'];
}
return $choice_parent_id;
}
}
So I'm about to create a form with three dropdowns which are interdependent.
When one or several channel1s are selected, the choices for channel 3 should change, according to what is selected for channel1. And dependent on that, the last dropdown "agencies" should change its choices.
I've already tried different solutions but none of them has worked so far. Right now I'm stuck on the solution provided by Symfony's documentation, which provides code for two entities but even with that one, my second dropdown doesn't have any values, so it's not working.
Here is my Form Type:
class SelectionType extends AbstractType {
protected $tokenStorage;
// private $manager;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
//solution Symfony Documentation
$channel1s = new Channel1();
$currentuser = $this->tokenStorage->getToken()->getUser();
$builder
->add('channel1s', EntityType::class, array(
'class' => 'AppBundle:Channel1',
'property' => 'name',
'label' => 'label.channel1s',
'empty_value' => 'label.select_channel1s',
'mapped' => false,
'expanded' => false,
'translation_domain' => 'UploadProfile',
'multiple' => true,
'required' => false,
));
$formModifier = function (FormInterface $form, Channel1 $channel1s = null) {
$channel3s = null === $channel1s ? array() : $channel1s->getChannel3();
$form->add('channel3s', EntityType::class, array(
'class' => 'AppBundle:Channel3',
'property' => 'name',
'label' => 'label.channel3s',
'empty_value' => 'label.select_channel3s',
'mapped' => false,
'expanded' => false,
'translation_domain' => 'UploadProfile',
'choices' => $channel3s,
'multiple' => true,
'choices_as_values' => true,
));
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data->getChannel1s());
}
);
$builder->get('channel1s')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$channel1s = $event->getForm()->getData();
$formModifier($event->getForm()->getparent(), $channel1s);
}
);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DocumentBundle\Entity\UploadProfile'
));
}
public function getName()
{
return 'uploadprofile';
}
}
I've also tried a solution with Subscribers from that page: http://showmethecode.es/php/symfony/symfony2-4-dependent-forms/ but it didn't work out either..
I think my problem is somewhere around that line:
$channel3s = null === $channel1s ? array() : $channel1s->getChannel3();
but that's just a guess..
I also added that ajax function:
var $channel1s = $('#uploadprofile_channel1s');
$channel1s.change(function() {
var $form = $(this).closest('form');
var data = {};
data[$channel1s.attr('name')] = $channel1s.val();
// data[channel3s.attr('name')] = channel3s.val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(html) {
$('#uploadprofile_channel3s').replaceWith(
$(html).find('#uploadprofile_channel3s')
);
}
});
});
My 3 entities have ManytoMany or OneToMany relationships and I should have all the right getters and setters, but if anyone needs them to varify, let me know and I will upload them!
I've been stuck on this for quite a while now, so I would be happy about any kind of help or advise!
NOTE: there's still a third entity (agency) to be added but since not even the first one's are working, I decided to upload only the first two..
ADDED:
or maybe somebody can explain to my that line:
$channel3s = null === $channel1s ? array() : $channel1s->getChannel3s();
might be, that this is my problem?
Symfony version 3.1.3
I have created a dropdown using the entity called Classes and you can see the Controller below,
public function studentAddClassAction( $id, Request $request )
{
// get the student from the student table
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('PIE10Bundle:Users')->find($id);
// new class object and create the form
$classes= $em->getRepository('PIE10Bundle:Classes')->findAll();
$form = $this->createForm(ClassType::class, $classes);
$form->handleRequest($request);
if( $form->isSubmitted() && $form->isValid() )
{
// form submit operations
}
return $this->render(
'PIE10Bundle:student:layout_student_addclass.html.twig',
array(
'user' => $user,
'title' => 'Add Class',
'tables'=> 1,
'form' => $form->createView()
)
);
}
and the ClassType is below
class ClassType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('classes',
EntityType::class,
array('class' => 'PIE10Bundle:Classes',
'expanded' => false,
'multiple' => false,));
$builder->add('Add Class',
SubmitType::class,
array('attr' => array('class' => 'btn btn-primary',
'style' => 'margin:15px 0;')) );
}
}
And this works fine and it gives all the classes from the database. Also I have another entity called Users and it has a column called roles (DC2Type:array) and it has a role called ROLE_PARENT and I can retrieve all the parents using the following query
$query = $this->getDoctrine()->getEntityManager()
->createQuery('SELECT u FROM PIE10Bundle:Users u WHERE u.roles LIKE :role')
->setParameter('role', '%"ROLE_PARENT"%' );
$users = $query->getResult();
My Question is how to add these parents list as a choice list into same above form in the studentAddClassAction Controller.
Please let me know in any other information is needed for this.
To have a custom set of entities as a choice list you need to use a query_builder option
So it will look like
$builder->add('parent',
EntityType::class,
array('class' => 'PIE10Bundle:Users',
'expanded' => false,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('u')
->where('u.roles LIKE :role')
->setParameter('role', '%"ROLE_PARENT"%');
},
'multiple' => false
));
I made several search but I still have a problem...
I want to make a dynamic form. I want to hydrate a select in function of an other select.
This is my configureFormFields:
protected function configureFormFields(FormMapper $formMapper)
{
$emIndustry = $this->modelManager
->getEntityManager('*\*\*\*\Entity\Industry')
;
$query = $emIndustry->getRepository(*:Industry')
->getFindAllParentsQueryBuilder()
;
$formMapper
->add('company')
->add('industry', 'sonata_type_model', [
'attr' => [
'onchange' => 'submit()',
],
'query' => $query,
'required' => false,
])
->add('subIndustry', 'sonata_type_model', [
'choices' => [],
'required' => false,
])
;
$builder = $formMapper->getFormBuilder();
$factory = $builder->getFormFactory();
$subject = $this->getSubject();
$modelManager = $this->getModelManager();
$builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use($formMapper, $subject, $emIndustry, $modelManager, $factory) {
$form = $event->getForm();
if(!is_null($subject->getIndustry())) {
$query = $emIndustry->getRepository('*:Industry')
->getFindChildrenByParentQueryBuilder($subject->getIndustry())
;
$form
->add(
$factory->createNamed('subIndustry', 'sonata_type_model', null, [
'class' => '*\*\*\*\Entity\Industry',
'query' => $query,
'required' => false,
])
)
;
}
});
}
When I change the value of the select Industry, no problem my form is submited. But nothing happend in second select subIndustry because : all attributes of my $subject object is null...
Have you any idea why ? Is there a best way to make a dynamic form ?
Thank's for your help.
AlexL
I have a Symfony 2 entity. When I create a new record, I must fill all the values using a form, but after saving it, one of the values, $amount shouldn't be updatable when I update the others members.
How can I accomplish this? It's possible to mark a form member as a read-only, in runtime?
By using the validation_groups and name options when creating your form, you can change the form.
The name attribute sets the form creation, and the validation_groups takes care of the validation.
For example, in the create/new method of your controller;
public function createAction(Request $request)
{
// Instantiate new Foo object
$client = new Foo();
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'create',
'validation_groups' => array('create')
)
);
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo:add.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And in the edit/update function of your controller;
public function updateAction($id, Request $request)
{
// Instantiate Client object
$client = new Foo($id);
// create the form (setting validation group)
$form = $this->formFactory->create('foo', $foo, array(
'name' => 'update',
'validation_groups' => array('update')
));
// form has been submitted...
if ('POST' === $request->getMethod()) {
// submits the form
$form->handleRequest($request);
// do validation
if ($form->isValid()) {
// do whatever
}
}
// either GET or validation failed, so show the form
return $this->template->renderResponse('FooBundle:foo/edit:index.html.twig', array(
'form' => $form->createView(),
'foo' => $foo
));
}
And your Form Type will look something like;
class FooType extends BaseAbstractType
{
protected $options = array(
'data_class' => 'FooBundle\Model\Foo',
'name' => 'foo',
);
private $roleManager;
public function __construct($mergeOptions = null)
{
parent::__construct($mergeOptions);
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$this->$options['name']($builder, $options);
}
private function create(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)'
)
));
// Name - only show on create
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'placeholder' => 'Your name'
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
private function update(FormBuilderInterface $builder, array $options)
{
// ID
$builder->add('Id', 'text', array(
'required' => true,
'label' => 'ID',
'attr' => array(
'placeholder' => 'Format: 2 alphanumeric (e.g. A1)',
)
));
// Name - just for show
$builder->add('Name', 'text', array(
'required' => true,
'label' => 'Name',
'attr' => array(
'readonly' => 'true' // stops it being editable
)
));
// add the submit form button
$builder->add('save', 'submit', array(
'label' => 'Save'
));
}
}
P.S. All my classes are declared as services, so how you call create forms/views/etc may be different.