18#include <nlohmann/json.hpp>
33using namespace Qt::StringLiterals;
35using namespace nlohmann;
41 setFlags(
flags() | QgsFieldFormatter::CanProvideAvailableValues );
46 return u
"ValueRelation"_s;
53 if ( cache.isValid() )
68 if ( config.value( u
"AllowMulti"_s ).toBool() )
72 if ( layer->
fields().
at( fieldIndex ).
type() == QMetaType::Type::QVariantMap )
75 keyList = value.toStringList();
82 QStringList valueList;
86 if ( keyList.contains( item.key.toString() ) )
88 valueList << item.value;
92 return valueList.join(
", "_L1 ).prepend(
'{' ).append(
'}' );
103 if ( item.key == value )
110 return u
"(%1)"_s.arg( value.toString() );
121 Q_UNUSED( fieldIndex )
130 return QVariant::fromValue<ValueRelationCache>(
createCache( config, project ) );
150 const int keyIdx = fields.
indexOf( config.value( u
"Key"_s ).toString() );
151 const int valueIdx = fields.
indexOf( config.value( u
"Value"_s ).toString() );
158 const int groupIdx = fields.
lookupField( config.value( u
"Group"_s ).toString() );
161 subsetOfAttributes << groupIdx;
164 const bool orderByField { config.value( u
"OrderByField"_s ).toBool() };
165 const int fieldIdx { orderByField ? layer->
fields().
lookupField( config.value( u
"OrderByFieldName"_s ).toString() ) : -1 };
166 const bool reverseSort { config.value( u
"OrderByDescending"_s ).toBool() };
167 if ( fieldIdx != -1 )
169 subsetOfAttributes << fieldIdx;
172 const QString descriptionExpressionString = config.value(
"Description" ).toString();
173 QgsExpression descriptionExpression( descriptionExpressionString );
175 descriptionExpression.
prepare( &context );
179 const QString filterExpression = config.value( u
"FilterExpression"_s ).toString();
198 QMap<QVariant, QVariant> orderByFieldValues;
204 if ( descriptionExpression.
isValid() )
207 description = descriptionExpression.
evaluate( &context ).toString();
209 const QVariant group = groupIdx > -1 ? f.
attribute( groupIdx ) : QVariant();
210 const QVariant keyValue = f.
attribute( keyIdx );
211 if ( fieldIdx != -1 )
213 orderByFieldValues.insert( keyValue, f.
attribute( fieldIdx ) );
219 if ( config.value( u
"OrderByValue"_s ).toBool() )
223 return p1.group == p2.group ? qgsVariantGreaterThan( p1.value, p2.value ) : qgsVariantGreaterThan( p1.group, p2.group );
225 return p1.group == p2.group ? qgsVariantLessThan( p1.value, p2.value ) : qgsVariantLessThan( p1.group, p2.group );
229 else if ( fieldIdx != -1 )
233 return p1.group == p2.group ? qgsVariantGreaterThan( orderByFieldValues.value( p1.key ), orderByFieldValues.value( p2.key ) ) : qgsVariantGreaterThan( p1.group, p2.group );
235 return p1.group == p2.group ? qgsVariantLessThan( orderByFieldValues.value( p1.key ), orderByFieldValues.value( p2.key ) ) : qgsVariantLessThan( p1.group, p2.group );
243 return p1.group == p2.group ? qgsVariantGreaterThan( p1.key, p2.key ) : qgsVariantGreaterThan( p1.group, p2.group );
245 return p1.group == p2.group ? qgsVariantLessThan( p1.key, p2.key ) : qgsVariantLessThan( p1.group, p2.group );
255 QList<QgsVectorLayerRef> result;
256 const QString layerId { config.value( u
"Layer"_s ).toString() };
257 const QString layerName { config.value( u
"LayerName"_s ).toString() };
258 const QString providerName { config.value( u
"LayerProviderName"_s ).toString() };
259 const QString layerSource { config.value( u
"LayerSource"_s ).toString() };
260 if ( !layerId.isEmpty() && !layerName.isEmpty() && !providerName.isEmpty() && !layerSource.isEmpty() )
262 result.append(
QgsVectorLayerRef( layerId, layerName, layerSource, providerName ) );
271 if (
auto *lProject = context.
project() )
273 const QgsVectorLayer *referencedLayer = qobject_cast<QgsVectorLayer *>( lProject->mapLayer( config[u
"Layer"_s].toString() ) );
274 if ( referencedLayer )
276 int fieldIndex = referencedLayer->
fields().
indexOf( config.value( u
"Key"_s ).toString() );
277 values = qgis::setToList( referencedLayer->
uniqueValues( fieldIndex, countLimit ) );
285 QStringList checkList;
286 if ( value.userType() == QMetaType::Type::QStringList )
288 checkList = value.toStringList();
292 QVariantList valuesList;
293 if ( value.userType() == QMetaType::Type::QString )
296 auto newVal { value };
297 if ( newVal.toString().trimmed().startsWith(
'{' ) )
302 else if ( newVal.toString().trimmed().startsWith(
'[' ) )
307 for (
auto &element : json::parse( newVal.toString().toStdString() ) )
309 if ( element.is_number_integer() )
311 valuesList.push_back( element.get<
int>() );
313 else if ( element.is_number_unsigned() )
315 valuesList.push_back( element.get<
unsigned>() );
317 else if ( element.is_string() )
319 valuesList.push_back( QString::fromStdString( element.get<std::string>() ) );
323 catch ( json::parse_error &ex )
325 QgsMessageLog::logMessage( QObject::tr(
"Cannot parse JSON like string '%1' Error: %2" ).arg( newVal.toString(), ex.what() ) );
329 else if ( value.userType() == QMetaType::Type::QVariantList )
331 valuesList = value.toList();
334 checkList.reserve( valuesList.size() );
335 for (
const QVariant &listItem : std::as_const( valuesList ) )
337 QString v( listItem.toString() );
339 checkList.append( v );
349 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
351 formVariables.intersect( usedVariables );
352 return formVariables;
358 QSet< QString > formVariables = qgis::listToSet( scope->variableNames() );
360 formVariables.intersect( usedVariables );
361 return formVariables;
376 QSet<QString> attributes;
380 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() ).intersect( exp.
referencedFunctions() ) );
384 for (
const auto &f : expFunctions )
387 if ( formFunctions.contains( fd->name() ) )
389 const QList<QgsExpressionNode *> cExpressionNodes { f->args()->list() };
390 for (
const auto ¶m : std::as_const( cExpressionNodes ) )
392 attributes.insert( param->eval( &exp, &context ).toString() );
401 QSet<QString> attributes;
405 const QSet<QString> formFunctions( qgis::listToSet( scope->functionNames() ).intersect( exp.
referencedFunctions() ) );
409 for (
const auto &f : expFunctions )
412 if ( formFunctions.contains( fd->name() ) )
414 const QList<QgsExpressionNode *> cExpressionNodes { f->args()->list() };
415 for (
const auto ¶m : std::as_const( cExpressionNodes ) )
417 attributes.insert( param->eval( &exp, &context ).toString() );
427 for (
auto it = attrs.constBegin(); it != attrs.constEnd(); it++ )
439 for (
auto it = parentAttrs.constBegin(); it != parentAttrs.constEnd(); it++ )
441 if ( !parentFeature.
attribute( *it ).isValid() )
452 QgsVectorLayerRef ref { config.value( u
"Layer"_s ).toString(), config.value( u
"LayerName"_s ).toString(), config.value( u
"LayerSource"_s ).toString(), config.value( u
"LayerProviderName"_s ).toString() };
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
static QString nullRepresentation()
Returns the string 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.
An expression node for expression functions.
Handles 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.
std::shared_ptr< QgsExpressionFunction > SharedFunctionPtr
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)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags 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.
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...
int fieldNameIndex(const QString &fieldName) const
Utility method to get attribute index from name.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
A context for field formatter containing information like the project.
QgsProject * project() const
Returns the project used in field formatter.
Container of fields for a vector layer.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
bool isEmpty() const
Returns true if the geometry is empty (eg a linestring with no vertices, or a collection with no geom...
QgsProject * project() const
Returns the parent project if this map layer is added to a project.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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,...
static QgsProject * instance()
Returns the QgsProject singleton instance.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
Q_INVOKABLE QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const final
Calculates a list of unique values contained within an attribute in the layer.
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.