I need to override the filter in Sonata AdminBundle to use a totally different kind of filter using images.
For now it's a html form:
/**
* #param DatagridMapper $datagridMapper
*/
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('orderIdentifier', null, ['label' => 'N°'])
->add('orderDeliveryAddress', null, ['label' => 'Client'])
->add('partner.name', null, ['label' => 'Partenaire'])
->add('postal', null, ['label' => 'Code postal'])
->add('product.code', null, ['label' => 'Produit'])
->add('volume', null, ['label' => 'Volume', 'template'])
->add('deliveryType', null, ['label' => 'Type de livraison'])
->add('createdAt', null, ['label' => 'Date'])
->add('state', null, array('show_filter' => true), 'choice', array(
'choices' => $this->getConfigurationPool()->getContainer()->get('fm.command.order_status_manager')->countAllOrderByStatus(),
))
;
}
How can I totally override this method?
I found a way to override the template:
We override the controller to add my new logic:
namespace Site\AdminBundle\Controller;
use Sonata\AdminBundle\Controller\CRUDController as Controller;
class CommandManagementController extends Controller
{
public function listAction()
{
$request = $this->getRequest();
$this->admin->checkAccess('list');
$preResponse = $this->preList($request);
if ($preResponse !== null) {
return $preResponse;
}
if ($listMode = $request->get('_list_mode')) {
$this->admin->setListMode($listMode);
}
$datagrid = $this->admin->getDatagrid();
$formView = $datagrid->getForm()->createView();
// set the theme for the current Admin Form
$this->get('twig')->getExtension('form')->renderer->setTheme($formView, $this->admin->getFilterTheme());
return $this->render('Admin/Command/list.html.twig', array(
'action' => 'list',
'form' => $formView,
'datagrid' => $datagrid,
'csrf_token' => $this->getCsrfToken('sonata.batch'),
), null, $request);
}
}
The twig template:
{% extends 'SonataAdminBundle:CRUD:base_list.html.twig' %}
{% block list_filters %}
{# Your HTML here #}
{% endblock %}
Related
I have to add filter for the group drop-down
->add('group', null, ['label' => 'postpaidpackproduct.form_label.group'])
full source code
public function configureDatagridFilters(DatagridMapper $filter)
{
$filter
->add('name')
->add('sku', null, ['label' => 'packproduct.form_label.product_id'])
->add('group', null, ['label' => 'postpaidpackproduct.form_label.group'])
;
}
I have this form field of a type ModelAutocompleteType that is supposed to show the result filtered by the "search" datagrid field of related admin:
class OperationAdmin extends AbstractAdmin
{
// ...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('business', ModelAutocompleteType::class, [
'label' => 'Business',
'property' => 'search'
]);
}
// ...
}
In this related "business" admin I have few filters defined as:
class BusinessAdmin extends AbstractAdmin
{
// ...
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('search', CallbackFilter::class, [
'label' => 'Search',
'show_filter' => true,
'advanced_filter' => false,
'callback' => function($qb, $alias, $field, $value) {
if (!$value['value']) return;
// ...
return true;
}
])
->add('state', ChoiceFilter::class, [
'label' => 'State',
'show_filter' => true,
'advanced_filter' => false,
'field_options' => ['choices' => Business::STATES],
'field_type' => 'choice'
]);
}
// ...
}
Now, if I set the default "state" datagrid field value using getFilterParameters to filter business list by state on initial page load:
public function getFilterParameters()
{
$this->datagridValues = array_merge([
'state' => ['type' => '', 'value' => 'active']
], $this->datagridValues);
return parent::getFilterParameters();
}
The related ModelAutocompleteType form field's result will also be filtered by "state" field even tho it's property is set to search.
How do I apply default filter values JUST to list view and nothing else? And why ModelAutocompleteType result depends on other datagrid fields even tho property is set to one?
In the end I left getFilterParameters method in to filter list by default, which is what I wanted:
public function getFilterParameters()
{
$this->datagridValues = array_merge([
'state' => ['type' => '', 'value' => 'active']
], $this->datagridValues);
return parent::getFilterParameters();
}
Unfortunatelly, that was also affecting ModelAutocompleteFilter and ModelAutocompleteType results, filtering them by 'active' state too, which I did not want.
To solve that I had to pass a callback property to ModelAutocompleteType field, to reset datagrid state value:
class OperationAdmin extends AbstractAdmin
{
// ...
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('business', ModelAutocompleteType::class, [
'label' => 'Business',
'property' => 'search',
'callback' => [$this, 'filterAllBusinessesCallback']
]);
}
public function filterAllBusinessesCallback(AdminInterface $admin, $property, $value)
{
$datagrid = $admin->getDatagrid();
$datagrid->setValue($property, null, $value);
$datagrid->setValue('state', null, null);
}
// ...
}
I am trying to add 2 additional form fields to the Wishlist Share form where the user input will be rendered in the email. I have been able to add the fields to the form, but I am not sure how to add the user's input in the email twig template.
Here is how I have updated the form() function:
public function form(array $form, FormStateInterface $form_state) {
$form['#tree'] = TRUE;
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
// Workaround for core bug #2897377.
$form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);
$form['to'] = [
'#type' => 'email',
'#title' => $this->t('Recipient Email'),
'#required' => TRUE,
];
// COMBAK my edit
$form['sender_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Your Name'),
'#required' => FALSE,
];
$form['sender_message'] = [
'#type' => 'textarea',
'#title' => $this->t('Your Message'),
'#required' => FALSE,
];
// COMBAK eo my edit
return $form;
}
/**
* {#inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send email'),
'#submit' => ['::submitForm'],
];
if ($this->isAjax()) {
$actions['submit']['#ajax']['callback'] = '::ajaxSubmit';
}
return $actions;
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
/** #var \Drupal\commerce_wishlist\Entity\WishlistInterface $wishlist */
$wishlist = $this->entity;
$to = $form_state->getValue('to');
// COMBAK: my added vars
$sender_name = $form_state->getValue('sender_name');
$sender_message = $form_state->getValue('sender_message');
$this->wishlistShareMail->send($wishlist, $to, $sender_name, $sender_message);
$this->messenger()->addStatus($this->t('Shared the wishlist to #recipient.', [
'#recipient' => $to,
]));
$form_state->setRedirectUrl($wishlist->toUrl('user-form'));
}
This is the function that calls the mailHandler that I have updated:
public function send(WishlistInterface $wishlist, $to, $sender_name, $sender_message) {
$owner = $wishlist->getOwner();
$subject = $this->t('Check out my #site-name wishlist', [
'#site-name' => $this->configFactory->get('system.site')->get('name'),
]);
$body = [
'#theme' => 'commerce_wishlist_share_mail',
'#wishlist_entity' => $wishlist,
// COMBAK: my added vars
'#sender_name' => $sender_name,
'#sender_message' => $sender_message,
];
$params = [
'id' => 'wishlist_share',
'from' => $owner->getEmail(),
'wishlist' => $wishlist,
];
return $this->mailHandler->sendMail($to, $subject, $body, $params);
}
And this is the preprocees function provided by the commerce wishlist module:
function template_preprocess_commerce_wishlist_share_mail(array &$variables) {
/** #var Drupal\commerce_wishlist\Entity\WishlistInterface $wishlist */
$wishlist = $variables['wishlist_entity'];
$wishlist_url = $wishlist->toUrl('canonical', ['absolute' => TRUE]);
$variables['wishlist_url'] = $wishlist_url->toString();
// COMBAK: my added vars
//$sender_name = $variables['sender_name'];
}
And finally the twig template for the email itself:
{#
/**
* #file
* Template for the wishlist share email.
*
* Available variables:
* - wishlist_entity: The wishlist entity.
* - wishlist_url: The wishlist url.
*
* #ingroup themeable
*/
#}
<p>
{% trans %}Check out my wishlist!{% endtrans %}
</p>
<p>
{% trans %}I use my wishlist for keeping track of items I am interested in.{% endtrans %} <br>
{% trans %}To see the list in the store and buy items from it, click here.{% endtrans %}
</p>
<p>
{% trans %}Thanks for having a look!{% endtrans %}
</p>
I haven't been able to figure out how to access the variables I added to the body[] array in the twig template.
Any help would be greatly appreciated.
Thanks!
I want to "Dynamically populate project drop down data based on client drop down choice data" in symfony 5. New page work properly, but when change client drop down value from edit page then ajax called and show this exception/error: "Expected argument of type "string", "null" given at property path "buildingPosition"."
Form Code:
class EnquiryType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('client', EntityType::class, [
// looks for choices from this entity
'class' => Client::class,
// uses the User.username property as the visible option string
'choice_label' => 'name',
'placeholder' => 'Select a Client...',
// 'label' => 'Contact Person Designation',
// used to render a select box, check boxes or radios
// 'multiple' => true,
// 'expanded' => true,
])
->add('buildingPosition', TextType::class, [
'attr' => ['class' => 'form-control'],
])
->add('offerSubmissionDate', DateType::class, [
'attr' => ['class' => 'form-control'],
'widget' => 'single_text',
'empty_data' => null,
])
->add('probability', ChoiceType::class, [
'choices' => [
'P1' => 'P1',
'P2' => 'P2',
'P3' => 'P3',
],
'placeholder' => 'Select a Probability...',
])
->add('notes', TextType::class, [
'attr' => ['class' => 'form-control'],
])
;
// Start Project data populate dynamically based on the value in the "client" field
$formModifier = function (FormInterface $form, Client $client = null) {
$projects = null === $client ? [] : $client->getProjects();
$form->add('project', EntityType::class, [
'class' => 'App\Entity\Project',
'placeholder' => '',
'choices' => $projects,
]);
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($formModifier) {
// this would be your entity, i.e. Enquiry
$data = $event->getData();
$formModifier($event->getForm(), $data->getClient());
}
);
$builder->get('client')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
// It's important here to fetch $event->getForm()->getData(), as
// $event->getData() will get you the client data (that is, the ID)
$client = $event->getForm()->getData();
// since we've added the listener to the child, we'll have to pass on
// the parent to the callback functions!
$formModifier($event->getForm()->getParent(), $client);
}
);
// End Project data populate dynamically based on the value in the "client" field
}
Controller Code:
public function edit(Request $request, $id): Response
{
$enquiry = new Enquiry();
$entityManager = $this->getDoctrine()->getManager();
$enquiry = $entityManager->getRepository(Enquiry::class)->find($id);
$form = $this->createForm(EnquiryType::class, $enquiry);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$this->getDoctrine()->getManager()->flush();
$this->addFlash('notice', 'Enquiry updated successfully!');
return $this->redirectToRoute('enquiry_index');
}
return $this->render('enquiry/edit.html.twig', [
'enquiry' => $enquiry,
'form' => $form->createView(),
]);
}
Twig/View Code:
{{ form_start(form) }}
{{ form_row(form.client, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.project, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.buildingPosition) }}
{{ form_row(form.offerSubmissionDate) }}
{{ form_row(form.probability, {'attr': {'class': 'form-control'}}) }}
{{ form_row(form.notes) }}
{{form_row(row)}}
JavaScript Code:
var $client = $('#enquiry_client');
// When sport gets selected ...
$client.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected sport value.
var data = {};
data[$client.attr('name')] = $client.val();
// Submit data via AJAX to the form's action path.
$.ajax({
url : $form.attr('action'),
type: "POST",
data : data,
success: function(html) {
// Replace current position field ...
$('#enquiry_project').replaceWith(
// ... with the returned one from the AJAX response.
$(html).find('#enquiry_project')
);
// Position field now displays the appropriate positions.
}
});
});
Please help me.....
Entities:
User:
class User implements AdvancedUserInterface, \Serializable
{
...
/**
* #ORM\ManyToMany(targetEntity="Role", inversedBy="users")
* #ORM\JoinTable(name="users_roles")
*
*/
private $roles;
...
}
Role:
class Role implements RoleInterface
{
...
/**
* #ORM\ManyToMany(targetEntity="User", mappedBy="roles")
*/
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function __toString()
{
return $this->getName();
}
...
}
Admin classes:
UsersAdmin:
<?php
namespace Lan\ConsoleBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
class UsersAdmin extends Admin
{
protected function configureShowField(ShowMapper $showMapper)
{
$showMapper
->add('id', null, array('label' => 'ID'))
->add('username', null, array('label' => 'Name'))
->add('password', null, array('label' => 'Password'))
->add('email', null, array('label' => 'Mail'))
->add('is_active', null, array('label' => 'Active', 'required' => false))
->add('roles');
}
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->with('General')
->add('username', null, array('label' => 'Name'))
->add('password', null, array('label' => 'Password'))
->add('email', null, array('label' => 'Mail'))
->add('is_active', 'checkbox', array('label' => 'Active', 'required' => false))
->end()
->with('Roles')
->add('roles', 'sonata_type_model',array('expanded' => true, 'compound' => true, 'multiple' => true))
->end();
}
}
RolesAdmin:
<?php
namespace Lan\ConsoleBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Knp\Menu\ItemInterface as MenuItemInterface;
class RolesAdmin extends Admin
{
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', null, array('label' => 'Заголовок'))
->add('role', null, array('label' => 'Роль'));
}
}
Screenshot:
http://img577.imageshack.us/img577/3565/jyte.png
After updating User i get this error message:
FatalErrorException: Error: Call to a member function add() on a
non-object in
\vendor\sonata-project\doctrine-orm-admin-bundle\Sonata\DoctrineORMAdminBundle\Model\ModelManager.php line 560
I think this error occurs because the object instead of 'Role' value is passed to the function Role-> __toString (). How can I solve this problem?
Try something arround this code:
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('name', null, array('label' => 'Заголовок'))
->add('Product' , 'entity' , array(
'class' => 'LanConsoleBundle:Role' ,
'property' => 'name' ,
'expanded' => true ,
'multiple' => true , ))
}