What's the best way to manage filter page and result page in Symfony?
I have a controller that manage filter form and execute query. The result of this query must pass in another controller action. The result must show in another controller action because I used knp_paginator. If I render the result in same controller action of filter form, when change page the controller show filter form and not result.
this is the approach that I used:
Action for create find form:
public function findAction(Request $request)
{
$form = $this->createFindForm($request);
$form->handleRequest($request);
if(($form->isSubmitted() && !$request->isXmlHttpRequest()))
{
if(($this->isValidFindForm($form) && $form->isValid()))
{
$parm = $request->request->get('findForm');
return $this->redirect($this->generateUrl('list_documents',$parm));
}
}
return $this->render(
'myBundle:Documents:document\document_find.html.twig',
array('form' => $form->createView())
);
}
private function createFindForm(Request $request)
{
$form = $this->createForm(
new findDocumentType(
$this->getDoctrine()->getManager(),
$request
),
null,
array(
'action' => $this->generateUrl('find_documents'),
'method' => 'POST',
)
);
return $form;
}
I used $parm = $request->request->get('findForm'); to get the querystring. "findForm" is the name of my filter form.
The redirecting action :
public function listAction(Request $request)
{
$documents = $this->searchDocument($request);
$paginator = $this->get('knp_paginator');
$pagination = $paginator->paginate(
$documents,
$this->get('request')->query->get('page', 1)
);
return $this->render(
'myBundle:Documents:document\document_list.html.twig',
array('pagination' => $pagination)
);
}
The request is passed to search function (request contains querystring)
In search action:
private function searchDocument(Request $request)
{
$parm = $request->query;
$repository = $this->getDoctrine()->getRepository('docliteBundle:Document\\document');
$query = $repository
->createQueryBuilder('d');
....
With $request->query->get() I have access to all parameter.
Hope this help someone.
P.S. for the pagination I used KNP_paginator
Related
I'm trying to implement a hook_form_alter method to modify the behaviour of one field through ajax callback in the form display of node.
The idea is when I select one option from the select list field (field_country) modify the values of other field list (field_laws). Specificly, when I select one country, the hook method pass this value (current) through ajax callback to changeLawsData. This callback get one external service that returns one array of values filtered by the country selected previously.
The issue is inside of callback method, i can't access to $form and $form_state objects that contain the previous hook_form_alter.
My question is: Is posible to pass by arguments this objects to the callback? With this i could handler the state of form and its field, for example.
Something like this:
$form['field_country']['widget']['#ajax'] = array(
'callback' => [$this,'changeLawsData'],
'event' => 'change',
'disable-refocus' => FALSE,
**'arguments' = array($form, $form_state)**
);
Here is the full code of this implementation.
<?php
namespace Drupal\obs_urban_system\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
* Our event subscriber class.
*/
class NodeUrbanSystemFormAlterEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
HookEventDispatcherInterface::FORM_ALTER => 'hookFormAlter'
];
}
/**
* Implements hook_form_alter
*/
public function hookFormAlter($event) {
if($event->getFormId() == 'node_urban_system_edit_form') {
$form = $event->getForm();
$country = $form['field_country']['widget']['#default_value'];
$form['field_laws']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');
$form['field_law_articles']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawArticlesByCountry($country);
$form['field_country']['widget']['#ajax'] = array(
'callback' => [$this,'changeLawsData'],
'event' => 'change',
'disable-refocus' => FALSE
);
$event->setForm($form);
}
}
/**
* #param $form
* #param \Drupal\Core\Form\FormStateInterface $form_state
* #return \Drupal\Core\Ajax\AjaxResponse
*/
function changeLawsData(&$form, FormStateInterface $form_state) {
<!--- HERE IM USING THE $form object --->
$country = $form['field_country']['widget']['#default_value'];
<!--- --->
$laws = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');
foreach ($laws as $key => $value) {
$option .= "<option value='" . $key . "'>" . $value . " </option>";
}
$response = new AjaxResponse();
$response->addCommand(new HtmlCommand('#edit-field-laws-0-value', $option));
return $response;
}
}
Thanks to all very much.
You need to do all the form manipulation within the form_alter.
When the ajax callback is fired, the form will be rebuilt and the current values of the form will be available in the form_state.
Your ajax callback should only return what is needed on the front end, it should not actually manipulate the form array.
Here is an example with your code (example only, untested)
<?php
namespace Drupal\obs_urban_system\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
* Our event subscriber class.
*/
class NodeUrbanSystemFormAlterEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
HookEventDispatcherInterface::FORM_ALTER => 'hookFormAlter'
];
}
/**
* Implements hook_form_alter
*/
public function hookFormAlter($event) {
if($event->getFormId() == 'node_urban_system_edit_form') {
$form = $event->getForm();
$country = $form['field_country']['widget']['#default_value'];
// Get the form state object.
$form_state = $event->getFormState();
// Here we should check if a country has been selected.
$country = $form_state->getValue('country');
if ($country) {
// populate the options from service here.
$form['field_laws']['widget']['#options'] = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');
} else {
// Populate with default options.
$form['field_laws']['widget']['#options'] = [];
}
$form['field_law_articles']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawArticlesByCountry($country);
$form['field_country']['widget']['#ajax'] = array(
'callback' => [$this,'changeLawsData'],
'event' => 'change',
'disable-refocus' => FALSE
);
$event->setForm($form);
}
}
/**
* #param $form
* #param \Drupal\Core\Form\FormStateInterface $form_state
* #return \Drupal\Core\Ajax\AjaxResponse
*/
function changeLawsData(&$form, FormStateInterface $form_state) {
$response = new AjaxResponse();
$response->addCommand(new HtmlCommand('#edit-field-laws', $form['field_laws']));
return $response;
}
}
Please remember that the above is an example...
I use this code for getting all users in the database
$users= $this->getDoctrine()
->getRepository('AppBundle:Users')
->findAll();
return $this->render('livre/users.html.twig',array(
'users' => $users,
));
But me I want get only some fields sush as name,email and hide fields like password..
Thanks.
You can do it by this way:
1/ Create a specific method in the UserRepository class:
public function findByNameAndEmail()
{
$query = $this->createQueryBuilder('u')
->select('u.name, u.email') // where name & email are properties of your User entity
;
return $query->getQuery()->getResult();
}
2/ And, call it in your controller:
public function someAction()
{
$users = $this->getDoctrine()->getRepository('AppBundle:Users')->findByNameAndEmail();
return $this->render('livre/users.html.twig',array(
'users' => $users,
));
}
I'm trying to do a little API with Symfony2.
I send a session id to my controller with a URL like this:
localhost/symfony2/web/app_dev.php/users/getuser/c5auv7mrp45rnd046cfv0vgl96
Then, in Symfony,
/**
* #Route("/getuser/{sessionId}")
*/
public function getSessionAction(Request $request, $sessionId)
{
// Here is what i'm trying to do
$packJson = array(
'user_id' => $userid
);
$response = new JsonResponse();
$response->setData($packJson);
return $response;
}
So, i would like to retrieve my user Id only with the sessionId argument.
Of course, it will be load from Db
I don't understand the logic between Session object and User Objet
Thanks
I think you want to use a token to identify a user. That means you have one token for each user in your database. If that is correct then it has nothing to do with sessions or a session-object.
you could simple retrieve your user with:
/**
* #Route("/getuser/{token}")
*/
public function getSessionAction($token)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AdminBundle:User')->findOneBy(array('token' => $token);
$response = new JsonResponse();
if (!$entity) {
$response->setData('error' => 'bad token');
return $response;
}
$packJson = array(
'user_id' => $entity->getId()
);
$response->setData($packJson);
return $response;
}
I'm working with Symfony 2 and I want to pass from my controller to my twig template a simple string and then use it on my template to descriminate the user role.
The controller code has something like :
public function modify_user_asAction(Request $request, $username)
{
$stringtopass="admin";
$um = $this->get('fos_user.user_manager');
$user = $um->findUserByUsername($username);
if($user == null){
//error page here..
}
$form = $this->createForm(new UserForm(), $user);
$form->handleRequest($request);
if ($form->isValid()) {
$um->updateUser($user);
return $this->redirect($this->generateUrl('acme_query_success'));
}
return $this->render('AcmeUserBundle:Default:modifyuserform.html.twig', array(
'form' => $form->createView(),
));
}
I want to pass $stringtopass in the generateUrl (if it's possible).
I can't find anything online.
Thanks
You are almost there!
API: generateUrl
Basically, just pass an array as second param to generateUrl.
return $this->redirect($this->generateUrl('acme_query_success', array('stringToPass' => $stringtopass)));
And also, #Brewal has a very valid point there. Be careful not to pass some sensitive data or leave unrestricted access to that controller's action. You could do more harm than good...
UPDATE:
public function acmeQuerySuccessAction(){
// ... action's logic
$stringToPass = $this->getRequest()->query->get('stringToPass');
// .....
return array(
'stringToPass' => $stringToPass,
// all other elements that you would normally return
);
}
I want to separate form validation logic:
public function contactAction()
{
$form = $this->createForm(new ContactType());
$request = $this->get('request');
if ($request->isMethod('POST')) {
$form->submit($request);
if ($form->isValid()) {
$mailer = $this->get('mailer');
// .. setup a message and send it
return $this->redirect($this->generateUrl('_demo'));
}
}
return array('form' => $form->createView());
}
I want to translate into 2 separate actions:
public function contactAction()
{
$form = $this->createForm(new ContactType());
return array('form' => $form->createView());
}
public function contactSendAction()
{
$form = $this->createForm(new ContactType());
$request = $this->get('request');
if ($request->isMethod('POST')) {
$form->submit($request);
if ($form->isValid()) {
$mailer = $this->get('mailer');
// .. setup a message and send it using
return $this->redirect($this->generateUrl('_demo'));
}
}
// errors found - go back
return $this->redirect($this->generateUrl('contact'));
}
The problem is that when errors exist in the form - after form validation and redirect the do NOT showed in the contactAction. (probably they already will be forgotten after redirection - errors context will be lost)
If you check out how the code generated by the CRUD generator handles this you will see that a failed form validation does not return a redirect but instead uses the same view as the GET method. So in your example you would just:
return $this->render("YourBundle:Contact:contact.html.twig", array('form' => $form->createView()))
rather than return the redirect. This means you do not lose the form errors as you do in a redirect. Something else the CRUD generator adds is the Method requirement which means you could specify that the ContactSendAction requires the POST method and thus not need the extra if($request->isMethod('POST')){ statement.
You can also just return an array if you specify the template elsewhere, for example you could use the #Template annotation and then just
return array('form' => $form->createView())
This seems to work for me in Symfony 2.8:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class MyController extends Controller {
public function templateAction()
{
$form = $this->createForm(new MyFormType(), $myBoundInstance);
if ($session->has('previousRequest')) {
$form = $this->createForm(new MyFormType());
$form->handleRequest($session->get('previousRequest'));
$session->remove('previousRequest');
}
return array(
'form' => $form->createView(),
);
}
public function processingAction(Request $request)
{
$form = $this->createForm(new MyFormType(), $myBoundInstance);
$form->handleRequest($request);
if ($form->isValid()) {
// do some stuff
// ...
return redirectToNextPage();
}
$session->set('previousRequest', $request);
// handle errors
// ...
return redirectToPreviousPage();
}
}
Please note that redirectToNextPage and redirectToPreviousPage, as well as MyFormType, are pseudo code. You would have to replace these bits with your own logic.