Problem/Motivation
Right now, the core's BulkForm field plugin is not available. Add it.
Proposed resolution
The particularity of SAPI views is that they are not based on a single entity type or they are, even, non-entity. But the core BulkForm one-entity-type centric thus it cannot be used as it is.
Development hints:
- Extend the core's BulkForm plugin to adapt it to SAPI.
- When building the list of applicable actions, collect all the entity types of the view's index and filter out all actions with type not in this list.
- When validating the form, check each row entity type and remove from selection the rows that are not compatible with the selected action (i.e. the action is designed for a different entity type than the row). Show a warning with the removed items from selection. Nice to have: Use Javascript to disable the checkboxes that are not compatible with the selected action.
- Inject also the entity type in the bulk form key hash, so that we can identify the entity type on a row-basis and execute the action only on rows.
Remaining tasks
How to handle a view with an index with entity and non-entity data sources? Should we avoid placing checkboxes on such rows? Or disable them?
User interface changes
SAPI views support for BulkForm.
API changes
None.
Data model changes
None.
Release notes snippet
N/A
Comments
Comment #2
claudiu.cristeaOf course, needs tests.
Comment #3
claudiu.cristeaComment #4
claudiu.cristeaSupport multi-language.
Comment #5
claudiu.cristeaAdded test coverage with a view on an index with 3 data sources: two of them are content entity data sources and one is a non-entity data sources. Checkboxes should not be applicable on non-entity objects.
Comment #6
pfrenssenVery nice work!
I would call this the same as the original field:
Views bulk operationsinstead ofBulk update. It is more consistent and this can do more than just update entities (e.g. delete them).This explains well why it is overridden, but to make it even more clear to future maintainers it would be good to also explain that this is because Search API views support multiple entity types, like is explained so well in the issue summary. How about something like this:
I think what is described in this
@todohas some UX implications and needs some careful thought. Disabling the checkboxes depending on the chosen action would mean that the user always needs to choose the action first, and after this select the entities from the list. Some users might prefer to first select the entities and secondly the actions. Depending on the default action and the entities that are first in the list it might be the case that none of the entities are actionable (and are disabled) until the user chooses a different action. This can be quite confusing for the end user.I think there are definitely good solutions for this: for example we could make it a 2 step operation: first the user chooses the action, and after this we will load the list of actionable entities using AJAX. But this is complex and not blocking for this so I would propose to handle it in a followup issue.
I found the current workflow to work reasonably well: if the user selects any entities that are not actionable then they will be removed from the list and a warning is shown.
Leaving this assigned to me for now, I still need to review the test coverage.
Comment #7
pfrenssenFinished the review and did manual testing + stepped through the test. The bulk operations view works flawlessly, very impressive work!
Some more remarks that would be good to address:
This is using a different standard for inheritdoc, Drupal uses
{@inheritdoc}.This has a typo in the state key:
serach_api_...should beseach_api_....The call to
\json_decode()is prefixed by a backslash so it will always be using the native implementation from the PHP JSON extension which lives in the global namespace. This is usually only necessary when a function with the same name is defined in the current namespace. It is best to avoid this unless needed, since developers will not be able to override thejson_decode()function with their own implementation if they need it for whatever reason.It's not 100% clear what is meant with "Selects a Views row", maybe change it to "Checks the checkbox in the Views row containing the given text." and rename the method to
checkCheckboxInRow($text)?Also provide the type of the parameter:
@param string $text.In this part of the test we are checking that the checkboxes are not shown for the non-entity data sources, but I would also add a test that the rows for these data sources are still visible. For example by checking if their labels are visible, and that the number of visible row is 7 (1 header + 6 result rows).
Comment #8
claudiu.cristeaThank you for review.
#6.1: Actually, it's correct in the patch. We're not extending the Views Bulk Operations plugin, which is provided by a contrib, but the core's BulkForm. I've just copied the same label and help text from the core. See
views_views_data(), line 164.#6.2: Fixed.
#6.3: I agree. That is a nice-to-have and should be carefully designed, that's why it should be fixed in a follow-up.
#7.1: Fixed.
#7.2: Fixed.
#7.3: True, fixed.
#7.4: Fixed.
#7.5: Fixed. Actually, I've created a custom assertion that, subsequently, asserts the existence of the text in the same row with the checkbox.
Comment #9
claudiu.cristeaOuch, by mistake I've switched to RTBC.
Comment #10
pfrenssenLooking good now, thanks!
Comment #11
drunken monkeyWow, thanks a lot for this amazing work! And sorry for the long delay in replying!
The code looks almost perfect, and there is even great test coverage. I’m amazed – again, thank you so much!
Thanks a lot for the thorough review, pfrenssen, and helping to give this the last polish. Also great job!
As I’m a bit of a pedant when it comes to coding standards, I do still have some improvements to suggest – please see/test/review the attached revision. But only minor nitpicks, really.
If these changes look OK to you, I’ll commit this.
Comment #12
claudiu.cristea@drunken monkey, thank you for review & tidying this. I have only a remark:
I have to disagree with these changes. In functional tests
$this->containermight get out-of-sync. It's always recommended to use\Drupal::service(...)in functional tests as opposed to Kernel tests, where$this->containeris safe. See the @alexpott's relevant comment, here #2066993-57: Use magic methods to sync container property to \Drupal::getContainer in functional testsComment #13
claudiu.cristeaFixing #12.
Comment #14
pfrenssenI reviewed the changes made in #11 and #13 and all is looking good. There was apparently still a lot of polish possible in #11, and the remarks in #12 are correct. The mixing of API calls inside the test + further API calls in requests made during the test can cause
$this->containerto contain stale data. This is something we have encountered on several occasions already. Thanks @claudiu.cristea for linking that comment, it is explaining it clearly.This looks very good to me, thanks all!
Comment #16
drunken monkeyThanks a lot for clarifying that!
Seems rather bad design, but now that you mention it I think I remember hearing something like this before. Should probably try to remember it this time … (I also opened #3125444: Get rid of $this->container in Functional tests to take care of the existing instances of it in this module.)
Thanks also, pfrenssen, for reviewing again and setting to RTBC!
Personally, I just prefer
\Drupal::getContainer()->get(…)to\Drupal::service(…), as PhpStorm is able to do type inference on the former, but not the latter. So, changed it to that and then committed.Thanks a lot again, both of you!