@@ -89,6 +89,12 @@ class SearchContext
8989 */
9090 protected $ withinRangeFieldsChecked = [];
9191
92+ /**
93+ * Specifications for searchable fields to add to the specifications returned from
94+ * searchableFields() on the model.
95+ */
96+ private array $ additionalFieldSpecs = [];
97+
9298 /**
9399 * A key value pair of values that should be searched for.
94100 * The keys should match the field names specified in {@link SearchContext::$fields}.
@@ -117,18 +123,17 @@ public function __construct($modelClass, $fields = null, $filters = null)
117123 */
118124 public function getSearchFields ()
119125 {
120- if ($ this ->fields ?->exists()) {
121- return $ this ->fields ;
122- }
123-
124- $ singleton = singleton ($ this ->modelClass );
125- if (!$ singleton ->hasMethod ('scaffoldSearchFields ' )) {
126- throw new LogicException (
127- 'Cannot dynamically determine search fields. Pass the fields to setFields() '
128- . " or implement a scaffoldSearchFields() method on {$ this ->modelClass }"
129- );
126+ if (!$ this ->fields ->exists ()) {
127+ $ singleton = singleton ($ this ->modelClass );
128+ if (!$ singleton ->hasMethod ('scaffoldSearchFields ' )) {
129+ throw new LogicException (
130+ 'Cannot dynamically determine search fields. Pass the fields to setFields() '
131+ . " or implement a scaffoldSearchFields() method on {$ this ->modelClass }"
132+ );
133+ }
134+ $ this ->fields = $ singleton ->scaffoldSearchFields ();
130135 }
131- return $ singleton -> scaffoldSearchFields () ;
136+ return $ this -> fields ;
132137 }
133138
134139 protected function applyBaseTableFields ()
@@ -175,7 +180,7 @@ private function search(DataList $query): DataList
175180 $ this ->withinRangeFieldsChecked = [];
176181 /** @var DataObject $modelObj */
177182 $ modelObj = Injector::inst ()->create ($ this ->modelClass );
178- $ searchableFields = $ modelObj -> searchableFields ( );
183+ $ searchableFields = $ this -> getSearchFieldsSpec ( $ modelObj );
179184 foreach ($ this ->searchParams as $ searchField => $ searchPhrase ) {
180185 $ searchField = str_replace ('__ ' , '. ' , $ searchField ?? '' );
181186 if ($ searchField !== '' && $ searchField === $ modelObj ->getGeneralSearchFieldName ()) {
@@ -514,6 +519,47 @@ public function getSearchParams()
514519 return $ this ->searchParams ;
515520 }
516521
522+ /**
523+ * Get the specification for searchable fields, e.g. which fields can be used in general search.
524+ * This is a combination of searchableFields() (where applicable) and getAdditionalFieldSpecs().
525+ */
526+ public function getSearchFieldsSpec (?object $ modelObject ): array
527+ {
528+ if (!$ modelObject ) {
529+ $ modelObject = Injector::inst ()->create ($ this ->modelClass );
530+ }
531+ if (!ClassInfo::hasMethod ($ modelObject , 'searchableFields ' )) {
532+ return $ this ->getAdditionalFieldSpecs ();
533+ }
534+ return array_merge ($ modelObject ->searchableFields (), $ this ->getAdditionalFieldSpecs ());
535+ }
536+
537+ /**
538+ * Get the additional field specifications that are applied on top of any searchableFields().
539+ */
540+ public function getAdditionalFieldSpecs (): array
541+ {
542+ return $ this ->additionalFieldSpecs ;
543+ }
544+
545+ /**
546+ * Add field specifications to be applied on top of any searchableFields().
547+ */
548+ public function addAdditionalFieldSpecs (array $ specs ): static
549+ {
550+ $ this ->additionalFieldSpecs = array_merge ($ this ->additionalFieldSpecs , $ specs );
551+ return $ this ;
552+ }
553+
554+ /**
555+ * Set field specifications to be applied on top of any searchableFields().
556+ */
557+ public function setAdditionalFieldSpecs (array $ specs ): static
558+ {
559+ $ this ->additionalFieldSpecs = $ specs ;
560+ return $ this ;
561+ }
562+
517563 public function getModelClass (): string
518564 {
519565 return $ this ->modelClass ;
0 commit comments