Submit Multiple Select Values to an Action in Laravel 4 - redirect

I've been trying to figure out how to submit multiple multi-select values using a redirect or something similar to an index() method/action in my resource controller in Laravel 4.
I've made several attempts to achieve this but have yet to work out how to do it.
I initially had it submitting to a query string, which worked to a certain extent but as soon as you selected more than 1 value in a single multi-select box, it wouldn't work, as it simply chose to use the last value.
This is the form in my view, with several multi-select boxes. http://paste.laravel.com/s1n
At the moment it's posting to an action/method in my EventsController called postSearch(). http://paste.laravel.com/s1p This method then redirects to my index() method and is supposed to take with it a $search_data variable to then be used for selecting results from an Eloquent model using http://paste.laravel.com/s1q
Now these methos are a bit of a mess at the moment, as I have been trying several things to get this working,
If anyone can offer any guidance on this I would really appreciate it. Thanks.

OK, so I've figured out how to do this. It may not be the best or most elegant solution but it works and I can refactor it later on.
First in my view, is the form.
{{ Form::open(array('action' => 'EventsController#postSearch', 'method' => 'POST', 'class' => 'view-only pull-left form-inline')) }}
{{ Form::label('courses', 'Course', array('class' => 'label')) }}
{{ Form::select('courses[]', $courses, '', array('class' => 'input-medium field', 'multiple' => 'multiple')) }}
{{ Form::label('suppliers', 'Supplier', array('class' => 'label')) }}
{{ Form::select('suppliers[]', $suppliers, '', array('class' => 'input-medium field', 'multiple' => 'multiple')) }}
{{ Form::label('locations', 'Location', array('class' => 'label')) }}
{{ Form::select('locations[]', $locations, '', array('class' => 'input-medium field', 'multiple' => 'multiple')) }}
{{ Form::label('venues', 'Venue', array('class' => 'label')) }}
{{ Form::select('venues[]', $venues, '', array('class' => 'input-medium field', 'multiple' => 'multiple')) }}
{{ Form::label('event_statuses', 'Status', array('class' => 'label')) }}
{{ Form::select('event_statuses[]', $event_statuses, '', array('class' => 'input-medium field', 'multiple' => 'multiple')) }}
{{ Form::submit('Search', array('class' => 'btn btn-success')) }}
{{ Form::close() }}
And my refined postSearch method in my EventsController:
public static function postSearch()
{
$courses_value = Input::get('courses');
$suppliers_value = Input::get('suppliers');
$locations_value = Input::get('locations');
$venues_value = Input::get('venues');
$status_value = Input::get('event_statuses');
// $tutor_value = Input::get('tutors');
$search_data = array(
'courses_value' => $courses_value,
'suppliers_value' => $suppliers_value,
'locations_value' => $locations_value,
'venues_value' => $venues_value,
'status_value' => $status_value,
// 'tutor_value' => $tutor_value
);
return Redirect::to('events')->with($search_data);
}
Then my index method contains the following:
$courses_value = Session::get('courses_value');
$suppliers_value = Session::get('suppliers_value');
$locations_value = Session::get('locations_value');
$venues_value = Session::get('venues_value');
$status_value = Session::get('status_value');
// $tutor_value = Session::get('tutor_value');
if (is_null($courses_value)) {
$courses = Course::all();
$course_titles = array();
foreach ($courses as $course) {
$course_titles[] = $course->title;
}
$courses_value = $course_titles;
}
if (is_null($suppliers_value)) {
$suppliers = Supplier::all();
$supplier_names = array();
foreach ($suppliers as $supplier) {
$supplier_names[] = $supplier->name;
}
$suppliers_value = $supplier_names;
}
if (is_null($locations_value)) {
$locations = Location::all();
$location_cities = array();
foreach ($locations as $location) {
$location_cities[] = $location->city;
}
$locations_value = $location_cities;
}
if (is_null($venues_value)) {
$venues = Venue::all();
$venue_names = array();
foreach ($venues as $venue) {
$venue_names[] = $venue->name;
}
$venues_value = $venue_names;
}
if (is_null($status_value)) {
$event_statuses = EventStatus::all();
$statuses = array();
foreach ($event_statuses as $event_status) {
$statuses[] = $event_status->status;
}
$status_value = $statuses;
}
This is the bit that could probably do with refactoring. Anyway, now when I select my results using:
$events = Event::where('active', '=', 1)
->leftJoin('courses', 'courses.id', '=', 'events.course_id')
->leftJoin('suppliers', 'suppliers.id', '=', 'events.supplier_id')
->leftJoin('locations', 'locations.id', '=', 'events.location_id')
->leftJoin('venues', 'venues.id', '=', 'events.venue_id')
->leftJoin('event_types', 'event_types.id', '=', 'events.event_type_id')
->leftJoin('event_statuses', 'event_statuses.id', '=', 'events.event_status_id')
->leftJoin('tutors', 'tutors.id', '=', 'events.tutor_id')
->orderBy($order[0], $order[1])
->select(array('events.*', DB::raw('events.id as eid'), DB::raw('suppliers.name as sname'), DB::raw('locations.city as lcity'), DB::raw('venues.name as vname'), DB::raw('venues.city as vcity'), DB::raw('tutors.first_name as tfirst_name')))
->whereIn('courses.title', $courses_value)
->whereIn('suppliers.name', $suppliers_value)
->whereIn('locations.city', $locations_value)
->whereIn('venues.name', $venues_value)
->whereIn('event_statuses.status', $status_value)
// ->whereIn('tutors.first_name', $tutor_value)
->paginate($perPage);
I am now able to select single or multiple values in the multi-select boxes and the results show as expected, which is great.
There's probably a much better way of doing this, so if you guys know of one please let me know.
What would be nice is to store the submission data in a variable that is not retrieved from the session, so I can use pagination on the results and the user doesn't have to re-search if they refresh or move away from and back to the page.

Related

symfony search form keep seach parameters

I made a searchform, but i don't know how to keep the selected search parameters in the form.
I would like to do this through the controller and not in twig file self.
I thought by setting the parameter it will preselect the option but that doesn't work.
Below my form
$builder
->add('type', EntityType::class, [
'class' => Producttype::class,
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('p')
->orderBy('p.id', 'ASC');
},
'choice_label' => 'Title',
'placeholder' => 'All types',
'required' => false,
])
->add('Search', SubmitType::class)
;
}
In the controller I have this
$query = $request->query->get('searchfilter');
$querybuilder = $em->getRepository(Product::class)
->createQueryBuilder('p');
if($query['type'] != NULL) {
$querybuilder->innerJoin('p.producttype', 'b')
->addSelect('b')
->andWhere('b.id like :type')
->setParameter('type', $query['type']);
;
}
and the form in twig
<div class="container">
{{ form_start(form, {'action': path('product_search'), 'method': 'GET'}) }}
{{ form_widget(form) }}
{{ form_end(form) }}
</div>
Thanks in advance!

Symfony form not valid and not submittable

When trying to submit my form, it is not working. The button is not doing anything at all. So I tried to dump something withing my:
if ($form->isValid() && $form->isSubmitted())
And realized that it's not even entering the if statement, so I assume that there is something wrong with my form. But I can't yet figure out what it would be, so I would be glad if anyone could help me!
/**
* #Route("/document/bulkdeactivate", name="documentBundle_document_bulkDeactivate")
* #Template()
*/
public function bulkDeactivateAction(Request $request) {
$em = $this->getDoctrine()->getManager();
$selected_documents = $request->request->all();
$form = $this->createForm(DocumentDeactivationType::class);
$form->handleRequest($request);
if ($form->isValid() && $form->isSubmitted()) {
foreach($selected_documents as $document) {
$documentR = json_decode(json_encode($document), true);
dump($documentR);
for($i=0; $i<count($documentR); $i++){
$doc = $em->getRepository('DocumentBundle:Document')->findOneById($documentR[$i]);
dump($doc);
$doc->setActive(false);
$em->persist($doc);
$em->flush();
}
}
$this->addFlash(
'success',
'The document has been deactivated!'
);
return $this->redirectToRoute('documentBundle_document_list');
}
return $this->render('DocumentBundle:Panels:ActivationPanel.html.twig', array(
'form' => $form->createView(),
));
}
my Form Type:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('documentlist', EntityType::class, array(
'class' => 'DocumentBundle:Document',
'choice_label' => 'name',
'required' => false,
'multiple' => true,
'expanded' => false,
'placeholder' => "Select Documents",
'label' => 'label.document_list',
))
->add('submit', SubmitType::class, array(
'label' => 'Submit',
'attr' => array(
'class' => 'btn btn btn-default',
),
));
}
And the form part of my twig template:
{% block content %}
{{ form_start(form) }}
{{ form(form.documentlist) }}
{{ form(form.submit) }}
{{ form_end(form) }}
{% endblock content %}
There must be some issues with the form, do you have any advice for my?
Actually there are several issues:
you cannot add a submit button that is not bound to a Symfony form in your case because Symfony will not identify it properly (for lack of a name that has to match SF's automatic input names generation)
In your formBuilder put:
$builder->add('submit', SubmitType::class);
your form has an incorrect name ("form") that conflicts with Twig's form() function, you have to change it,
you might have an issue (not tested, just a guess) regarding the many calls made to Twig's form() because this function dumps the whole form content, to manually dump each part use
Your {% block content %} should have this:
{{ form_widget(form.your_widget_name, attrs) }}
I eventually solved my issue:
1. as pointed out by #NaeiKinDus, I needed a Submit button that actually belonged to my form, plus I changed my form name to a custom deactivationForm
{% block content %}
{{ form(deactivationForm) }}
{% endblock content %}
the buildForm method:
$builder
->add('documentlist', EntityType::class, array(
'class' => 'DocumentBundle:Document',
'choice_label' => 'name',
'required' => false,
'multiple' => true,
'expanded' => false,
'placeholder' => "Select Documents",
'label' => 'label.document_list',
'translation_domain' => 'Documents',
'query_builder' => function (EntityRepository $er) {
return $er->createQueryBuilder('d')
->where('d.active = FALSE');
},
))
->add('submit', SubmitType::class, array(
'label' => 'Submit',
'attr' => array(
'class' => 'btn btn btn-default',
),
));
then in my controller:
/**
* #Route("/document/bulkdeactivate", name="documentBundle_document_bulkDeactivate")
* #Template()
*/
public function bulkDeactivateAction(Request $request) {
/*
* GET DOCUMENT FROM DB
*/
$em = $this->getDoctrine()->getManager();
$selected_documents = $request->request->all();
$deactivationForm = $this->createForm(DocumentDeactivationType::class);
$deactivationForm->handleRequest($request);
if ($deactivationForm->isValid() && $deactivationForm->isSubmitted()) {
foreach($selected_documents as $document) {
$documentR = json_decode(json_encode($document), true);
dump(count($documentR['documentlist']));
for($i=0; $i<count($documentR['documentlist']); $i++){
$doc = $em->getRepository('DocumentBundle:Document')->findOneById($documentR['documentlist'][$i]);
dump($documentR['documentlist'][$i]);
$doc->setActive(true);
$em->persist($doc);
$em->flush();
}
}
$this->addFlash(
'success',
'The selected document(s) have been deactivated!'
);
return $this->redirectToRoute('documentBundle_document_list');
}
return $this->render('DocumentBundle:Panels:ActivationPanel.html.twig', array(
'deactivationForm' => $deactivationForm->createView(),
));
}
I tried to access the wrong array positions (I didn't realize that the decoding of the ajax data sent me 3 array positions but I only wanted to access the first 'documentlist' and then get the document IDs from that one.

Symfony2 : Change {{ form_enctype }} to {{ form_start }} with a custom path

I'm working on updating my code to Symfony3 standards but I come to some problems with some of my forms. I tried several things but nothing is working.
In my twig :
<form action="{{ path('paces_colle_gestioncsv_imprimer',
{ 'id' : document.id }) }}" method="post" {{ form_enctype(form) }}>
</form>
{{ form_enctype }} is deprecated since Symfony 2.8. I have to get the path to the controller to change it to {{ form_start }}
In my controller :
public function ajouterErrataAction($id)
{
$em = $this->getDoctrine()->getManager();
$document = $em->getRepository('PACESColleBundle:DocumentCSV')->find($id);
if(!$document){
throw $this->createNotFoundException('Document inexistant');
}
$colle = $document->getColle();
$form = $this->createForm(ajouterErrataType::class);
return $this->render('PACESColleBundle:Classement:ajouterErrata.html.twig', array(
"colle" => $colle,
"document" => $document,
"form" => $form->createView()
));
}
My formType :
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('errata', TextareaType::class, array('required' => false,
'attr' => array('class'=>'ckeditor')
))
->add('precision', TextareaType::class, array('required' => false,
'attr' => array('class'=>'ckeditor')))
->add('submit',SubmitType::class, array('label' => 'Imprimer'));
;
}
You can override action in controller or template:
http://symfony.com/doc/current/book/forms.html#changing-the-action-and-method-of-a-form
Have you tried?
You can set the action path like this:
$form = $this->createForm(ajouterErrataType::class, $data, [
'action' => $this->generateUrl(''paces_colle_gestioncsv_imprimer', ['id' => $document->getId()]),
'method' => 'POST',
]);
The Symfony 3 way of doing it is like this:
$form = $this->createForm(TaskType::class, $task, array(
'action' => $this->generateUrl('target_route'),
'method' => 'GET',
));

Symfony form builder only works when fields are in a given order

Here is a working code:
$formBuilder = $this->get('form.factory')->createBuilder('form', $scene);
$formBuilder->add('text', 'textarea', array('required' => false));
$formBuilder->add('duration', 'time', array('with_seconds' => true, 'input' => 'string'));
$formBuilder->add('submit', 'submit');
$form = $formBuilder->getForm();
$form->handleRequest($request);
When I switch the lines like this:
$formBuilder = $this->get('form.factory')->createBuilder('form', $scene);
$formBuilder->add('duration', 'time', array('with_seconds' => true, 'input' => 'string'));
$formBuilder->add('text', 'textarea', array('required' => false));
$formBuilder->add('submit', 'submit');
$form = $formBuilder->getForm();
$form->handleRequest($request);
it doesn't work anymore: the value of duration becomes 0 instead of something in the form of 00:00:00.
I have no template to display them. In my twig page, I simple use {{ form(form) }}.
What's going on, there?
I copied your code and find anything wrong with it, you should be able to change the order of lines. But indirect solution would be leaving your code from the first example and manipulate order in the twig template:
{{ form_errors(form) }}
{{ form_row(form.duration) }}
{{ form_row(form.text) }}
{{ form_rest(form) }}

Laravel 4 change form open with if else statement

I'm new to Laravel and trying some stuff out. Hope you guys can point me in the right direction.
I have a form to add categories on a the page with the overview of all the categories. Now I'm routing to the same page when you want to add or edit a category. Problem is, I want the normal add form to show when the url is category/add and when the url is category/edit/id I want it to show the form to edit (with form model binding). I placed the code in an if else statement, but it always shows the edit form, therefore if I want to add a new category on the url categories I get an error because it's the edit form.
Here's the code in the index.bade.php:
#if(Request::url() === 'categories' )
{{ Form::open(array('url' => 'category/add', 'class' => 'form-horizontal')) }}
#else
{{ Form::model($category, array(
'url' => 'category/edit/'.$category->id ,
$category->id,
'class' => 'form-horizontal'))
}}
#endif
My controller:
public function index()
{
$categories = Category::get();
$category = new Category();
View::share('title', 'Mini Blog - Categories');
$view = View::make('category.index')->with('category', $category);
$view->categories = $categories;
return $view;
}
public function edit($id)
{
$categories = Category::get();
$category = Category::find($id);
View::share('title', 'Mini Blog - Edit category');
return View::make('category.index')->with(array('category' => $category, 'categories' => $categories));
}
Is this the way to go? Or is there a better way?
Instead of checking the URL I suggest you depend on the model variable. Ever Eloquent model has a property exists that tells you if the model has been loaded from the DB or it's a new instance.
#if( ! $category->exists)
{{ Form::open(array('url' => 'category/add', 'class' => 'form-horizontal')) }}
#else
{{ Form::model($category, array(
'url' => 'category/edit/'.$category->id ,
$category->id,
'class' => 'form-horizontal'))
}}
#endif
Actually you can use Form::model with an empty model as well, so you should be able to reduce it to this:
{{ Form::model($category, array(
'url' => ($category->exists ? 'category/edit/'.$category->id : 'category/add'),
'class' => 'form-horizontal'))
}}
There is a much better way.
In your controller do this:
public function create()
{
return View::make('create');
}
public function edit($id)
{
return View::make('edit')->withId($id);
}
Then in your views you have
Create.blade.php
{{ Form::open(array('url' => 'category/add', 'class' => 'form-horizontal')) }}
#include ('form')
{{ Form::close() }}
Edit.blade.php
{{ Form::model($category, array('url' => 'category/edit/'.$category->id, category->id, 'class' => 'form-horizontal')) }}
#include ('form')
{{ Form::close() }}
Form.blade.php
//Put your HTML form stuff here