Im getting a Class not found error on this line:
new \Placas\FrontendBundle\Form\Type\ChoiceOrTextType(),
I have the class at Placas/FrontendBundle/Form/Type.
Here are the codes, the instantation:
namespace Placas\FrontendBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Route\RouteCollection;
use Placas\FrontendBundle\Form\Type\ChoiceOrTextType;
class CategoriaAdmin extends Admin
{
public $last_position = 0;
private $container;
private $positionService;
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container)
{
$this->container = $container;
}
public function setPositionService(\Pix\SortableBehaviorBundle\Services\PositionHandler $positionHandler)
{
$this->positionService = $positionHandler;
}
protected $datagridValues = array(
'_page' => 1,
'_sort_order' => 'ASC',
'_sort_by' => 'position',
);
protected function configureFormFields(FormMapper $formMapper)
{
$options = array('required' => false, 'label' => 'Imagen');
if ($this->getSubject()->getImageName()) {
$path = $this->getSubject()->getImageName();
$options['help'] = '<img style="width: 300px; border: 1px solid #ccc" src="/images/' . $path . '" />';
}
$formMapper
->add('nombre', 'text', array('label' => 'Nombre', 'required' => false ))
//->add('image', 'file', $options)
->add('activado', 'checkbox', array('label' => 'Activado', 'required' => false ))
->add('menu', new ChoiceOrTextType(), array(
'choices' => array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'),
'required' => false,))
;
}
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$this->last_position = $this->positionService->getLastPosition($this->getRoot()->getClass());
$listMapper
->addIdentifier('nombre')
->add('activado')
->add('position', array(), array('label' => 'Posición'))
->add('_action', 'actions', array(
'label' => 'Cambio orden',
'actions' => array(
'move' => array('template' => 'PixSortableBehaviorBundle:Default:_sort.html.twig'),
)));
}
protected function configureRoutes(RouteCollection $collection)
{
$collection->add('move', $this->getRouterIdParameter() . '/move/{position}');
}
}
The class:
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Placas\FrontendBundle\DataTransformer\ValueToChoiceOrTextTransformer;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
class ChoiceOrTextType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('choice', 'choice', array(
'choices' => $options['choices'] + array('Other' => 'Other'),
'required' => false,
))
->add('text', 'text', array(
'required' => false,
))
->addModelTransformer(new ValueToChoiceOrTextTransformer($options['choices']))
;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setRequired(array('choices'));
$resolver->setAllowedTypes(array('choices' => 'array'));
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
}
public function finishView(FormView $view, FormInterface $form, array $options)
{
}
public function getParent()
{
return 'form';
}
public function getName()
{
return 'tags';
}
}
$ pwd
/home/tirengarfio/workspace/mareva/src/Placas/FrontendBundle/Form/Type
$ ls
ChoiceOrTextType.php
Im just trying to follow the answer here.
Your ChoiceOrTextType is missing the namespace statement:
<?php
namespace Placas\FrontendBundle\Form\Type;
class ChoiceOrTextType
{
....
}
Related
I want to add a field "subCategories" in a form which should appear when a category is selected.
Here is my formType:
<?php
namespace App\Form;
use App\Entity\Categories;
use App\Entity\Keywords;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ArticlesSearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('category', EntityType::class, [
'class' => Categories::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->andWhere('c.parent is NULL')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'label' => false,
'placeholder' => 'Recherche par catégorie',
'required' => false
]);
$formModifier = function (FormInterface $form, Categories $categories = null) {
$subCategories = null === $categories ? [] : $categories->getSubCategories();
$form->add('subCategories', EntityType::class, [
'class' => Categories::class,
'choices' => $subCategories,
'required' => false,
'choice_label' => 'name',
'placeholder' => 'Sous-catégorie (Choisir une catégorie)',
'label' => 'Sous-catégorie'
]);
};
$builder->get('category')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$categories = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $categories);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'method' => 'get',
'csrf_protection' => false
]);
}
public function getBlockPrefix()
{
return '';
}
}
This line
$event->getForm()->getParent()
displays the following error:
"Expected type 'Symfony\Component\Form\FormInterface'. Found 'null'."
This is my Categories entity configuration:
/**
* #ORM\ManyToOne(targetEntity=Categories::class, inversedBy="subCategories", cascade={"persist"})
*/
private $parent;
/**
* #ORM\OneToMany(targetEntity=Categories::class, mappedBy="parent", cascade={"persist"})
*/
private $subCategories;
public function getParent(): ?self
{
return $this->parent;
}
public function setParent(?self $parent): self
{
$this->parent = $parent;
return $this;
}
/**
* #return Collection|self[]
*/
public function getSubCategories(): Collection
{
return $this->subCategories;
}
public function addCategory(self $category): self
{
if (!$this->subCategories->contains($category)) {
$this->subCategories[] = $category;
$category->setParent($this);
}
return $this;
}
Finally this is how it looks in my database:
So i just found the solution... wich is to add a = null on the FormInterface $form.
My code looks like this now (and it works fine):
<?php
namespace App\Form;
use App\Entity\Categories;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ArticlesSearchType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('category', EntityType::class, [
'class' => Categories::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('c')
->andWhere('c.parent is NULL')
->orderBy('c.name', 'ASC');
},
'choice_label' => 'name',
'label' => false,
'placeholder' => 'Recherche par catégorie',
'required' => false
]);
$formModifier = function (FormInterface $form = null, Categories $category = null) {
$subCategories = null === $category ? [] : $category->getSubCategories();
$form->add('subCategories', EntityType::class, [
'class' => Categories::class,
'choices' => $subCategories,
'choice_label' => 'name',
'label' => false,
'placeholder' => 'Sous-catégorie (Choisir une catégorie)',
'required' => false,
]);
};
$builder->get('category')->addEventListener(
FormEvents::POST_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$category = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $category);
}
);
$builder->get('category')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$category = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $category);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'method' => 'get',
'csrf_protection' => false
]);
}
public function getBlockPrefix()
{
return '';
}
}
I don't know though why it's not documented as so !
Got some question about passing custom variables to a custom FormType
I have a custom FormType named KontoType:
I pass some custom variables to it and this works like as expected, if i override the method buildForm and dump the passed $options array, the mandant exists and is a entity.
But how the heck can i now pass this custom variable to the function getChoices() which loads the choices based on the mandant in this custom FormType?
Even if i did reset the $options in the override buildForm function like $options['choices'] = $this->getChoices($options['mandant']) the select box is empty if i render this form.
<?php
namespace App\Form\Type;
use App\Entity\Core\Finanzen\Konto;
use App\Entity\Core\Organisation\Mandant;
use App\Services\LocaleService;
use Doctrine\Common\Persistence\ObjectManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Translation\TranslatorInterface;
class KontoType extends AbstractType
{
/**
* #var ObjectManager
*/
private $manager;
/**
* #var TranslatorInterface
*/
private $translator;
/**
* #var LocaleService
*/
private $localeService;
public function __construct(ObjectManager $manager, TranslatorInterface $translator, LocaleService $localeService)
{
$this->manager = $manager;
$this->translator = $translator;
$this->localeService = $localeService;
}
private function getChoices(Mandant $mandant=null)
{
return $this->manager->getRepository(Konto::class)->findBuchbar(true, $mandant);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'label' => 'konto.name',
'help' => 'konto.help',
'choices' => null,
'attr' => array(
'class' => 'bs-select',
'aria-hidden' => 'true',
'ref' => 'input',
'multiple' => false,
'tabindex' => 1,
'data-live-search' => true,
'data-size' => 6
),
'choice_label' => function ($choiceValue, $key, $value) {
return $choiceValue->getKonto()." ".$this->localeService->doTranslate($choiceValue);
},
'choice_value' => function(Konto $konto = null) {
return $konto ? $konto->getId() : '' ;
},
'required' => true,
'multiple' => false,
'empty_data' => null,
'label_attr' => array(
'class' => 'control-label'
),
'placeholder' => 'message.bitte wählen',
'mandant' => null
));
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$options['choices'] = $this->getChoices($options['mandant']);
parent::buildForm($builder, $options); // TODO: Change the autogenerated stub
}
public function getParent() {
return ChoiceType::class;
}
}
Hi I'm trying to 'Create a Custom Admin Action' for sonata admin bundle. But I'm facing this issue,
Runtime Notice: Declaration of
AdminBundle\Admin\VideoAdmin::configureRoutes() should be compatible
with
Sonata\AdminBundle\Admin\Admin::configureRoutes(Sonata\AdminBundle\Route\RouteCollection
$collection) in C:\wamp\www\videocenter\app/config. (which is being
imported from "C:\wamp\www\videocenter\app/config\routing.yml").
This is my configureRoutes() function,
protected function configureRoutes(RouteCollection $collection) {
$collection->add('clone', $this->getRouterIdParameter() . '/clone');
}
This is my complete admin class,
namespace AdminBundle\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 AdminBundle\Entity\Video;
use Doctrine\ORM\Query\ResultSetMapping;
class VideoAdmin extends Admin {
protected function configureFormFields(FormMapper $formMapper) {
$formMapper->add('name', 'text');
$formMapper->add('category', 'sonata_type_model', array(
'class' => 'AdminBundle\Entity\VideoCategory',
'property' => 'name',
));
$formMapper->add('thumbUrl', 'text');
$formMapper->add('url', 'text');
// $formMapper->add('videoKey', 'text');
$formMapper->add('isPublic', 'checkbox', array(
'label' => 'Show public',
'required' => false,
));
$formMapper->add('isEnabled', 'checkbox', array(
'label' => 'Enable',
'required' => false,
));
}
protected function configureDatagridFilters(DatagridMapper $datagridMapper) {
$datagridMapper
->add('name')
->add('category', null, array(), 'entity', array(
'class' => 'AdminBundle\Entity\VideoCategory',
'property' => 'name'))
->add('videoKey');
}
protected function configureListFields(ListMapper $listMapper) {
$listMapper
->addIdentifier('name')
->add('category.name')
->add('url')
->add('videoKey')
->add('isPublic')
->add('isEnabled')
->add('_action', 'actions', array(
'actions' => array(
'show' => array(),
'edit' => array(),
'delete' => array(),
'clone' => array(
'template' => 'AdminBundle:CRUD:list__action_clone.html.twig'
),
)
))
;
}
public function postPersist($object) {
global $kernel;
if ('AppCache' == get_class($kernel)) {
$kernel = $kernel->getKernel();
}
$em = $kernel->getContainer()->get('doctrine.orm.entity_manager');
$query = "select a.random_num from (SELECT FLOOR(RAND() * 99999) AS random_num) a WHERE A.RANDOM_NUM NOT IN (SELECT COALESCE(B.VIDEO_KEY,0) FROM VIDEO B)";
$stmt = $em->getConnection()->prepare($query);
$stmt->execute();
$randum = $stmt->fetchAll();
$video = $em->getRepository('AdminBundle:Video')->find($object->getId());
if ($video) {
$video->setVideoKey($randum[0]['random_num']);
$em->flush();
}
}
public function toString($object) {
return $object instanceof Video ? $object->getName() : 'Video'; // shown in the breadcrumb on the create view
}
public function getBatchActions() {
// retrieve the default batch actions (currently only delete)
$actions = parent::getBatchActions();
if (
$this->hasRoute('edit') && $this->isGranted('EDIT') &&
$this->hasRoute('delete') && $this->isGranted('DELETE')
) {
$actions['merge'] = array(
'label' => 'action_merge',
'translation_domain' => 'SonataAdminBundle',
'ask_confirmation' => true
);
}
return $actions;
}
protected function configureRoutes(RouteCollection $collection) {
$collection->add('clone', $this->getRouterIdParameter() . '/clone');
}
}
Did you import?
use Sonata\AdminBundle\Route\RouteCollection;
I doesn't see that in yours VideoAdmin class
I have a field type entity that is rendered as checkboxes I want to add for each checkbox a field of type textarea, how I can I do this ?
Code:
// OfferType.php
$builder
->add('payment_method', new OfferPaymentType(), [
'required' => false,
'mapped' => false,
'expanded' => true,
'multiple' => true,
])
;
// OfferPaymentType.php
class OfferPaymentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('payment', null, [
'multiple' => true,
'expanded' => true,
'compound' => true,
])
;
$factory = $builder->getFormFactory();
$formModifier = function (FormInterface $form, $payments = null) use ($factory) {
foreach ($form as $child) {
//dump($child);die;
$child->add(
$factory->createNamed('metadata', 'textarea', null, [
'auto_initialize' => false,
'compound' => true,
]),
null,
['compound' => true]
);
}
};
$builder->get('payment')->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
$data = $event->getData();
$formModifier($event->getForm(), $data);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'multiple' => true,
'expanded' => true,
'class' => 'AppBundle:OfferPayment',
'data_class' => 'AppBundle\Entity\OfferPayment',
'translation_domain' => 'app',
'compound' => true,
));
}
public function getName()
{
return 'offer_payment';
}
}
You need to create a custom form type which gonna have two embedded fields a checkbox and a textarea
class OfferPaymentType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('payement', 'checkbox')
->add('metadata', 'textarea');
}
}
And in your form Type you will do something like
class CustomType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('payment_method', 'collection', array(
'type' => new OfferPaymentType(),
'allow_add' => true,
'allow_delete' => true
)
);
}
}
I am trying to figure out how to modify html attributes on the fly with Symfony2 forms.
The situation is a case where a default placeholder is used most of the time, but occasionally, the developer needs to write a custom message.
My Form type looks like this:
<?php
namespace My\AwesomeBundle\FormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use My\AwesomeBundle\Transformer\ProcedureCodeTransformer;
class ProcedureType extends AbstractType
{
private $em;
private $user;
public function __construct($em, $securityContext)
{
$this->em=$em;
$this->user=$securityContext->getToken()->getUser();
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new ProcedureTransformer( $this->em );
$builder->addModelTransformer( $transformer );
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$user = $this->user;
$resolver->setDefaults(
array(
'class' => 'My\AwesomeBundle\Entity\Procedure',
'label' => 'Procedure',
'label_attr' => array( 'class'=> 'control-label' ),
'required' => false,
'empty_value' => '',
'attr' => array(
'class' => 's2',
'data-select2-placeholder' => 'Select Procedure',
),
)
);
$resolver->setOptional( array( 'placeholder' ) );
}
public function getParent() {
return 'hidden';
}
public function getName() {
return 'procedure';
}
}
The default render then has "Select Procedure" for the data-select2-placeholder element and javascript is used to display it. This is then used in a more complex type:
<?php
namespace My\AwesomeBundle\FormType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProcedureType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options){
$builder->add('biller', 'billers', array( 'placeholder' => 'New Text'));
[...]
}
public function setDefaultOptions(OptionsResolverInterface $resolver){
$resolver->setDefaults( array(
'data_class' => 'My\AwesomeBundle\Entity\Procedure'
) );
}
public function getName(){
return 'my_awesomebundle_proceduretype';
}
}
I would like 'New Text' to be placed into the data-select2-placeholder html attribute. However, if I call the builder like this:
$builder->add('procedure',
new ProcedureCodeType()),
array( 'attr' => array('data-select2-placeholder' => 'New Text') )
);
The entire html attribute array is replaced. this is not surprising. Is there a function in the form builder that has eluded me to add or modify a single html attribute?
Unfortunately, there is no way to modify a single attr the way you would want it. This is the closest and most simple solution I could come with:
What you have to do in your situation is to use callback functions in your options.
For each default attribute in your form types, instead of a normal value, you can use a callback function that accepts a single input representing your options (Symfony class Symfony\Component\OptionsResolver\Options). The function is called when building the form and the return value is then used as the value of the option.
That way, you can build the resulting option depending on the other provided options. An example:
use Symfony\Component\OptionsResolver\Options;
class ProcedureType extends AbstractType
{
...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$attr = function(Options $options) {
$default = array(
'class' => 's2',
'data-select2-placeholder' => 'Select Procedure',
);
return array_replace($default, $options['new_attr']);
};
$user = $this->user;
$resolver->setDefaults(
array(
'class' => 'My\AwesomeBundle\Entity\Procedure',
'label' => 'Procedure',
'label_attr' => array( 'class'=> 'control-label' ),
'required' => false,
'empty_value' => '',
'attr' => $attr,
'new_attr' => array(),
)
);
$resolver->setOptional( array( 'placeholder' ) );
}
That way, what you have to modify will be new_attr.
You might want to create a new class that resolves the nested options for you.
<?php
namespace My\AwesomeBundle\Form\Attr;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\Options;
class ProcedureCodeAttr
{
/**
* #var array
*/
protected $options;
/**
* #param array $options
*/
public function __construct(array $options = array())
{
$resolver = new OptionsResolver();
$this->setDefaultOptions($resolver);
$this->options = $resolver->resolve($options);
}
/**
* #param OptionsResolverInterface $resolver
*/
protected function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'class' => 's2',
'data-select2-placeholder' => 'Select Procedure',
));
}
/**
* #return array
*/
public function toArray()
{
return $this->options;
}
/**
* #param array $options
* #return array
*/
public static function resolve(array $options = array())
{
$attr = new static($options);
return $attr->toArray();
}
}
Then in your form type class you'd use it like so
$builder->add('procedure', new ProcedureCodeType()), array(
'attr' => ProcedureCodeAttr::resolve(array(
'data-select2-placeholder' => 'New Text'
))
));
Symfony 4.4 and above now support nested options https://symfony.com/doc/4.4/components/options_resolver.html#nested-options
Instead of defining a nested array, create a callback with OptionsResolver.
For example, a custom form type exists with the following defaults:
class CustomType extends AbstractType {
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('attr', function (OptionsResolver $attrResolver) {
$attrResolver->setDefaults([
'class' => 'form-control-lg',
'placeholder' => 'My Default Placeholder',
'autocomplete' => 'off',
]);
});
$resolver->setDefaults([
'label' => 'Name',
'help' => 'Enter a name',
]);
}
....
}
Attr is now a callback, not an array, now in any forms that use it just defined the attr properties like normal.
class MyFormType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', CustomType::class, [
'label' => 'My Form',
'help' => 'My Form Help',
'attr' => [
'placeholder' => 'My Form Placeholder',
],
])
....
}
The generated form will have the class: 'form-control-lg' and the label: 'My Form Placeholder'.