Time picker (no date) in a Sonata-admin form - symfony

I'm upgrading a Symfony project's Sonata Admin usage, from Sonata-Admin 2.2 to 2.3, as part of upgrading the overall project to Symfony 2.7.
We have a number of 'time' fields (that is, defined in Doctrine as "time", with no meaningful date component.)
In Sonata-Admin 2.2 the FormMapper definition was simple:
$formMapper
->tab('tab.general')
->add('start', null, array('label' => 'label.start')
->end()
and that gave the layout of "half-form-width" Hour and Minute selection boxes, side by side, in the form:
But Sonata-Admin 2.3 is showing them over two full lines:
which is not so nice or useable.
So, what should I be setting to get the same rendering?
I've tried using the 'sonata_type_datetime_picker' but that is really insistent on displaying a date. These fields do not have a date. There seems to be no equivalent for just picking the time.

Had the same issue and solved it by adding my own CSS file for overrides.
1) Add class to your form field
->add('start', 'time', array(
'attr' => array('class' => 'fixed-time')
))
2) Create a new CSS file in your bundle under Resources/public/css/override.css with overrides by fixed-time class
For example:
.fixed-time .select2-container {
width: auto !important;
}
3) Then somewhere in your bundle (Resources/views) create a new template called standard_layout.html.twig and override Sonata template + add newly created CSS file (remember to call parent() first, to load sonata code first).
{% extends 'SonataAdminBundle::standard_layout.html.twig' %}
{% block stylesheets %}
{{ parent() }}
<link rel="stylesheet" href="{{asset('bundles/yourbundle/css/override.css')}}" type="text/css" media="all"/>
{% endblock %}
4) Then register your standard_layout twig template into Sonata admin (in your config.yml).
sonata_admin:
templates:
layout: YourBundle::standard_layout.html.twig
Do not forget to install new assets (assets:install) and clear cache (remove selected folder from app/cache), otherwise you will not see any change.

Related

Api pagination in symfony 3

I am new to symfony I need to create api and paginate the result
I used to do that easily in Laravel
Is there anything that would help me do the same with symfony 3.6?
symfony doesnt ship that out of the box.
there is pagerfanta and KnpPaginatorBundle. I read a bunch of subjective opinions that pagerfanta is better here https://github.com/symfony/demo/issues/305
depending on the size/amount of the APIs you need to create, you should maybe look into ApiPlatform. There is as well good videos about ApiPlatform on symfonycasts for 25$ for one month (am not affiliated)
composer require babdev/pagerfanta-bundle
composer require pagerfanta/doctrine-orm-adapter
composer require pagerfanta/twig
in controller
$pagerfanta = new Pagerfanta(new QueryAdapter($queryBuilder));
$pagerfanta->setMaxPerPage(5);
$pagerfanta->setCurrentPage($request->query->get('page', 1));
return $this->render('yourpage/index.html.twig', [
'pager' => $pagerfanta,
]);
in view
{% for yourEntity in pager %}
...
{% endfor %}
{{ pagerfanta(pager) }}
see as well
https://symfonycasts.com/screencast/doctrine-relations/pager-links#play

Service to get/set current entity

I have a Project entity, which has a controller defining many routes:
projects/1
projects/1/foo
projects/1/bar
I need a service to provide the current project. The use case is that I have dependencies in my base twig templates which need to know the current project. i.e. a dropdown project selector that is outside the context of the template the current controller is serving.
I've tried creating a service getting route info with $container->get('router.request_context');, however, that only provides a path. I don't want to have to parse the path string if I don't have to.
What is the most proper approach?
If I understood you correctly the solution for your problem is rendering/embedding controller. Of course it's simplest, yet somehow acceptable solution for rendering parts of html with some custom logic apart from current template.
You can read about rendering/embedding controllers.
Some snippets...
Define controller:action (obviously the logic in my example is pretty straight forward):
/**
* Generate select input field.
*
* #Route("/widget", name="project_widget")
* #Method("GET")
*/
public function widgetAction()
{
$repo = $this->getDoctrine()
->getManager()
->getRepository('AppBundle:Project');
// #NOTICE: Wee need master request info, because our widget
// is rendered and called as subrequest.
$masterRequest = $this->get('request_stack')->getMasterRequest();
// #NOTICE: You need second parameter as default value in case there is no id in masterRequest route.
$currentProjectId = $masterRequest->get('id', 0);
$currentProject = $repo->find($currentProjectId);
$projects = $repo->findAll();
return $this->render('project/widget.html.twig', [
'currentProject' => $currentProject,
'projects' => $projects,
]);
}
Then you need to create the template project/widget.html.twig for it:
<div class="widget_project_selection">
{% if projects is not empty %}
<select name="widget_project_selection" id="widget_project_selection">
<option value="">None</option>
{% for project in projects %}
<option value="{{ project.id }}"
{# #NOTICE: If this is current project, select it #}
{{- currentProject and project.id == currentProject.id
? 'selected="selected"'
: '' -}}
>
{{- project.title -}}
</option>
{% endfor %}
</select>
{% else %}
<span>{{ 'Sadly, no projects yet :('|trans }}</span>
{% endif %}
</div>
and at last (but not least) render it somewhere like in base.html.twig:
{{- render(controller('AppBundle:Project:widget')) -}}
I've created for you a Github repo as reference. It's a small Symfony app with similar setup. You can even run it if you like, don't forget about dependencies and database update thou. Entry point is /app_dev.php/project/
Take a look at widgetAction, widget template and example usage in base.html.twig.
EDIT: But that's not everything. You said you need a service. If for some reason rendering/embedding controller is not an option for you or you really whant to use a Service (as in Dependency Container) you can extend Twig and use the full power of services.
I've also implemented a Twig Filter as example to show you the real power of Twig Extensions in here and here (usage in templates).
Check out Twig Extension and Extending Twig for more info about Twig Extensions.
Also check out service.yml for service and extension definitions - if you are not using Symfony3.3+, there will be some additional work to do - defining service and extension directly.
In your controller, you can use type hinting to load the "current" entity via the route.
For example:
#myrouter.yml
current_project:
path: /projects/{project}
Separately, your controller...
//mycontroller.php
public function myControllerAction(Request $request, Project $project)
{
//$project is entity (assuming valid) loaded via the route above
return $this->render('mytemplate.twig', ['project' => $project]);
}

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

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

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,
);
}

How can I make a custom field type in symfony2?

I want to make a custom form field in symfony2 named daterange, which will extends the default symfony date type form field and take date range(start date and end date) into two different text-box.
Cause I don't like twig template engine this example only for PHP templating
What you need is to make:
New TestBundle\Form\Extension\Core\Type\DateRangeType which extends Symfony\Component\Form\AbstractType
Here you should:
a. write your own getParent, getName, buildForm methods
b. getParent return 'field'
c. getName return 'daterange'
d. buildForm has $builder->add('start', ...)->add('end', ...)->setAttribute('widget', 'daterange')
Add it to the DI (config.yml as example)
services:
form.type.daterange:
class: TestBundle\Form\Extension\Core\Type\DateRangeType
tags:
- { name: form.type, alias: daterange }
Create new widget for it in TestBundle/Resources/views/Form/daterange_widget.html.php
you can take date widget as example. Src/vendor/symfony/src/Symfony/Bundle/FrameworkBundle/Resources/views/Form/date_widget.html.php
Add to config (config.yml as example)
framework:
templating:
form:
resources:
- 'TestBundle:Form'
And for more widget customization as nefo_x said check form customization.
In order to do that, you need to add the following lines into app/config/config.yml
twig:
form:
resources:
- 'YourSuperBundle:Form:fields.html.twig'
then in src/Your/SuperBundle/Resources/views/Form/fields.html.twig:
{% extends 'form_div_layout.html.twig' %}
{% block daterange_widget %}
... do the customization.
{% endblock %}
For additional reference please read form customization of Symfony 2.0 book.
There is a good entry in official cookbook on creating custom field type

Resources