Symfony2 error on render controller with relation OneToOne on same table - symfony

I have a problem to get a object from a controller called by the render controller method.
This is my Entity with the self OneToOne relation :
class Family
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="Family")
* #ORM\JoinColumn(name="brother_id", referencedColumnName="id")
**/
private $brother;
/**
* #ORM\Column(type="string", length=100)
*/
private $label;
}
This is my action:
/**
* #Template()
*/
public function testAction()
{
$em = $this->getDoctrine()->getManager();
$brothers = $em->getRepository('FifaAdminBundle:Family')->findAll();
return array(
'brothers' => $brothers,
);
}
My view
{% for brother in brothers %}
{{ brother.id }} - {{ brother.label }}
<hr />
{% render controller('AdminBundle:Test:show', {'brother': brother}) %}
<hr />
{{ render(controller('AdminBundle:Test:show', { 'brother': brother })) }}
<hr />
{% endfor %}
My other controller
public function showAction($brother)
{
if (is_object($brother))
{
return new \Symfony\Component\HttpFoundation\Response('OK');
}
else
{
var_dump($brother);
return new \Symfony\Component\HttpFoundation\Response('KO');
}
}
The first element isgood.
But if it has a brother_id, this brother in not load by the showAction.
It gives me this:
array(1) { ["__isInitialized__"]=> string(1) "1" }
Please help me.

You probably want to use the #ParamConverter annotation in your case.
Your controller would go as follow:
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
use Admin\Bundle\Entity\Family;
/**
* #Route("/show/{id}")
* #ParamConverter("family", class="AdminBundle:Family")
*/
public function showAction(Family $brother)
{
//Do your stuff
}
And the view:
{% for brother in brothers %}
{{ brother.id }} - {{ brother.label }}
<hr />
{{ render(controller('AdminBundle:Test:show', { 'brother': brother.id })) }}
<hr />
{% endfor %}
Note that if no Family object is found, a 404 Response is generated. So you don't need to check if $brother is an object or not in your controller.
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html

Thank you cheesemacfly
Indeed, it's work with the #ParamConverter
But it is wird because if I remove the OneToOne relation, it works without #ParamConverter

Related

My Symfony form errors are always global. Not linked to the specific field like supposed to

I have a Symfony form where I get errors when the fields are blanks.
I already try to set error_bubbling to false but it still not work (And is supposed to be false by default)
This is my code where I remove everything that is not necessary:
Controller:
/**
* #Route("/add", name="add")
*/
public function add(Request $request)
{
$post = new Post();
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { } else { }
return $this->render('blog/add.html.twig', array('form' => $form->createView()));
}
Entity:
/**
* #ORM\Entity(repositoryClass="App\Repository\PostRepository")
* #ORM\HasLifecycleCallbacks()
*/
class Post
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\NotBlank
*/
private $Title;
/**
* #ORM\Column(type="text")
* #Assert\NotBlank
*/
private $Content;
...
FormType:
namespace App\Form;
use App\Entity\Post;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('title')
->add('content');
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Post::class
]);
}
}
Form:
{% extends "base.html.twig" %}
{% block body %}
<h2>
Create a post
</h2>
{{ form_start(form) }}
{{ form_widget(form) }}
<input type="submit" class="btn" value="Create" />
{{ form_end(form) }}
{% endblock %}
When I look at the object after the post all the errors are linked to the form and there's no errors in the childs (The form fields).
Does anyone know what can be wrong?
In buildForm(), you need to capitalize your fields. They are case sensitive and they are capitalized in your database.

Twig extension disable auto escape

I made a twig extension to be able to call functions in the templates, but unfortunately now the rendered html content of these functions is escaped. Do you have any idea how I could disable that?
FrontendTwigExtension.php
class FrontendTwigExtension extends Twig_Extension
{
/**
* #var DataProviderService
*/
private $dataProvider;
/**
* FrontendTwigExtension constructor.
* #param DataProviderService $dataProvider
*/
public function __construct(DataProviderService $dataProvider)
{
$this->dataProvider = $dataProvider;
}
/**
* #return array
*/
public function getFunctions(): array
{
return array(
new Twig_Function('getProductDetailData',
[$this, 'getProductDetailData'],
['needs_environment' => true]
),
new Twig_Function('getPageHeader',
[$this, 'getPageHeader'],
['needs_environment' => true]
)
);
}
/**
* #param Twig_Environment $env
* #return string
* #throws Twig_Error_Loader
* #throws Twig_Error_Runtime
* #throws Twig_Error_Syntax
*/
public function getPageHeader(Twig_Environment $env): string
{
return $env->render('Component/PageHeader/pageHeader.html.twig');
}
/**
* #param Twig_Environment $env
* #return string
* #throws Twig_Error_Loader
* #throws Twig_Error_Runtime
* #throws Twig_Error_Syntax
*/
public function getProductDetailData(Twig_Environment $env): string
{
$service = new ProductDetailDataService($this->dataProvider);
return $env->render('Module/ProductDetailPage/productDetailData.html.twig',
[
'productData' => $service->getData()
]
);
}
}
template.html.twig
{% extends 'base.html.twig' %}
{% block pageHeader %}
{{ getPageHeader() }}
{{ getProductDetailData() }}
{% endblock %}
services.yaml
App\Extension\FrontendTwigExtension:
arguments:
- '#App\DataProvider\DataProviderService'
tags:
- { name: 'twig.extension' }
You nedd to set the is_safe option:
['needs_environment' => true, 'is_safe' => ['html']]
Try using raw function in twig.
{% extends 'base.html.twig' %}
{% block pageHeader %}
{{ getPageHeader()|raw }}
{{ getProductDetailData()|raw }}
{% endblock %}
source: https://twig.symfony.com/doc/2.x/filters/raw.html

Symfony / Twig - Recursion decrease db queries

I have my category tree in twig template:
{% for category in categories %}
<li>
<div class="li">{{ category.name|trans({}, 'categories')|raw }}</div>
{% if category.children is not empty %}
<ul>
{% include "default/_menu_links.html.twig" with {'categories':category.children} only %}
</ul>
{% endif %}
</li>
{% endfor %}
It creates +- 53 database queries if I have 6 categories and 7 subcategories in each single category.
Is there a way to decrease that number?
I'm using useResultCache(true) in doctrine, but looks like it's not loading from the cache(at least not in dev mode).
How are you handling category trees?
UPDATE:
Entity:
...
/**
* One Category has Many Subcategories.
* #ORM\OneToMany(targetEntity="Category", mappedBy="parent", cascade={"persist"}))
*/
private $children;
/**
* Many Subcategories have One Category.
* #ORM\ManyToOne(targetEntity="Category", inversedBy="children", cascade={"persist"})
* #ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent;
...
/**
* Add child
*
* #param \App\Entity\Product\Category $child
*
* #return Category
*/
public function addChild(\App\Entity\Product\Category $child): Category
{
$this->children[] = $child;
return $this;
}
/**
* Remove child
*
* #param \App\Entity\Product\Category $child
*/
public function removeChild(\App\Entity\Product\Category $child)
{
$this->children->removeElement($child);
}
/**
* Get children
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getChildren(): Collection
{
return $this->children;
}
/**
* Set parent
*
* #param \App\Entity\Product\Category $parent
*
* #return Category
*/
public function setParent(\App\Entity\Product\Category $parent = null): Category
{
$this->parent = $parent;
return $this;
}
/**
* Get parent
*
* #return \App\Entity\Product\Category
*/
public function getParent()
{
return $this->parent;
}
...
According to my comment: you have to JOINyour subcategories. I'm assuming you are currently doing something like this to retrieve your categories:
public function getCategories()
{
return $this->getEntityManager()->createQueryBuilder()
->select("category")
->from("App:Category", "category")
->where("category.parent IS NULL")
->getQuery()->getResult();
}
So if you are now iterating over this array of categories and trying to access the children property, a sub query will be fired for each children which causes this high amount of database queries.
Instead you should JOIN them like this:
public function getCategories()
{
return $this->getEntityManager()->createQueryBuilder()
->select("category", "subcat")
->from("App:Category", "category")
->leftJoin("category.children", "subcat")
->where("category.parent IS NULL")
->getQuery()->getResult();
}
If you now iterate over your categories and access the children property, no extra query will be fired!
Using the query above and this snippet in twig will result only in one database query:
{% for category in categories %}
<p>cat={{ category.id }}</p>
{% for subcat in category.children %}
<p>subcat={{ subcat.id }}</p>
{% endfor %}
<hr>
{% endfor %}

symfony2 get property values from form

i have a symfony entity called Config
class Config
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="key_name", type="string", length=255)
*/
private $keyName;
/**
* #var string
*
* #ORM\Column(name="key_value", type="text", nullable=true)
*/
private $keyValue;
/**
* #var string
*
* #ORM\Column(name="key_type", type="string", length=255)
*/
private $keyType;
/**
* #var string
*
* #ORM\Column(name="key_tab", type="string", length=255)
*/
private $keyTab;
controller :
class ConfigController extends Controller
{
/**
* Lists all Config entities.
*
*/
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('MyBundle:Config')->findAll();
$configCollection = array('configs'=>$entities);
$collection = $this->createForm(new ConfigsType, $configCollection);
return $this->render('MyBundle:Config:index.html.twig', array(
'edit_form' => $collection->createView(),
));
}
view :
{% macro config_row(elem) %}
<div class="form-group">
{{ form_label(elem.keyValue, 'fff', { 'label_attr': { 'class': 'col-md-3 control-label' }}) }}
{# elem.keyName|humanize #}
<div class="col-md-4">
{{ form_widget(elem.keyValue, {'attr': { 'class': 'form-control input-large' }}) }}
{{ form_errors(elem.keyValue) }}
</div>
</div>
{% endmacro %}
<form action="{{ path('my_config') }}" method="post" {{ form_enctype(edit_form) }} >
{% for conf in edit_form.configs %}
{{ _self.config_row(conf) }}
{% endfor %}
</div>
{{ form_rest(edit_form) }}
</form>
what i need is for each config row i can get properties values in the config_row template to customize html rows structure based on their values
any idea please ?
thank you.
If I understand you correct you want to retrieve the value of each property of instance of entity Config, right?
If so, properties should be accessible by doing this:
{{ conf.vars.data.id }}
{{ conf.vars.data.keyName }}
{{ conf.vars.data.keyValue }}

Symfony 3: An exception has been thrown during the rendering

The full error I am getting is this one.
An exception has been thrown during the rendering of a template ("Some mandatory parameters are missing ("id") to generate a URL for route "FooBlogBundle_articles".") in "FooBlogBundle:Article:articles.html.twig".
This is the controller, that handles the action:
public function articlesAction($id)
{
$em = $this->getDoctrine()->getManager();
$blog = $em->getRepository('FooBlogBundle:Blog')->find($id);
if(!$em){
throw $this->createNotFoundException('Unable to find blog posti');
}
return $this->render('FooBlogBundle:Article:articles.html.twig', ['blog'=>$blog]);
}
}
and the routing
FlickBlogBundle_articles:
pattern: /foo/{id}
defaults: { _controller: FooBlogBundle:Article:articles }
requirements:
_method: GET
id: \d+
Twig and database, are completely normal, no type or problems. But this error is kind of hard to spot, where I have gone wrong.
EDIT: Including template:
{% extends 'FooBlogBundle::layout.html.twig' %}
{% block body %}
{{ blog.title }}<br/>
{{ blog.author }}<br/>
{{ blog.blog }}<br/>
{{ blog.tags }}<br/>
{{ blog.comments }}
{% endblock %}
The above template is located in views/article/ it extends another templates found at views/
whic is just
{% extends 'FooBlogBundle::layout.html.twig' %}
In FooBlogBundle:Article:articles.html.twig template you have something like {{ path(FlickBlogBundle_articles) }} (can not tell exactly because I do not see a template). This route requires additional parameter id. So change it to {{ path(FlickBlogBundle_articles, {'id':article.id}) }}
I got the same error and in my case I forgot to set public Getters & Setters for my private foreign Key:
/**
* #var Provider
*
* #ORM\ManyToOne(targetEntity="Provider")
* #ORM\JoinColumn(name="fk_provider", referencedColumnName="id", nullable=true)
*/
private $fkProvider;
And
/**
* #return Provider
*/
public function getFkProvider()
{
return $this->fkProvider;
}
/**
* #param Provider $fkProvider
*/
public function setFkProvider($fkProvider)
{
$this->fkProvider = $fkProvider;
}

Resources