-
Notifications
You must be signed in to change notification settings - Fork 49
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1554 from reynoldtan/tv4g2-issue1553-chadoProject…
…Autocomplete Project Autocomplete Controller
- Loading branch information
Showing
3 changed files
with
340 additions
and
1 deletion.
There are no files selected for viewing
131 changes: 131 additions & 0 deletions
131
tripal_chado/src/Controller/ChadoProjectAutocompleteController.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
<?php | ||
|
||
namespace Drupal\tripal_chado\Controller; | ||
|
||
use Drupal\Core\Controller\ControllerBase; | ||
use Symfony\Component\HttpFoundation\Request; | ||
use Symfony\Component\HttpFoundation\JsonResponse; | ||
|
||
/** | ||
* Controller, Chado Project Autocomplete. | ||
*/ | ||
class ChadoProjectAutocompleteController extends ControllerBase { | ||
/** | ||
* Controller method, autocomplete project name. | ||
* | ||
* @param Request request | ||
* @param int $type_id | ||
* Project type set in projectprop.type_id to restrict projects to specific type. | ||
* Default to 0, return projects regardless of type. | ||
* Must be declared in autocomplete route parameter ie. ['type_id' => 0]. | ||
* @param int $count | ||
* Desired number of matching names to suggest. | ||
* Default to 5 items. | ||
* Must be declared in autocomplete route parameter ie. [count => 5]. | ||
* | ||
* @return Json Object | ||
* Matching project rows in an array where project name | ||
* is both the value to the array keys label and value. | ||
*/ | ||
public function handleAutocomplete(Request $request, int $type_id = 0, int $count = 5) { | ||
// Array to hold matching project records. | ||
$response = []; | ||
|
||
if ($request->query->get('q')) { | ||
// Get typed in string input from the URL. | ||
$string = trim($request->query->get('q')); | ||
|
||
if (strlen($string) > 0 && $count > 0) { | ||
// Proceed to autocomplete when string is at least a character | ||
// long and result count is set to a value greater than 0. | ||
|
||
// Transform string as a search keyword pattern. | ||
$keyword = '%' . strtolower($string) . '%'; | ||
|
||
if ($type_id > 0) { | ||
// Restrict to type provided by type_id in the route parameter. | ||
$sql = "SELECT name FROM {1:project} AS p LEFT JOIN {1:projectprop} AS t USING (project_id) | ||
WHERE LOWER(p.name) LIKE :keyword AND t.type_id = :type_id ORDER BY p.name ASC LIMIT %d"; | ||
$args = [':keyword' => $keyword, ':type_id' => $type_id]; | ||
} | ||
else { | ||
// Match projects regardless of type. | ||
$sql = "SELECT name FROM {1:project} WHERE LOWER(name) LIKE :keyword ORDER BY name ASC LIMIT %d"; | ||
$args = [':keyword' => $keyword]; | ||
} | ||
|
||
// Prepare Chado database connection and execute sql query by providing value | ||
// to :keyword and/or :type_id placeholder text. | ||
$connection = \Drupal::service('tripal_chado.database'); | ||
$query = sprintf($sql, $count); | ||
$results = $connection->query($query, $args); | ||
|
||
// Compose response result. | ||
if ($results) { | ||
foreach($results as $record) { | ||
$response[] = [ | ||
'value' => $record->name, // Value returned and value displayed by textfield. | ||
'label' => $record->name // Value shown in the list of options. | ||
]; | ||
} | ||
} | ||
} | ||
} | ||
|
||
return new JsonResponse($response); | ||
} | ||
|
||
/** | ||
* Fetch the project id number, given a project name value. | ||
* | ||
* @param string $project | ||
* Project name value. | ||
* | ||
* @return integer | ||
* Project id number of the project name or 0 if no matching | ||
* project record was found. | ||
*/ | ||
public static function getProjectId(string $project): int { | ||
$id = 0; | ||
|
||
if (!empty($project)) { | ||
$sql = "SELECT project_id FROM {1:project} WHERE name = :name LIMIT 1"; | ||
|
||
$connection = \Drupal::service('tripal_chado.database'); | ||
$result = $connection->query($sql, [':name' => $project]); | ||
|
||
if ($result) { | ||
$id = $result->fetchField(); | ||
} | ||
} | ||
|
||
return $id; | ||
} | ||
|
||
/** | ||
* Fetch the project name, given a project id number. | ||
* | ||
* @param int $project | ||
* Project id number value. | ||
* | ||
* @return string | ||
* Corresponding project name of the project id number or | ||
* empty string if no matching project record was found. | ||
*/ | ||
public static function getProjectName(int $project): string { | ||
$name = ''; | ||
|
||
if ($project > 0) { | ||
$sql = "SELECT name FROM {1:project} WHERE project_id = :project_id LIMIT 1"; | ||
|
||
$connection = \Drupal::service('tripal_chado.database'); | ||
$result = $connection->query($sql, ['project_id' => $project]); | ||
|
||
if ($result) { | ||
$name = $result->fetchField(); | ||
} | ||
} | ||
|
||
return $name; | ||
} | ||
} |
193 changes: 193 additions & 0 deletions
193
tripal_chado/tests/src/Functional/Controller/ChadoTableProjectAutocompleteTest.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,193 @@ | ||
<?php | ||
|
||
namespace Drupal\Tests\tripal_chado\Functional; | ||
|
||
use Drupal\Tests\tripal_chado\Functional\ChadoTestBrowserBase; | ||
use Drupal\tripal_chado\Controller\ChadoProjectAutocompleteController; | ||
use Symfony\Component\HttpFoundation\Request; | ||
|
||
/** | ||
* Test autocomplete project name. | ||
*/ | ||
class ChadoTableProjectAutocompleteTest extends ChadoTestBrowserBase { | ||
/** | ||
* Registered user with access content privileges. | ||
* | ||
* @var \Drupal\user\Entity\User | ||
*/ | ||
private $registered_user; | ||
|
||
|
||
/** | ||
* Test autocomplete project name. | ||
*/ | ||
public function testAutocompleteProject() { | ||
// Setup registered user. | ||
$this->registered_user = $this->drupalCreateUser( | ||
['access content'], | ||
); | ||
|
||
$this->drupalLogin($this->registered_user); | ||
|
||
// Ensure we see all logging in tests. | ||
\Drupal::state()->set('is_a_test_environment', TRUE); | ||
|
||
// Create a new test schema for us to use. | ||
$connection = $this->createTestSchema(ChadoTestBrowserBase::PREPARE_TEST_CHADO); | ||
|
||
$projects = [ | ||
'Project Okay', | ||
'Project Good', | ||
'Project Best', | ||
'Project Winner', | ||
'Project Super', | ||
'Project Great', | ||
'Project Green', | ||
'Awesome Project', | ||
'Wow Project', | ||
'Yes Project', | ||
]; | ||
|
||
// Create test project records and projects with type_id set. | ||
$insert_project = " | ||
INSERT INTO {1:project} (name, description) | ||
VALUES | ||
('$projects[0]', '$projects[0] description'), | ||
('$projects[1]', '$projects[1] description'), | ||
('$projects[2]', '$projects[2] description'), | ||
('$projects[3]', '$projects[3] description'), | ||
('$projects[4]', '$projects[4] description'), | ||
('$projects[5]', '$projects[5] description'), | ||
('$projects[6]', '$projects[6] description'), | ||
('$projects[7]', '$projects[7] description'), | ||
('$projects[8]', '$projects[8] description'), | ||
('$projects[9]', '$projects[9] description') | ||
"; | ||
$connection->query($insert_project); | ||
|
||
// Set type_id of Null on projects: Good, Winner and Great. | ||
$ids = $connection->query(" | ||
SELECT project_id FROM {1:project} WHERE name | ||
IN ('Project Good', 'Project Winner', 'Project Great') | ||
")->fetchAllKeyed(0, 0); | ||
|
||
$ids = array_values($ids); | ||
//var_dump($ids); | ||
|
||
$insert_type = " | ||
INSERT INTO {1:projectprop} (project_id, type_id) | ||
VALUES | ||
($ids[0], 1), | ||
($ids[1], 1), | ||
($ids[2], 1) | ||
"; | ||
$connection->query($insert_type); | ||
|
||
$m = $connection->query("select * from {1:projectprop}") | ||
->fetchAllKeyed(0, 1); | ||
|
||
$autocomplete = new ChadoProjectAutocompleteController(); | ||
$this->assertNotNull($autocomplete); | ||
|
||
// Any project regardless of type. | ||
$request = Request::create( | ||
'chado/project/autocomplete/0/10', | ||
'GET', | ||
['q' => 'project'] | ||
); | ||
|
||
// Test Limit/count. | ||
// Request will return all projects (10 rows) but suggest 1 - 10: | ||
// Error on 0 count. | ||
$suggest_count = range(0, 10); | ||
foreach($suggest_count as $count) { | ||
if ($count > 0) { | ||
$suggest = $autocomplete->handleAutocomplete($request, 0, $count) | ||
->getContent(); | ||
|
||
$this->assertEquals(count(json_decode($suggest)), $count); | ||
|
||
// Each suggestion matches the projects that were inserted. | ||
foreach(json_decode($suggest) as $item) { | ||
$is_found = (in_array($item->value, $projects)) ? TRUE : FALSE; | ||
$this->assertTrue($is_found); | ||
} | ||
} | ||
else { | ||
$suggest = $autocomplete->handleAutocomplete($request, 0, $count) | ||
->getContent(); | ||
|
||
$this->assertEquals($suggest, '[]'); | ||
} | ||
} | ||
|
||
// Restrict to project with projectprop.type_id set to null (id: 1). | ||
// Will return - 'Project Good', 'Project Winner', 'Project Great'. | ||
$request = Request::create( | ||
'chado/project/autocomplete/1/10', | ||
'GET', | ||
['q' => 'project'] | ||
); | ||
|
||
$suggest = $autocomplete->handleAutocomplete($request, 0, $count) | ||
->getContent(); | ||
|
||
// Test Limit/count. | ||
// Request will return 3 projects but suggest 1 - 3: | ||
$suggest_count = range(1, 3); | ||
foreach($suggest_count as $count) { | ||
$suggest = $autocomplete->handleAutocomplete($request, 0, $count) | ||
->getContent(); | ||
|
||
$this->assertEquals(count(json_decode($suggest)), $count); | ||
|
||
// Each suggestion matches the projects that were inserted. | ||
foreach(json_decode($suggest) as $item) { | ||
$is_found = (in_array($item->value, $projects)) ? TRUE : FALSE; | ||
$this->assertTrue($is_found); | ||
} | ||
} | ||
|
||
// Test partial keyword. | ||
// Will return Project Great and Project Green. | ||
$request = Request::create( | ||
'chado/project/autocomplete/0/10', | ||
'GET', | ||
['q' => 'gre'] | ||
); | ||
|
||
$suggest = $autocomplete->handleAutocomplete($request, 0, 5) | ||
->getContent(); | ||
|
||
foreach(json_decode($suggest) as $item) { | ||
$is_found = (in_array($item->value, ['Project Great', 'Project Green'])) ? TRUE : FALSE; | ||
$this->assertTrue($is_found); | ||
} | ||
|
||
// Test getProjectName(). | ||
// Ids of project with type_id set. see insert query above. | ||
$id_name = []; | ||
foreach($ids as $project) { | ||
$name = ChadoProjectAutocompleteController::getProjectName($project); | ||
$id_name[] = $name; | ||
$match = (in_array($name, ['Project Good', 'Project Winner', 'Project Great'])) ? TRUE : FALSE; | ||
$this->assertTrue($match); | ||
} | ||
|
||
// Not found. | ||
$not_found = ChadoProjectAutocompleteController::getProjectName(11111111111111111); | ||
$this->assertEquals($not_found, ''); | ||
|
||
// Test getProjectId(). | ||
// Ids of project with type_id set. see insert query above. | ||
foreach($id_name as $project) { | ||
$id = ChadoProjectAutocompleteController::getProjectId($project); | ||
$match = (in_array($id, $ids)) ? TRUE : FALSE; | ||
$this->assertTrue($match); | ||
} | ||
|
||
// Not found. | ||
$not_found = ChadoProjectAutocompleteController::getProjectId('Project Not Found'); | ||
$this->assertEquals($not_found, 0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters