Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH Use SearchableDropdownField for autoscaffolded has_one relationships #11071

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Forms/SearchableDropdownField.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class SearchableDropdownField extends DropdownField

public function __construct(
string $name,
string $title,
DataList $source,
?string $title = null,
?DataList $source = null,
mixed $value = null,
string $labelField = 'Title'
) {
Expand Down
4 changes: 2 additions & 2 deletions src/Forms/SearchableDropdownTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ trait SearchableDropdownTrait

private bool $useSearchContext = false;

private DataList $sourceList;
private ?DataList $sourceList = null;

private string $labelField = 'Title';

Expand Down Expand Up @@ -386,7 +386,7 @@ public function saveInto(DataObjectInterface $record): void
/** @var DataObject $record */
$classNameField = substr($name, 0, -2) . 'Class';
if ($record->hasField($classNameField)) {
$record->$classNameField = $ids ? $this->sourceList->dataClass() : '';
$record->$classNameField = $ids ? $record->ClassName : '';
}
}
$record->write();
Expand Down
4 changes: 2 additions & 2 deletions src/Forms/SearchableMultiDropdownField.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class SearchableMultiDropdownField extends MultiSelectField

public function __construct(
string $name,
string $title,
DataList $source,
?string $title = null,
?DataList $source = null,
$value = null,
$labelField = 'Title'
) {
Expand Down
59 changes: 9 additions & 50 deletions src/ORM/FieldType/DBForeignKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
use SilverStripe\Assets\File;
use SilverStripe\Assets\Image;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FileHandleField;
use SilverStripe\Forms\NumericField;
use SilverStripe\Forms\SearchableDropdownField;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;

Expand All @@ -23,16 +22,14 @@
*/
class DBForeignKey extends DBInt
{

/**
* @var DataObject
*/
protected $object;

/**
* This represents the number of related objects to show in a dropdown before it reverts
* to a NumericField. If you are tweaking this value, you should also consider constructing
* your form field manually rather than allowing it to be scaffolded
* Number of related objects to show in a dropdown before it switches to using lazyloading
* This will also be used as the lazy load limit
*
* @config
* @var int
Expand All @@ -47,6 +44,7 @@ class DBForeignKey extends DBInt
* Cache for multiple subsequent calls to scaffold form fields with the same foreign key object
*
* @var array
* @deprecated 5.2.0 Will be removed without equivalent functionality to replace it
*/
protected static $foreignListCache = [];

Expand Down Expand Up @@ -77,52 +75,13 @@ public function scaffoldFormField($title = null, $params = null)
}
return $field;
}

// Build selector / numeric field
$titleField = $hasOneSingleton->hasField('Title') ? 'Title' : 'Name';
$labelField = $hasOneSingleton->hasField('Title') ? 'Title' : 'Name';
$list = DataList::create($hasOneClass);
// Don't scaffold a dropdown for large tables, as making the list concrete
// might exceed the available PHP memory in creating too many DataObject instances
$threshold = self::config()->get('dropdown_field_threshold');

// Add the count of the list to a cache for subsequent calls
if (!isset(static::$foreignListCache[$hasOneClass])) {
// Let the DB do the threshold check as it will be faster - depending on the SQL engine it might only have
// to count indexes
$dataQuery = $list->dataQuery()->getFinalisedQuery();

// Clear order-by as it's not relevant for counts
$dataQuery->setOrderBy(false);
// Remove distinct. Applying distinct shouldn't be required provided relations are not applied.
$dataQuery->setDistinct(false);

$dataQuery->setSelect(['over_threshold' => '(CASE WHEN count(*) > ' . (int)$threshold . ' THEN 1 ELSE 0 END)']);
$result = $dataQuery->execute()->column('over_threshold');

$overThreshold = !empty($result) && ((int) $result[0] === 1);

static::$foreignListCache[$hasOneClass] = [
'overThreshold' => $overThreshold,
];
}

$overThreshold = static::$foreignListCache[$hasOneClass]['overThreshold'];

if (!$overThreshold) {
// Add the mapped list for the cache
if (!isset(static::$foreignListCache[$hasOneClass]['map'])) {
static::$foreignListCache[$hasOneClass]['map'] = $list->map('ID', $titleField);
}

$field = new DropdownField($this->name, $title, static::$foreignListCache[$hasOneClass]['map']);
$field->setEmptyString(' ');
} else {
$field = new NumericField($this->name, $title);
$field->setRightTitle(_t(
self::class . '.DROPDOWN_THRESHOLD_FALLBACK_MESSAGE',
'Too many related objects; fallback field in use'
));
}
$overThreshold = $list->count() > $threshold;
$field = SearchableDropdownField::create($this->name, $title, $list, $labelField)
->setIsLazyLoaded($overThreshold)
->setLazyLoadLimit($threshold);
return $field;
}

Expand Down