mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
NEW Relation search for GridFieldAddExistingAutocompleter
Now also searches 1:n relations if they where defined in searchableFields() with dot notation.
This commit is contained in:
parent
26c449d69b
commit
8108f7f936
@ -154,6 +154,8 @@ more specifically the `[api:GridFieldAddExistingAutocompleter]` and `[api:GridFi
|
||||
They provide a list/detail interface within a single record edited in your ModelAdmin.
|
||||
The `[GridField](/reference/grid-field)` docs also explain how to manage
|
||||
extra relation fields on join tables through its detail forms.
|
||||
The autocompleter can also search attributes on relations,
|
||||
based on the search fields defined through `[api:DataObject::searchableFields()]`.
|
||||
|
||||
## Permissions
|
||||
|
||||
|
@ -28,10 +28,25 @@ class GridFieldAddExistingAutocompleter
|
||||
protected $searchList;
|
||||
|
||||
/**
|
||||
* Which columns that should be used for doing a "StartsWith" search.
|
||||
* Define column names which should be included in the search.
|
||||
* By default, they're searched with a {@link StartsWithFilter}.
|
||||
* To define custom filters, use the same notation as {@link DataList->filter()},
|
||||
* e.g. "Name:EndsWith".
|
||||
*
|
||||
* If multiple fields are provided, the filtering is performed non-exclusive.
|
||||
* If no fields are provided, tries to auto-detect a "Title" or "Name" field,
|
||||
* and falls back to the first textual field defined on the object.
|
||||
* If no fields are provided, tries to auto-detect fields from
|
||||
* {@link DataObject->searchableFields()}.
|
||||
*
|
||||
* The fields support "dot-notation" for relationships, e.g.
|
||||
* a entry called "Team.Name" will search through the names of
|
||||
* a "Team" relationship.
|
||||
*
|
||||
* @example
|
||||
* array(
|
||||
* 'Name',
|
||||
* 'Email:StartsWith',
|
||||
* 'Team.Name'
|
||||
* )
|
||||
*
|
||||
* @var Array
|
||||
*/
|
||||
@ -192,15 +207,16 @@ class GridFieldAddExistingAutocompleter
|
||||
$dataClass));
|
||||
}
|
||||
|
||||
// TODO Replace with DataList->filterAny() once it correctly supports OR connectives
|
||||
$stmts = array();
|
||||
$params = array();
|
||||
foreach($searchFields as $searchField) {
|
||||
$stmts[] .= sprintf('"%s" LIKE \'%s%%\'', $searchField,
|
||||
Convert::raw2sql($request->getVar('gridfield_relationsearch')));
|
||||
$name = (strpos($searchField, ':') !== FALSE) ? $searchField : "$searchField:StartsWith";
|
||||
$params[$name] = $request->getVar('gridfield_relationsearch');
|
||||
}
|
||||
$results = $allList->where(implode(' OR ', $stmts))->subtract($gridField->getList());
|
||||
$results = $results->sort($searchFields[0], 'ASC');
|
||||
$results = $results->limit($this->getResultsLimit());
|
||||
$results = $allList
|
||||
->subtract($gridField->getList())
|
||||
->filterAny($params)
|
||||
->sort(strtok($searchFields[0], ':'), 'ASC')
|
||||
->limit($this->getResultsLimit());
|
||||
|
||||
$json = array();
|
||||
foreach($results as $result) {
|
||||
@ -250,20 +266,44 @@ class GridFieldAddExistingAutocompleter
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect searchable
|
||||
* Detect searchable fields and searchable relations.
|
||||
* Falls back to {@link DataObject->summaryFields()} if
|
||||
* no custom search fields are defined.
|
||||
*
|
||||
* @param String
|
||||
* @return Array
|
||||
* @param String the class name
|
||||
* @return Array|null names of the searchable fields
|
||||
*/
|
||||
protected function scaffoldSearchFields($dataClass) {
|
||||
public function scaffoldSearchFields($dataClass) {
|
||||
$obj = singleton($dataClass);
|
||||
if($obj->hasDatabaseField('Title')) {
|
||||
return array('Title');
|
||||
} else if($obj->hasDatabaseField('Name')) {
|
||||
return array('Name');
|
||||
} else {
|
||||
return null;
|
||||
$fields = null;
|
||||
if($fieldSpecs = $obj->searchableFields()) {
|
||||
$customSearchableFields = $obj->stat('searchable_fields');
|
||||
foreach($fieldSpecs as $name => $spec) {
|
||||
if(is_array($spec) && array_key_exists('filter', $spec)) {
|
||||
// The searchableFields() spec defaults to PartialMatch,
|
||||
// so we need to check the original setting.
|
||||
// If the field is defined $searchable_fields = array('MyField'),
|
||||
// then default to StartsWith filter, which makes more sense in this context.
|
||||
if(!$customSearchableFields || array_search($name, $customSearchableFields)) {
|
||||
$filter = 'StartsWith';
|
||||
} else {
|
||||
$filter = preg_replace('/Filter$/', '', $spec['filter']);
|
||||
}
|
||||
$fields[] = "{$name}:{$filter}";
|
||||
} else {
|
||||
$fields[] = $name;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (is_null($fields)) {
|
||||
if ($obj->hasDatabaseField('Title')) {
|
||||
$fields = array('Title');
|
||||
} elseif ($obj->hasDatabaseField('Name')) {
|
||||
$fields = array('Name');
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -472,6 +472,14 @@ class GridFieldTest_Team extends DataObject implements TestOnly {
|
||||
);
|
||||
|
||||
static $many_many = array('Players' => 'GridFieldTest_Player');
|
||||
|
||||
static $has_many = array('Cheerleaders' => 'GridFieldTest_Cheerleader');
|
||||
|
||||
static $searchable_fields = array(
|
||||
'Name',
|
||||
'City',
|
||||
'Cheerleaders.Name'
|
||||
);
|
||||
}
|
||||
|
||||
class GridFieldTest_Player extends DataObject implements TestOnly {
|
||||
@ -483,6 +491,14 @@ class GridFieldTest_Player extends DataObject implements TestOnly {
|
||||
static $belongs_many_many = array('Teams' => 'GridFieldTest_Team');
|
||||
}
|
||||
|
||||
class GridFieldTest_Cheerleader extends DataObject implements TestOnly {
|
||||
static $db = array(
|
||||
'Name' => 'Varchar'
|
||||
);
|
||||
|
||||
static $has_one = array('Team' => 'GridFieldTest_Team');
|
||||
}
|
||||
|
||||
class GridFieldTest_HTMLFragments implements GridField_HTMLProvider, TestOnly{
|
||||
public function __construct($fragments) {
|
||||
$this->fragments = $fragments;
|
||||
|
@ -3,9 +3,28 @@ class GridFieldAddExistingAutocompleterTest extends FunctionalTest {
|
||||
|
||||
static $fixture_file = 'GridFieldTest.yml';
|
||||
|
||||
protected $extraDataObjects = array('GridFieldTest_Team', 'GridFieldTest_Player');
|
||||
protected $extraDataObjects = array('GridFieldTest_Team', 'GridFieldTest_Player', 'GridFieldTest_Cheerleader');
|
||||
|
||||
public function testSearch() {
|
||||
function testScaffoldSearchFields() {
|
||||
$autoCompleter = new GridFieldAddExistingAutocompleter($targetFragment = 'before', array('Test'));
|
||||
$gridFieldTest_Team = singleton('GridFieldTest_Team');
|
||||
$this->assertEquals(
|
||||
$autoCompleter->scaffoldSearchFields('GridFieldTest_Team'),
|
||||
array(
|
||||
'Name:PartialMatch',
|
||||
'City:StartsWith',
|
||||
'Cheerleaders.Name:StartsWith'
|
||||
)
|
||||
);
|
||||
$this->assertEquals(
|
||||
$autoCompleter->scaffoldSearchFields('GridFieldTest_Cheerleader'),
|
||||
array(
|
||||
'Name:StartsWith'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function testSearch() {
|
||||
$team1 = $this->objFromFixture('GridFieldTest_Team', 'team1');
|
||||
$team2 = $this->objFromFixture('GridFieldTest_Team', 'team2');
|
||||
|
||||
@ -17,21 +36,26 @@ class GridFieldAddExistingAutocompleterTest extends FunctionalTest {
|
||||
$response = $this->post(
|
||||
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search'
|
||||
. '/?gridfield_relationsearch=Team 2',
|
||||
array(
|
||||
(string)$btns[0]['name'] => 1
|
||||
)
|
||||
array((string)$btns[0]['name'] => 1)
|
||||
);
|
||||
$this->assertFalse($response->isError());
|
||||
$result = Convert::json2array($response->getBody());
|
||||
$this->assertEquals(1, count($result));
|
||||
$this->assertEquals(array($team2->ID => 'Team 2'), $result);
|
||||
|
||||
$response = $this->post(
|
||||
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/'
|
||||
. 'search/?gridfield_relationsearch=Heather',
|
||||
array((string)$btns[0]['name'] => 1)
|
||||
);
|
||||
$this->assertFalse($response->isError());
|
||||
$result = Convert::json2array($response->getBody());
|
||||
$this->assertEquals(1, count($result), "The relational filter did not work");
|
||||
|
||||
$response = $this->post(
|
||||
'GridFieldAddExistingAutocompleterTest_Controller/Form/field/testfield/search'
|
||||
. '/?gridfield_relationsearch=Unknown',
|
||||
array(
|
||||
(string)$btns[0]['name'] => 1
|
||||
)
|
||||
array((string)$btns[0]['name'] => 1)
|
||||
);
|
||||
$this->assertFalse($response->isError());
|
||||
$result = Convert::json2array($response->getBody());
|
||||
@ -78,7 +102,7 @@ class GridFieldAddExistingAutocompleterTest_Controller extends Controller implem
|
||||
public function Form() {
|
||||
$player = DataObject::get('GridFieldTest_Player')->find('Email', 'player1@test.com');
|
||||
$config = GridFieldConfig::create()->addComponents(
|
||||
$relationComponent = new GridFieldAddExistingAutocompleter('before', 'Name'),
|
||||
$relationComponent = new GridFieldAddExistingAutocompleter('before'),
|
||||
new GridFieldDataColumns()
|
||||
);
|
||||
$field = new GridField('testfield', 'testfield', $player->Teams(), $config);
|
||||
|
@ -15,4 +15,8 @@ GridFieldTest_Player:
|
||||
player1_team1:
|
||||
Name: Player 1
|
||||
Email: player1@test.com
|
||||
Teams: =>GridFieldTest_Team.team1
|
||||
Teams: =>GridFieldTest_Team.team1
|
||||
GridFieldTest_Cheerleader:
|
||||
cheerleader1_team1:
|
||||
Name: Heather
|
||||
Team: =>GridFieldTest_Team.team4
|
Loading…
x
Reference in New Issue
Block a user