Symfony 2 - Twig templates - Entity function results - symfony

I am looking for best solution how to send value returned by one of entity function's in symfony2 to twig template.
The problem is connecting with getting file url for file uploaded according to "How to Handle File Uploads with Doctrine" manual (http://symfony.com/doc/current/cookbook/doctrine/file_uploads.html). I was following the last example ("Using the id as the Filename").
In controller I am getting one of documents entity.
$document = $this->getDoctrine()->getRepository('AppBundle:Documents')->find($id);
and I provide entity details to twig template:
return $this->render('AppBundle:Documents:details.html.twig', array('document' => $document));
However in the template I need to get link to the file which is generated by getAbsolutePath() function.
public function getAbsolutePath()
{
return null === $this->link
? null
: $this->getUploadRootDir().'/'.$this->id.'.'.$this->link;
}
I may use in controller the following code:
return $this->render('AppBundle:Documents:details.html.twig', array('document' => $document, 'link' => $document->getAbsolutePath()));
but this solution does not seems to tidy for me, as I am already sending $document to twig. What would be your practical solution?

Its simple. In a Twig template you can simply do:
{{ document.getAbsolutePath() }}

Related

error when rendering Symfony 4 edit form that includes a File type

I'm following the steps outlined in this documentation https://symfony.com/doc/current/controller/upload_file.html to allow a file to be uploaded. It is working perfectly for adding a new item, but when I try to edit my entity, I'm getting the following error:
The form's view data is expected to be an instance of class Symfony\Component\HttpFoundation\File\File, but is a(n) string. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms a(n) string to an instance of Symfony\Component\HttpFoundation\File\File.
I've tried code like what is suggested in that article to append the path of the folder as File type to the entity like this in my update method:
public function editAction(Request $request, Advertiser $advertiser)
{
$advertiser->setLogo(
new File($this->getParameter('logo_directory') .'/' . $advertiser->getLogo())
);
$editForm = $this->createForm(AdvertiserType::class, $advertiser);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$this->getDoctrine()->getManager()->flush();
return $this->redirectToRoute('advertiser_list');
}
return $this->render('advertiser/index.html.twig', [
'form' => $editForm->createView()
]);
}
The logo_directory parameter is properly defined (and working fine for creating new entities).
Please let me know if you have any ideas what I am doing wrong.
Thanks for the help.
UPDATE: In this article The form's view data is expected to be an instance of class ... but is a(n) string there is a proposed solution to include in the form builder code the following:
->add('file', FileType::class, array('data_class' => null))
So I'm doing this now and the edit form will show - but it doesn't prepoulate with the previous selection.
->add('logo', FileType::class, array('data_class' => null), ['label' => 'Logo (JPG or PNG file)'])
Any thoughts on how this can be changed to allow the form to show with the previous selection pre-populated?
Setting a null data_class will remove the warning but it will not work, you don't need it at this point.
This is due to the fact that once your file is persisted, what remains in your database is just a path, not the file itself (which is on disk);
If you want to edit this entity again, the path (a string) must be converted to a File entity again; That's what the error message says.
.. and this is what you did when you wrote :
$advertiser->setLogo(
new File($this->getParameter('logo_directory') .'/' . $advertiser->getLogo())
);
Now, the problem that remains is that you want to prepopulate the file field. In fact, that is not possible, since the file field points to a location in your own computer, not to a file on your server (and you cannot automatically upload something from someone's computer like that, that would be very dangerous).
What you want to do is possibly indicate that a file is already stored, get its path and maybe display it to your user.
So in your Twig template, something like that (change with your real logo directory) :
{% if form.logo.vars.data %}
<img src="{{ asset('/uploads/logos_directory/' ~ form.logo.vars.data.filename) }}"/>
{% endif %}
Hope it's clear.

How to retrieve translation of a "sub entity" using Symfony a2lix knp doctrine behaviors translatable

I'm new in symfo but I need to translate content of my site.
I'm using a2lix (last version) and KNP doctrine behaviors (Translatable).
Let's say that I have 2 entities (e.g. Articles and Categories).
As in the doc (https://github.com/KnpLabs/DoctrineBehaviors) for translations, I'm using 2 classes for Categories (Category and CategoryTranslation).
To retrieve the translations, of my category, I'm using a query with the locale. I get the locale with Request $request ($locale = $request->getLocale();). Here is an example of my controller and the query in my repository.
Controller
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$locale = $request->getLocale();
$entities = $em->getRepository('AcmeBundle:Category')->findAllByLocale($locale);
return $this->render('CTCArtworkBundle:Backend/Artwork:index.html.twig', array(
'entities' => $entities,
));
}
Repository
I'm trying to retrieve informations for the locale.
public function findAllByLocale($locale){
return $this->createQueryBuilder('a')
->join('a.translations', 'aTrans')
->where('aTrans.locale = :locale')
->setParameter("locale", $locale)
->addSelect('aTrans')
->getQuery()
->getResult()
;
}
I don't know if it's a good practice but it works for me. I retrieve fr/en categories in my Twig template like so when I change the url :
<tr>
<th>Category</th>
<td>{{ category.translations|First.name }}</td>
</tr>
My problem
For the translation of my article, I do the same. I have 3 properties
- title
- description
- category (I'm using a2lix_translatedEntity (http://a2lix.fr/bundles/translation-form/#bundle-additional))
When I try to render a record of Article, I never retrieve the translation for my category Name but well for title and description.
I also read that (https://github.com/KnpLabs/DoctrineBehaviors#guess-the-current-locale) but I don't really understand. Is that a way to always pass locale ?
What am I doing wrong ?
I'm blocked and don't find any documentation to resolve my problem. Sorry for my english ;-)
Any help would be very appreciate. Many Thanks
KNP has its own way to guess the current locale, simply by accessing current request scope. The whole "passing locale" thing is useful if you want to pull records for specific locale.
Now, for your category translation. Since you did not include your entities, I will try to show you some examples to access your translations.
In your Category entity, lets say you have a property name that would return your category name. Then you can define a simple helper method that would return that name, by current locale:
public function getName() {
if( $name == $this->translate()->getName() ) {
return $name;
}
return '';
}
So, what have we done here?
$this->translate()->getName() - this line looks for your translation entity (in this case that would be CategoryTranslation) and invokes method getName() . Then, we either return translated category name, or an empty string if no translation has been added.
And lastly, this is how you can access your category name in your twig template:
Since we defined our helper method, there is no longer any need to access .translations in your template. You can simply call:
{{ category.name }}
Hope you got the idea.
And you can also use this
{{ category.translate.name }}
With DoctrineBehaviors v2, you can add this to your Category class:
public function __call($name, $arguments)
{
return $this->proxyCurrentLocaleTranslation($name, $arguments);
}
Here's what it does. So, in your Category entity, lets say you have a property description that would hold your category description. The code above will generate a corresponding property getter: getDescription(). Which ultimately will allow you to use this property in your Twig template:
{{ category.description }}

Where is code for `form_widget()` Twig function?

I went looking to see how Symfony's extended Twig function form_widget actually works. I was expecting to find the function in symfony / src / Symfony / Bridge / Twig / Extension / FormExtension.php . It's added to the function list there:
public function getFunctions()
{
return array(
...
new \Twig_SimpleFunction('form_widget', null, array('node_class' => 'Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode', 'is_safe' => array('html'))),
...
);
}
But no callable is listed (ie the 2nd arg is null). So what code is called when I use form_widget(a_form_element) in a Twig template?
My answer is a little late but I just happened to investigate the same question. This is what I found:
The code which is called when you use form_widget() is that produced directly by the SearchAndRenderBlockNode class when your template is compiled. The compiled template code then results in a call to Symfony\Component\Form\FormRenderer::searchAndRenderBlock() where the proper Twig block is located, provided with context, and rendered.
I don't recognize the name space there. Or that code.
Mine is in Symfony\Bridge\Twig\Extension\FormExtension.php
It uses Symfony\Bridge\Twig\Node\SearchAndRenderBlockNode
You can find it in FrameworkBundle/Resources/views/Form/form_widget.html.php or it equivalent in template Symfony/Bridge/Twig/Resources/views/Form/form_div_layout.html.twig.
This is now depreciated but here is available code directly using methods to render form when you call eg. form_row() twig function Here it is: https://github.com/symfony/symfony/blob/4.4/src/Symfony/Bundle/FrameworkBundle/Templating/Helper/FormHelper.php

Return a value from entity to view file in symfony

I want to return a value from entity to view file. Below is my entity function
public function getVisitorName($id)
{
$repository = $this->getDoctrine()->getRepository('SystemVmsBundle:VisitorsDetails');
$product = $repository->findOneBy(array('id' =>$id));
$name=$product->getFirstname();
return $name;
}
This is the line in my view file which calls that function
{{ entity.visitorName(entity.visitorId) }}
Its not giving me any error. But only a blank page. How can i fix this?
This is my controller code
public function indexAction()
{
$em = $this->getDoctrine()->getManager();
$entities = $em->getRepository('SystemVmsBundle:EntryDetails')->findAll();
return array(
'entities' => $entities,
);
}
I am trying to fetch the visitors name(from visitors table) corresponding to the visitor id(in entry table).How will i do it then?
you have two ways of doing it:
1) Map your SystemVmsBundle:EntryDetails entity, to SystemVmsBundle:VisitorsDetails as OntToOne by adding field details to your EntryDetails; , and then in twig template just call it via
{{ entity.details.name }}
2) instead of creating getVisitorName(), it is better to create twig function for this, with same functionality.
Your indexAction() is not returning a response object, it is just returning an array of entities. Controller actions should return a Response containing the html to be displayed (unless they are for e.g. ajax calls from javascript). If you are using twig templates you can use the controller render() method to create your response, something like this:
return $this->render('<YourBundle>:<YourViewsFolder>:<YourView>.html.twig', array(
'entities' => $entities,
));
When you've corrected that I suspect you'll get an error because $this->getDoctrine() won't work from an entity class. The code you have in the getVisitorName() method just shouldn't be in an entity class.
As #pomaxa has already suggested, I believe there should be a relationship between your EntryDetails and VisitorsDetails entities although I don't know enough about your data from the question to know what type of relationship it should be (OneToOne / ManyToOne). If your EntryDetails entity had a relationship to VisitorsDetails, the EntryDetails class would then contain a $visitorsDetails attribute and methods to get/set it. Then the line in your twig file would look like this:
{{ entity.visitorsDetails.firstName }}
There is a section on Entity Relationships / Associations in the Symfony Manual.
Also, I hope you don't mind me giving you a little advice:
Be careful when you copy and paste code as it appears you have done in getVisitorName(). You have kept the variable name '$product' although there are no products in your system. This sort of thing can cause bugs and make the code more difficult to maintain.
I recommend you avoid tacking 'Details' onto the end of entity names unless you genuinely have two separate and related entities such as Visitor + VisitorDetails and a good reason for doing so. I think the entities in your example are actually 'Visitor' and 'VistorEntry'.
Unless you are writing a re-usable component, I recommend you use specific variable names like '$visitorEntries' rather than '$entities' in your controller and twig.
In general, the more meaningful your variable names, the more readable, maintainable and bug-free your code is likely to be. Subsequently, it will also be much easier for people on SO to understand your code and give you help when you need it.

How do you define the getter to use in a CRUD form besides defining __toString()?

If you've used Symfony2's generators to create CRUD forms from database entities, you may wind with an error like this on the "create new record" screen:
StringCastException: A "__toString()" method was not found on the objects of type
"ScrumBoard\ServiceBundle\Entity\Users" passed to the choice field. To read a
custom getter instead, set the option "property" to the desired property path.
If I'm reading this correctly, the problem is that it needs to display a dropdown list of users for the record I'm creating, but it doesn't know how to turn a "User" entity into a string.
Defining the __toString() method on my Users entity class fixes the problem. However, I can see right from the text of the error message that there is an alternative: read a customer getter instead, which is accomplished by "[setting] the option "property" to the desired property path."
This sounds like some kind of annotation. But in my searching, I can't figure out what that is. Because I want to have a thorough understanding of Symfony2--can someone help me out?
Thanks!
When creating an entity ( superclass of choice ) field type in a form. You need to specify which property shall be used for the labels/values otherwise the __toString() method of the underlying object will be used.
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'property' => 'username',
));
Read more about it in the Form Type Reference for entity field Type.
Additional Info
A __toString() related error generally often comes from twig when generating a route in a template aswell.
if outputting an object an object in twig with {{ object }} ... twig will call the object'S __toString method.
This "trick" is used by the crud generated templates with SensioGeneratorBundle.
{{ path('article_show', {'id': article}) }}
with the route being something like this:
article_show:
pattern: /article/{id}
defaults: { _controller: AcmeArticleBundle:Article:show }
If you have the __toString method in your Article entity set to something like ...
public function __toString()
{
return $this->id;
}
... you dont't need to type
{{ path('article_show', {'id': article.id) }}
Generally Twig will automatically output Article::getId() if you use
{{ article.id }}
Hope this clarifies your findings.

Resources