i have a question about the linking system in symfony.
I use normal links like this:
click me
and then in the controller the corrosponding action looks like:
/**
* #Route("/my_path", name="my_path")
* #Template()
*/
public function myAction()
{ ...
now, i have this links in a list and i need to add a item.id to each.
so the controller looks like:
/**
* #Route("/my_path/id", name="my_path")
* #Template()
*/
public function myAction($id)
{ ...
and all of this is in a loop:
{% for item in items %}
click me <br>
{% endfor %}
how to add the item.id into the path?
found the solution:
{% for item in items %}
click me <br>
{% endfor %}
Related
I'm trying to render a tree of links using Doctrine's self referencing association. I have an entity like:
<?php
namespace App\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table(name="link")
* #ORM\Entity(repositoryClass="App\Repository\LinkRepository")
*/
class Link
{
/**
* #ORM\Column(name="link_id", type="integer", nullable=false, options={"unsigned"=true})
* #ORM\Id()
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $linkId;
/**
* #ORM\Column(name="name", type="string", length=50)
*/
private $name;
/**
* #ORM\Column(name="url", type="string", length=500)
*/
private $url;
/**
* #ORM\Column(name="parent_id", type="integer")
*/
private $parentId;
/**
* #ORM\OneToMany(targetEntity="Link", mappedBy="parent")
*/
private $children;
/**
* #ORM\ManyToOne(targetEntity="Link", inversedBy="children")
* #ORM\JoinColumn(name="parent_id", referencedColumnName="link_id")
*/
private $parent;
public function __construct()
{
$this->children = new ArrayCollection();
}
public function getLinkId(): ?int
{
return $this->linkId;
}
// Getters and setters ...
/**
* #return ArrayCollection
*/
public function getChildren()
{
return $this->children;
}
}
and in my controller I have it fetching the link and calling the twig template like:
public function link(int $linkId, LinkRepository $linkRepository)
{
$link = $linkRepository->findOneBy(['linkId' => $linkId]);
return $this->render('link.html.twig',
[
'link' => $link
]);
}
And the twig template is something like this:
{% extends 'base.html.twig' %}
{% block body %}
{{ link.name }}
<h2>Children:</h2>
{% import _self as macros %}
<ul>
<li>{{ link.name }}
{{ macros.link_tree(link) }}
</li>
</ul>
{% endblock %}
{% macro link_tree(link) %}
{% import _self as macros %}
<ul>
{% for linkChild in link.children %}
<li>
{{ link.name }}
{% if linkChild.children %}
<ul>
{{ macros.link_tree(linkChild.children) }}
</ul>
{% endif %}
</li>
{% endfor %}
</ul>
{% endmacro %}
Although when I call this controller it gives me this error:
Neither the property "children" nor one of the methods "children()",
"getchildren()"/"ischildren()"/"haschildren()" or "__call()" exist and
have public access in class "Doctrine\ORM\PersistentCollection".
This seems to be when I make reference to linkChild.children in the twig template.
How can I loop recursively over children() in twig?
First of all your Doctrine mapping is invalid: You should remove the parentId field, there is no need for it since you have already added the appropriate association with parent field.
Secondly, you should use Symfony's ParamConverter to get the link inside the controller like this:
public function link(Link $link)
Yes, it is as easy as it seems, you can get the link just by typehinting the link variable in your controller action, no need to use the LinkRepository there. You can find more about ParamConverter here.
Finally, it seems to be an issue in your data, because you got an instance of Doctrine Collection when you expect an instance of Link class. Try to debug, using {{ dump() }} inside your twig template, at this point there are not enough data to help you further with this one. But you definitely should first fix the mapping issue before trying again.
So it turns out I was sending the wrong thing to the macro, it was expecting linkChild and I was passing linkChild.children. Inside the macro it was trying to reference linkChild.children.children
This was fixed by using:
{{ link.name }}
{% if linkChild.children %}
<ul>
{{ macros.link_tree(linkChild) }}
</ul>
{% endif %}
i have 3 entities named Answer, Skill and Jointure.
Answer and Skill are linked to Jointure with a ManyToOne relation.
I display them in twig like that :
class HomeController extends AbstractController
{
/**
* #var JointureRepository
*/
public function __construct(JointureRepository $repository, ObjectManager $em)
{
$this->repository = $repository;
$this->em = $em;
}
/**
* #Route("/", name="home")
*/
public function index()
{
$JointureRepository = $this->getDoctrine()->getRepository(Jointure::class);
$arrJointures = $JointureRepository->findAll();
$this->em->flush();
return $this->render('pages/home.html.twig', [
'controller_name' => 'HomeController',
'jointure' => $arrJointures,
]);
}
}
and in my twig view :
{% for object in jointure %}
{% for skill in object.skills %}
{{skill.label}}
{% endfor %}
{% endfor %}
I've created a dropdown button who list all the skill.label properties who exists like that :
EDIT : Here my twig button :
<div class="form-group ">
<select id="inputState " class="form-control">
<option selected>Compétence</option>
{% for object in jointure %}
{% for skill in object.skills %}
<option>{{skill.label}}</option>
{% endfor %}
{% endfor %}
</select>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary btn-block">Search</button>
</div>
</div>
I want to show / display all answer.userEmail who have this related skill.label in a view in my template.I have to use EntityType ? Many thanks
You should start using Symfony Forms. Here is documentation https://symfony.com/doc/current/forms.html. It is not that simple at the beginning but it definitely worith it. Then you will be able to use EntityType https://symfony.com/doc/current/reference/forms/types/entity.html
Im trying my first project using symfony2 + twig. I created basic twig template with defined blocks. It basically looks like this
{% block content %}
some content...
{% endblock %}
{% block footer %}
{{ footer.content}}
{% endblock %}
I want footer to be same for all pages. Footer is loaded from DB and its set in controller. I wanted inherit from template described above to other pages but I have to always set footer in controller otherwise variable is not defined.
My questions is if exists any 'nice' way how to set footer variable for multiple templates inherited from parent template?
The solution : Embedding Controllers
In some cases, you need to do more than include a simple template.
Suppose you have a sidebar in your layout that contains the three most
recent articles. Retrieving the three articles may include querying
the database or performing other heavy logic that can't be done from
within a template.
The solution is to simply embed the result of an entire controller
from your template. First, create a controller that renders a certain
number of recent articles:
Controller
// src/AppBundle/Controller/ArticleController.php
namespace AppBundle\Controller;
// ...
class ArticleController extends Controller
{
public function recentArticlesAction($max = 3)
{
// make a database call or other logic
// to get the "$max" most recent articles
$articles = ...;
return $this->render(
'article/recent_list.html.twig',
array('articles' => $articles)
);
}
}
View
{# app/Resources/views/article/recent_list.html.twig #}
{% for article in articles %}
<a href="/article/{{ article.slug }}">
{{ article.title }}
</a>
{% endfor %}
Layout
{# app/Resources/views/base.html.twig #}
{# ... #}
<div id="sidebar">
{{ render(controller(
'AppBundle:Article:recentArticles',
{ 'max': 3 }
)) }}
</div>
you can do with one of the following, 'write a custom Twig Extension'
<?php
namespace AppBundle\Extension;
class MyTwigExtension extends \Twig_Extension
{
private $em;
private $conn;
public function __construct(\Doctrine\ORM\EntityManager $em) {
$this->em = $em;
$this->conn = $em->getConnection();
}
public function getFunctions()
{
return array(
'users' => new \Twig_Function_Method($this, 'getUsers'),
);
}
public function getUsers()
{
$sql = "SELECT * FROM users ORDER BY accountname";
return $this->conn->fetchAll($sql);
}
public function getName()
{
return 'smproc4_twig_extension';
}
}
Register an Extension as a Service
services:
my.twig.extension:
class: AppBundle\Extension\MyTwigExtension
tags:
- { name: twig.extension }
arguments:
em: "#doctrine.orm.entity_manager"
Using the custom Extension
Hello {{ name }}!
<ul>
{% for user in users() %}
<li>{{ user.accountname }}</li>
{% endfor %}
</ul>
have a look at the
How to Write a custom Twig Extension
Creating an Extension
Is it possible to put a collection of static html files under a symfony 2.x app such that the URLs won't change at all?
I want to maintain urls like this: http ://example.com/town/Blah-blah-in-Bayeux.html
I know that I can rewrite example.com/app.php/town/Blah-blah-in-Bayeux.html so that the app.php isn't part of the script, but how can I ensure that my route to, say, the town action of the default controller ends in .html?
It's quite simple! Here I've used a FrameworkExtraBundle #Route annotation where the routing pattern simply ends in .html.
<?php
namespace Foo\BarBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
class HomeController extends Controller
{
/**
* Demo of a route which includes a .html suffix
*
* #Route("/town/Blah-blah-in-{townName}.html", name="town")
* #Template()
*/
public function townAction($townName)
{
# Lookup $town slug to get a town
# ...
# 404 if town not found
# ...
# just for illustration
$town = $townName;
return array('town' => $town);
}
}
{# src/Foo/BarBundle/Resources/Home/town.html.twig #}
{% extends 'FooBarBundle::layout.html.twig' %}
{% block title %}Blah blah in {{ town }} - {{ parent() }}{% endblock %}
{% block content %}
<h1>Blah blah in {{ town }}!</h1>
<p> and other content from the original /town/Blah-blah-in-{{ town }}.html</p>
{% endblock %}
When I've created a form:
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'button')->getForm();
and render it:
<div><button type="button" id="form_add" name="form[add]">Add</button></div>
the attributes type, id and name are created.
I want to erase this attributes but I don't know how to do it. I've tried to do:
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'button', array( 'attr' => array() ) )->getForm();
without any success.
How could I do it?
Greetings and thanks
I messed around with this for a while and the closest I could get was to have them render with empty attributes. However, that's because the project I tested with is Symfony 2.0, and in that version it's impossible to completely remove the attributes since Symfony\Component\Form\FormView::$vars is private.
However, in Symfony 2.1 and later, that same property is public so you should be able to modify (or delete) the attributes/vars directly without being constrained by the FormView api.
First, create your own type to represent this "naked button"
src/Your/Bundle/Form/NakedButtonType.php
<?php
namespace Your\Bundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;
class NakedButtonType extends AbstractType
{
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.FormTypeInterface::getName()
*/
public function getName()
{
return "naked_button";
}
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.AbstractType::getParent()
*/
public function getParent(array $options)
{
return 'button';
}
/**
* (non-PHPdoc)
* #see Symfony\Component\Form.AbstractType::buildViewBottomUp()
*/
public function buildViewBottomUp(FormView $view, FormInterface $form)
{
// Symfony 2.0
// This will still render the attributes, but they will have no value
$view->set('id', null);
$view->setAttribute('type', null);
// Symfomy >= 2.1
// This *should* remove them completely
unset( $view->vars['id'] );
unset( $view->vars['attr']['type'] );
}
}
Now, tell the service container how to build your type
app/config/config.yml
services:
form.type.naked_button:
class: Your\Bundle\Form\NakedButtonType
tags:
- {name: form.type, alias: naked_button}
Then update your parent form to use your new type instead of the ootb "button" type.
$builder = $this->createFormBuilder();
$form = $builder->add( 'add', 'naked_button')->getForm();
All of that being said...
If you want these buttons w/o any attributes, why not just put them like that directly into your view?
<form>
{{ form_errors(form) }}
{{ form_rest(form) }}
<div>
<button>Add</button>
</div>
</form>
All of this custom type nonsense seems like alot of overhead to render something you clearly don't need Symfony to manage for you.
While not a good idea, I will help you load the gun and shoot yourself in the foot :-)
Create a new Resources/views/Form/fields.html.twig file, and put the following in it:
{% block widget_attributes %}
{% spaceless %}
id="{{ id }}" name="{{ full_name }}"{% if read_only %} readonly="readonly"{% endif %}{% if disabled %} disabled="disabled"{% endif %}{% if required %} required="required"{% endif %}{% if max_length %} maxlength="{{ max_length }}"{% endif %}{% if pattern %} pattern="{{ pattern }}"{% endif %}
{% for attrname, attrvalue in attr %}{% if attrname in ['placeholder', 'title'] %}{{ attrname }}="{{ attrvalue|trans({}, translation_domain) }}" {% else %}{{ attrname }}="{{ attrvalue }}" {% endif %}{% endfor %}
{% endspaceless %}
{% endblock widget_attributes %}
If you really, really want to remove all the attributes on there, you can remove the {% for attrname ... %} line. That will remove the attributes from all form fields. If you add some logic, you can have it apply to just specific fields.
Next step, you need to register your fields helpers. In your app/config/config.yml file, add the following line:
twig:
form:
resources:
- 'SomeBundle:Form:fields.html.twig'