Sonata Admin 4: Add an additional display to edit form - symfony

It appears to be possible to simply put a string / template to the list view, but is the same possible to the edit view of an entity in Sontata Admin 4?
I found https://docs.sonata-project.org/projects/SonataAdminBundle/en/4.x/reference/templates/#configuring-templates, but it does not give access to the form itself. This is the include I found in base_edit.html.twig:
{% block form %}
{{ block('parentForm') }}
{% endblock %}
I would like to achieve this thou:
How would this be possible?

Ok, it looks like the help attribute of an input can be filled with markup:
src/Admin/PageAdmin.php:
class PageAdmin extends AbstractAdmin {
// ...
protected function configureFormFields(FormMapper $formMapper) : void
{
/** #var Page $page */
$page = $this->getSubject();
$adminHint = '';
if ($page) {
$adminHint = implode(', ',array_map(function (User $admin) {
return "<strong><a href='/admin/sso/user/{$admin->getId()}/show' target='_blank'>{$admin->getUsername()}</a></strong>";
}, $page->getExplicitAdmins()->toArray()));
}
$formMapper
->add('title', TextType::class)
// ....
->add('admins', ModelAutocompleteType::class, [
'multiple' => true,
'required' => false,
'property' => ['username', 'id'],
'btn_add' => false,
'help' => $adminHint ? "$adminHint have already explicit edit rights (might be inherited from parents). Admins entered here will also get access to all subpages." : "Admins entered here will also get access to all subpages.", // <-- some dynamic content
'help_html' => true, // <-- to enable html rendering
])
}
See also https://symfony.com/bundles/SonataAdminBundle/3.x/cookbook/recipe_image_previews.html.
What bugs me a bit is, that this is rather messy. Rendering a template here would make it much cleaner.

Related

How can I set a value in Twig when the select is built using EntityType?

In a Form say I have a builder option like this:
->add('choice', ChoiceType::class, [
'choices' => [
'Cheese' => 'cheese',
'Plain' => 'plain
]
])
And let's say we are editing this option, in the database they've already selected plain. With twig we can write the widget like this:
{{ form_widget(BurgerForm.choice, {
'value': burger.type
}) }}
This will make the value in the database the pre-selected value for the select. But if you do the same thing with EntityType:
->add('choice', EntityType::class, [
'class' => 'AppBundle:BurgersTypes',
'choice_label' => 'type'
])
And you use the same twig it doesn't pre-select the option from the database. How can I get the value from the database to show as the pre-selected value of the widget?
Pre-selecting a value for this form means setting a value on the underlying data. In your case, the controller ought to look something like:
// src/AppBundle/Controller/MyController.php
namespace AppBundle\Controller\MyController;
use AppBundle\Entity\Order;
use AppBundle\Entity\BurgersTypes;
use AppBundle\Form\Type\FormType;
use Symfony\Component\HttpFoundation\Request;
public function formAction(Request $request)
{
$obj = new Order();
$defaultBurgerChoice = new BurgersTypes('cheese');
$ob->setChoice($defaultBurgerChoice);
$form = $this->create(FormType::class, $obj);
$form->handleRequest($request);
...
// Now, if the form needs to render a value for `choice`,
// it will have the value of BurgerForm.choice determined
// intentionally - by your default, or overridden and
// handled in the request!
return [
'BurgerForm' => $form->createView()
]
}

Use the category node name as a label in Zikula

I do use Zikula 1.5.2dev
My module is generated with modulestudio
I have made two entries in the Category registry. One is showing at the node "Global" and one at the node "Type"
In Global are several entries I can select. Some other entries are inside Type.
The selection is working in my template like expected. But how can I use the node names as a label?
I can not figure out in which template I have to place to label (have to do more searching). But more important, I do not know the right twig syntax to catch the categories label.
if you assign a category to the template, the properties are accessible like normal class properties.
{{ category.name }}
if you need the display name, this is stored as an array with lang codes as keys
{{ category.display_name['de'] }}
Hope that helps.
That sounds good. But now I have recognized this label seem not to be placed in a pure template. There is a form type defined:
class ShowRoomItemType extends AbstractShowRoomItemType
{
/**
* #inheritDoc
*/
public function addCategoriesField(FormBuilderInterface $builder, array $options)
{
$builder->add('categories', CategoriesType::class, [
'label' => $this->__('Category') . ':',
'empty_data' => null,
'attr' => [
'class' => 'category-selector'
],
'required' => false,
'multiple' => false,
'module' => 'RKShowRoomModule',
'entity' => 'ShowRoomItemEntity',
'entityCategoryClass' => 'RK\ShowRoomModule\Entity\ShowRoomItemCategoryEntity',
// added:
'includeGrandChildren' => true
]);
}
}
In my template it is called like this:
{{ form_row(quickNavForm.categories) }}
For this my skills are very limmited. I will write a feature request at modulestudio. (https://github.com/Guite/MostGenerator/issues/1147)
But big thanks for your reply!
This has been fixed for core 1.5.4 / 2.0.4 in https://github.com/zikula/core/pull/3846

How can I access to the twig app.request.get('_route_params') in a twig rendered for a forwarded controller?

I'm trying to use the "forward" method from a main Controller. first I'm assuring me that the original "request" is sent from the first controller to the "forwarded controller" (If I do a var_dump in both controllers I get the same request object.). But when I try access to the "request object" from the twig rendered from forwarded controller, the app.request.get(_route), app.request.get(_route_params) and others are differents. It is as if the original request is lost only from twig globals.
namespace MyBundle\Controller;
//...
class BookingController extends Controller {
/**
* #Route("/book/hotel/{slug}", name="booking_hotel")
*/
public function establishmentAction(Request $request, $slug = null) {
\dump($request); // dumper 1
return $this->forward('ExpediaBundle:Booking:index', array('request' => $request);
}
}
namespace ExpediaBundle\Controller;
//...
class BookingController extends Controller {
/**
* #Route("/expedia/booking", name="expedia_booking")
* #Template()
*/
public function indexAction(Request $request)
{
\dump($request); // dumper 2
// The dumper 1 === dumper 2
return array();
}
}
{# file: ExpediaBundle:Booking:index.html.twig #}
{{ dump(app.request.attributes.get('_route_params')) }} -> works fine
{{ dump(app.request.get('_route_params')) }} -> works fine
When I enter to the route http://localhost/expedia/booking (without the forward) I can see the _route_params dumps correctly. But if I use the forward controller the varDumper returns null for both cases.
http://localhost/book/hotel/hotelname:
{{ dump(app.request.attributes.get('_route_params')) }} -> null
{{ dump(app.request.get('_route_params')) }} -> null
What am I doing wrong?
Thank you in advanced.
Finally, I was solve my problem.
Maybe its a symfony bug as it is commented by rivaros in the symfony's issues https://github.com/symfony/symfony/issues/5804#issuecomment-17331590
The practical solution is posted here:
https://github.com/symfony/symfony/issues/5804#issuecomment-17379777
return $this->forward('Yourbundle:Controller:action',
array(
//... your parameters...,
'_route' => $this->getRequest()->attributes->get('_route'),
'_route_params' => $this->getRequest()->attributes->get('_route_params');
));
Thank you!
The above solution worked well for me since I wanted to access the URL route parameters from my custom template but app.request.attributes.get('_route_params') was not working.
I did a minor change to implement it on my_module file (Drupal-8):
function my_module_theme(){
$themes['my_module_template_name'] = [
'template' => 'my-module-template-name',
'variables' => [
// define variable(s) you want available on your template
'session_request' => \Drupal::request(),
];
return $themes;
}
Then you can access/dump the whole request data on your theme template file by:
{{ dump(session_request) }}
You can fine filter out the data you pass to your custom template by narrowing down to a particular data element on the \Drupal::request() from within the function my_module_theme(){} definition.
Another option is to pass the variables data that you need from a controller method like so:
/**
* Method to call your custom template
* You can call this method from your routes
*/
public function MyMethod() {
$query_params = \Drupal::request()->query->get('name');
$route_params = \Drupal::request()->attributes->get('_route_params')['name_of_parameter'];
return array(
'#theme' => 'my_module_template_name',
'#query_params' => $query_params,
'#route_params' => $route_params,
);
}
Then on your .module file you register your template file:
function my_module_theme(){
$themes['my_module_template_name'] = [
'template' => 'my-module-template-name',
'variables' => [ // define the variable(s)
'query_params' => '',
'route_params' => '',
];
return $themes;
}
Finally, you can access these variables on your template file by: {{ query_params }} or {{ dump(route_params) }}
Thank You!

Symfony 2.8 - how to create "terms & conditions" check-box which isn't mapped to the underlying model

I'm using Symfony 2.8 and I'm trying to create a registration form containing a "terms & conditions" check-box which isn't mapped to the underlying data model.
I've followed this cookbook article:
How to Implement a Simple Registration Form
Everything in the form validation works, except for the the "terms & conditions" check-box. After submitting the form, the check-box validation doesn't get triggered.
This is my code:
namespace Likestripe\AdminBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use FOS\UserBundle\Form\Type\RegistrationFormType as BaseType;
use Symfony\Component\Validator\Constraints\IsFalse;
use Symfony\Component\Validator\Constraints\IsTrue;
class RegistrationFormType extends AbstractType { //BaseType
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options) {
// call parent constructor
//parent::buildForm($builder, $options);
// add your custom fields
$builder->add('salutation', ChoiceType::class, array('choices' => array('Herr' => 0, 'Frau' => 1), 'choices_as_values' => true));
$builder->add('firstname', TextType::class, array());
$builder->add('lastname', TextType::class, array());
$builder->remove('username');
$builder->add('company', new CompanyFormType());
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
$builder->add('submit', SubmitType::class, array('label' => 'Registrieren'));
} // end function
public function getParent() {
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
// Or for Symfony < 2.8
// return 'fos_user_registration';
}
/**
* function deprecated since 2.8 https://github.com/symfony/symfony/blob/2.8/UPGRADE-2.8.md#form
*
* #return string
*/
public function getName() {
//return 'likestripe_user_registration';
return $this->getBlockPrefix();
} // end function
public function getBlockPrefix() {
return 'likestripe_user_registration';
}
} // end class
I can't see any difference between my code and the code demonstrated in the cookbook article.
Screen capture of my Symfony debug console:
UPDATE:
As Kamil proposed, I've checked if the 'conditions' check-box form parameter gets posted after submitting the form.
The param gets posted als long as the check-box is checked, but if not, the "conditions" form parameter doesn't get posted at all... this behavior reminds me of this case.
I'm still wondering why the official Symfony documentation proposes a isTrue Validator which doesn't seem to be the solution to check for an unchecked check-box, any suggestions how to fix this?
Checkbox checked:
Checkbox unchecked:
Thanks in advance for your help
ninsky
If you let the 'required' of your CheckboxType to true, the constraints isTrue is not useful because the checkbox will always be true !
If change change that to :
$builder->add('conditions', CheckboxType::class, array('mapped' => false, 'required' => false, 'constraints' => new IsTrue(array("message" => "Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen."))));
With this configuration you can submit the form and if the box is not checked the constraints will send your message.
Hope this help ...
As a workaround, you can add a form listener to make sure you submit the value. Add following to the bottom of your buildForm() function:
$builder->addEventListener(FormEvents::PRE_BIND, function (FormEvent $event) {
$data = $event->getData();
if (!isset($data['conditions'])) {
$data['conditions'] = false;
}
$event->setData($data)
});
As another workaround, you can perform a check in controller and show flash message if request headers do not contain 'conditions'.
Symfony docs mention it:
$builder
->add('email', EmailType::class);
// ...
->add('termsAccepted', CheckboxType::class, array(
'mapped' => false,
'constraints' => new IsTrue(),
))
);
Well, I don't see why is not working for you so in the meantime I give you an alternative way :)
$builder->add('conditions', CheckboxType::class , array('mapped' => false, 'required' => true, 'attr'=>array('onchange'=>"try{setCustomValidity('')}catch(e){}",'oninvalid'=>"setCustomValidity('Bitte akzeptieren Sie noch die AGB und Nutzungsbedingungen.')")));
Hope this work for you.
I've run your code with everything but the CompanyFormType line and it is working as expected.
Could you remove this CompanyFormType line and give it a try ?
You have to set the validation group Overriding Default FOSUserBundle Forms
By default, the Registration validation group is used when validating
a new user registration. Unless you have overridden this value in the
configuration, make sure you add the validation group named
Registration to your name property.
How to do that you can find out here: Overriding Default FOSUserBundle Validation

Render form without value in Symfony2

I'm building a form which have default value in some fields:
$builder->add('field', 'text', array('data' => 'Default value');
Then render this form field in Twig like:
{{ form_widget(form.field) }}
It's worked OK, but I don't want 'Default value' set in input tag rendered in HTML, because I only want this default value set internal that End-user doesn't aware about this value. Is there any built-in method in Symfony2 to handle it or I have to make some custom code?
You could modify your entity in order to do this:
class MyEntity{
const DEFAULT_FOO = "Default value";
// ...
private $foo;
// ...
public function setFoo($foo){
if ( $foo === null ){
$foo = self::DEFAULT_FOO;
}
$this->foo = $foo;
}
// ...
}
And then make sure that you set by_reference in order to ensure setter is being invoked each time:
$builder->add('field', 'text', array(
'by_reference' => true
));
As far i understand
try to add required false and handle it in your controller action.
$builder->add('field', 'text', array('required' => false));

Resources