Symfony2 Google Books API - php

I have an issue getting a book from the Google Books API in Symfony2
I am posting this form for a page (/google)...
<form action="/googlevolume" method="post">
<input type="text" name="title" id="title" size="40" value="">
<input type="submit" value="Search">
</form>
and this is my controller for the result page (/googlevolume)...
public function googlevolume(Request $request)
{
$enquiry = new Enquiry();
$form->bind($request);
$response = $enquiry->get("https://www.googleapis.com/books/v1/volumes?q=".$form->get('title')->getData());
$data=$response->json();
$response2=$data['items'];
return $this->render('BloggerBlogBundle:Page:googlevolume.html.twig', array('items' => $response2));
}
I have tried posting this number from the form
1781100489
Which is the same as going to:
https://www.googleapis.com/books/v1/volumes?q=1781100489
However, when i put that number in the form and press search, i get this error
Controller "Blogger\BlogBundle\Controller\PageController::googlevolumeAction" for URI "/googlevolume" is not callable.
this is from my routing file...
google:
pattern: /google
defaults: { _controller: BloggerBlogBundle:Page:google }
requirements:
_method: GET
googlevolume:
pattern: /googlevolume
defaults: { _controller: BloggerBlogBundle:Page:googlevolume }
requirements:
_method: POST
this is googlevolume.html.twig ...
{# src/Blogger/BlogBundle/Resources/views/Page/googlevolume.html.twig #}
{% extends 'BloggerBlogBundle::layout.html.twig' %}
{% block title %} Google Books{% endblock%}
{% block body %}
<header>
<h1>Google Book</h1>
</header>
<br>
{% for item in items %}
<article>
<img src="{{ item.volumeInfo.imageLinks.thumbnail}}"/>
<h4>{{ item.volumeInfo.title}}</h4>
{% if item.volumeInfo.description is defined %}
{{ item.volumeInfo.description }}
{% endif %}
<strong> {{ item.volumeInfo.publishedDate }}</strong><br/>
<b>{{ item.volumeInfo.authors | join }}</b>
</article>
{% endblock %}
Anyone got any ideas where i'm going wrong with this?
Thanks

It looks like you are trying to serialize the request object when really you just want the value from the form. Try changing your request to the api to:
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
...
public function googlevolumeAction(Request $request)
{
$form = $this->createFormBuilder(null,[
'csrf_protection' => false
])
->add('title','text')
->add('Search', 'submit')
->getForm();
$form->bind($request);
if($form->isValid()){
$enquiry = new Enquiry();
$response = json_decode(file_get_contents("https://www.googleapis.com/books/v1/volumes?q=".$form->get('title')->getData()),true);
if(array_key_exists('items',$response)){
return $this->render('BloggerBlogBundle:Page:googlevolume.html.twig', [
'items' => $response['items']
]);
} else {
return new Response('Google Volume did not have any items', 400);
}
}
return new Response('Google Volume Not Found', 404);
}
Then the route:
googlevolume:
pattern: /googlevolume
defaults: { _controller: BloggerBlogBundle:Page:googlevolumeAction }
requirements:
_method: POST
Then clear the cache:
php app/console cache:clear --env=prod
or just for dev
php app/console cache:clear

Related

"Variable "form" does not exist." Symfony

Trying to make a simple form but i keep getting "Variable "subscriptionForm" does not exist."
enter image description here
Here is Controller method:
public function subscriptionForm(Request $request): \Symfony\Component\HttpFoundation\RedirectResponse|array
{
$subscriptionForm = $this
->getFactory()
->createSubscriptionForm()
->handleRequest($request);
if ($subscriptionForm->isSubmitted() && $subscriptionForm->isValid()) {
// Call the client for e.g. to save the subscriber.
// Redirect to home page after successful subscription
return $this->redirectResponseInternal('home');
}
return $this->viewResponse([
'subscriptionForm' => $subscriptionForm->createView(),
]);
}
Twig file:
{% block body %}
{{ form_start(subscriptionForm) }}
{{ form_widget(subscriptionForm.email) }}
{{ form_errors(subscriptionForm.email) }}
<input type="submit" value="Subscribe" />
{{ form_end(subscriptionForm) }}
{% endblock %}
Spryker assigns all twig variables within a _view object. Try adding these code on top of the twig file:
{% define data = {
subscriptionForm: _view.subscriptionForm
} %}

How to create a auto complete text field in symfony with AbstractType Form?

I am trying to create a auto complete text field in symfony with AbstractType Form which gets data from database and I want it to give me suggestions on whatever I enter as input , I have tried several third party plugins for that but its not working , may be i have missed some installation procedure .
here are the link of plugin i have used form git
after following the complete installation steps it gives me plain text field.
is there any other way to create a auto complete text box ,
here is my code for FormType
$builder->add('autoText', 'autocomplete', array(
'class' => 'MyBundle:Demo',
));
my html.twig template:
<div class="col-md-10">
[{% for auto in results -%}
{{ {id: auto.id, label: auto.name, value: auto.name}|json_encode|raw }}
{%- if not loop.last %},{% endif -%}
{%- endfor %}]
</div>
<!-- js -->
{% javascripts
'js/jquery.js'
'js/jquery-ui.js'
'#PUGXAutocompleterBundle/Resources/public/js/autocompleter-jqueryui.js'
%}
{% endjavascripts %}
{% javascripts %}
<script type="text/javascript">
$('#autoText').autocompleter({url_list: '/auto_search', url_get: '/auto_get/'});
</script>
{% endjavascripts %}
my DemoController
Public class DemoController extends Controller{
//....
public function searchAutoAction(Request $request) {
$q = $request->get('AutoText');
$em = $this->getDoctrine()->getManager();
$results = $em->getRepository('MyBundle:Demo')->findLikeName($q);
return array('results' => $results);
}
public function getAutoAction($id) {
$em = $this->getDoctrine()->getManager();
$auto = $em->getRepository('MyBundle:Demo')->find($id);
return new Response($auto->getName());
}
.....//
}
route.yml
auto_search:
path: /auto_search/
defaults: { _controller:MyBundle:Demo:searchAuto }
auto_get:
path: /auto_get/
defaults: { _controller:MyBundle:Demo:getAuto }
Read this doc page, and add js libraries like this:
{% javascripts
'js/jquery.js'
'js/jquery-ui.js'
'#PUGXAutocompleterBundle/Resources/public/js/autocompleter-jqueryui.js'
%}
<script src="{{ asset_url }}"></script>
{% endjavascripts %}

Symfony 2: pass custom variable to twig form template

In just started at symfony and Im trying to do something very simple (seems simple to me) using the form templating system, but I cant find a way to do it.
My goal is to render a "back" button besides the submit button. I know how to set the form template and how to override the submit_widget, but the problem is: the back button URL must be defined at the template which is calling the form, so I need to pass this as a variable to the submit_widget somehow, and I cant find a way to do it.
Ideally, it would work like this:
Template:
{% form_theme form 'TutsAdminBundle:Form:bootstrap-horizontal.html.twig' %}
{% block content %}
<h1>User creation</h1>
{{ form(form, { 'attr': {'role': 'form', 'class': 'form-horizontal'}, 'back': path('list_user') }) }}
{% endblock %}
And then, at the form template
{% block submit_widget %}
<div class="col-sm-6 col-sm-offset-1">
<div class="pull-right">
<a class="btn btn-warning" href="{{ back }}"> Back</a>
<button type="submit" class="btn btn-success">Save</button>
</div>
</div>
{% endblock submit_widget %}
But I just cant find a way to access my "back" variable inside the submit_widget block.
How can I achieve that?
UPDATE:
I managed to do what I wanted by:
1- creating a custom field following Chausser advice bellow:
class SaveButtonType extends SubmitType
{
public function __construct($router)
{
$this->_router = $router;
}//constructor
protected $_router;
/* (non-PHPdoc)
* #see \Symfony\Component\Form\AbstractType::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
}//buildForm
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$href = function(Options $options, $value){
return array('href'=>$this->_router->generate($options['route'], $options['params']), 'class'=>'btm btn-warning');
};
$resolver->setDefaults(array('mapped'=>false))->setRequired(array('route', 'params'));
$resolver->setNormalizers(array('attr'=>$href));
}//setDefaultOptions
public function getName()
{
return 'save_button';
}//getName
}//SaveButtonType
Ive made it extends the SubmitType otherwise it would be rendered inside a form_row.
services.yml
tuts_admin.form.field.type.save_button:
class: Tuts\AdminBundle\Form\Field\SaveButtonType
arguments: ["#router"]
tags:
- {name: "form.type", alias: "save_button"}
2- then I defined its template (on that same file Im using to override the form theme
{% block save_button_widget %}
<div class="col-sm-6 col-sm-offset-1">
<div class="pull-right marginL25">
<button type="submit" class="btn btn-success">Save</button>
</div>
<div class="pull-right">
Back
</div>
</div>
{% endblock %}
This type render the submit and the back button at once.
3- finally, since I could not find a way to prevent the rendering of another submit button, I overrided the submit_widget:
{% block submit_widget %}
{% endblock submit_widget %}
A lot more complicated than I was expecting, but it does works.
Thank you Chausser for all your help.
Personally I would write a Custom Field Type and make if for back buttons. Something that will accept a route name and route params.
//Acme/DemoBundle/Form/Type/BackButtonType
<?php
namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\OptionsResolver\Options;
class BackButtonType extends AbstractType
{
protected $router;
public function __construct($router)
{
$this->router = $router;
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$attr = function(Options $options, $value){
return array(
'href'=>$this->_router->generate($options['route'], $options['params']),
'class'=>'btn btn-warning'
);
};
$resolver
->setDefaults(array(
'attr' => $attr,
'mapped' => false
'params' => array(),
))
->setRequired(array(
'route',
))
->setNormalizers(array(
'attr'=>$attr
));
}
public function getName()
{
return 'back_button';
}
}
Then you need to register this as a new form type:
parameters:
acme_demo.form.type.back_button.class: Acme\DemoBundle\Form\Type\BackButtonType
services:
acme_demo.form.type.back_button:
class: %acme_demo.form.type.back_button.class%
arguments: ["#router"]
tags:
- { name: "form.type", alias: "back_button" }
Now you can use this in your normal forms. Last step is to create the twig block to render this form.
//Acme/DemoBundle/Resources/Twig/fields.html.twig
{% block back_button_widget %}
<a {{block('widget_attributes')}}>{{block('form_label')}}</a>
{% endblock %}
Then you need to add this file to the form resources for twig:
//app/config/config.yml
twig:
debug: %kernel.debug%
strict_variables: %kernel.debug%
form:
resources:
- 'AcmeDemoBunlde:Twig:fields.html.twig'
After you have done that you need to clear your caches:
php app/console cache:clear --env=dev
php app/console cache:clear --env=prod
Now how to use this:
//In your Form
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
// Add other fields
->add('back', 'back_button',array('route'=>'my_custom_route_name','params'=>array('user_id'=>$this->getUser()->getUserId())))
->add('submit','submit',array('attr'=>array('class'=>'btn btn-primary')));
}
This should work. Hasnt been fully tested but if you have any issues with it let me know.
I believe that you don't need to pass variable, but only make it available (known) to Twig, since form rendering (as opposed to to extending and including other templates) is just block rendering. That is, those variables are in the same scope.
So:
Parent template
{% set back = "MyFooValue" %}
Form template:
<div class="{{ back }}">Some content</div>
Also, I like defining default as fallback in case I forget to (or do not at all) define variable:
<div class="{{ back|default("") }}">Some content</div>
Hope this helps...

Advanced routing parameter handling in twig and Symfony2

I have this configuration:
A page containing a search field, at the submit in the same page I want a list of every result matching the research, everyone linking to a corresponding route. For example if I find 4 elements, I want that in the resulting page 4 links Azienda1, Azienda2 ecc.
Now I get this error:
An exception has been thrown during the rendering of a template ("The "ABCAziendaBundle_visualizza_azienda" route has some missing mandatory parameters ("id_azienda").") in ::base.html.twig at line 27.
500 Internal Server Error - Twig_Error_Runtime
1 linked Exception:
MissingMandatoryParametersException
Here are the key files,
#config.yml
ABCAziendaBundle_visualizza_azienda:
pattern: /visualizza_azienda/{id_azienda}
defaults: { _controller: ABCAziendaBundle:Default:showAzienda }
requirements:
id_azienda: \d+
ABCAziendaBundle_azienda_index:
pattern: /
defaults: { _controller: ABCAICAziendaBundle:Default:indexAzienda }
#DefaultController.php
public function indexAziendaAction(Request $request) {
$searchFormType = new SearchAziendaType();
$form = $this->createForm($searchFormType);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
{
$data = $form->getData();
$em = $this->getDoctrine()->getEntityManager();
$aziende = $em->getRepository('ABCAziendaBundle:Azienda')->findAziendaByAliasOrRagioneSocialeSubstring($data["search_field"]);
return $this->render('ABCAziendaBundle:Default:indexAzienda.html.twig', array('form' => $form->createView(), 'aziende' => $aziende));
}
}
return $this->render('ABCAziendaBundle:Default:indexAzienda.html.twig', array('form' => $form->createView()));
}
public function showAziendaAction($id_azienda) {
echo "non entra qui";
}
#indexAzienda.html.twig
{% extends "::base.html.twig" %}
{% block pagetitle %}ABC{% endblock %}
{% block body %}
<h2>Ricerca azienda</h2>
<div id="form_container">
<form action="{{ path('ABCAziendaBundle_azienda_index') }}" method="post" {{ form_enctype(form) }}>
{{ form_label(form.search_field, "Ricerca Azienda") }}
{{ form_widget(form.search_field) }}
<input type="submit" />
</form>
<button>nuova azienda</button>
</div>
{% if aziende is defined %}
{% for azienda in aziende %}
<div class="areaTot">
{{azienda.alias}}
</div>
{% endfor %}
{% else %}
<div class="areaTot">
<p>"NIENTE"</p>
</div>
{% endif %}
{% endblock %}
I think its a typo on your side:
<a href="{{ path('ABCAziendaBundle_visualizza_azienda', { 'azienda_id' : azienda.id }) }}">
Should be:
<a href="{{ path('ABCAziendaBundle_visualizza_azienda', { 'id_azienda' : azienda.id }) }}">
The difference is the route parameter, you wrote azienda_id initialy, but the route parameter name is id_azienda
Should clear the error.
Regards,
Matt

Symfony2 multiple forms in different JQuery UI tabs but single page

I'm facing a problem that I can summarize as it follows:
I have a TWIG template page like this (reg.html.twig):
{% extends "::base.html.twig" %}
{% block body %}
<ul class="tabs">
<li class="left">tab1</li>
<li class="left">tab2</li>
<li class="left">tab3</li>
<li class="right">tab4</li>
</ul>
<div class="tabs_container">
<div id="tab1" class="blocco-tab">
<form action="{{ path('AAA') }}" method="post" {{ form_enctype(form) }}>
<div id="name_field">
{{ form_row(form.name) }}
</div><!-- /name_field -->
<div id="address">
{{ form_row(form.addresses[0].road) }}
</div><!-- /address_field -->
</form>
</div>
<div id="tab2" class="blocco-tab">
<form action="{{ path('BBB') }}" method="post" {{ form_enctype(form) }}>
<div id="surname_field">
{{ form_row(form.surname) }}
</div><!-- /surname_field -->
</form>
</div>
</div> <!-- contenitore_tabs -->
{% endblock %}
Fields name, surname and addresses belong to a sample Symfony2 entity Person.
addresses is the first and only element of a collection of addresses (I need this as collection for other reasons)
The working JS file is:
jQuery(document).ready(function() {
$(".blocco-tab").hide();
$("ul.tabs li:first").addClass("active").show();
$(".blocco-tab:first").show();
$("ul.tabs li").click(function() {
$("ul.tabs li").removeClass("active");
$(this).addClass("active");
$(".blocco-tab").hide();
var activeTab = $(this).find("a").attr("href");
$(activeTab).fadeIn();
return false;
});
});
The Entity file:
class Person {
protected $name;
protected $surname;
protected $addresses;
public function __construct(){
$this->addresses = new ArrayCollection();
}
}
And in the DefaultController:
public function tab1Action(Request $request){
$person = new Person();
$address = new Address();
$addr_coll = new ArrayCollection();
$addr_coll->add($address);
$tab1_type = new Tab1Type();
$person->setAddresses($addr_coll);
$form = $this->createForm($tab1_type, $person);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
/*ecc ecc ecc*/
}
public function tab2Action(Request $request){
$person = new Person();
$tab2_type = new Tab2Type();
$form = $this->createForm($tab2_type, $person);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
/*ecc ecc ecc*/
}
Actually I took the way of having every FormType having all fields that I don't need rendered but put 'hidden' and 'property_path' => false, because I can't render only my desired fields cause the other ones will cause errors at runtime (they're null) , but I still get problems handling both cases in a joined way.
Putting every form in a different page (== different Route), with different Controllers, everything works fine, so it's not a problem related to basic use of symfony, It's the integration of N forms in a single page with JQuery UI that makes me cry.
Fixed that I have to use this tabs, how can I solve?
Do I have to make a single Action handling everything?
Do I have to make a single form?
Do I miss something?
Thanks in advance, I hope I've been clear in explaining my issue.
You just used the same variable for different forms
<div id="tab1" class="blocco-tab">
<form action="{{ path('AAA') }}" method="post" {{ form_enctype(**form1**) }}>
<div id="name_field">
{{ form_row(**form1**.name) }}
</div><!-- /name_field -->
<div id="address">
{{ form_row(**form1**.addresses[0].road) }}
</div><!-- /address_field -->
</form>
</div>
<div id="tab2" class="blocco-tab">
<form action="{{ path('BBB') }}" method="post" {{ form_enctype(**form2**) }}>
<div id="surname_field">
{{ form_row(**form2**.surname) }}
</div><!-- /surname_field -->
</form>
</div>
Try with single form
{% extends "::base.html.twig" %}
{% block body %}
<form action="{{ path('AAA') }}" method="post" {{ form_enctype(form) }}>
<ul class="tabs">
<li class="left">tab1</li>
<li class="left">tab2</li>
<li class="left">tab3</li>
<li class="right">tab4</li>
</ul>
<div class="tabs_container">
<div id="tab1" class="blocco-tab">
<div id="name_field">
{{ form_row(form.name) }}
</div><!-- /name_field -->
<div id="address">
{{ form_row(form.addresses[0].road) }}
</div><!-- /address_field -->
</div>
<div id="tab2" class="blocco-tab">
<div id="surname_field">
{{ form_row(form.surname) }}
</div><!-- /surname_field -->
</div>
</div> <!-- contenitore_tabs -->
</form>
{% endblock %}
Then you have in ocontroller aaaAction()
public function aaaAction(Request $request){
$person = new Person();
$address = new Address();
$addr_coll = new ArrayCollection();
$addr_coll->add($address);
$person->setAddresses($addr_coll);
$form = $this->createForm(new PersonType(), $person);
if ($request->getMethod() == 'POST')
{
$form->bindRequest($request);
if ($form->isValid())
/*ecc ecc ecc*/
}
and class for form builder like
class PersonType extends AbstractType {
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('name', null, array())
->add('surname', null, array())
->add('addresses', null, array())
;
}
public function getName()
{
return 'person';
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Acme\YourBundle\Entity\Person',
);
}
}
as I mentioned before I solved wrapping all tabs in a single form. Both of your solutions are ok, thank you for your time.
Linuxatico
In case anyone else has this problem, you can solve it like this (I am not sure if this is the most elegant solution but I think its better than making one big form for everything). This example mixes the profile and changePassword FOSUserBundle forms:
Inside your main show template (Profile:show.html.twig in my case):
{% if profileForm is defined %}
{% include 'MyBundle:Profile:edit.html.twig' with {'form':profileForm} %}
{% else %}
{% render 'MyBundle:Profile:edit' %}
{% endif %}
Repeat for changePassword:
{% if passwdForm is defined %}
{% include 'MyBundle:ChangePassword:changePassword.html.twig' with {'form':passwdForm} %}
{% else %}
{% render 'FOSUserBundle:ChangePassword:changePassword' %}
{% endif %}
In your controllers (add else):
if ($form->isValid()) {
.....
else {
return $this->container->get('templating')->renderResponse('FOSUserBundle:Profile:show.html.'.$this->container->getParameter('fos_user.template.engine'),
array('user' => $user, 'profileForm' => $form->createView()));
}
Add the profileForm and passwdForm accordingly. In my case the modified controllers were the ProfileController and ChangePasswordControllers (FOSUserBundle overrides).
As for your tabs, you can add javascript (or twig) to open the tab if any error is found.
Hope this helps :)

Categories