3

I want to make my entity list/table searchable, but the docs are not helping. I created custom module that generates

My table: enter image description here

I have entity Event and it has NAME variable. The result i want to get is like in domain/admin/people - Search by name

enter image description here

2 Answers 2

6

Not that complicated and you don't need Views. Most of the functionality is right there, built-in, you only need to add your own bits.

Filtering

In addition to EntityListBuilder, create a form of the filter to be exposed. Just create a usual form extending FormBase, building whatever UI you need (eg. a dropdown with items to select from, with Filter and Reset buttons). Let's call it DocumentFilterForm, this is its form builder:

public function buildForm(array $form, FormStateInterface $form_state) {
  $request = \Drupal::request();

  $form['filter'] = [
    '#type' => 'container',
    '#attributes' => [
      'class' => ['form--inline', 'clearfix'],
    ],
  ];

  $form['filter']['foo'] = [
    '#type' => 'select',
    '#title' => 'Which foo?',
    '#options' => ...,
    '#default_value' => $request->get('foo') ?? 0,
  ];

  $form['filter']['bar'] = [
    '#type' => 'select',
    '#title' => 'Which bar?',
    '#options' => ...,
    '#default_value' => $request->get('bar') ?? 0,
  ];

  $form['actions']['wrapper'] = [
    '#type' => 'container',
    '#attributes' => ['class' => ['form-item']],
  ];

  $form['actions']['wrapper']['submit'] = [
    '#type' => 'submit',
    '#value' => 'Filter',
  ];

  if ($request->getQueryString()) {
    $form['actions']['wrapper']['reset'] = [
      '#type' => 'submit',
      '#value' => 'Reset',
      '#submit' => ['::resetForm'],
    ];
  }

  return $form;
}

We have two dropdowns, as per usual, with the two buttons. The Reset only appears when needed, just like with the standard Views displays. submitForm() redirects to the URL with the appropriate query (remember to change the route name), resetForm() redirects to the original URL.

public function submitForm(array &$form, FormStateInterface $form_state) {
  $query = [];

  $foo = $form_state->getValue('foo') ?? 0;
  if ($foo) {
    $query['foo'] = $foo;
  }

  $bar = $form_state->getValue('bar') ?? 0;
  if ($bar) {
    $query['bar'] = $bar;
  }

  $form_state->setRedirect('entity.document.collection', $query);
}

public function resetForm(array $form, FormStateInterface &$form_state) {
  $form_state->setRedirect('entity.document.collection');
}

Back in the list builder, we modify getEntityIds() to apply our filters, when present:

protected function getEntityIds() {
  $query = \Drupal::entityQuery($this->entityTypeId);
  $request = \Drupal::request();

  $foo = $request->get('foo') ?? 0;
  if ($foo) {
    $query->condition('foo', $foo);
  }

  $bar = $request->get('bar') ?? 0;
  if ($bar) {
    $query->condition('bar', $bar);
  }

  if ($this->limit) {
    $query->pager($this->limit);
  }

  return $query->execute();
}

And finally, to actually include the filter form on the page:

public function render() {
  $build['form'] = \Drupal::formBuilder()->getForm('Drupal\...\Form\DocumentFilterForm');
  $build += parent::render();
  return $build;
}

Sorting

Sorting is even simpler, you only have to add to your list builder. First, set up the headers like with any other similar table:

public function buildHeader() {
  $header = [
    'foo' => [
      'data' => 'Foo',
      'field' => 't.foo',
    ],
    'bar' => [
      'data' => 'Bar',
      'field' => 't.bar',
      'sort' => 'asc',
    ],
  ];
  return $header + parent::buildHeader();
}

Second, add the following to your previous getEntityIds():

protected function getEntityIds() {
  $query = \Drupal::entityQuery($this->entityTypeId);
  $request = \Drupal::request();
  
  // ...

  $order = $request->get('order');
  if ($order) {
    $sort = $request->get('sort');
    foreach ($this->buildHeader() as $name => $field) {
      if (is_array($field) && $field['data'] == $order) {
        $header = [$name => $field + [
          'specifier' => $name,
          'sort' => $sort ?? $field['sort'] ?? 'asc',
        ]];
      }
    }
    if ($header) {
      $query->tableSort($header);
    }
  }
  else {
    $query->sort('bar', 'asc');
  }
  
  return $query->execute();
}

Note that this relies on the data elements in the header being simple strings. If you use t(), you need to compare to $field['data']->getUntranslatedString() instead. It also assumes that your header items (foo and bar) use the same name as the underlying field. If not, modify accordingly. tableSort() requires a simple nested array that has a specifier pointing to the field name and a sort to describe the sort direction, you can create it however you wish, it will work.

3

You are using an entity list builder? There is no standard support for filters/sort, if you want to implement something you have to do it yourself.

All content lists that use filters like admin/people and admin/content are views, expose your entity type to views if it is a content entity type.

One special case is admin/structure/views, which is a config entity list builder that has support for filters. But that's actually a client-side implementation done with javascript.

2
  • Yea, i'm using listBuilder and i can create the view (list with filters) but don't have idea how to CODE it. Feb 15, 2018 at 0:16
  • Why do you need to code it if you have a view? Answering in detail how to build such an exposed folter seems like a lot for an answer here, but in short, you need to build your form, submit it and then store the values somewhere, views uses query parameters or session variables. And when querying the list, you check those arguments and add the relevant conditions to your query by overriding the relevant methods. You can find lots of tutorials on how to build a form with drupal
    – Berdir
    Feb 15, 2018 at 7:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.