I'm building a page in Symfony2 that permits me to search a term (e.g. a name of a person) in my Database and show in the same page (even after a page reload) all matching records (e.g. all the person with this name).
This is my anagrafica.html.twig
{# src/Acme/MyBundle/Resources/views/Page/anagrafica.html.twig #}
{% extends 'AcmeMyBundle::layout.html.twig' %}
{% block body %}
<form id="formsearch" name="p" method="get" action="anagrafica">
<span>
<input type="text" name="search_name" id="search_name" />
</span>
<input type="image" name="button_search" />
</form>
{% for anagrafica in anagrafiche %}
<article class="blog">
<div class="date">{{ anagrafica.DataNascita|date('c') }}</div>
<header>
<h2>{{ anagrafica.nome }}</h2>
</header>
<div class="snippet">
<p>{{ anagrafica.cognome }}</p>
<p class="show_complete">Show all data</p>
</div>
</article>
{% else %}
<p>There are no entries</p>
{% endfor %}
{% endblock %}
This is my PageController.php
<?php // src/Acme/MyBundle/Controller/PageController.php
namespace Acme\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class PageController extends Controller {
//...
public function anagraficaAction() {
$em = $this->getDoctrine()
->getEntityManager();
$anagrafiche = $em->createQueryBuilder()
->select('b')
->from('AcmeMyBundle:Anagrafiche', 'b')
->where("b.nome = :nome")
->setParameter('nome', 'Alex' )
->addOrderBy('b.id', 'DESC')
->getQuery()
->getResult();
return $this->render('AcmeMyBundle:Page:anagrafica.html.twig', array('anagrafiche' => $anagrafiche));
}
}
I think I only need to update my PageController.php and replace the name 'Alex' in :
->setParameter('nome', 'Alex' )
with a variable that refers to the entry in my form definied in anagrafica.html.twig.
Anyway I have no idea of how to do this, and a quick search on google and forums do not helped me.
Any suggestion?
You get GET-parameters via
$searchName = $request->query->get('search_name');
But for this you still need the $request variable. You can use it as parameter and change your method signature to this:
public function anagraficaAction(Request $request)
This way, you can call the $request parameter in your method.
The other way is to get the request of the current controller inside your method.
$request = $this->get('request');
Using this you can change your setParameter to this:
setParameter('nome', $searchName)
change this setParameter('nome', 'Alex') to something like setParameter('nome', $_GET['search_name']) since this form passing data by using get method.
I found the solution. I edited my PageController.php such this:
<?php
// src/Acme/MyBundle/Controller/PageController.php
namespace Acme\MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class PageController extends Controller
{
public function indexAction()
{
return $this->render('AcmeMyBundle:Page:index.html.twig');
}
public function infoAction()
{
return $this->render('AcmeMyBundle:Page:info.html.twig');
}
public function anagraficaAction(Request $request)
{
$em = $this->getDoctrine()
->getEntityManager();
$title = $request->get('search_name');
$anagrafiche = $em->createQueryBuilder()
->select('b')
->from('AcmeMyBundle:Anagrafiche', 'b')
->where("b.nome = :nome")
->setParameter('nome', $title )
->addOrderBy('b.id', 'DESC')
->getQuery()
->getResult();
return $this->render('AcmeMyBundle:Page:anagrafica.html.twig', array('anagrafiche' => $anagrafiche));
}
}
Related
I am creating a library management system in Symfony to progress.
So I have a book, user, category entity.
So I want to create the borrow a book function.
However, when I perform the action, the book does not change in the database (book must become borrowed).
So here is my controller (knowing that it is a user who borrows, and the user can addBook as there is a join):
<?php
namespace App\Controller;
use Doctrine\Persistence\ManagerRegistry;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use App\Entity\Book;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
class BookController extends AbstractController
{
//Home page of Book, to render all the books
#[Route('/book', name: 'app_book')]
public function index(ManagerRegistry $doctrine): Response
{
$books = $doctrine->getRepository(Book::class)->findAll();
return $this->render('book/index.html.twig', [
'controller_name' => 'BookController',
'books' => $books,
]);
}
// Show a book
#[Route("/book/{id}", name: 'book_show')]
public function show(Book $article, EntityManagerInterface $manager , Request $request, $id){
$repo = $manager->getRepository(Book::class);
$book = $repo->find($id);
return $this->render('book/show.html.twig', [
'book' => $book,
]);
}
// Display the borrow form
#[Route("/book/borrow/{id}", name: 'borrow_book')]
public function RenderBorrow(Book $book, EntityManagerInterface $manager, $id){
$repo = $manager->getRepository(Book::class);
$book = $repo->find($id);
return $this->render('book/borrow.html.twig', [
'book' => $book,
]);
}
// method to actually borrow the book, then redirect to the book page
#[Route("/book/borrowed/{id}", name: 'book_borrowed')]
public function borrow(Book $book, EntityManagerInterface $manager, $id){
$repo = $manager->getRepository(Book::class);
$book = $repo->find($id);
// return true or false if book is borrowed or not
$availabilty = $book->isBorrowed();
// if available, then the user can borrow it.
if($availabilty){
// we get the user.
/** #var \App\Entity\User $user */
$user = $this->getUser();
$user->addBook($book);
// the book borrow becomes borrowed.
$book->setBorrowed(true);
$manager->persist($book);
$manager->flush();
$manager->persist($user);
$manager->flush();
}
return $this->redirectToRoute('book_show', ['id' => $book->getId()]);
}
}
And my twig borrow.html.twig
{% extends 'base2.html.twig' %}
{% block title %} Emprunter {{ book.title}} {% endblock %}
{% block body %}
<!-- Page to borrow a specified book
-->
<!-- If book is borrowed, then we display yes, or else no. If no, we can display a
button to borrow it -->
<section class="articles">
<form action="{{ path('book_borrowed', {'id' : book.id })}}" method="post">
{% if not book.borrowed %}
<h5> Emprunter </h5>
<h6> Nom du livre : {{ book.title}}</h6>
<h6> Catégorie du livre : {{ book.categorie}}</h6>
<button type="submit" class="btn btn-primary"> Emprunter </button>
</form>
{% else %}
<div class="alert alert-dismissible alert-danger">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
<strong>Oh !</strong> <a href="#" class="alert-link">Vous tentez d'emprunter un livre déjà emprunté !
</div>
{% endif %}
</section>
{% endblock %}
Please help me, because normally afterwards the book should update and become borrowed...
You are updating the borrowed boolean when availability is equal to true.
What you want to do is check if the book is not borrowed, and if it's not update the value of your boolean.
You do not need to persist $book and $user since they are already persisted. Also you only need to flush once at the end.
Moreover, Book $book should give you the proper $book depending on the id, without having to look it up yourself in the repository
You could replace your method with:
// method to actually borrow the book, then redirect to the book page
#[Route("/book/borrowed/{id}", name: 'book_borrowed')]
public function borrow(Book $book, EntityManagerInterface $manager, $id){
// if available, then the user can borrow it.
if(!$book->isBorrowed()){
// we get the user.
/** #var \App\Entity\User $user */
$user = $this->getUser();
$user->addBook($book);
// the book borrow becomes borrowed.
$book->setBorrowed(true);
$manager->flush();
}
return $this->redirectToRoute('book_show', ['id' => $book->getId()]);
}
I am learning Symfony so I am testing various ways to work with database. The idea is to make select dropdown list that contains data from table in database. Each option in select returns value that is id of column from table. Dropdown list is defined in View in this way ('find_res' in action is route to controller):
<form action="{{ path('find_res') }}" method="POST" name="formpt">
<select name="patientId">
{% for patient in patients %}
{{dump(patient)}}
<option value="{{patient.id}}">{{patient.lastname}}{{patient.firstname}}</option><br />
{% endfor %}
</select>
<button type="submit" name="submit">Find results</button>
</form>
so that for chosen patient.id it should show results in table which is defined in code bellow this as standard html table.
In controller method I created list of all patients from table in database and sent it as parameter to this view. And that works just fine. Also, I would not get into details how query is built because the problem is that any $request I try does not return anything, just null. I tried:
$request->request->get('patientId'); //returns null always
$request->get('patientId'); //also null
I even tried to give name to form in view like for instance 'formpt' and then tried it with:
$request->request->get('formpt')['patientId'];
still nothing. This test code:
if($request->getMethod() == 'POST' ) {
\Doctrine\Common\Util\Debug::dump($request->request->get('patientId'));
exit;
}
always returns NULL or string(0) "" for whatever I put in "dump".
What I am doing wrong?
Thanks!
The following is a simple way to implement what you're trying to do with the standard form component (note that I explicitly didn't create a dedicated form type!).
<?php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Validator\Constraints\NotBlank;
use AppBundle\Entity\Patient;
class DefaultController extends Controller
{
/**
* #Route("/", name="index")
*
* #param Request $request
*
* #return Response
*/
public function indexAction(Request $request)
{
$form = $this->createFormBuilder()
->add('patient', EntityType::class, [
'required' => false,
'placeholder' => '--- Select ---',
'class' => Patient::class,
'choice_label' => function (Patient $patient) {
return sprintf(
'%s, %s',
$patient->getLastname(),
$patient->getFirstname()
);
},
'constraints' => [
new NotBlank()
],
])
->getForm()
->handleRequest($request)
;
if ($form->isSubmitted() && $form->isValid()) {
/** #var $selected Patient */
$selected = $form->get('patient')->getData();
dump($selected);
}
return $this->render('default/index.html.twig', [
'form' => $form->createView()
]);
}
}
the view being:
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
{{ form_widget(form) }}
<button>Find results</button>
{{ form_end(form) }}
{% endblock %}
I understand that the form component (especially form customization) has it's learning curve, but ultimately, if you want to use symfony fully you'll end up using it anyway (why reinvent the wheel). So the sooner you start with it the better.
Maybe try this
For mapped field
public function yourAction(Request $request){
$patient = new Patient();
$form = $this->createForm('AppBundle\Form\PatientType', $patient);
$form->handleRequest($request);
if ($form->isSubmitted())
{
$patientId = $patient->getId();
}
}
If you need to get a non mapped field (field that's not a Patient property)
public function yourAction(Request $request){
$patient = new Patient();
$form = $this->createForm('AppBundle\Form\PatientType', $patient);
$form->handleRequest($request);
if ($form->isSubmitted())
{
$patientCustomField = $form['customField']->getData();
}
}
I have created a custom action that renders a small form at the bottom of my show template for orders. The form is a basic checkbox and a select field to with tow buttons. It works perfectly but the rendering is not right.
I know the way I render the show template is not 100% correct, because when it renders, the left hand side menu doesn't work anymore.
Here is my custom controller with action;
namespace Qi\Bss\FrontendBundle\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Qi\Bss\FrontendBundle\Crud\Crud;
use Qi\Bss\BaseBundle\Entity\Business\PmodOrder;
use Symfony\Component\HttpFoundation\RedirectResponse;
class PmodOrderController extends Controller
{
/**
* #Route("/{id}/approve", name = "order_approve")
* #Security("is_granted('IS_AUTHENTICATED_FULLY')")
* #Method({"GET", "POST"})
*/
public function approveAction(Request $request, $id){
$em = $this->getDoctrine()->getManager();
$order = $em->getRepository('QiBssBaseBundle:PmodOrder')->find($id);
$approveForm = $this->createFormBuilder($order)
->add('requireApproval', 'checkbox', array('label' => 'Require second Approval', 'required' => false, 'mapped' => false))
->add('secondApprover', 'choice', array('choices' => Crud::enumStatus(), 'label' => 'User', 'required' => false))
->getForm();
$approveForm->handleRequest($request);
if ($approveForm->isSubmitted() && $approveForm->isValid()) {
$secondApproval = $request->request->get('form');
$approval = $approveForm->getData();
if (isset($secondApproval['requireApproval'])) {
$approval->setStatus(PmodOrder::STATUS_PARTLY_APPROVED);
$em->persist($approval);
$em->flush();
return new RedirectResponse($this->container->get('router')->generate('admin_bss_base_business_pmodorder_show', array('id' => $order->getId())));
} else {
$approval->setSecondApprover(NULL);
$approval->setStatus(PmodOrder::STATUS_APPROVED);
$em->persist($approval);
$em->flush();
return new RedirectResponse($this->container->get('router')->generate('admin_bss_base_business_pmodorder_show', array('id' => $order->getId())));
}
}
return $this->render('QiBssFrontendBundle:PmodOrder:order_approve.html.twig', array(
'order' => $order,
'form' => $approveForm->createView(),
));
}
}
What bothers me is the fact that I'm actually suppose to extend from Sonata's CRUDController. And when I do that I get an error;
An exception has been thrown during the rendering of a template
("There is no _sonata_admin defined for the controller
Path\To\Controller\PmodOrderController and the current
route ``")
And I am also aware that I'm actually suppose to use a return like return new RedirectResponse($this->admin->generateUrl('show'));
At this point I don't know what to do anymore. If somebody can please guide me how to extend correctly from CRUDController in my scenario, it would be really appreciated
Here an example, I don't know if it's the best solution but I hope that can help you :
1- Create a custom CRUDcontroller :
# CustomCRUDcontroller.php :
class CustomCRUDDController extends Controller
{
/**
* Show action.
*
* #param int|string|null $id
* #param Request $request
*
* #return Response
*
* #throws NotFoundHttpException If the object does not exist
* #throws AccessDeniedException If access is not granted
*/
public function showAction($id = null)
{
$request = $this->getRequest();
// DO YOUR LOGIC IN THE METHOD, for example :
if(isset($request->get('yourFormParam'))){
$this->doTheJob();
}
$id = $request->get($this->admin->getIdParameter());
$object = $this->admin->getObject($id);
if (!$object) {
throw $this->createNotFoundException(sprintf('unable to find the object with id : %s', $id));
}
$this->admin->checkAccess('show', $object);
$preResponse = $this->preShow($request, $object);
if ($preResponse !== null) {
return $preResponse;
}
$this->admin->setSubject($object);
return $this->render($this->admin->getTemplate('show'), array(
'action' => 'show',
'object' => $object,
'elements' => $this->admin->getShow(),
), null);
}
}
2- Register it in admin.yml :
# admin.yml :
x.admin.x:
class: Namespace\YourAdminClass
arguments: [~, Namespace\Entity, Namespace:CustomCRUD]
tags:
- {name: sonata.admin, manager_type: orm, group: X, label: X}
3- Create your own custom_show.html.twig (just a copy and paste of the original template base_show.html.twig located in the sonata-admin folder), here you can display extra elements to the view :
# custom_show.html.twig :
{% extends base_template %}
{% import 'SonataAdminBundle:CRUD:base_show_macro.html.twig' as show_helper %}
{% block actions %}
{% include 'SonataAdminBundle:CRUD:action_buttons.html.twig' %}
{% endblock %}
{% block tab_menu %}
{{ knp_menu_render(admin.sidemenu(action), {
'currentClass' : 'active',
'template': sonata_admin.adminPool.getTemplate('tab_menu_template')
}, 'twig') }}
{% endblock %}
{% block show %}
<div class="sonata-ba-view">
{{ sonata_block_render_event('sonata.admin.show.top', { 'admin': admin, 'object': object }) }}
{% set has_tab = (admin.showtabs|length == 1 and admin.showtabs|keys[0] != 'default') or admin.showtabs|length > 1 %}
{% if has_tab %}
<div class="nav-tabs-custom">
<ul class="nav nav-tabs" role="tablist">
{% for name, show_tab in admin.showtabs %}
<li{% if loop.first %} class="active"{% endif %}>
<a href="#tab_{{ admin.uniqid }}_{{ loop.index }}" data-toggle="tab">
<i class="fa fa-exclamation-circle has-errors hide"></i>
{{ admin.trans(name, {}, show_tab.translation_domain) }}
</a>
</li>
{% endfor %}
</ul>
<div class="tab-content">
{% for code, show_tab in admin.showtabs %}
<div
class="tab-pane fade{% if loop.first %} in active{% endif %}"
id="tab_{{ admin.uniqid }}_{{ loop.index }}"
>
<div class="box-body container-fluid">
<div class="sonata-ba-collapsed-fields">
{% if show_tab.description != false %}
<p>{{ show_tab.description|raw }}</p>
{% endif %}
{{ show_helper.render_groups(admin, object, elements, show_tab.groups, has_tab) }}
</div>
</div>
</div>
{% endfor %}
</div>
</div>
{% elseif admin.showtabs is iterable %}
{{ show_helper.render_groups(admin, object, elements, admin.showtabs.default.groups, has_tab) }}
{% endif %}
</div>
{{ sonata_block_render_event('sonata.admin.show.bottom', { 'admin': admin, 'object': object }) }}
{% endblock %}
4- Then indicate to your adminController to display your custom_show template when the current route is "show" (instead of the default template base_show.html.twig) :
# YourEntityAdminController.php :
class YourEntityAdminController extends Controller
{
// allows you to chose your custom showAction template :
public function getTemplate($name){
if ( $name == "show" )
return 'YourBundle:Admin:custom_show.html.twig' ;
return parent::getTemplate($name);
}
}
In my case i have 30 entities that there are no relation between them and every entities have two columns. I wanted to have 6 or 7 entities in the one form, but i don't know what is the best way to do it? this is my code ...
this is my Controller:
public function general1Action(Request $request)
{
$example1 = new Example1();
$example2 = new Example2();
$example3 = new Example3();
$example4 = new Example4();
$formexample1 = $this->createForm('...Bundle\Form\Example1Type', $example1);
$formexample2 = $this->createForm('...Bundle\Form\ Example2Type', $example2);
$formexample3 = $this->createForm('...Bundle\Form\ Example3Type', $example3);
$fprmexample4 = $this->createForm('...Bundle\Form\Example4Type', $example4);
$example1->handleRequest($request);
$example2->handleRequest($request);
$example3->handleRequest($request);
$example4->handleRequest($request);
$em = $this->getDoctrine()->getManager();
if ($example1->isSubmitted() && $example1->isValid()) {
/**
* To generate the value example1 first column(e1fc) and second column(e1sc)
*/
$prefix=$this->container->getParameter('prefix');
$e1fcCle=$em->getRepository("...Bundle:Example1")->genereCle('Example1',$prefix);
$example1->sete1fc($e1fcCle);
/**
* To generate the value example1 second column(e1sc)
*/
$example1->sete1sc("e1sc".($e1fcCle));
/**
* to check, the fields are not empty
*/
if(($formexample2["e2sc"]->getData())!=""){
$example2->sete1fc($e1fcCle);
$em->persist($example2);
}
if(($formexample3["e3sc"]->getData())!=""){
$example3->sete1fc($e1fcCle);
$em->persist($example3);
}
if(($formexample4["e4sc"]->getData())!=""){
$example4-> sete1fc($e1fcCle);
$em->persist($example4);
}
$em->persist($example1);
$em->flush();
return $this->forward('...Bundle:General...: general2',
array('E1FC' => $e1fcCle));
}
return $this->render('.../general1.html.twig', array(
'example1' => $example1,
'formExample1' => $formexample1->createView(),
'formExample2' => $formexample2->createView(),
'formExample3' => $formexample3->createView(),
'formExample4' => $formexample4->createView(),
));
}`
and this is my general1.html.twig :
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(formExample1) }}
{{ form_row(formExample2.e2sc) }}
{{ form_row(formExample3.e3sc) }}
{{ form_row(formExample4.e4sc) }}
<input type="submit" value="Next" />
{{ form_widget(formExample1._token) }}
{{ form_end(formExample1, {"render_rest":false}) }}
<ul>
<li>
Back to the list
</li>
</ul>
{% endblock %}`,
I have another question : when i try to use isValid() for the other form like this
if(($formexample2["e2sc"]->getData())!=""&& $example2->isValid())
i have this error: Fatal error: Call to a member function get...() on null
I'm rewriting an application using the Silex framework. In this application, users can comment on posts and comments. In the non-MVC application, inspired by this question, I wrote it like this:
function display_comments($postid, $parentid=0, $level=0){
// Get the current comment from DB and display with HTML code
display_comments($needid, $comment['id'], $level+1);
}
However, in the Silex application, I want to retrieve them comment(s) from the database in a repository, send it to a twig-template in the controller and finally display the HTML code in the template. This makes the previous solution incompatible.
What is a good solution for this problem in Silex? What do I put in the view, what in the controller and what in the model?
EDIT
I wrote the function in the controller now:
$app->get('/needdetail/{id}', function ($id) use ($app) {
$need = $app['need']->findNeed($id);
function display_comments($app, $needid, $comments=array(), $parentid=0, $level=0){
$replies = $app['comment']->findByNeed($needid, $parentid);
foreach($replies as $reply){
$reply['level'] = $level;
array_push($comments, $reply);
display_comments($app, $needid, $comments, $reply['id'], $level+1);
}
return $comments;
}
return $app['twig']->render('needdetail.html', array('need' => $need, 'comments' => display_comments($app, $id)));
})
The level 0 comments are now shown, but a deeper level isn't.
I managed to get the required result with a slightly different approach. The controller as well as the view contains a recursive function:
Controller:
$app->get('/needdetail/{id}', function ($id) use ($app) {
$need = $app['need']->findNeed($id);
function get_comments($app, $needid, $parentid=0){
$comments = array();
$replies = $app['comment']->findByNeed($needid, $parentid);
foreach($replies as $comment){
$comment['replies'] = get_comments($app, $needid, $comment['id']);
array_push($comments, $comment);
}
return $comments;
}
return $app['twig']->render('needdetail.html', array('need' => $need, 'comments' => get_comments($app, $id)));
})
View:
{% for comment in comments %}
{% include 'comment.html' with {'level': 0} %}
{% endfor %}
Comment.html:
<div class="comment">
//Comment HTML
</div>
{% if comment.replies %}
{%for reply in comment.replies %}
{% include 'comment.html' with {'comment': reply, 'level': level+1} %}
{% endfor %}
{% endif %}