Skip to content

Commit

Permalink
Introduce IndexQueryTrait
Browse files Browse the repository at this point in the history
  • Loading branch information
Ioannis committed Mar 12, 2024
1 parent 0d5e5ae commit cb0e5e6
Show file tree
Hide file tree
Showing 3 changed files with 212 additions and 110 deletions.
84 changes: 30 additions & 54 deletions app/src/Controller/ApiV2Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@

class ApiV2Controller extends AppController {
use \App\Lib\Traits\LabeledLogTrait;

use \App\Lib\Traits\IndexQueryTrait;

/**
* Perform Cake Controller initialization.
*
Expand Down Expand Up @@ -77,6 +78,8 @@ public function initialize(): void {
public function add() {
// $this->name = Models
$modelsName = $this->name;
// $table = the actual table object
$table = $this->$modelsName;
// $tableName = models
$tableName = $this->tableName;

Expand Down Expand Up @@ -144,15 +147,20 @@ public function beforeRender(\Cake\Event\EventInterface $event) {
public function delete($id) {
// $this->name = Models (ie: from ModelsTable)
$modelsName = $this->name;

// $table = the actual table object
$table = $this->$modelsName;
// $tableName = models
$tableName = $table->getTable();


// Make sure the requested object exists
try {
$obj = $this->$modelsName->findById($id)->firstOrFail();
$obj = $table->findById($id)->firstOrFail();

// XXX document AR-CO-1 when we implement hard delete/changelog
// note similar logic in StandardController
$this->$modelsName->deleteOrFail($obj);
$table->deleteOrFail($obj);

if(method_exists($obj, "isReadOnly") && $obj->isReadOnly()) {
throw new BadRequestException(__d('error', 'edit.readonly'));
}
Expand Down Expand Up @@ -184,10 +192,12 @@ public function delete($id) {
public function edit($id) {
// $this->name = Models (ie: from ModelsTable)
$modelsName = $this->name;
// $table = the actual table object
$table = $this->$modelsName;
// $tableName = models
$tableName = $this->$modelsName->getTable();
$tableName = $table->getTable();

$query = $this->$modelsName->findById($id);
$query = $table->findById($id);

try {
// Pull the current record
Expand All @@ -203,14 +213,14 @@ public function edit($id) {
throw new BadRequestException(__d('error', 'api.object', [$modelsName]));
}

$obj = $this->$modelsName->patchEntity($obj, $json[$modelsName]);
$obj = $table->patchEntity($obj, $json[$modelsName]);

$this->$modelsName->saveOrFail($obj);
$table->saveOrFail($obj);

// Trigger provisioning, letting errors bubble up (AR-GMR-5)
if(method_exists($this->$modelsName, "requestProvisioning")) {
if(method_exists($table, "requestProvisioning")) {
$this->llog('rule', "AR-GMR-5 Requesting provisioning for $modelsName " . $obj->id);
$this->$modelsName->requestProvisioning(id: $obj->id, context: ProvisioningContextEnum::Automatic);
$table->requestProvisioning(id: $obj->id, context: ProvisioningContextEnum::Automatic);
}

// Let the view render
Expand All @@ -224,7 +234,6 @@ public function edit($id) {
$err = $this->exceptionToError($e);

$this->llog('debug', $err);
$results[] = ['error' => $err];

throw new BadRequestException($this->exceptionToError($e));
}
Expand Down Expand Up @@ -293,47 +302,12 @@ public function generateApiKey(string $id) {
public function index() {
// $modelsName = Models
$modelsName = $this->name;

$query = $this->$modelsName->find();

// PrimaryLinkTrait
$link = $this->getPrimaryLink(true);

// We automatically allow API calls to be filtered on primary link
if(!empty($link->attr) && !empty($link->value)) {
$query = $query->where([$this->$modelsName->getAlias().'.'.$link->attr => $link->value]);
}
// $table = the actual table object
$table = $this->$modelsName;

// This will produce a nested object which is very useful for vue integration
if($this->request->getQuery('extended') !== null) {
$modelContain = [];
$associations = $this->$modelsName->associations();
foreach($associations->getByType(['BelongsTo']) as $a) {
$modelContain[] = $a->getClassName();
}

if(!empty($modelContain)) {
$query = $query->contain($modelContain);
}
}
// Construct the Query
$query = $this->getIndexQuery();

if($modelsName == 'AuthenticationEvents') {
// Special case for filtering on authenticated identifier. There is a
// similar filter in AuthenticationEventsController::beforeFilter.
// If other special cases show up this should get refactored into a trait
// populated by the table (or something similar).

if($this->getRequest()->getQuery('authenticated_identifier')) {
$query = $query->where(['authenticated_identifier' => \App\Lib\Util\StringUtilities::urlbase64decode($this->getRequest()->getQuery('authenticated_identifier'))]);
} else {
// We only allow unfiltered queries for platform users

if(!$this->RegistryAuth->isPlatformAdmin()) {
throw new \InvalidArgumentException(__d('error', 'input.notprov', 'authenticated_identifier'));
}
}
}

// This magically makes REST calls paginated... can use eg direction=,
// sort=, limit=, page=
$this->set($this->tableName, $this->paginate($query));
Expand All @@ -351,14 +325,16 @@ public function index() {
public function view($id = null) {
// $this->name = Models
$modelsName = $this->name;
// $table = the actual table object
$table = $this->$modelsName;
// $tableName = models
$tableName = $this->$modelsName->getTable();
$tableName = $table->getTable();

if(empty($id)) {
throw new InvalidArgumentException(__d('error', 'notprov', ['id']));
}

$obj = $this->$modelsName->findById($id)->firstOrFail();
$obj = $table->findById($id)->firstOrFail();

$this->set($tableName, [$obj]);

Expand Down
67 changes: 11 additions & 56 deletions app/src/Controller/StandardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
use \App\Lib\Util\StringUtilities;

class StandardController extends AppController {
use \App\Lib\Traits\IndexQueryTrait;

// Pagination defaults should be set in each controller
public $pagination = [];

Expand Down Expand Up @@ -565,64 +567,17 @@ public function index() {
$table = $this->$modelsName;
// $tableName = models
$tableName = $table->getTable();
// PrimaryLinkTrait
$link = $this->getPrimaryLink(true);

// AutoViewVarsTrait
$this->populateAutoViewVars();
// Initialize the Query Object
$query = $table->find($this->paginate['finder'] ?? 'all');
// Get a pointer to my expressions list
$newexp = $query->newExpr();

if(!empty($link->attr)) {
// If a link attribute is defined but no value is provided, then query
// where the link attribute is NULL
// "all" is the default finder. But since we are utilizing the paginator here, we will check the configuration
// for any custom finder.
$newexp->add([$table->getAlias().'.'.$link->attr => $link->value]);
}

// QueryModificationTrait
if(method_exists($table, 'getIndexContains')
&& $table->getIndexContains()) {
$query->contain($table->getIndexContains());
}

// SearchFilterTrait
if(method_exists($table, 'getSearchableAttributes')) {
$searchableAttributes = $table->getSearchableAttributes($this->name,
$this->viewBuilder()->getVar('vv_tz'));

if(!empty($searchableAttributes)) {
// Here we iterate over the attributes, and we add a new where clause for each one
foreach($searchableAttributes as $attribute => $options) {
// Add the Join Clauses
$query = $table->addJoins($query, $attribute, $this->request);

// Construct and apply the where Clause
if(!empty($this->request->getQuery($attribute))) {
$newexp = $table->expressionsConstructor($query, $newexp, $attribute, $this->request->getQuery($attribute));
} elseif (!empty($this->request->getQuery($attribute . '_starts_at'))
|| !empty($this->request->getQuery($attribute . '_ends_at'))) {
$search_date = [];
// We allow empty for dates since we might refer to infinity (from whenever or to always)
$search_date[] = $this->request->getQuery($attribute . '_starts_at') ?? '';
$search_date[] = $this->request->getQuery($attribute . '_ends_at') ?? '';
$newexp = $table->expressionsConstructor($query, $newexp, $attribute, $search_date);
}
}

$this->set('vv_searchable_attributes', $searchableAttributes);
}
}

$query = $query->where($newexp);
// Construct the Query
$query = $this->getIndexQuery();
// Fetch the data and paginate
$resultSet = $this->paginate($query);


// Pass vars to the View
$this->set($tableName, $resultSet);
$this->set('vv_permission_set', $this->RegistryAuth->calculatePermissionsForResultSet($resultSet));

// AutoViewVarsTrait
$this->populateAutoViewVars();

// Default index view title is model name
[$title, , ] = StringUtilities::entityAndActionToTitle($resultSet, $modelsName, 'index');
$this->set('vv_title', $title);
Expand Down Expand Up @@ -769,7 +724,7 @@ protected function populateAutoViewVars(object $obj=null) {
break;
default:
// XXX I18n? and in match?
throw new \LogicException('Unknonwn Auto View Var Type {0}', [$avv['type']]);
throw new \LogicException('Unknonwn Auto View Var Type {0}', $avv['type']);
break;
}
}
Expand Down
Loading

0 comments on commit cb0e5e6

Please sign in to comment.