I have a show page and on the bottom the page I have a 'Related' articles section that show 4 related articles that the user can click on.
The problem is, I want to exclude the current article that is being shown from the 'Related' articles section on the bottom of the page so it doesn't appear twice.
Is there a conditional I can use in Twig to hide the current article I am showing in the 'Related' section so it doesn't appear twice (once for the show and again on the bottom of the page as a Related article) on the bottom of the page?
Example of show page
Show page - Article 1
blah...
...blah
blah...
Related: (4 articles on the bottom of page)
Article 1, Article 2, Article 3, Article 4 (I want to hide Article 1 in this section as it's being shown already)
Related partial I am using:
<h2>Related</h2>
<aside id="" class="">
<div class='featuredfour cf'>
{% for article in articles %}
{% if not article.id == showedArticleId %}
<div class="featuredfourpost">
<a href="{{ path('acme_demo_article_show', { slug: article.slug }) }}">
<img width="150" height="150" src="{{ asset(['images/', article.image]|join) }}" class="" alt="" />
<h3 class="title">{{ article.title }}</h3>
</a>
</div>
{% endif %}
{% endfor %}
</div>
</aside>
Show page
{% extends "AcmeDemoBundle::layout.html.twig" %}
{% set showedArticleId = article.id %}
{% block body %}
[...]
<div class="entry-content">
{% autoescape false %}
<p>{{ include(template_from_string(article.body|raw|nl2br)) }}</p>
{% endautoescape %}
</div>
[...]
{# Related Articles section #}
{{ include('AcmeDemoBundle:Partial:_featured.html.twig', { articles: articles }) }}
{% endblock %}
Doctrine for relatedPosts
public function getRelatedArticles($limit)
{
return $this
->createQueryBuilder('article')
->setMaxResults($limit)
->orderBy('article.createdAt', 'DESC')
->getQuery()
->execute();
}
You can try this for your Doctrine query:
public function getRelatedArticles($exceptArticle, $limit)
{
return $this
->createQueryBuilder('article')
->where('article != :exceptArticle')
->setMaxResults($limit)
->orderBy('article.createdAt', 'DESC')
->setParameter('exceptArticle', $exceptArticle);
->getQuery()
->execute();
}
You just give the article to except from the query.
Inside your twig, on the part where you're showing the article, add (somewhere, without code is difficult to say exactly where)
[...]
{% set showedArticleId = article.id %}
[...]
Then use it for check and skip already showned one
[...]
{% for article in articles %}
{% if article.id != showedArticleId %}
[...]
{% endif %}
{% endfor %}
[...]
Of course I'm making some guessing on your article entity fileds: I'm pretty sure that id will be there
Related
I'm trying to set flash message from ContactAction then redirect on Homepage, but on it, I can't see my flash message, maybe my session is reset ? Can I have some help, I'm a beginer on Symfony.
CoreController that contain both index and contact functions :
<?php
namespace OC\CoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class CoreController extends Controller
{
public function indexAction()
{
$ServiceAdverts = $this->container->get('oc_core.listAdverts');
$adList = $ServiceAdverts->getListAdverts();
return $this->render("OCCoreBundle:Core:index.html.twig", array(
'listAdverts' => $adList
));
}
public function contactAction()
{
$this->addFlash('info', 'Contact page not ready yet !');
return $this->redirectToRoute('oc_core_homepage');
}
}
Twig template (homepage) :
{% block body %}
<div>
Messages flash :
{% for msg in app.session.flashBag.get('info') %}
<div class="alert alert-success">
{{ msg }}
</div>
{% endfor %}
</div>
<h2>Liste des annonces</h2>
<ul>
{% for advert in listAdverts %}
<li>
<a href="{{ path('oc_platform_view', {'id': advert.id}) }}">
{{ advert.title }}
</a>
par {{ advert.author }},
le {{ advert.date|date('d/m/Y') }}
</li>
{% else %}
<li>Pas (encore !) d'annonces</li>
{% endfor %}
</ul>
Contact
{% endblock %}
Symfony 3.3 made improvements to flash messages so your Twig template should look different. The app.session.flashBag.get() call is now replaced by app.flashes().
So your Twig code would now be:
{% for msg in app.flashes('success') %}
<div class="alert alert-success">
{{ msg }}
</div>
{% endfor %}
Try this, works for me in 3.2 and 3.4
{% for type, flash_messages in app.session.flashBag.all %}
{% for msg in flash_messages %}
<div class="alert alert-{{ type }}">
{{ msg }}
</div>
{% endfor %}
{% endfor %}
Another thing is that once you called the flashBag it turns empty so you can't use it twice. Check your code that it hasn't been called on another page right before a second redirect ...
On a single page, I need to add an include, on the bottom of the page, that add 3 projects.
I have a content type called projects I made with ACF.
single-project.twig
I have a "single-project.twig" page that display the content of a post (which is also a project). At the bottom of this page, I need to add a block like this:
{% block related %}
{% include 'inc-related.twig' %}
{% endblock %}
inc-related.twig
Into "inc-related.twig", I have this:
{% for post in related_posts %}
{% if post.thumbnail %}
<a href="{{post.link}}" class="l-basicgrid-work work">
<article>
<figure>
<img data-src="{{post.get_thumbnail.src('medium_large')|resize(800, 533)}}" alt="{{post.title}}" class="lazy">
</figure>
<figcaption>
<h2>{{ post.title }}</h2>
</figcaption>
</article>
</a>
{% endif %}
{% endfor %}
related.php
I also create a "related.php" page, that integrate the following rendering:
$context = Timber::get_context();
$context['related_posts'] = Timber::get_posts('post_type=project&posts_per_page=3');
Timber::render('inc-related.twig', $context);
Questions
I have 2 questions:
The project are not displaying into the single page. What I have done
wrong?
Can I select 3 project except the one who is displayed on the
single page?
Thank you
you have include twig template but not send data to him. Your related.php is not include automatically. You should write $context['related_posts'] = Timber::get_posts('post_type=project&posts_per_page=3'); in your single-project.twig. And do not use post name in inc-related.twig. May be conflict with current post object.
To single-project.php add $context['related_posts'] = Timber::get_posts('post_type=project&posts_per_page=3');
This is what I did.
single-project.php
I have created a single-project.php page. It contains the following script:
$context = Timber::get_context();
$args = array(
// Get post type project
'post_type' => 'project',
// Get 3 posts
'posts_per_page' => 3,
// Order random
'orderby' => 'rand',
);
$context['related_posts'] = Timber::get_posts( $args );
Timber::render('single-project.twig', $context);
single-project.twig
On my single-project.twig, I have the code that get the result of the php page. Actually, that code is included into an "inc-related.twig" page. And it works!
{% for post in related_posts %}
{% if post.thumbnail %}
<a href="{{post.link}}" class="l-basicgrid-work work">
<article>
<figure>
<img data-src="{{post.get_thumbnail.src('medium_large')|resize(800, 533)}}" alt="{{post.title}}" class="lazy">
</figure>
<figcaption>
<h2>{{ post.title }}</h2>
</figcaption>
</article>
</a>
{% endif %}
{% endfor %}
But on the same page (single-project.twig), I also have data that display content from Advanced Custom Field.
{% if post.project_images %}
{% for item in post.get_field('project_images') %}
<figure class="{{item.project_media_css}}">
<img data-src="{{TimberImage(item.project_media).src}}" alt="{{item.alt_media}}" class="lazy">
</figure>
{% endfor %}
{% endif %}
The problem now is that the ACF field don't return anything.
How should I modify single-project.php to get content from "related_project" and ACF field?
The ACF context is passed to the single page template via Timber $context, using the get_posts function.
single-project.php
$context['related_posts'] = Timber::get_posts('post_type=project&posts_per_page=3');
Twig sends along the active context by default, so this template is fine as-is.
{% block related %}
{% include 'inc-related.twig' %}
{% endblock %}
Finally, the posts in $context['related_posts'] can be looped over. However, within the for loop, refer to each post by anything other than post, such as rel_post. This avoids conflict with the regular post object.
{% for rel_post in related_posts %}
{% if rel_post.thumbnail %}
...
{% endif %}
{% endfor %}
I am completely new to PHP, Wordpress and Timber. I have a custom post type called Projects, it uses the posts archive template and I am going crazy to create a specific Projects archive template so I can have a different layout for it.
This is what my index.twig looks like
{% extends "layouts/base.twig" %}
{% block content %}
<div class="uk-child-width-1-3#m" uk-grid uk-scrollspy="cls: uk-animation-fade; target: > div > .uk-card; delay: 500; repeat: false">
{% for post in posts %}
{% include "tease-post.twig" %}
{% endfor %}
</div>
{% endblock %}
and this is the tease-post.twig
{% block content %}
<div>
<div class="uk-card uk-card-default">
<div class="uk-card-media-top">
<img src="{{post.thumbnail.src('full')}}" alt="">
</div>
<div class="uk-card-body">
<h3 class="uk-card-title">{{post.title}}</h3>
<p>{{post.get_preview(25,false,false,true)}}</p>
</div>
<div class="uk-card-footer">
Read more
</div>
</div>
</div>
{% endblock %}
Any idea how it works? Can't find any related documentation..
There are a few diff't methods depending on what you're looking to achieve. It would seem the simplest is...
{% extends "layouts/base.twig" %}
{% block content %}
<div class="uk-child-width-1-3#m" uk-grid uk-scrollspy="cls: uk-animation-fade; target: > div > .uk-card; delay: 500; repeat: false">
{% for post in posts %}
{% include "tease-'~ post.type ~'.twig" %}
{% endfor %}
</div>
{% endblock %}
You could then create a file called tease-project.twig (assuming that `project is the name of your custom post type's slug) that might look something like this...
{# tease-project.twig #}
<h2>My cool project is... {{ post.title }}</h2>
If you're looking to do something special with a CPT's specific archive page...
Check your archive.php file, if you're using the starter theme it should load a file called archive-projects.twig (assuming that projects is the name of the custom post type).
The logic is fully customizable so you can load any .twig files you want depending on the circumstance
I have this
base.html.twig
<html>
{% block template_scope_vars %}
{% endblock %}
<head></head>
<body>
{% block content %}
{% endblock %}
</body>
</html>
user.html.twig
extends base...
{%block template_scope_vars %}
{% set test= "bippo" %}
{%endblock%}
{%block content %}
{{ test }}
{% endblock %}
Error
Variable "test" does not exist
Ok please please don't tell me that I don't have to use blocks for this, the use case is more difficult than this simple example obviously. The idea is that test gets available in the base.html.twig as well, as well as any sub-templates of it that I might be using in future
for the special use case I had I could use this. It is a shame though, there is no neater solution. All other solution I could come up with, would require to create a new file for each set of template scope variables. Which plain sucks
base.html.twig
<div id="menu"> //this is like a sidebar menu
<div class="menuitem {% block foo %}{% endblock %}">
Foo
</div>
<div class="menuitem {% block bar %}{% endblock %}">
Bar
</div>
<div>
{% block content %}
{% endblock %}
Then in
foo.html.twig
{% extends "MyBundle::base.html.twig" %}
{% block foo %}active{% endblock %}
{% block content %}
Some content specific for foo
{% endblock %}
bar.html.twig
{% extends "MyBundle::base.html.twig" %}
{% block bar%}active{% endblock %}
{% block content %}
Some content specific for bar
{% endblock %}
and then you set the css for the background of e.g.
CSS
menuitem.active {
background-color: red;
}
base.html.twig
<div id="menu"> //this is like a sidebar menu
<div class="menuitem{{ active == 'foo' ? ' active' : '' }}">
Foo
</div>
<div class="menuitem{{ active == 'bar' ? ' active' : '' }}">
Bar
</div>
<div>
{% block content %}
{% endblock %}
foo.html.twig
{% extends "MyBundle:Whatever:base.html.twig" %}
{% block content %}
{{ content }}
{% endblock %}
Controller
/**
* #Route("/foo", defaults={"param" = null}, name="foo")
* #Route("/foo/{param}", name="foo_with_param")
* #Template()
*/
public function fooAction($param)
{
// This would really be getting data from the Model
if ($param == 'something') {
$content = 'Some content';
$active = 'foo';
} else {
$content = 'Different content';
$active = 'bar';
}
return array(
'content'=> $content,
'active' => $active,
);
}
When rendering foo.html.twig, base.html.twig will get the variable "active".
I have such hierarchy of twig files
my main (for controller) twig
{% extends "MainSiteBundle::layout.html.twig" %}
{% block footer_moderator_buttons %}
some buttons
{% endblock %}
{% block content_body %}
<p>hello moderator</p>
{{ include ('MainBlogBundle:_parts:postList.html.twig', {'postList': aPostDraft}) }}
{% endblock %}
postList.html.twig
<div class="post-list">
{% for postSingle in postList %}
{{ include ('MainBlogBundle:_parts:postSingle.html.twig', {'postSingle': postSingle}) }}
{% endfor %}
</div>
postSingle.html.twig
<div class="post">
<div class="post-header">
<a class="title" href="3">{{ postSingle.title }}</a>
</div>
<div class="post-meta">
<div>Date: {{ postSingle.date|date('D M Y') }}</div>
<div>Category: {{ postSingle.getCategory.getTitle }}</div>
<div>Author: {{ postSingle.getUser.username }}
</div>
</div>
<div class="post-body">
<div class="content">
<img width="450" height="200" src="#">
<div class="text">{{ postSingle.content }}</div>
</div>
</div>
<div class="post-footer">
{% block footer_moderator_buttons %}f{% endblock %}
<div>Views: 152</div>
<div>Comments: 1231</div>
<div>
<a class="link" href="#">More... </a>
</div>
</div>
</div>
As you can see last (postSingle.html.twig) has block "footer_moderator_buttons", so how can i change it from main twig (first one) ? Current is not working, what I need change \ do ?
In Twig 1.8 there is embed tag (http://twig.sensiolabs.org/doc/tags/embed.html).
You would have to remove the postList.html.twig file or work around it.
{% embed "MainBlogBundle:_parts:postSingle.html.twig" with {'postSingle': postSingle} %}
{% block footer_moderator_buttons %}
custom buttons here
{% endblock %}
{% endembed %}
So, you question is actually "I want to understand how\what twig can". Well, the answer to that question is: "It can't overwrite blocks from "main" to the smaller ones."
If you want to use twig, you have to stop thinking in php include() way where you make new files and you "PUT" components in them over and over again, components such as header, footer, menu etc.
In twig, you define main twig file with blocks which can be imagined as empty spaces which can, but do not have to, be overwritten. Surely, it still means you can have postList.html.twig as include in some file that extends MainSiteBundle::layout.html.twig. The same goes for postSingle.html.twig.
I think you catch the logic of twig except don't try to overwrite blocks from the wrong side - in this case, from MainSiteBundle::layout.html.twig to it's smaller portions.
how about this:
{% extends "MainSiteBundle::layout.html.twig" %}
....
{% block footer_moderator_buttons %}
{{ parent() }}
{% endblock %}
woops didnt put parent..
the {{ parent() }} will inherit {% block footer_moderator_buttons %}{% endblock %} from extended twig.