I am creating simple search form that will query and render data using Doctrine LIKE expression.I am confused how to get the parameter like this
$name = 'San Francisco China';
In the controller i created a simple form
public function searchAction(Request $request)
{
$data = array();
$form= $this->createFormBuilder($data)
->add('name_city', 'text', array(
'label' => 'Search Here',
'error_bubbling' => true,
))
->add('search', 'submit')
->getForm();
if ($request->isMethod('POST')) {
$form->handleRequest($request);
$data = $form->getData();
}
$name = $request->request->get('name_city');//this confused me
//$name = 'Beijing Angeles';//this will work
$em = $this->getDoctrine()->getManager();
$city = $em->getRepository('Bundle:City')->searchCity($name);
return $this->render('Bundle:City:list.html.twig', array(
'city' => $city,
'form' => $form->createView(),
));
}
In this case, the $name variable is passed to the searchCity method as the argument
public function searchCity($name)
{
return $this
->createQueryBuilder('c')
->select('c')
->where('c.name LIKE :name_city')
->setParameter('name_city', '%'.$name.'%')
// ->orderBy('v.dateCreated', 'DESC')
->getQuery()
->getResult()
;
}
//list.twig
{% extends '::base.html.twig' %}
{% block body %}
{{ form(form)}}
{% for city in city %}
{{ city }}
{% endfor %}
{% endblock %}
This will not work since all city name are displayed during page load. If i manually add a value to the $name, e.g $name = 'Beijing Washington', search works.Whats the correct way?
As for the setting the default name_city, you could do this:
$data = array(
'name_city' => 'Beijing Angeles'
);
$form= $this->createFormBuilder($data)
->add('name_city', 'text', array(
'label' => 'Search Here',
'error_bubbling' => true,
))
->add('search', 'submit')
->getForm();
// The rest of your code
Then, get the city name like:
$form->handleRequest($request);
$data = $form->getData();
$name = $data['name_city'];
You might notice that I removed the POST check. That's because search forms are traditionally set to method="get", because of history/bookmarks. So, in your example, you would never check for POST but handlerRequest() each and every time.
Hope this helps...
Feel free to correct me if I misunderstood the intention here ;)
Related
I want to do this:
$builder->add('participants', EntityType::class, array(
'label' => 'Teilnehmer',
'class' => SchoolUser::class,
'multiple' => true,
'choices' => $this->getParticipantsOfEntry($builder),
'empty_value' => 'All',
'empty_data' => null,
'preferred_choices' => array(null)
));
But I get no selected 'All' - field at all. This should not be hard, I wonder where is my mistake?
'placeholder' = 'All',
did also not work for me.
How can I do this?
I know the question was 4-months ago but I ran into a similar problem so thought I would share my solution in case it helps others.
First in the Form object:
class MealFormType extends AbstractType {
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder->add( 'courses', EntityType::class, array(
'class' => MealCourse::class,
'multiple' => true,
) );
$builder->addEventListener( FormEvents::PRE_SUBMIT, function( FormEvent $event ) {
// Remove added entries here... maybe something like
$data = $event->getData();
if( ($key = array_search( 'all', $data['courses'] ) ) !== false ) {
unset( $data['courses'][$key] );
$event->setData( $data );
}
} );
}
public function configureOptions( OptionsResolver $resolver ) {
$resolver->setDefaults( ['data_class' => Meal::class] );
}
public function finishView( FormView $view, FormInterface $form, array $options) {
$newChoice = new ChoiceView( null, 'all', 'I want them all!' );
array_unshift( $view->children['courses']->vars['choices'], $newChoice );
}
}
This creates the form with multiple meal courses that can be selected, such as appetizer, soup, salad, entree, coffee, desert, ... using the buildForm() method. Also in this method an event listener for the PRE_SUBMIT event is added whose job it will be to remove any choices we have added since they are probably not real (if they were, we wouldn't have to add them in this painful way).
It sets the data_class as usual in configureOptions().
Lastly it provides the finishView() method that creates a new choice with value of 'all' that will display as 'I want them all' in the select, then adds it at the beginning for the 'choices' array for the 'courses' form entry (this would be $view->children['courses']->vars['choices'][] = $newChoice; to put it at the end).
Ok, this is fine but it really doesn't do anything more than having an additional option to click or unclick. To use this we need some Javascript to manage the form. I just stuck this Javascript in the bottom of my Twig file and use jQuery.
The Twig file:
{% block body %}
{{ form_start(mealForm) }}
{{ form_row(mealForm.courses,
{'attr': {'class': 'js-meal_course-select'}}) }}
<button type="submit">Save</button>
{{ form_end(mealForm) }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
var mealCourseAllSelected = false;
jQuery(document).ready( function() {
// This will be called whenever an entry is selected or deselected
$('.js-meal_course-select').change( function() {
var selectedSet = $(this).val();
if( mealCourseAllSelected &&
(selectedSet.length != $(this).get(0).options.length - 1 ||
selectedSet.includes( 'all' )) ) {
var opts = $(this).get(0).options;
var len = opts.length;
for( var i = 0; i < len; ++i ) {
if( opts[i].value == 'all' ) opts[i].selected = false;
}
mealCourseAllSelected = false;
} else if( selectedSet.includes( "all" ) ||
selectedSet.length == $(this).get(0).options.length - 1 ) {
$('.js-meal_course-select option').prop( 'selected', true );
mealCourseAllSelected = true;
}
}
);
</script>
{% endblock %}
The JavaScript responds to changes to what is selected. If the select option with the value of 'all' is in the selected set, then all the select options are marked as selected, and a flag is set to say so. This is also done if everything is selected except the 'all' entry because that is kind of the definition of all.
If the 'all are selected' flag is set and there is a change, then that change must be deselecting at least one, unless that one is the select option with the value of 'all', it deselects the selection option with the value of 'all' and clears the 'all are selected' flag. If all are selected and the user tries to unselect just the select option with the value of 'all', it is just reselected.
I'm currently creating a Drupal 8 module. I would like to be able to use a custom template to display a select element in a form. Here is the code I have written so far:
File my_module/templates/colorselector.html.twig:
{% spaceless %}
<div class="select-wrapper">
{% set classes = ['form-control', 'color-selector'] %}
<select{{ attributes.addClass(classes) }}>
{% for option in options %}
<option value="{{ option.value }}" data-color="{{ option.code }}"{{ option.selected ? ' selected="selected"' }}>{{ option.label }}</option>
{% endfor %}
</select>
</div>
<script>
$('.color-selector').colorselector();
</script>
{% endspaceless %}
File my_module/my_module.module:
use Drupal\Core\Render\Element;
use Drupal\Core\Render\Element\RenderElement;
function my_module_theme($existing, $type, $theme, $path) {
return [
// ...
'colorselector' => [
'render element' => 'select'
]
];
}
function my_module_preprocess_colorselector(&$variables) {
$element = $variables['element'];
Element::setAttributes($element, ['id', 'name', 'size']);
RenderElement::setAttributes($element, ['form-select']);
$variables['attributes'] = $element['#attributes'];
$variables['options'] = [];
foreach ($element['#options'] as $colorName => $colorCode) {
$option = [];
$option['value'] = $colorName;
$option['label'] = $colorName;
$option['code'] = $colorCode;
$variables['options'][] = $option;
}
}
File my_module/src/Form/MyCustomForm.php:
namespace Drupal\my_module\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class MyCustomForm extends FormBase {
public function buildForm(array $form, FormStateInterface $form_state) {
// ...
$form['color'] = [
'#type' => 'select',
'#theme' => 'colorselector',
'#title' => $this->t('Couleur'),
'#required' => TRUE,
'#options' => $options
];
// ...
return $form;
}
// ...
}
I get the following error message when I try to display the form: The website encountered an unexpected error. Please try again later.
If I remove '#theme' => 'colorselector' from $form['color'], it displays the form correctly but it does not use my template obviously.
Do you know where the error comes from and how to fix it?
Thanks!
Your hook_theme function should be like this.
function my_module_theme($existing, $type, $theme, $path) {
return array(
'color_selector' => array(
'variables' => array('params' => null),
'template' => 'colorselector'
)
);
}
Now, your template name might be my_module/templates/colorselector.html.twig
I use Sonata Admin Bundle in my Symfony project and created an ArticleAdmin class for my Article entity.
In the list page, I added some custom actions to quickly publish, unpublish, delete & preview each article.
What I want to do is to hide publish button when an article is already published & vice versa.
To do this, I need to have access to each object in method configureListFields(). I would do something like this:
protected function configureListFields(ListMapper $listMapper)
{
$listMapper->add('title');
// ...
/** #var Article article */
$article = $this->getSubject();
// Actions for all items.
$actions = array(
'delete' => array(),
'preview' => array(
'template' => 'AppBundle:ArticleAdmin:list__action_preview.html.twig',
),
);
// Manage actions depending on article's status.
if ($article->isPublished()) {
$actions['draft']['template'] = 'AppBundle:ArticleAdmin:list__action_draft.html.twig';
} else {
$actions['publish']['template'] = 'AppBundle:ArticleAdmin:list__action_preview.html.twig';
}
$listMapper->add('_actions', null, array('actions' => $actions));
}
But $this->getSubjet() always returns NULL. I also tried $listMapper->getAdmin()->getSubject() and many other getters but always the same result.
What am I doing wrong ?
Thanks for reading & have a good day :)
You can do the check directly in the _action template, as you can access the current subject.
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->add('title')
->add('_action', 'actions', array(
'actions' => array(
'delete' => array(),
'preview' => array(
'template' => 'AppBundle:ArticleAdmin:list__action_preview.html.twig',
),
'draft' => array(
'template' => 'AppBundle:ArticleAdmin:list__action_draft.html.twig',
),
'publish' => array(
'template' => 'AppBundle:ArticleAdmin:list__action_publish.html.twig',
),
))
;
}
And for example in AppBundle:ArticleAdmin:list__action_draft.html.twig, you can check your condition :
{% if object.isPublished %}
your html code
{% endif %}
There is the following code
$form->with('Item')->add('parent', null, array(
'label' => 'Category',
'required' => true,
'query_builder' =>
function($er) use ($id) {
$qb = $er->createQueryBuilder('p');
if ($id){
$qb->where('p.id <> :id')
->setParameter('id', $id);
}
$qb->orderBy('p.root, p.lft', 'ASC');
return $qb;
}
.........
Result is the entity-objects collection which is given to the string (__toString method).
It return name-field.
But I need get the another field - url.
How to get url value instead the name in the select-list form?
The query_builder type return object => how to change this form that it works likewise query_builder?
I didn't work with SonataAdminBundle forms, but I think that it works absolutely like symfony forms. All that you need here is to add 'class' and 'property' values to your options list:
$form->with('Item')->add('parent', null, array(
'class' => 'Acme\DemoBundle\Entity\Category',
'property' => 'url',
'label' => 'Category',
'required' => true,
'query_builder' =>
function($er) use ($id) {
$qb = $er->createQueryBuilder('p');
if ($id){
$qb->where('p.id <> :id')
->setParameter('id', $id);
}
$qb->orderBy('p.root, p.lft', 'ASC');
return $qb;
}
property - is the name of the field in your entity that will represent your Entity's value instead of calling __toString().
But also... If you need always represent your Entity as URL you can simply override __toString() method in the Entity class to something like that:
public function __toString() {
return $this->url;
}
In my case, ->orderBy() used in query_builder worked, but was overwritten for unknown reasons "somewhere" later. Fun fact: when I used extended => true, everything was rendered like sorted in the query_builder. But by using extended => false my <option>s are re-sorted before select2 touched it.
As a workaround I did this:
config/packages/twig.yaml
twig:
paths:
'%kernel.project_dir%/templates': '%kernel.project_dir%/templates'
# This is to prevent a infinite loop when extending the parent template
'vendor/sonata-project/admin-bundle/src/Resources/views': SonataAdminBundleOriginal
And then I done the sorting I wanted again in twig for the project entity:
templates/bundles/SonataAdminBundle/Form/form_admin_fields.html.twig:
{% extends '#SonataAdminBundleOriginal/Form/form_admin_fields.html.twig' %}
{%- block choice_widget_options -%}
{% if name == 'project' %}
{% set options = options|sort((a, b) => a.data.numberSortable <=> b.data.numberSortable)|reverse %}
{% endif %}
{{ parent() }}
{%- endblock choice_widget_options -%}
Just in case if someone needs to "filter" an entity by a FK (foreign key) just like i needed (and searched for 3 days), here is the solution:
In the __construct you can set/find what you need. In my case i work this "sessions" (year). So i find the session->status=true:
$this->sessionActive = $this->sessionRepository->findOneBy(['status'=>true]);
Then in the QueryBuilder:
protected function configureQuery(ProxyQueryInterface $query): ProxyQueryInterface
{
$rootAlias = current($query->getRootAliases());
$query
->select($rootAlias)
->where($rootAlias.'.session = :id')
->addOrderBy($rootAlias.'.dateStart', 'ASC')
->setParameter('id', $this->sessionActive->getId());
return $query;
}
Note: I have always one Session->status set to "true". If you might have null values don't forget to make a condition so that it doesn't give you an error!
Regards.
I went to this tutorial. I got the example to work. But I don't get it because I can't customize it for my case.
I want to display a picture for each of my facebook friend and pass the adress of the profile picture from facebook to an image tag in front of the checkbox element.
Update
part of my action:
//the array is on part of my update.
$choice_test = array();
$choice_test[] = array('id' => 1, 'username' => 'test');
$choice_test[] = array('id' => 2, 'username' => 'test2');
->add('friend', 'choice', array(
'required' => true,
'expanded' => true,
'choice_list' => $choice_test, //this is my update
'choices' => $fb_friends_form, //$fb_friends_form[1]= 'First Lastane';
'multiple' => true,
'constraints' => array(new CheckChoicesFbFriends(array('fb_friends_form' => $fb_friends_form))),
'mapped' => true
))
return $this->render('FrontendChancesBundle::createrequest.html.php', array(
'form' => $form->createView()));
template:
<?php $view['form']->setTheme($form, array('FrontendDemoBundle:Form')) ;?>
<?php echo $view['form']->widget($form['friend'])?>
in FrontendDemoBundle/Ressources/views/Form/form_widget_pics.html.php:
<input
type="<?php echo isset($type) ? $view->escape($type) : 'checkbox' ?>"
<?php if (!empty($value)): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
<?php echo $view['form']->block($form, 'checkbox_widget') ?>
/>
How can I pass even a variable, in my case the username (`$fb_username[0] = 'username' of facebook to from_widget_pics.html.php and how can I display it like this with the form bulider in my aciton:
<img src="www.facebook.com/+FirstLastname>
<input type="checkbox"
id="form_friend_0"
name="form[friend][]"
value="1"/>
<label for="form_friend_0" >First Lastname</label>
<img src="www.facebook.com/+nextfriend>
I had the very same exception message when trying to pass an array or collection of custom selected objects to a form using 'choice_list' option.
I solved it this way (simplified example):
Controller code:
use Symfony\Component\Form\Extension\Core\ChoiceList\SimpleChoiceList;
class UsedCarsController extends Controller {
..... action code:
$usedCars = $carRepository->findAllUsed();
$choiceList = new SimpleChoiceList($usedCars);
$form = $this->createForm(new UsedCarsType($choiceList),null, array('label' => 'Used cars form'));
... now render view, etc.
UsedCarsType:
class UsedCarsType extends AbstractType
{
private $choicesList;
public function __construct($choicesList)
{
$this->choicesList = $choicesList;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('usedCars', 'choice',array('label'=>'Used cars',
'required'=>true,
'choice_list' => $this->choicesList,
'empty_value' => '-- used car --'))
;
}
Choice_list option must be an instance of SimpleChoiceList class, not an array.
A bit late but hope this helps.
BTW: I am using Symfony 2.3