Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

NEW Relation search for GridFieldAddExistingAutocompleter

Now also searches 1:n relations if they where defined in searchableFields() with dot notation.
  • Loading branch information...
commit 8108f7f93610a522c4cbd06ce5eec63f07bdc7db 1 parent 26c449d
@chillu chillu authored
View
2  docs/en/reference/modeladmin.md
@@ -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
View
80 forms/gridfield/GridFieldAddExistingAutocompleter.php
@@ -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 @@ public function doSearch($gridField, $request) {
$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 @@ public function getSearchFields() {
}
/**
- * 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;
}
/**
View
16 tests/forms/GridFieldTest.php
@@ -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;
View
42 tests/forms/gridfield/GridFieldAddExistingAutocompleterTest.php
@@ -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 @@ public function testSearch() {
$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);
View
6 tests/forms/gridfield/GridFieldTest.yml
@@ -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
Please sign in to comment.
Something went wrong with that request. Please try again.