QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
qgsvaluerelationfieldformatter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsvaluerelationfieldformatter.cpp - QgsValueRelationFieldFormatter
3 
4  ---------------------
5  begin : 3.12.2016
6  copyright : (C) 2016 by Matthias Kuhn
7  email : [email protected]
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgis.h"
19 #include "qgsproject.h"
20 #include "qgsvectorlayer.h"
21 #include "qgsexpressionnodeimpl.h"
22 #include "qgsapplication.h"
24 #include "qgsvectorlayerref.h"
25 #include "qgspostgresstringutils.h"
26 #include "qgsmessagelog.h"
27 
28 #include <nlohmann/json.hpp>
29 using namespace nlohmann;
30 
31 #include <QSettings>
32 
34 {
35  return qgsVariantLessThan( p1.key, p2.key );
36 }
37 
39 {
40  return qgsVariantLessThan( p1.value, p2.value );
41 }
42 
44 {
46 }
47 
49 {
50  return QStringLiteral( "ValueRelation" );
51 }
52 
53 QString QgsValueRelationFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
54 {
55  ValueRelationCache vrCache;
56 
57  if ( cache.isValid() )
58  {
60  }
61  else
62  {
64  }
65 
66  if ( config.value( QStringLiteral( "AllowMulti" ) ).toBool() )
67  {
68  QStringList keyList;
69 
70  if ( layer->fields().at( fieldIndex ).type() == QVariant::Map )
71  {
72  //because of json it's stored as QVariantList
73  keyList = value.toStringList();
74  }
75  else
76  {
77  keyList = valueToStringList( value );
78  }
79 
80  QStringList valueList;
81 
82  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
83  {
84  if ( keyList.contains( item.key.toString() ) )
85  {
86  valueList << item.value;
87  }
88  }
89 
90  return valueList.join( QStringLiteral( ", " ) ).prepend( '{' ).append( '}' );
91  }
92  else
93  {
94  if ( value.isNull() )
95  {
97  }
98 
99  for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : qgis::as_const( vrCache ) )
100  {
101  if ( item.key == value )
102  {
103  return item.value;
104  }
105  }
106  }
107 
108  return QStringLiteral( "(%1)" ).arg( value.toString() );
109 }
110 
111 QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
112 {
113  return representValue( layer, fieldIndex, config, cache, value );
114 }
115 
116 QVariant QgsValueRelationFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
117 {
118  Q_UNUSED( layer )
119  Q_UNUSED( fieldIndex )
120  return QVariant::fromValue<ValueRelationCache>( createCache( config ) );
121 
122 }
123 
125 {
126  ValueRelationCache cache;
127 
128  const QgsVectorLayer *layer = resolveLayer( config, QgsProject::instance() );
129 
130  if ( !layer )
131  return cache;
132 
133  QgsFields fields = layer->fields();
134  int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
135  int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
136 
137  QgsFeatureRequest request;
138 
140  request.setSubsetOfAttributes( QgsAttributeList() << ki << vi );
141 
142  const QString expression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
143 
144  // Skip the filter and build a full cache if the form scope is required and the feature
145  // is not valid or the attributes required for the filter have no valid value
146  if ( ! expression.isEmpty() && ( ! expressionRequiresFormScope( expression )
147  || expressionIsUsable( expression, formFeature ) ) )
148  {
150  if ( formFeature.isValid( ) )
151  context.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
152  request.setExpressionContext( context );
153  request.setFilterExpression( expression );
154  }
155 
156  QgsFeatureIterator fit = layer->getFeatures( request );
157 
158  QgsFeature f;
159  while ( fit.nextFeature( f ) )
160  {
161  cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString() ) );
162  }
163 
164  if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
165  {
166  std::sort( cache.begin(), cache.end(), orderByValueLessThan );
167  }
168  else
169  {
170  std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
171  }
172 
173  return cache;
174 }
175 
176 
177 QList<QgsVectorLayerRef> QgsValueRelationFieldFormatter::layerDependencies( const QVariantMap &config ) const
178 {
179  QList<QgsVectorLayerRef> result;
180  const QString layerId { config.value( QStringLiteral( "Layer" ) ).toString() };
181  const QString layerName { config.value( QStringLiteral( "LayerName" ) ).toString() };
182  const QString providerName { config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
183  const QString layerSource { config.value( QStringLiteral( "LayerSource" ) ).toString() };
184  if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() )
185  {
186  result.append( QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
187  }
188  return result;
189 }
190 
191 QVariantList QgsValueRelationFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
192 {
193  QVariantList values;
194 
195  if ( context.project() )
196  {
197  const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( context.project()->mapLayer( config[QStringLiteral( "Layer" )].toString() ) );
198  if ( referencedLayer )
199  {
200  int fieldIndex = referencedLayer->fields().indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
201  values = referencedLayer->uniqueValues( fieldIndex, countLimit ).toList();
202  }
203  }
204  return values;
205 }
206 
207 QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
208 {
209  QStringList checkList;
210  if ( value.type() == QVariant::StringList )
211  {
212  checkList = value.toStringList();
213  }
214  else
215  {
216  QVariantList valuesList;
217  if ( value.type() == QVariant::String )
218  {
219  // This must be an array representation
220  auto newVal { value };
221  if ( newVal.toString().trimmed().startsWith( '{' ) )
222  {
223  //normal case
224  valuesList = QgsPostgresStringUtils::parseArray( newVal.toString() );
225  }
226  else if ( newVal.toString().trimmed().startsWith( '[' ) )
227  {
228  //fallback, in case it's a json array
229  try
230  {
231  for ( auto &element : json::parse( newVal.toString().toStdString() ) )
232  {
233  if ( element.is_number_integer() )
234  {
235  valuesList.push_back( element.get<int>() );
236  }
237  else if ( element.is_number_unsigned() )
238  {
239  valuesList.push_back( element.get<unsigned>() );
240  }
241  else if ( element.is_string() )
242  {
243  valuesList.push_back( QString::fromStdString( element.get<std::string>() ) );
244  }
245  }
246  }
247  catch ( json::parse_error &ex )
248  {
249  QgsMessageLog::logMessage( QObject::tr( "Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) );
250  }
251  }
252  }
253  else if ( value.type() == QVariant::List )
254  {
255  valuesList = value.toList( );
256  }
257 
258  checkList.reserve( valuesList.size() );
259  for ( const QVariant &listItem : qgis::as_const( valuesList ) )
260  {
261  QString v( listItem.toString( ) );
262  if ( ! v.isEmpty() )
263  checkList.append( v );
264  }
265  }
266  return checkList;
267 }
268 
269 
270 QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
271 {
272  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
273  QSet< QString > formVariables = scope->variableNames().toSet();
274  const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
275  formVariables.intersect( usedVariables );
276  return formVariables;
277 }
278 
280 {
281  return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
282 }
283 
284 QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
285 {
286  QSet<QString> attributes;
287  QgsExpression exp( expression );
288  std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
289  // List of form function names used in the expression
290  const QSet<QString> formFunctions( scope->functionNames()
291  .toSet()
292  .intersect( exp.referencedFunctions( ) ) );
293  const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
294  QgsExpressionContext context;
295  for ( const auto &f : expFunctions )
296  {
297  QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
298  if ( formFunctions.contains( fd->name( ) ) )
299  {
300  for ( const auto &param : f->args( )->list() )
301  {
302  attributes.insert( param->eval( &exp, &context ).toString() );
303  }
304  }
305  }
306  return attributes;
307 }
308 
309 bool QgsValueRelationFieldFormatter::expressionIsUsable( const QString &expression, const QgsFeature &feature )
310 {
311  const QSet<QString> attrs = expressionFormAttributes( expression );
312  for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
313  {
314  if ( ! feature.attribute( *it ).isValid() )
315  return false;
316  }
317  if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
318  return false;
319  return true;
320 }
321 
322 QgsVectorLayer *QgsValueRelationFieldFormatter::resolveLayer( const QVariantMap &config, const QgsProject *project )
323 {
324  QgsVectorLayerRef ref { config.value( QStringLiteral( "Layer" ) ).toString(),
325  config.value( QStringLiteral( "LayerName" ) ).toString(),
326  config.value( QStringLiteral( "LayerSource" ) ).toString(),
327  config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
328  return ref.resolveByIdOrNameOnly( project );
329 }
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
Class for parsing and evaluation of expressions (formerly called "search strings").
Wrapper for iterator of features from vector data provider or vector layer.
static QSet< QString > expressionFormVariables(const QString &expression)
Returns a list of variables required by the form context expression.
TYPE * resolveByIdOrNameOnly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match...
QList< QgsVectorLayerRef > layerDependencies(const QVariantMap &config) const override
Returns a list of weak layer references to other layers required by this formatter for the given conf...
A context for field formatter containing information like the project.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Container of fields for a vector layer.
Definition: qgsfields.h:42
static QVariantList parseArray(const QString &string)
Returns a QVariantList created out of a string containing an array in postgres array format {1...
QVariant sortValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const override
If the default sort order should be overwritten for this widget, you can transform the value in here...
QgsValueRelationFieldFormatter()
Constructor for QgsValueRelationFieldFormatter.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
static QgsExpressionContextScope * formScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current attribute form/table feat...
QVariantList availableValues(const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context) const override
Returns a list of the values that would be possible to select with this widget type On a RelationRefe...
QVector< QgsValueRelationFieldFormatter::ValueRelationItem > ValueRelationCache
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:121
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
This class wraps a request for features to a vector layer (or directly its vector data provider)...
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:91
static QgsVectorLayer * resolveLayer(const QVariantMap &config, const QgsProject *project)
Returns the (possibly NULL) layer from the widget&#39;s config and project.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
An expression node for expression functions.
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
static QSet< QString > expressionFormAttributes(const QString &expression)
Returns a list of attributes required by the form context expression.
bool orderByKeyLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
QString name() const
The name of the function.
bool orderByValueLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
A abstract base class for defining QgsExpression functions.
QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const override
Create a pretty String representation of the value.
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
static QStringList valueToStringList(const QVariant &value)
Utility to convert a list or a string representation of an (hstore style: {1,2...}) list in value to ...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:450
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
QString id() const override
Returns a unique id for this field formatter.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
static bool expressionIsUsable(const QString &expression, const QgsFeature &feature)
Check whether the feature has all values required by the expression.
QgsGeometry geometry
Definition: qgsfeature.h:67
QList< int > QgsAttributeList
Definition: qgsfield.h:26
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg)
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsProject * project() const
Returns the project used in field formatter.
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression. ...
QVariant::Type type
Definition: qgsfield.h:57
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.