FIX Don't load data up front for lazy-loaded searchable dropdown (#11278)

This commit is contained in:
Guy Sartorelli 2024-06-17 11:59:59 +12:00 committed by GitHub
parent 9546450cf2
commit 1e0b9056f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 70 additions and 4 deletions

View File

@ -9,7 +9,7 @@ use SilverStripe\ORM\DataList;
class SearchableDropdownField extends DropdownField
{
use SearchableDropdownTrait;
// This needs to be defined on the class, not the trait, otherwise there is a PHP error
protected $schemaComponent = 'SearchableDropdownField';

View File

@ -265,6 +265,13 @@ trait SearchableDropdownTrait
return $this->getListMap($this->sourceList);
}
public function Field($properties = [])
{
$context = $this;
$this->extend('onBeforeRender', $context, $properties);
return $context->customise($properties)->renderWith($context->getTemplates());
}
/*
* @param mixed $source
*/
@ -450,9 +457,16 @@ trait SearchableDropdownTrait
public function getSchemaStateDefaults(): array
{
$data = parent::getSchemaStateDefaults();
$data = $this->updateDataForSchema($data);
return $data;
$state = [
'name' => $this->getName(),
'id' => $this->ID(),
'value' => $this->getDefaultSchemaValue(),
'message' => $this->getSchemaMessage(),
'data' => [],
];
$state = $this->updateDataForSchema($state);
return $state;
}
/**
@ -467,6 +481,14 @@ trait SearchableDropdownTrait
return $this;
}
private function getDefaultSchemaValue()
{
if (!$this->getIsLazyLoaded() && $this->hasMethod('getDefaultValue')) {
return $this->getDefaultValue();
}
return $this->Value();
}
private function getOptionsForSearchRequest(string $term): array
{
if (!$this->sourceList) {

View File

@ -3,6 +3,7 @@
namespace SilverStripe\Forms\Tests;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\SearchableDropdownField;
@ -217,4 +218,47 @@ class SearchableDropdownTraitTest extends SapphireTest
$this->assertSame('My placeholder', $schema['placeholder']);
$this->assertFalse($schema['searchable']);
}
public function provideLazyLoadedDoesntCallGetSource()
{
$methodsToCall = [
'Field',
'getSchemaStateDefaults',
'getSchemaState',
'getSchemaDataDefaults',
'getSchemaData',
];
$classes = [
SearchableMultiDropdownField::class,
SearchableDropdownField::class,
];
$scenarios = [];
foreach ($classes as $class) {
foreach ($methodsToCall as $method) {
$scenarios[] = [
'fieldClass' => $class,
'methodToCall' => $method,
];
}
}
return $scenarios;
}
/**
* @dataProvider provideLazyLoadedDoesntCallGetSource
*/
public function testLazyLoadedDoesntCallGetSource(string $fieldClass, string $methodToCall)
{
// Some methods aren't shared between the two form fields.
if (!ClassInfo::hasMethod($fieldClass, $methodToCall)) {
$this->markTestSkipped("$fieldClass doesn't have method $methodToCall - skipping");
}
// We have to disable the constructor because it ends up calling a static method, and we can't call static methods on mocks.
$mockField = $this->getMockBuilder($fieldClass)->onlyMethods(['getSource'])->disableOriginalConstructor()->getMock();
$mockField->expects($this->never())->method('getSource');
$mockField->setIsLazyLoaded(true);
$mockField->setSource(Team::get());
$mockField->setForm(new Form());
$mockField->$methodToCall();
}
}