Skip to content

Commit

Permalink
SearchKit - Add display of type entity
Browse files Browse the repository at this point in the history
An entity display does not produce user-facing output, instead it writes to a SQL table
which can then be queried from SearchKit, the API, or other SQL-based tools like Drupal Views.
  • Loading branch information
colemanw committed May 19, 2023
1 parent a57570b commit 7d4be6b
Show file tree
Hide file tree
Showing 17 changed files with 840 additions and 28 deletions.
39 changes: 19 additions & 20 deletions CRM/Core/BAO/SchemaHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -70,30 +70,29 @@ public static function createTable($params) {
*
* @return string
*/
public static function buildTableSQL($params) {
public static function buildTableSQL($params): string {
$sql = "CREATE TABLE {$params['name']} (";
if (isset($params['fields']) &&
is_array($params['fields'])
) {
$separator = "\n";
$prefix = NULL;
foreach ($params['fields'] as $field) {
$sql .= self::buildFieldSQL($field, $separator, $prefix);
$sql .= self::buildFieldSQL($field, $separator);
$separator = ",\n";
}
foreach ($params['fields'] as $field) {
$sql .= self::buildPrimaryKeySQL($field, $separator, $prefix);
$sql .= self::buildPrimaryKeySQL($field, $separator);
}
foreach ($params['fields'] as $field) {
$sql .= self::buildSearchIndexSQL($field, $separator);
$sql .= self::buildSearchIndexSQL($field, $separator, 'INDEX ');
}
if (isset($params['indexes'])) {
foreach ($params['indexes'] as $index) {
$sql .= self::buildIndexSQL($index, $separator, $prefix);
$sql .= self::buildIndexSQL($index, $separator);
}
}
foreach ($params['fields'] as $field) {
$sql .= self::buildForeignKeySQL($field, $separator, $prefix, $params['name']);
$sql .= self::buildForeignKeySQL($field, $separator, '', $params['name']);
}
}
$sql .= "\n) {$params['attributes']};";
Expand All @@ -102,12 +101,12 @@ public static function buildTableSQL($params) {

/**
* @param array $params
* @param $separator
* @param $prefix
* @param string $separator
* @param string $prefix
*
* @return string
*/
public static function buildFieldSQL($params, $separator, $prefix) {
public static function buildFieldSQL($params, $separator, $prefix = ''): string {
$sql = '';
$sql .= $separator;
$sql .= str_repeat(' ', 8);
Expand Down Expand Up @@ -138,12 +137,12 @@ public static function buildFieldSQL($params, $separator, $prefix) {
/**
* @param array $params
* @param $separator
* @param $prefix
* @param string $prefix
*
* @return NULL|string
* @return string
*/
public static function buildPrimaryKeySQL($params, $separator, $prefix) {
$sql = NULL;
public static function buildPrimaryKeySQL($params, $separator, $prefix = ''): string {
$sql = '';
if (!empty($params['primary'])) {
$sql .= $separator;
$sql .= str_repeat(' ', 8);
Expand Down Expand Up @@ -193,7 +192,7 @@ public static function buildSearchIndexSQL($params, $separator, $prefix = '', $e
*
* @return string
*/
public static function buildIndexSQL(&$params, $separator, $prefix) {
public static function buildIndexSQL(&$params, $separator) {
$sql = '';
$sql .= $separator;
$sql .= str_repeat(' ', 8);
Expand Down Expand Up @@ -247,14 +246,14 @@ public static function changeFKConstraint($tableName, $fkTableName) {

/**
* @param array $params
* @param $separator
* @param $prefix
* @param string $separator
* @param string $prefix
* @param string $tableName
*
* @return NULL|string
* @return string
*/
public static function buildForeignKeySQL($params, $separator, $prefix, $tableName) {
$sql = NULL;
public static function buildForeignKeySQL($params, $separator, $prefix, $tableName): string {
$sql = '';
if (!empty($params['fk_table_name']) && !empty($params['fk_field_name'])) {
$sql .= $separator;
$sql .= str_repeat(' ', 8);
Expand Down
11 changes: 7 additions & 4 deletions Civi/Api4/Generic/Traits/SavedSearchInspectorTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,14 @@ trait SavedSearchInspectorTrait {

/**
* If SavedSearch is supplied as a string, this will load it as an array
* @param int|null $id
* @throws UnauthorizedException
* @throws \CRM_Core_Exception
* @throws \Civi\API\Exception\UnauthorizedException
*/
protected function loadSavedSearch() {
if (is_string($this->savedSearch)) {
protected function loadSavedSearch(int $id = NULL) {
if ($id || is_string($this->savedSearch)) {
$this->savedSearch = SavedSearch::get(FALSE)
->addWhere('name', '=', $this->savedSearch)
->addWhere($id ? 'id' : 'name', '=', $id ?: $this->savedSearch)
->execute()->single();
}
if (is_array($this->savedSearch)) {
Expand All @@ -64,6 +65,8 @@ protected function loadSavedSearch() {
];
$this->savedSearch['api_params'] += ['version' => 4, 'select' => [], 'where' => []];
}
// Reset internal cached metadata
$this->_selectQuery = $this->_selectClause = $this->_searchEntityFields = NULL;
$this->_apiParams = ($this->savedSearch['api_params'] ?? []) + ['select' => [], 'where' => []];
}

Expand Down
15 changes: 12 additions & 3 deletions Civi/Schema/Traits/OptionsSpecTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ trait OptionsSpecTrait {
*/
private $optionsCallback;

/**
* @var array
*/
private $optionsCallbackParams = [];

/**
* @param array $values
* @param array|bool $return
Expand All @@ -45,7 +50,7 @@ trait OptionsSpecTrait {
public function getOptions($values = [], $return = TRUE, $checkPermissions = TRUE) {
if (!isset($this->options)) {
if ($this->optionsCallback) {
$this->options = ($this->optionsCallback)($this, $values, $return, $checkPermissions);
$this->options = ($this->optionsCallback)($this, $values, $return, $checkPermissions, $this->optionsCallbackParams);
}
else {
$this->options = FALSE;
Expand Down Expand Up @@ -76,11 +81,15 @@ public function setSuffixes($suffixes) {

/**
* @param callable $callback
*
* Function to be called, will receive the following arguments:
* ($this, $values, $returnFormat, $checkPermissions, $params)
* @param array $params
* Array of optional extra data; sent as 5th argument to the callback
* @return $this
*/
public function setOptionsCallback($callback) {
public function setOptionsCallback($callback, array $params = []) {
$this->optionsCallback = $callback;
$this->optionsCallbackParams = $params;
return $this;
}

Expand Down
47 changes: 47 additions & 0 deletions ext/search_kit/Civi/Api4/Action/SKEntity/Refresh.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Civi\Api4\Action\SKEntity;

use Civi\API\Request;
use Civi\Api4\Generic\AbstractAction;
use Civi\Api4\Generic\Result;
use Civi\Api4\Query\Api4SelectQuery;

/**
* Store the results of a SearchDisplay as a SQL table.
*
* For displays of type `entity` which save to a DB table
* rather than outputting anything to the user.
*
* @package Civi\Api4\Action\SKEntity
*/
class Refresh extends AbstractAction {

/**
* @param \Civi\Api4\Generic\Result $result
* @throws \CRM_Core_Exception
*/
public function _run(Result $result) {
[, $displayName] = explode('_', $this->getEntityName(), 2);
$display = \Civi\Api4\SearchDisplay::get(FALSE)
->setSelect(['settings', 'saved_search_id.api_entity', 'saved_search_id.api_params'])
->addWhere('type', '=', 'entity')
->addWhere('name', '=', $displayName)
->execute()->single();

$apiParams = $display['saved_search_id.api_params'];
foreach ($display['settings']['sort'] ?? [] as $item) {
$apiParams['orderBy'][$item[0]] = $item[1];
}
$api = Request::create($display['saved_search_id.api_entity'], 'get', $apiParams);
$query = new Api4SelectQuery($api);
$query->forceSelectId = FALSE;
$select = $query->getSql();
$tableName = 'civicrm_sk_' . \CRM_Utils_String::convertStringToSnakeCase($displayName);
$columnSpecs = array_column($display['settings']['columns'], 'spec');
$columns = implode(', ', array_column($columnSpecs, 'name'));
\CRM_Core_DAO::executeQuery("TRUNCATE TABLE `$tableName`");
\CRM_Core_DAO::executeQuery("INSERT INTO `$tableName` ($columns) $select");
}

}
Loading

0 comments on commit 7d4be6b

Please sign in to comment.