Show the number of rows on a connected table in Sonata Admin - symfony

I have a user table, and a list in Sonata Admin.
Also, I have a file table, where the users are connected to the files with a user_id field.
Now, this is the config for the list of the users. So far so good, it works.
// Fields to be shown on lists
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('username')
->addIdentifier('email')
->addIdentifier('firstName')
->addIdentifier('lastName')
;
}
Tried to find it in the documentation, but it's unclear to me how do I add a field to the list, where the number of connected files are indicated, or even better, if there is at least one uploaded file for the user, I have a flag about it in the list in a separate field.
Thanks for your help in advance!

You have to create a custom template where you display the information you want :
list_files.html.twig
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
<div>
{{ object.files|length }}
</div>
{% endblock %}
call it in your list method
protected function configureListFields(ListMapper $listMapper)
{
$listMapper
->addIdentifier('username')
->addIdentifier('email')
->addIdentifier('firstName')
->addIdentifier('lastName')
->add('picture', null, array(
'template' => 'ApplicationSonataAdminBundle:User:list_files.html.twig'
));
;
}
You might need to adapt the template path.
Read this for more details : Sonata admin bundle preview image from some entity in list mapper without sonata media bundle

Related

symfony parent template controller

Is there a standard/preferred way in Symfony to pass common variables to the base template?
There are things that are on every page, like a username in the menu that I obviously don't want to have to remember to add for every controller.
My thought was to create a controller for the template and return the data. But wondering if there is something more built in to handle this.
return $this->render(
'#secured/account/profile.html.twig',
array('userForm' => $form->createView(),
'base' => call_base_layout_controller()
);
{# templates/account/profile.html.twig #}
{% extends '#secured/base.html.twig' %}
{% block body %}
{% endblock %}
I cannot find it in the current version docs but, as far as I know you can still access a lot directly from twig through the app twig variable defined automatically by the framework for every request https://symfony.com/doc/3.4/templating/app_variable.html
For example, you can get the current user's username as follows:
{{ app.user.username }}
app holds user, request, session, environment, and debug so you can put other values needed into the session or environment variables, and fetch/render those values directly in the template.
If the data that you want is related to the authenticated user, I would recommend what Arleigh Hix said
Otherwise you can create a class that extends AbstractExtension and fill it with your logic
Anything on this class can be accessed from all your twig pages
// src/Twig/AppExtension.php
namespace App\Twig;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class AppExtension extends AbstractExtension
{
public function getFunctions()
{
return [
new TwigFunction('myGlobalData', [$this, 'myGlobalData']),
];
}
public function myGlobalData()
{
return "what ever you want ( ex: a query resualt..)";
}
}
Than on your twig template just call it like this
{{ myGlobalData() }}
Even if it retuns an array, u can access it.

Grouped view for datasets in sonata admin list view

I am wondering if there is a easy way to implement a group view for datasets grouped by its "root" element.
Lets say i have a "person" entity. Many persons can have a "root" person they belong to. So i would add a reference to the person entity itself.
Now i don't want to show every person in the flat list view. Instead i want to display only every root-person and with clicking this dataset a accordion opens with the subordinated person entities ... how is that possible?
It would also be fine without a accordion, it can be enough if the subordinated entities are indented a bit ...
Can somebody give me a clue which approach i should follow? I would be the if i can reuse the most of the sonata admin functionality, especially the templates ...
Thanks
Was solving something similar in past. Got that working in few steps:
Override first field in your Datagrid
With this code you say that you want use my_custom_template.html.twig to render this field (in your admin class).
protected function configureListFields(ListMapper $list)
{
$list->add('yourFirstField', null, ['template' => 'my_custom_template.html.twig'])
}
Enable filtering for yourFirstField
Prepare filtering for your parent field (in your admin class)
protected function configureDatagridFilters(DatagridMapper $filter)
{
$filter->add('parent');
}
Write custom template for first field
Then in your custom template you use to set up filter value on click on link.
{% extends 'SonataAdminBundle:CRUD:base_list_field.html.twig' %}
{% block field %}
{% if object.isRoot %}
{{ object.name }}
{% else %}
{{ object.name }}
{% endif %}
{% endblock %}
Later on you can override break breadcrumbs builder service (https://sonata-project.org/bundles/admin/master/doc/reference/breadcrumbs.html) and create nice path in breadcrumbs, so user can go navigate.

Unable to set custom data in show action field in symfony sonata admin

I have a show page and I want to add a custom value.
I have tried doing what I did in other actions which is to add an array to the
third parameter with the data key like so:
protected function configureShowFields(ShowMapper $showMapper)
{
$showMapper
->add('name')
->add('dateEnd')
->add('example', null,
array('data' => 'example value')
)
;
}
In the configureListFields action, this works. I have injected custom values with the data attribute.
But still I am not able to access key example in the show.html.twig file.
It gives me this error
Variable "example" does not exist.
What should I do to access this custom variable in the twig file ?
Try
{{ elements.elements.example.options.data }}
in your twig template
I used this solution. In the configureShowFields() method of an Admin class:
$showMapper
->with('Tab Name')
->add(
'any_name',
null,
[
'template' => 'Admin/Custom/any_name_show_template.html.twig',
'customData' => $this->someRepository->getSomeEntityBy($field),
'anotherCustomData' => $this->someService->getSomeDataBy($value),
]
)
;
In the custom template, you can access custom data by field_description.options.<customFieldName>, so for provided example data accessors would be {{ field_description.options.customData }} and {{ field_description.options.anotherCustomData }}
For the shorter field name in the Twig template, you can do like this:
{% set customData = field_description.options.customData %}
and access the custom data like {{ customData }}
Hope this helps and saves time.

edit only the first entity inside an embedded collection

I have a parent entity called Publisher and a child entity called User with a ManyToMany relation.
Inside the publisher form, I want to create/edit also the first user, which I achieve like this:
$builder
->add('title')
->add('users', 'collection', array(
'type' => new UserType(),
'allow_add' => true,
))
and in my twig template, I do
{{ form_row(edit_form.users.0.firstname) }}
{{ form_row(edit_form.users.0.lastname) }}
{{ form_row(edit_form.users.0.email) }}
This obviously only works as long as there is just one user assigned to the publisher, because otherwise symfony tries to validate the other users as well, whose data is missing.
Can someone give me a hint how to edit only the first user item in the collection from the publisher form?
You can set the field users "rendered" after having displayed the firt user:
{{ form_row(edit_form.users.0.firstname) }}
{{ form_row(edit_form.users.0.lastname) }}
{{ form_row(edit_form.users.0.email) }}
{% do edit_form.users.setRendered %}
With setRendered, Symfony2 won't try to validate the next users.
I solved the problem using Cerad's solution by introducing a getter and setter for the first user.
public function setFirstUser($user)
{
$this->users[0] = $user;
return $this;
}
public function getFirstUser()
{
return $this->users[0];
}
and in the form doing this
$builder
->add('title')
->add('firstUser', new UserType())
and calling the fields of the firstUser in the twig template The setRendered line just supresses other properties of the user object.
{{ form_row(edit_form.firstUser.firstname) }}
{{ form_row(edit_form.firstUser.lastname) }}
{{ form_row(edit_form.firstUser.email) }}
{% do edit_form.firstUser.setRendered %}
EDIT: because of Cerads feedback about the non-deterministic ordering of SQL rows I chose to create a real property "adminUser", which is a OneToOne relation to the first user attached to the entity.

Using repository class methods inside twig

In a Symfony2.1 project, how to call custom entity functions inside template? To elaborate, think the following scenario; there are two entities with Many-To-Many relation: User and Category.
Doctrine2 have generated such methods:
$user->getCategories();
$category->getUsers();
Therefore, I can use these in twig such as:
{% for category in categories %}
<h2>{{ category.name }}</h2>
{% for user in category.users %}
{{ user.name }}
{% endfor %}
{% endfor %}
But how can I get users with custom functions? For example, I want to list users with some options and sorted by date like this:
{% for user in category.verifiedUsersSortedByDate %}
I wrote custom function for this inside UserRepository.php class and tried to add it into Category.php class to make it work. However I got the following error:
An exception has been thrown during the rendering of
a template ("Warning: Missing argument 1 for
Doctrine\ORM\EntityRepository::__construct(),
It's much cleaner to retrieve your verifiedUsersSortedByDate within the controller directly and then pass it to your template.
//...
$verifiedUsersSortedByDate = $this->getYourManager()->getVerifiedUsersSortedByDate();
return $this->render(
'Xxx:xxx.xxx.html.twig',
array('verifiedUsersSortedByDate' => $verifiedUsersSortedByDate)
);
You should be very carefull not to do extra work in your entities. As quoted in the doc, "An entity is a basic class that holds the data". Keep the work in your entities as basic as possible and apply all the "logic" within the entityManagers.
If you don't want get lost in your code, it's best to follow this kind of format, in order (from Entity to Template)
1 - Entity. (Holds the data)
2 - Entity Repositories. (Retrieve data from database, queries, etc...)
3 - Entity Managers (Perform crucial operations where you can use some functions from your repositories as well as other services.....All the logic is in here! So that's how we can judge if an application id good or not)
4 - Controller(takes request, return responses by most of the time rendering a template)
5 - Template (render only!)
You need to get the users inside your controller via repository
$em = $this->getDoctrine()->getEntityManager();
$verifiedusers = $em->getRepository('MYBundle:User')->getVerifiedUsers();
return array(
'verifiedusers' => $verifiedusers,
);
}

Resources