|
|
@@ -20,6 +20,8 @@ class AclQueryFeatures {
|
|
|
public $_query;
|
|
|
public $_total;
|
|
|
public $_legacyMode;
|
|
|
+ public $_selectLocalFields; // TODO: leave here or move to AclQueryBuilder? use join for perf?
|
|
|
+ public $_selectRemote; // TODO: ...
|
|
|
|
|
|
public function __construct($acl, $params, $legacyMode = false) {
|
|
|
$this->_acl = $acl;
|
|
|
@@ -28,6 +30,8 @@ class AclQueryFeatures {
|
|
|
$this->_total = null;
|
|
|
$this->_legacyMode = $legacyMode;
|
|
|
// TODO: _legacyMode = ($from instanceof simple schema or another programmed objects)
|
|
|
+ $this->_selectLocalFields = null;
|
|
|
+ $this->_selectRemote = null;
|
|
|
}
|
|
|
|
|
|
public function parseQueryValue($fieldName, $searchQuery, $fieldType = 'xsd:string') {
|
|
|
@@ -227,7 +231,7 @@ class AclQueryFeatures {
|
|
|
public function parseSpecialFilterAccess() {
|
|
|
$userLogin = User::getLogin();
|
|
|
$usrAclGroups = User::getLdapGroupsNames();
|
|
|
- DBG::log(['login'=>$userLogin, 'groups'=>$usrAclGroups, 'acl'=>$this->_acl], 'array', "parse SpecialFilter Access");
|
|
|
+ DBG::log(['login'=>$userLogin, 'groups'=>$usrAclGroups, 'hasFieldWrite'=>$this->_acl->hasField('A_ADM_COMPANY'), 'hasFieldRead'=>$this->_acl->hasField('A_CLASSIFIED'), 'acl'=>$this->_acl], 'array', "parse SpecialFilter Access");
|
|
|
$orWhere = [];
|
|
|
if ($this->_acl->hasField('A_ADM_COMPANY')) {
|
|
|
$orWhere[] = ['A_ADM_COMPANY', '=', ''];// TODO: allow empty for everyone?
|
|
|
@@ -251,7 +255,7 @@ class AclQueryFeatures {
|
|
|
}
|
|
|
|
|
|
public function getQuery() {
|
|
|
- if ($this->_query) return $this->_query;
|
|
|
+ if ($this->_query) return clone($this->_query);
|
|
|
// $ds = $this->_acl->getDataSource(); // TODO: only for TableAcl // TODO: move _parseSqlWhere to this class
|
|
|
$filtrIsInstance = []; // $filtrIsInstance = [ $this->_acl->getNamespace() ];
|
|
|
$filtrIsNotInstance = [];
|
|
|
@@ -296,6 +300,7 @@ class AclQueryFeatures {
|
|
|
list($comparisonSign, $value) = $this->parseQueryValue($fieldName, $v, $fieldType);
|
|
|
DBG::log([ $fieldName, $comparisonSign, $value, $fieldType ], 'array', "parseQueryValue");
|
|
|
$this->_query->where([$fieldName, $comparisonSign, $value]);
|
|
|
+ } else if ('__backRef' === $k) { // skip - parse below
|
|
|
} else if ('limit' === $k) {
|
|
|
} else if ('limitstart' === $k) {
|
|
|
} else if ('order_by' === $k) {
|
|
|
@@ -308,15 +313,35 @@ class AclQueryFeatures {
|
|
|
// sf_Access: if 'SHOW' then show all rows, but data with ***
|
|
|
if ('SHOW' !== V::get('sf_Access', '', $this->_params)) $this->_query->where($this->parseSpecialFilterAccess());
|
|
|
|
|
|
- return $this->_query;
|
|
|
+ if (array_key_exists('__backRef', $this->_params)) {
|
|
|
+ $backRef = $this->_params['__backRef'];
|
|
|
+ if (!is_array($backRef)) throw new Exception("Wrong back ref structure - expected array");
|
|
|
+ if (empty($backRef['namespace'])) throw new Exception("Wrong back ref structure - missing namespace");
|
|
|
+ if (empty($backRef['primaryKey'])) throw new Exception("Wrong back ref structure - missing primaryKey");
|
|
|
+ if (empty($backRef['fieldName'])) throw new Exception("Wrong back ref structure - missing fieldName");
|
|
|
+ // TODO: $this->_query->where([ '__backRef' ]); or $this->_query->join([ '__backRef' ]);
|
|
|
+ $refAcl = ACL::getAclByNamespace($backRef['namespace']);
|
|
|
+ if ($refAcl->getSourceName() !== $this->_acl->getSourceName()) throw new Exception("Not implemented join with different source");
|
|
|
+ $refTable = ACL::getRefTable($refAcl->getNamespace(), $backRef['fieldName']);
|
|
|
+ // TODO: 'in' operator? // $this->_query->where($pkField, 'in', "");
|
|
|
+ $sqlPk = $this->getAclSqlPrimaryKeyField();
|
|
|
+ $sqlBackRefPk = DB::getPDO()->quote($backRef['primaryKey']);
|
|
|
+ $this->_query->where("
|
|
|
+ t.{$sqlPk} in (
|
|
|
+ select refTable.REMOTE_PRIMARY_KEY
|
|
|
+ from `{$refTable}` refTable
|
|
|
+ where refTable.PRIMARY_KEY = {$sqlBackRefPk}
|
|
|
+ )
|
|
|
+ ");
|
|
|
+ }
|
|
|
+
|
|
|
+ return clone($this->_query);
|
|
|
}
|
|
|
|
|
|
public function getTotal() {
|
|
|
if ($this->_legacyMode) return $this->_acl->getTotal($this->_params);
|
|
|
if (null !== $this->_total) return $this->_total;
|
|
|
- $this->_total = $this->getQuery()
|
|
|
- ->select([ 'rawSelect' => "count(1) as cnt" ]) // TODO: fetchTotal() ? a// TODO: add AclQueryBuilder::fetchTotal()
|
|
|
- ->fetchValue();
|
|
|
+ $this->_total = $this->getQuery()->fetchTotal();
|
|
|
return $this->_total;
|
|
|
}
|
|
|
|
|
|
@@ -343,6 +368,7 @@ class AclQueryFeatures {
|
|
|
|
|
|
$select = $this->prepareSelect();
|
|
|
DBG::log($select, 'array', "\$select is(TableAcl)=(".($this->_acl instanceof TableAcl).")");
|
|
|
+ DBG::log($this->getQuery(), 'array', "\$select is(TableAcl)=(".($this->_acl instanceof TableAcl).") \$this->getQuery()");
|
|
|
|
|
|
return $this->fetchRowsRefs(
|
|
|
$this->getQuery()
|
|
|
@@ -366,27 +392,82 @@ class AclQueryFeatures {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- public function prepareSelect() {
|
|
|
+ public function prepareSelect() { // TODO: replace with getSelectLocal
|
|
|
// TODO: select from params: 'cols' => [ fieldName, ... ]
|
|
|
// TODO: select from params: '@instances' => 1
|
|
|
// TODO: if no fields set, then '*'
|
|
|
// TODO: select must contain primaryKey
|
|
|
- $select = [];
|
|
|
- $select = [
|
|
|
- 'rawSelect' => ($this->_acl instanceof TableAcl)
|
|
|
- ? $this->_acl->getDataSource()->_getSqlCols()
|
|
|
- : '*'
|
|
|
- ];
|
|
|
- if (!empty($this->_params['@instances'])) $select[] = '@instances';
|
|
|
+ return $this->getSelectLocal();
|
|
|
+ }
|
|
|
+ public function getSelectLocal() { // @returns [ $fieldName, ... ]
|
|
|
+ // TODO: rawSelect!
|
|
|
+ if (null !== $this->_selectLocalFields) return $this->_selectLocalFields;
|
|
|
+ $this->_selectLocalFields = [];
|
|
|
+ $todoFetchAllCols = false; // select t.*
|
|
|
+ if (!empty($this->_params['cols'])) {
|
|
|
+ if (in_array('*', $this->_params['cols'])) $todoFetchAllCols = true;
|
|
|
+ $acl = $this->_acl;
|
|
|
+ $this->_selectLocalFields = array_filter($this->_params['cols'], function ($fieldQuery) use ($acl) {
|
|
|
+ if ('*' === $fieldQuery) return false;
|
|
|
+ list($fieldName, $subFieldQuery) = explode('/', $fieldQuery, 2);
|
|
|
+ if (!empty($subFieldQuery)) return false;
|
|
|
+ return $acl->isLocalField($fieldName);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (!empty($this->_params['@instances'])) $this->_selectLocalFields[] = '@instances';
|
|
|
+ if (empty($this->_selectLocalFields)) $todoFetchAllCols = true;
|
|
|
+ if (1 === count($this->_selectLocalFields) && in_array('@instances', $this->_selectLocalFields)) $todoFetchAllCols = true;
|
|
|
+ // if ($this->_acl instanceof TableAcl) {
|
|
|
+ // $rawSelect = $this->_acl->getDataSource()->_getSqlCols();
|
|
|
+ // DBG::log($rawSelect, 'string', "DBG raw select");
|
|
|
+ // if ('*' !== $rawSelect && 't.*' !== $rawSelect) {
|
|
|
+ // $this->_selectLocalFields['rawSelect'] = $rawSelect;
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ if (!empty($this->_params['cols'])) DBG::log($this->_params['cols'], 'array', '$this->_params[cols]');
|
|
|
+
|
|
|
+ if ($todoFetchAllCols) {
|
|
|
+ // $this->_selectLocalFields[] = '*'; // TODO: select all $this->from local fields
|
|
|
+ DBG::log($this->_acl->getLocalFieldList(), 'array', "\$this->_acl->getLocalFieldList()");
|
|
|
+ foreach ($this->_acl->getLocalFieldList() as $localFieldName) {
|
|
|
+ $this->_selectLocalFields[] = $localFieldName;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ $primaryKey = $this->getAclSqlPrimaryKeyField();
|
|
|
+ if (!in_array($primaryKey, $this->_selectLocalFields)) {
|
|
|
+ $this->_selectLocalFields[] = $primaryKey;
|
|
|
+ }
|
|
|
+
|
|
|
+ DBG::log($this->_selectLocalFields, 'array', '$this->_selectLocalFields');
|
|
|
+ return $this->_selectLocalFields;
|
|
|
+ }
|
|
|
+ public function getSelectRemote() { // @returns [ $fieldName => [ $fieldName, ... ] ]
|
|
|
+ if (null !== $this->_selectRemote) return $this->_selectRemote;
|
|
|
+ $this->_selectRemote = [];
|
|
|
if (!empty($this->_params['cols'])) {
|
|
|
- foreach ($this->_params['cols'] as $fieldName) {
|
|
|
- $select[] = $fieldName;
|
|
|
+ $cols = $this->_params['cols'];
|
|
|
+ if (in_array('*', $cols)) {
|
|
|
+ $this->_selectLocalFields[] = '*';
|
|
|
+ $cols = array_filter($cols, function ($fieldQuery) { return '*' !== $fieldQuery; });
|
|
|
+ }
|
|
|
+ $acl = $this->_acl;
|
|
|
+ $cols = array_filter($cols, function ($fieldQuery) use ($acl) {
|
|
|
+ list($fieldName, $subFieldQuery) = explode('/', $fieldQuery, 2);
|
|
|
+ if (empty($subFieldQuery)) return false;
|
|
|
+ return !$acl->isLocalField($fieldName);
|
|
|
+ });
|
|
|
+ foreach ($cols as $fieldQuery) { // group by fieldName
|
|
|
+ list($fieldName, $subFieldQuery) = explode('/', $fieldQuery, 2);
|
|
|
+ if (!array_key_exists($fieldName, $this->_selectRemote)) $this->_selectRemote[$fieldName] = [];
|
|
|
+ $this->_selectRemote[$fieldName][] = $subFieldQuery;
|
|
|
}
|
|
|
}
|
|
|
- return $select;
|
|
|
+ return $this->_selectRemote;
|
|
|
}
|
|
|
|
|
|
public function fetchRowsRefs($rows) {
|
|
|
+ return array_map([ $this, 'fetchRowRefs' ], $rows);
|
|
|
// if (!empty($rows) && !empty($rows[0])) {
|
|
|
// $rows[0]['default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK'] = [
|
|
|
// [ 'xlink' => 'default_db__x3A__CRM_WSKAZNIK:CRM_WSKAZNIK.999' ],
|
|
|
@@ -398,7 +479,30 @@ class AclQueryFeatures {
|
|
|
|
|
|
public function fetchRowRefs($row) {
|
|
|
if (!$row) return $row;
|
|
|
+ $sqlPk = $this->getAclSqlPrimaryKeyField();
|
|
|
+ $primaryKey = $row[$sqlPk];
|
|
|
+ DBG::log($row, 'array', "DBG primaryKey '{$primaryKey}'");
|
|
|
+ if (!$primaryKey) throw new Exception("Missing primaryKey");
|
|
|
+ foreach ($this->getSelectRemote() as $fieldName => $cols) {
|
|
|
+ DBG::log($cols, 'array', "add select remote '{$fieldName}' \$cols");
|
|
|
+ $items = ACL::getAclByTypeName($fieldName)->buildQuery([
|
|
|
+ 'cols' => $cols,
|
|
|
+ '__backRef' => [
|
|
|
+ 'namespace' => $this->_acl->getNamespace(),
|
|
|
+ 'primaryKey' => $primaryKey,
|
|
|
+ 'fieldName' => $fieldName,
|
|
|
+ ]
|
|
|
+ ])->getItems();
|
|
|
+ DBG::log($items, 'array', "TODO: add remote items '{$fieldName}' \$items");
|
|
|
+ $row[$fieldName] = $items;
|
|
|
+ }
|
|
|
return $row;
|
|
|
}
|
|
|
|
|
|
+ public function getAclSqlPrimaryKeyField() {
|
|
|
+ return ($this->_acl instanceof Core_AclBase)
|
|
|
+ ? $this->_acl->getSqlPrimaryKeyField()
|
|
|
+ : 'ID';
|
|
|
+ }
|
|
|
+
|
|
|
}
|