I am trying to consume the Google Books API, but it says undefined index for "title", as I am attempting to search for a book title and for the results to appear within twig.
My Controller :-
public function googlebooksAction($id)
{
$client = new Client();
$response = $client->get('https://www.googleapis.com/books/v1/volumes?q=id='.$id);
$data = $response->json();
$books = $data['title'];
return $this->render('ReviewerReviewBundle:Google:googlebooks.html.twig',
array(
'title' => $books
));
}
My Twig File :-
{% extends "::base.html.twig" %}
{% block title %}{{ book.volumeInfo.title }}{% endblock %}
{% block body %}
<h1>Google Books</h1>
{% for book in books %}
<article>{{ book.volumeInfo['title'] }}</article>
{% endfor %}
{% endblock %}
Related
For the purpose of a user-management ui I want to get all users with resulting permissions. I use the native implementation of Cartalyst\Sentinel and tried:
$users = Sentinel::getUserRepository()->with('roles')->get();
$permissions = array();
foreach ($users as $user) {
$user_permissions = Sentinel::getResultingPermissionsFor($user);
$permissions[$user['id']] = $user_permissions;
}
But the function "getResultingPermissionsFor()" seems not to be available anymore in V5.
I solved this by passing the roles-Object to the twig template:
router.php
$app->get('/admin/users', function (Request $request, Response $response) {
$loggedUser = Sentinel::check();
$users = Sentinel::getUserRepository()->with('roles')->get();
$roles = Sentinel::getRoleRepository()->get();
if (!$loggedUser) {
// do sth.
}
if (!$loggedUser->hasAccess('user.*')) {
// do sth.
}
$view = Twig::fromRequest($request);
$view->render($response, 'admin.users.html.twig', array(
'loggedUser' => $loggedUser,
'users' => $users,
'roles' => $roles
));
return $response;
});
twig-template:
{% for user in users %}
{% set rolePermissions = [] %}
{% for role in user.roles %}
{% set rolePermissions = rolePermissions|merge(role.permissions) %}
{% endfor %}
{% set resultingPermissions = rolePermissions %}
{% set resultingPermissions = resultingPermissions|merge(user.permissions) %}
{% endfor %}
// followed by output html
I'm trying to create a simple pagination with a twig view.
I'm not using Symfony.
Here is my method from my manager :
public function getAllPosts()
{
if(isset($_GET['p']) && (!isset($_GET['page']))){
$currentPage = 1;
}
else {
$currentPage = $_GET['page'];
}
$q= $this->_db->query('SELECT COUNT(id) AS numberposts FROM posts');
$data = $q->fetch(PDO::FETCH_ASSOC);
$number_posts= $data['numberposts'];
$perPage = 1;
$numberPages = ceil($number_posts/$perPage);
$q = $this->_db->query("SELECT * FROM posts ORDER BY date DESC LIMIT ".(($currentPage-1)*$perPage).",$perPage");
while($data = $q->fetch(PDO::FETCH_ASSOC))
{
$datas[] = new Post($data);
}
return $datas;
}
I want to create a loop in my view, this is what I'm doing
{% for posts in allPosts %}
{% for i in 1..numberPages %}
{{ i }}
{% endfor %}
{% endfor %}
But it's not working. It seems like I can't access to numberPages and I don't know why.
If anybody can help me !
Thanks a lot
EDIT
My pagination is working now.
I had this in my method like #darkbee :
return array(
'records' => $datas,
'numberPages' => $numberPages,
);
And in my view :
{% for i in 1.. allPosts.numberPages %}
<li>{{ loop.index}}</li>
{% endfor %}
But now I have another issue. I only get the same posts in all the pages.
EDIT
I forgot the page= on my pages links ...
<li>{{ loop.index}}</li>
It's working now !
Thanks !
You need to return the number of pages as well.
An aproach could be this,
public function getAllPosts() {
/** ... code .. **/
return array(
'records' => $data,
'numberPages' => $numberPages,
);
}
{% for posts in allPosts.records %}
{% for i in 1.. allPosts.numberPages %}
{{ i }}
{% endfor %}
{% endfor %}
I am new to Twig and need to check whether the way I use it in my MVC is the 'correct' way. I have a feeling that it isn't;
I want to have a controller for each region in my site and have each controller render their own twig template. I read about including twig templates inside twig templates such as:
main.twig
{% include 'header.twig' %}
{% include 'menu.twig' %}
{% include 'content.twig' %}
{% include 'footer.twig' %}
The problem with this is that I cannot run a separate controller for each region before the template is included. I would have to pass the variables for all regions as once to main.twig and I don't like to do that.
So I now do something like the following:
$regions=[];
//...preprocessing menu items here in a controller...
$template=$twig->loadTemplate('regions/menu.twig');
$regions['menu'] = $template->render(array(
'home' => 'Go to Home',
'contact' => 'Contact page'
));
//...other regions...
$template=$twig->loadTemplate('main.twig');
echo $template->render([
'regions'=>$regions
]);
And regions inside main.twig are then printed using the raw value: {{regions.menu|raw}}
This way I have full control over the data that is passed to each template which is what I want. However I have the feeling that I am now not using Twig the way it is supposed to, because I am saving rendered html in variables and then rendering it again.
If what I am trying to achieve is possible in a better way, please let me know.
I'm thinking it's causing a lot of overhead as you always will need to copy/paste the regions whenever you want to create a new page/controller. Idealy would be to use a main template with the includes and let your views extend from the base one.
base.twig.html
<!DOCTYPE html>
<html>
<head>
<title>{{ page.title | default('') }}</title>
<link rel="stylesheet" type="text/css" href="default.css" />
{% block css %}
{% endblock %}
</head>
<body>
{% block nav %}
<nav id="main">
{% for link in main.links %}
{{ link.title }}
{% endfor %}
</nav>
{% endblock %}
<div id="content">
{% block content %}
{% endblock %}
</div>
{% block javascript %}
{% endblock %}
</body>
</html>
{% extends "base.twig.html" %}
{% block content %}
<h1>{{ title }}</h1>
{% endblock %}
If you want to have a controller for each region you could create a helper class which calls all the controllers you need a returning an multi-dimensional array defined by the class name of the region.
This way your variables will never collide as you can access them by e.g. main.title / menu.title / title
(code is just pseudo-code, did not test/run it, just to give you an idea)
<?php
$regions = (new \Project\Regions\Container())->addRegion('Main')
->addRegion('Menu');
echo $twig->render('child.html', array_merge($regions->getParameters(), [
'title' => 'Hello World',
]);
class Container {
private $regions = [];
public function __construct($regions = []) {
$this->regions = $regions;
}
public function setRegions($regions = []) {
$this->regions = $regions;
return $this;
}
public function addRegion($region) {
if (!in_array($region, $this->regions)) $this->regions[] = $region;
return $this;
}
public function getParameters() {
$data = [];
foreach($this->regions as $region) {
$class = '\Project\Regions\\'.$region;
if (!class_exists($class)) continue;
$data[strtolower($region)] = (new $class())->getParameters();
}
return $data;
}
}
<?php
namespace Project\Regions;
abstract class Region {
public function getParameters() {
return [];
}
}
<?php
namespace Project\Regions;
class Page extends Region {
public function getParamters() {
return [
'title' => 'foo',
];
}
}
<?php
namespace Project\Regions;
class Menu extends Region {
return [
'title' => 'bar',
];
}
How i can wrap every block code with spaceless to crop whitespaces from my twig/html
for example now i have:
{% block content %}
<div class="box clearfix clearall">
<div class="ct colcontainer">
<div class="col-1">
<div class="chars">
<table class="layout data-char">
<thead>
blabla
{% endblock %}
And when symfony try to render it, i want that symfony saw
{% block content %}
{% spaceless %}
<div class="box clearfix clearall">
<div class="ct colcontainer">
<div class="col-1">
<div class="chars">
<table class="layout data-char">
<thead>
blabla
{% endspaceless %}
{% endblock %}
Define a custom Twig tag (the copy-and-paste way)
You can define a custom Twig tag spacelessblock which combines block and spaceless. Then you can use {% spacelessblock xyz %}…{% endspacelessblock %} in your templates. Here is how you do it the quick and dirty (copy and paste) way.
A new Twig node
First, define a class Twig_Node_SpacelessBlock (e.g. in the Extension directory of your bundle):
class Twig_Node_SpacelessBlock extends \Twig_Node_Block
{
public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
{
parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
// top part of Block.compile
$compiler
->addDebugInfo($this)
->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n")
->indent()
;
// the content of the body is treated like in Spaceless.compile
$compiler
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));\n")
;
// bottom part of Block.compile
$compiler
->outdent()
->write("}\n\n")
;
}
}
A new Twig token parser
Our new Twig node needs to be built somewhere whenever Twig finds a {% spacelessblock xyz %} in a template. For that, we need a token parser which we call Twig_TokenParser_SpacelessBlock. We basically copy and paste Twig_TokenParser_Block:
class Twig_TokenParser_SpacelessBlock extends \Twig_TokenParser
{
public function parse(Twig_Token $token)
{
// …
$this->parser->setBlock($name, $block = new Twig_Node_SpacelessBlock($name, new Twig_Node(array()), $lineno));
// …
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endspacelessblock');
}
public function getTag()
{
return 'spacelessblock';
}
}
Tell Twig about it
In your extension class:
class Extension extends \Twig_Extension
{
public function getTokenParsers()
{
return array(
new Twig_TokenParser_SpacelessBlock(),
);
}
}
Tell Symfony about it
If not already done, add the following to your your services.yml:
services:
# …
my.extension:
class: Acme\MyBundle\Extension\Extension
tags:
- { name: twig.extension }
Better alternatives
Preprocessor
A better way would be to use a preprocessor to simply replace
{% spacelessblock xyz %}
…
{% endspacelessblock %}
by
{% block xyz %}{% spaceless %}
…
{% endspaceless %}{% endblock %}
which reuses all the code that already has been written in the Twig project, including possible changes.
I'm using a form with 2 classes ("ArticleType" and "ArticleHandler") for my class Article.
I would like to send the id of the article I've just created, but I can't manage to show this id parameter in the view :
In the controller, I send my article's id :
$handler = new ArticleHandler($form, $request, $em);
if ($handler->process()){
return $this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) );
}
and in the view, I've got an error with :
{% block body %}
<p>the id :</p>
{{ id }}
{% endblock %}
or entity.id (as in the CRUD) :
Variable "id" does not exist...
Variable "entity.id" does not exist...
Do you know how to fix this?
Thanks
EDIT :
here's my method :
public function addAction()
{
$article = new Article();
$form = $this->createForm(new ArticleType(), $article);
$request = $this->getRequest();
$em = $this->getDoctrine()->getEntityManager();
$handler = new ArticleHandler($form, $request, $em);
if ($handler->process()){
return $this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) );
}
return $this->render('ProjBlogBundle:Blog:add.html.twig', array(
'form' => $form->createView(),
));
}
and here's the view :
{% extends "ProjBlogBundle::layout.html.twig" %}
{% block title %}
the title - {{ parent() }}
{% endblock %}
{% block sousbody %}
<p>here's the article i've just created :</p>
{{ id }}
{% endblock %}
EDIT N°2 :
myproject_show:
pattern: /show/{id}
defaults: { _controller: ProjBlogBundle:Blog:show, id:5 }
To use a variable in a template you need to pass it when you render your template:
//ProjBlogBundle:Blog:show
public function showAction($id)
{
return $this->render('ProjBlogBundle:Blog:show.html.twig', array(
'id' => $id
));
}
$this->redirect($this->generateUrl('myproject_show', array('id' => $article->getId())) ); returns only HTTP 302-response without rendering a template, and the browser is redirected to the generated url...