QGIS API Documentation 3.27.0-Master (75dc696944)
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
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"
22#include "qgsapplication.h"
24#include "qgsvectorlayerref.h"
26#include "qgsmessagelog.h"
27
28#include <nlohmann/json.hpp>
29using 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
53QString 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 : std::as_const( vrCache ) )
83 {
84 if ( keyList.contains( item.key.toString() ) )
85 {
86 valueList << item.value;
87 }
88 }
89
90 return valueList.join( QLatin1String( ", " ) ).prepend( '{' ).append( '}' );
91 }
92 else
93 {
94 if ( value.isNull() )
95 {
97 }
98
99 for ( const QgsValueRelationFieldFormatter::ValueRelationItem &item : std::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
111QVariant QgsValueRelationFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
112{
113 return value.isNull() ? QString() : representValue( layer, fieldIndex, config, cache, value );
114}
115
116QVariant 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 const QVariantMap &config,
126 const QgsFeature &formFeature,
127 const QgsFeature &parentFormFeature )
128{
129 ValueRelationCache cache;
130
131 const QgsVectorLayer *layer = resolveLayer( config, QgsProject::instance() );
132
133 if ( !layer )
134 return cache;
135
136 QgsFields fields = layer->fields();
137 int ki = fields.indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
138 int vi = fields.indexOf( config.value( QStringLiteral( "Value" ) ).toString() );
139
140 QgsFeatureRequest request;
141
143 QgsAttributeIds subsetOfAttributes { ki, vi };
144
145 const QString descriptionExpressionString = config.value( "Description" ).toString();
146 QgsExpression descriptionExpression( descriptionExpressionString );
148 descriptionExpression.prepare( &context );
149 subsetOfAttributes += descriptionExpression.referencedAttributeIndexes( layer->fields() );
150 request.setSubsetOfAttributes( qgis::setToList( subsetOfAttributes ) );
151
152 const QString filterExpression = config.value( QStringLiteral( "FilterExpression" ) ).toString();
153
154 // Skip the filter and build a full cache if the form scope is required and the feature
155 // is not valid or the attributes required for the filter have no valid value
156 // Note: parent form scope is not checked for usability because it's supposed to
157 // be used into a coalesce that retrieve the current value of the parent
158 // from the parent layer when used outside of an embedded form
159 if ( ! filterExpression.isEmpty() && ( !( expressionRequiresFormScope( filterExpression ) )
160 || expressionIsUsable( filterExpression, formFeature ) ) )
161 {
162 QgsExpressionContext filterContext = context;
163 if ( formFeature.isValid( ) && QgsValueRelationFieldFormatter::expressionRequiresFormScope( filterExpression ) )
164 filterContext.appendScope( QgsExpressionContextUtils::formScope( formFeature ) );
165 if ( parentFormFeature.isValid() && QgsValueRelationFieldFormatter::expressionRequiresParentFormScope( filterExpression ) )
166 filterContext.appendScope( QgsExpressionContextUtils::parentFormScope( parentFormFeature ) );
167 request.setExpressionContext( filterContext );
168 request.setFilterExpression( filterExpression );
169 }
170
171 QgsFeatureIterator fit = layer->getFeatures( request );
172
173 QgsFeature f;
174 while ( fit.nextFeature( f ) )
175 {
176 QString description;
177 if ( descriptionExpression.isValid() )
178 {
179 context.setFeature( f );
180 description = descriptionExpression.evaluate( &context ).toString();
181 }
182 cache.append( ValueRelationItem( f.attribute( ki ), f.attribute( vi ).toString(), description ) );
183 }
184
185 if ( config.value( QStringLiteral( "OrderByValue" ) ).toBool() )
186 {
187 std::sort( cache.begin(), cache.end(), orderByValueLessThan );
188 }
189 else
190 {
191 std::sort( cache.begin(), cache.end(), orderByKeyLessThan );
192 }
193
194 return cache;
195}
196
197
198QList<QgsVectorLayerRef> QgsValueRelationFieldFormatter::layerDependencies( const QVariantMap &config ) const
199{
200 QList<QgsVectorLayerRef> result;
201 const QString layerId { config.value( QStringLiteral( "Layer" ) ).toString() };
202 const QString layerName { config.value( QStringLiteral( "LayerName" ) ).toString() };
203 const QString providerName { config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
204 const QString layerSource { config.value( QStringLiteral( "LayerSource" ) ).toString() };
205 if ( ! layerId.isEmpty() && ! layerName.isEmpty() && ! providerName.isEmpty() && ! layerSource.isEmpty() )
206 {
207 result.append( QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
208 }
209 return result;
210}
211
212QVariantList QgsValueRelationFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
213{
214 QVariantList values;
215
216 if ( auto *lProject = context.project() )
217 {
218 const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( lProject->mapLayer( config[QStringLiteral( "Layer" )].toString() ) );
219 if ( referencedLayer )
220 {
221 int fieldIndex = referencedLayer->fields().indexOf( config.value( QStringLiteral( "Key" ) ).toString() );
222 values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) );
223 }
224 }
225 return values;
226}
227
228QStringList QgsValueRelationFieldFormatter::valueToStringList( const QVariant &value )
229{
230 QStringList checkList;
231 if ( value.type() == QVariant::StringList )
232 {
233 checkList = value.toStringList();
234 }
235 else
236 {
237 QVariantList valuesList;
238 if ( value.type() == QVariant::String )
239 {
240 // This must be an array representation
241 auto newVal { value };
242 if ( newVal.toString().trimmed().startsWith( '{' ) )
243 {
244 //normal case
245 valuesList = QgsPostgresStringUtils::parseArray( newVal.toString() );
246 }
247 else if ( newVal.toString().trimmed().startsWith( '[' ) )
248 {
249 //fallback, in case it's a json array
250 try
251 {
252 for ( auto &element : json::parse( newVal.toString().toStdString() ) )
253 {
254 if ( element.is_number_integer() )
255 {
256 valuesList.push_back( element.get<int>() );
257 }
258 else if ( element.is_number_unsigned() )
259 {
260 valuesList.push_back( element.get<unsigned>() );
261 }
262 else if ( element.is_string() )
263 {
264 valuesList.push_back( QString::fromStdString( element.get<std::string>() ) );
265 }
266 }
267 }
268 catch ( json::parse_error &ex )
269 {
270 QgsMessageLog::logMessage( QObject::tr( "Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) );
271 }
272 }
273 }
274 else if ( value.type() == QVariant::List )
275 {
276 valuesList = value.toList( );
277 }
278
279 checkList.reserve( valuesList.size() );
280 for ( const QVariant &listItem : std::as_const( valuesList ) )
281 {
282 QString v( listItem.toString( ) );
283 if ( ! v.isEmpty() )
284 checkList.append( v );
285 }
286 }
287 return checkList;
288}
289
290
291QSet<QString> QgsValueRelationFieldFormatter::expressionFormVariables( const QString &expression )
292{
293 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
294 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
295 const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
296 formVariables.intersect( usedVariables );
297 return formVariables;
298}
299
301{
302 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() );
303 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
304 const QSet< QString > usedVariables = QgsExpression( expression ).referencedVariables();
305 formVariables.intersect( usedVariables );
306 return formVariables;
307}
308
310{
311 return !( expressionFormAttributes( expression ).isEmpty() && expressionFormVariables( expression ).isEmpty() );
312}
313
315{
316 return !( expressionParentFormAttributes( expression ).isEmpty() && expressionParentFormVariables( expression ).isEmpty() );
317}
318
320{
321 QSet<QString> attributes;
322 QgsExpression exp( expression );
323 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::parentFormScope() );
324 // List of form function names used in the expression
325 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
326 .intersect( exp.referencedFunctions( ) ) );
327 const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
328 QgsExpressionContext context;
329 for ( const auto &f : expFunctions )
330 {
331 QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
332 if ( formFunctions.contains( fd->name( ) ) )
333 {
334 const QList<QgsExpressionNode *> cExpressionNodes { f->args( )->list() };
335 for ( const auto &param : std::as_const( cExpressionNodes ) )
336 {
337 attributes.insert( param->eval( &exp, &context ).toString() );
338 }
339 }
340 }
341 return attributes;
342}
343
344QSet<QString> QgsValueRelationFieldFormatter::expressionFormAttributes( const QString &expression )
345{
346 QSet<QString> attributes;
347 QgsExpression exp( expression );
348 std::unique_ptr< QgsExpressionContextScope > scope( QgsExpressionContextUtils::formScope() );
349 // List of form function names used in the expression
350 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() )
351 .intersect( exp.referencedFunctions( ) ) );
352 const QList<const QgsExpressionNodeFunction *> expFunctions( exp.findNodes<QgsExpressionNodeFunction>() );
353 QgsExpressionContext context;
354 for ( const auto &f : expFunctions )
355 {
356 QgsExpressionFunction *fd = QgsExpression::QgsExpression::Functions()[f->fnIndex()];
357 if ( formFunctions.contains( fd->name( ) ) )
358 {
359 const QList<QgsExpressionNode *> cExpressionNodes { f->args( )->list() };
360 for ( const auto &param : std::as_const( cExpressionNodes ) )
361 {
362 attributes.insert( param->eval( &exp, &context ).toString() );
363 }
364 }
365 }
366 return attributes;
367}
368
370 const QgsFeature &feature,
371 const QgsFeature &parentFeature )
372{
373 const QSet<QString> attrs = expressionFormAttributes( expression );
374 for ( auto it = attrs.constBegin() ; it != attrs.constEnd(); it++ )
375 {
376 if ( feature.fieldNameIndex( *it ) < 0 )
377 return false;
378 }
379
380 if ( ! expressionFormVariables( expression ).isEmpty() && feature.geometry().isEmpty( ) )
381 return false;
382
383 if ( parentFeature.isValid() )
384 {
385 const QSet<QString> parentAttrs = expressionParentFormAttributes( expression );
386 for ( auto it = parentAttrs.constBegin() ; it != parentAttrs.constEnd(); it++ )
387 {
388 if ( ! parentFeature.attribute( *it ).isValid() )
389 return false;
390 }
391 if ( ! expressionParentFormVariables( expression ).isEmpty() && parentFeature.geometry().isEmpty( ) )
392 return false;
393 }
394 return true;
395}
396
397QgsVectorLayer *QgsValueRelationFieldFormatter::resolveLayer( const QVariantMap &config, const QgsProject *project )
398{
399 QgsVectorLayerRef ref { config.value( QStringLiteral( "Layer" ) ).toString(),
400 config.value( QStringLiteral( "LayerName" ) ).toString(),
401 config.value( QStringLiteral( "LayerSource" ) ).toString(),
402 config.value( QStringLiteral( "LayerProviderName" ) ).toString() };
403 return ref.resolveByIdOrNameOnly( project );
404}
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QgsExpressionContextScope * parentFormScope(const QgsFeature &formFeature=QgsFeature(), const QString &formMode=QString())
Creates a new scope which contains functions and variables from the current parent attribute form/tab...
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 form...
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
A abstract base class for defining QgsExpression functions.
QString name() const
The name of the function.
An expression node for expression functions.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
QList< const T * > findNodes() const
Returns a list of all nodes of the given class which are used in this expression.
QSet< QString > referencedVariables() const
Returns a list of all variables which are used in this expression.
QSet< QString > referencedFunctions() const
Returns a list of the names of all functions which are used in this expression.
QVariant evaluate()
Evaluate the feature and return the result.
bool isValid() const
Checks if this expression is valid.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setExpressionContext(const QgsExpressionContext &context)
Sets the expression context used to evaluate filter expressions.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
Definition: qgsfeature.cpp:350
QgsGeometry geometry
Definition: qgsfeature.h:67
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:216
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:335
A context for field formatter containing information like the project.
QgsProject * project() const
Returns the project used in field formatter.
Flags flags() const
Returns the flags.
@ CanProvideAvailableValues
Can provide possible values.
void setFlags(const Flags &flags)
Sets the flags.
QVariant::Type type
Definition: qgsfield.h:58
Container of fields for a vector layer.
Definition: qgsfields.h:45
int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:207
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
static QVariantList parseArray(const QString &string)
Returns a QVariantList created out of a string containing an array in postgres array format {1,...
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:104
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:478
static QgsVectorLayer * resolveLayer(const QVariantMap &config, const QgsProject *project)
Returns the (possibly NULL) layer from the widget's config and project.
static QSet< QString > expressionFormVariables(const QString &expression)
Returns a list of variables required by the form context expression.
static bool expressionRequiresFormScope(const QString &expression)
Check if the expression requires a form scope (i.e.
QString id() const override
Returns a unique id for this field formatter.
static bool expressionRequiresParentFormScope(const QString &expression)
Check if the expression requires a parent form scope (i.e.
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.
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.
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
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...
static QSet< QString > expressionParentFormVariables(const QString &expression)
Returns a list of variables required by the parent form's form context expression.
static QSet< QString > expressionParentFormAttributes(const QString &expression)
Returns a list of attributes required by the parent form's form context expression.
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 ...
static QSet< QString > expressionFormAttributes(const QString &expression)
Returns a list of attributes required by the form context expression.
static bool expressionIsUsable(const QString &expression, const QgsFeature &feature, const QgsFeature &parentFeature=QgsFeature())
Check whether the feature has all values required by the expression, optionally checks for parentFeat...
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
QgsValueRelationFieldFormatter()
Constructor for QgsValueRelationFieldFormatter.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
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:119
QgsSQLStatement::Node * parse(const QString &str, QString &parserErrorMsg, bool allowFragments)
bool orderByKeyLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
bool orderByValueLessThan(const QgsValueRelationFieldFormatter::ValueRelationItem &p1, const QgsValueRelationFieldFormatter::ValueRelationItem &p2)
QSet< int > QgsAttributeIds
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
TYPE * resolveByIdOrNameOnly(const QgsProject *project)
Resolves the map layer by attempting to find a matching layer in a project using a weak match.