Dynamic links in menu (Symfony 3) - php

I'm working with Symfony and I want to add menu bar to my site. I have a simple menu bar with 3 links that are static and always be the same like "Home" but I need to add new links that should be dynamic. I have my admin panel where I can add new pages (info about them is stored in database - title, content etc.) and what I want to achieve is that after adding a new page, a link to this page should appear in this menu. I have this menu in my header.html.twig file which I include to every page same as footer.
Best of what I was able to achieve at that moment:
DefaultController.php
public function headerAction ()
{
$news = $this->getDoctrine()
->getManager()
->createQueryBuilder()
->from('AppBundle:News', 'n')
->select('n')
->where('n.type = :news')
->setParameter('news', 'other')
->getQuery()
->getResult();
$parsedown = new Parsedown();
foreach($news as $key => $new){
$news[$key]->setContent($parsedown->text($new->getContent()));
}
return $this->render('default/other_menu.html.twig', array(
'news' => $news
));
}
header-menu.html.twig
{# some bootstrap #}
{# simple static links #}
{{ render(controller('AppBundle:Default:header')) }}
{# simple static links #}
{# some bootstrap #}
other_menu.html.twig
{% for new in news %}
{{ new.title }}
{% endfor %}
But this solution works only when I have one page in database. With more of them - one long link is created (page1page2page3)
Any help will be appreciated.

Related

ezplatform render links with url and object name from multi-relational content item in content type

does anyone know now to create a custom view type for ez platform? The default 3 have been exhausted and we need a new one for 'link'
Alternatively, does anyone know how to use the render( controller( with a custom template as this would also resolve out block right now.
Basically, we have a multi-relational field in a content object used and we need to print links to all the related contentIds, path works great but we cannot find a way to extract the name of the content object for the link without doing some fairly funky tpl logic of passing in params.
EG: As a hack for now we can pass in "embed_type" as a custom param with the render(controller("ez_content:viewAction" to pull in an alternate view for the content object for a specific content type and view type.
{% if embed_type is defined %}
{% include "embed/#{embed_type}.html.twig" %}
{% else %}
<h1>{{ ez_field_value( content, 'name') }}</h1>
{% endif %}
However, this is very ugly and all we really want to do is use 1 template for all content types, so all we need to do is loop through the relational field and print links (as the only thing available in the content field: "destination ids"). I am sure there used to be this option in the docs but i cannot find it anymore eg:
{% set links = ez_field_value( footer, "first_links_row" ).destinationContentIds%}
{% for id in links %}
{{ render(controller("ez_content:viewAction", {"contentId": id, "template": "link.html.twig"})) }}
{% endfor %}
Where the link.html.twig would simple print the link:
<a href="{{ path( "ez_urlalias", {"contentId": id} ) }}">
{{ ez_field_value( content, "name" ) }}
</a>
If using a custom tpl is not possible with the render (controller ( helper then a new custom view type would also fix this issue, but i cannot find documentation for either.
You can create a twig function that would do that. We have something like this:
Definition:
new Twig_SimpleFunction(
'content_name',
array($this, 'getContentName')
),
Implementation:
public function getContentName($content, $forcedLanguage = null)
{
if (!$content instanceof Content && !$content instanceof ContentInfo) {
$contentInfo = $this->repository->getContentService()->loadContentInfo($content);
} elseif ($content instanceof Content) {
$contentInfo = $content->contentInfo;
} else {
$contentInfo = $content;
}
return $this->translationHelper->getTranslatedContentNameByContentInfo($contentInfo, $forcedLanguage);
}
which enables you to provide either content id, content info or content itself, and it returns translated content name

OctoberCMS news list / detail page

After programming plain HTML/CSS/Javascript/PHP I've just started using the CMS called OctoberCMS since both the Laravel framework and OctoberCMS look very well structured and easy to use/maintain. But I'm a little confused on how to process a single detail page or a overview page.
Let's take a news page for example. So far I've made this page:
title = "News"
url = "/news/:news_id?|^[0-9]+$"
layout = "default"
description = "This is the news page."
is_hidden = "0"
meta_title = "News"
meta_description = "News page meta description"
==
<?php
function onStart()
{
$news_id = $this->param('news_id');
if(isset($news_id)) {
$news_article = []; //get the news article by id
$this['news_article'] = $news_article;
} else {
$news = []; //get an array of news articles (last ... articles ordered by datetime desc)
$this['news'] = $news;
}
}
?>
==
<div class="container">
<h1 class="block-title">
News
</h1>
{% if news_article is defined %}
Article
{% else %}
Overview
{% endif %}
</div>
But where do I actually manage to make some sort of library for my news articles? I've read something about creating a new class in a new plug-in but I can't find any tutorials or documentation for this problem, or I'm just using the wrong terms while searching. Can someone make a small example (maybe with news articles) or post a link where I can find a tutorial/documentation?
That is more comfortable to use plugin instead for write all code yourself.
Rain lab plugin allow create, manage, categorize, edit all kinds articles (include news).
You can get admin part from that plugin and use your visitor view.
Documentation: https://octobercms.com/docs/plugin/registration
If you want to generate some code in command line here are some useful commands:
Generate plugin registration file and folders
php artisan create:plugin AuthorName.PluginName
Generate model
php artisan create:model AuthorName.PluginName ModelName
Generate controller
php artisan create:controller AuthorName.PluginName ModelNames
Refresh (reinstall) plugin
php artisan plugin:refresh AuthorName.PluginName
This should get you going, docs will be helpful after that.
Use Builder (https://octobercms.com/plugin/rainlab-builder) plugin to manage CRUD very easily.
Suppose you have a model named NewsModel and you want to show the news listing or a single news in the frontend then you can modify your code by the following..
N.B: No need to write php opening and closing tag in the php section, just write
use Namespace\Plugin\Models\NewsModel; //needed to get data through model
function onStart()
{
$news_id = $this->param('news_id');
if($news_id) {
$news_article = []; //get the news article by id
$this['news_article'] = $news_article;
} else {
$news = []; //get an array of news articles (last ... articles ordered by datetime desc)
$this['news_list'] = $news;
}
}
==
<div class="container">
{% if news_article %}
<h1 class="block-title"> News Details</h1>
<div>{{ news_article.details }}</div> <!--Suppose you have a field named 'details' in the news table -->
{% elseif news_list %}
<h1 class="block-title"> News List</h1>
<ul>
{% for news in news_list %}
<li> {{ news.title }}</li><!--Suppose you have a field named 'title' in the news table -->
{% endfor %}
</ul>
{% else %}
No news found !
{% endif %}
</div>

Symfony2 render form for each list item

How could I achieve a form for each list item using csrf and validation in symfony way?
I have a Task entity, which has comments property with a relation OneToMany. So I want to list all tasks and include a hidden comment form for every task. Usually I pass generated forms from controller to template, but how to create them dinamically in template?
{% for task in tasks %}
<taskinfo>
<task comment form>
{% endfor %}
Solved using this way:
In controller:
$forms = array();
foreach($tasks as $task) {
$entity = new TaskComment();
$forms[$task -> getId()] = $this ->createTaskCommentForm($entity, $task -> getId());
}
return $this->render('Bundle:blah:index.html.twig', array(
....
'forms' => $forms
));
An then comment box near every Task box in view:
...task info...
{% for task in tasks %}
<div class="comment-box">
{{ form(forms[task.id]) }}
</div>
{% endfor %}
P.S. I'm using collapsible panels to show/hide each task.
Maybe you need to embed a collection of forms? If so, here and here you can read more.

twig: create custom tag that calls a functions

SETUP:
Twig 1.13.1
PHP 5.4.3
PROBLEM:
I am needing help setting up a custom tag that calls a function that i have already built...
Current Code:
Template Code
{% set stories = get_latest_stories(2, sports) %}
{% for story in stories %}
{{ story.headline }} <br>
{% endfor %}
Controller
$function = new Twig_SimpleFunction('getViewStories', function (section, limit) {
return news_stories::getStories(section,limit);
});
$twig->addFunction($function);
$twig->render("storyList.html");
GOAL:
No with that said I would like to use a custom tag like
{% get_latest_stories 2 sports %}
to call the same function as above. The new way looks nicer and is easier to follow
Why not fetch your stories in the controller instead of the template? This does not seem like a job for the view layer...
So, something like this:
$twig->render("storyList.html", array(
'stories' => news_stories::getStories($section, $limit)
));
Then, you'll have a stories variable available in your template.
here is simple example how to write twig extension
Following code is taken from my unfinished project
function file_import($value){
//some code here
return $value;
}
$app['twig']->addFunction('file_import', new Twig_Function_Function('file_import'));
usage
{{ file_import('value') }}

Creating breadcrumbs in symfony 2.1 using knpmenu bundle

What's the best way to create breadcrumbs using knpmenu bundle in symfony 2.1.x ? Aside from using 3-rd party bundles.
UPDATE:
Hi, theunraveler, sorry for late answer. Now I've been following your example and I'm stuck at one moment. Here, code below throws an exception, that
Missing argument 2 for Acme\DemoBundle\Menu\MenuBuilder::getBreadCrumbs()
{% set item = knp_menu_get('main') %}
{{ knp_menu_render(item) }}
{% block breadcrumbs %}
{% set breadcrumbs = knp_menu_get('breadcrumbs', [], {'request': app.request, 'menu': item }) %}
{{ dump(breadcrumbs) }}
{% endblock %}
Why it doesn't accepts "item" variable?
Since version 2.0, getBreadcrumbsArray has been moved to Knp\Menu\Util\MenuManipulator.
Possible workout to this solution is to create a twig extension:
<?php
namespace Kimwild\CommonBundle\Twig;
use Knp\Menu\Util\MenuManipulator;
use Knp\Menu\ItemInterface;
class MenuManipulatorExtension extends \Twig_Extension
{
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('menu_manipulator', array($this, 'menuManipulator')),
);
}
public function menuManipulator(ItemInterface $item)
{
$manipulator = new MenuManipulator();
return $manipulator->getBreadcrumbsArray($item);
}
public function getName()
{
return 'menu_manipulator';
}
}
Register twig extension:
kimwild_common.menu_manipulator_extension:
class: Kimwild\CommonBundle\Twig\MenuManipulatorExtension
public: false
tags:
- { name: twig.extension }
In breadcrumb.html.twig:
{% block root %}
{%- for link in menu_manipulator(item) %}
/* whatever you want to do ... */
{%- endfor %}
{% endblock %}
The Knp\Menu\MenuItem class has a getBreadcrumbsArray() method. It should return an array of items in the current active menu trail. If you are on an earlier version of KnpMenu (<= 1.1.2, I think), the returned array will be in the form of label => uri. Otherwise, it will be an array with each item having keys label, uri, and item.
To find the current menu item, you'll probably want to create a method in your controller (or somewhere else, if it makes more sense for your project) that looks something like this:
public function getCurrentMenuItem($menu)
{
foreach ($menu as $item) {
if ($item->isCurrent()) {
return $item;
}
if ($item->getChildren() && $current_child = $this->getCurrentMenuItem($item)) {
return $current_child;
}
}
return null;
}
From there, you can call getBreadcrumbsArray() on the returned value:
$this->getCurrentMenuItem($your_menu)->getBreadcrumbsArray();
I guess what I would do ultimately is create a Twig extension that registers a breadcrumbs global, and put the getCurrentMenuItem() method in there. That way, you can have the breadcrumb variable in all of your templates without having to manually render it in each controller.
Source: https://github.com/KnpLabs/KnpMenu/blob/master/src/Knp/Menu/MenuItem.php#L544.
Since KnpMenu 2.1, there is a new twig function: knp_menu_get_breadcrumbs_array
You can take a look at my gist: https://gist.github.com/fsevestre/b378606c4fd23814278a
I added a new twig function knp_menu_get_current_item, which retrieve the current menu item and work fine with the knp_menu_get_breadcrumbs_array function.
--
Edit:
With KnpMenu 2.2, you can now do:
<ol class="breadcrumb">
{% for breadcrumb_item in knp_menu_get_breadcrumbs_array(knp_menu_get_current_item('main')) %}
{% if not loop.last %}
<li>{{ breadcrumb_item.label }}</li>
{% else %}
<li class="active">{{ breadcrumb_item.label }}</li>
{% endif %}
{% endfor %}
</ol>
https://github.com/KnpLabs/KnpMenu/blob/master/doc/02-Twig-Integration.markdown#functions
The knp_menu_get_current_item('main') Twig function will retrieve the current menu item, for the main menu.

Categories