QGIS API Documentation 4.1.0-Master (659fe69c07c)
Loading...
Searching...
No Matches
qgsrelationreferencefieldformatter.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrelationreferencefieldformatter.cpp - QgsRelationReferenceFieldFormatter
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
20#include "qgsmessagelog.h"
21#include "qgsproject.h"
22#include "qgsrelation.h"
23#include "qgsrelationmanager.h"
24#include "qgsvectorlayer.h"
25
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
31{
32 setFlags( flags() | QgsFieldFormatter::CanProvideAvailableValues );
33}
34
36{
37 return u"RelationReference"_s;
38}
39
40QString QgsRelationReferenceFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
41{
42 if ( cache.isValid() )
43 {
44 // See regression #66339: the value type should be converted to the type of the key in the cache,
45 // otherwise we might not find the value in the cache, even if it is there, because of type mismatch.
46 // This can happen for example when the value is a int and the key in the cache is a long long.
47 const QMap<QVariant, QString> cacheMap = cache.value<QMap<QVariant, QString>>();
48 if ( cacheMap.size() > 0 )
49 {
50 const QMetaType keyType = value.metaType();
51 const QMetaType cacheKeyType = cacheMap.firstKey().metaType();
52 if ( keyType != cacheKeyType && value.canConvert( cacheKeyType ) )
53 {
54 QVariant key = value;
55 if ( key.convert( cacheKeyType ) )
56 {
57 return cacheMap.value( key );
58 }
59 }
60 }
61 return cacheMap.value( value );
62 }
63
64 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
65
66 // Some sanity checks
67 if ( !config.contains( u"Relation"_s ) )
68 {
69 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
70 return value.toString();
71 }
72
73 const QString relationName = config[u"Relation"_s].toString();
74 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationName ); // skip-keyword-check
75 if ( !relation.isValid() )
76 {
77 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
78 return value.toString();
79 }
80 QgsVectorLayer *referencingLayer = relation.referencingLayer();
81 if ( layer != referencingLayer )
82 {
83 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
84 return value.toString();
85 }
86 const int referencingFieldIdx = referencingLayer->fields().lookupField( relation.fieldPairs().at( 0 ).first );
87 if ( referencingFieldIdx != fieldIndex )
88 {
89 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent fieldIndex parameter w.r.t relation referencingFieldIdx" ).arg( layer->name(), fieldName ) );
90 return value.toString();
91 }
92 QgsVectorLayer *referencedLayer = relation.referencedLayer();
93 if ( !referencedLayer )
94 {
95 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
96 return value.toString();
97 }
98
99 // Attributes from the referencing layer
100 QgsAttributes attrs = QgsAttributes( layer->fields().count() );
101 // Set the value on the foreign key field of the referencing record
102 attrs[referencingFieldIdx] = value;
103
104 const QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs );
105 QgsFeature feature;
106 referencedLayer->getFeatures( request ).nextFeature( feature );
107 if ( !feature.isValid() )
108 return value.toString();
109
110 QgsExpression expr( referencedLayer->displayExpression() );
112 context.setFeature( feature );
113 QString title = expr.evaluate( &context ).toString();
114 if ( expr.hasEvalError() )
115 {
116 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
117 title = feature.attribute( referencedFieldIdx ).toString();
118 }
119 return title;
120}
121
122QVariant QgsRelationReferenceFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
123{
124 return representValue( layer, fieldIndex, config, cache, value );
125}
126
127QVariant QgsRelationReferenceFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
128{
129 Q_UNUSED( fieldIndex )
130 QMap<QVariant, QString> cache;
131
132 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
133
134 // Some sanity checks
135 if ( !config.contains( u"Relation"_s ) )
136 {
137 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
138 return QVariant();
139 }
140 const QString relationName = config[u"Relation"_s].toString();
141 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[u"Relation"_s].toString() ); // skip-keyword-check
142 if ( !relation.isValid() )
143 {
144 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
145 return QVariant();
146 }
147 QgsVectorLayer *referencingLayer = relation.referencingLayer();
148 if ( layer != referencingLayer )
149 {
150 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
151 return QVariant();
152 }
153 QgsVectorLayer *referencedLayer = relation.referencedLayer();
154 if ( !referencedLayer )
155 {
156 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
157 return QVariant();
158 }
159
160 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
161 if ( referencedFieldIdx == -1 )
162 {
164 QObject::tr( "Layer %1, field %2: Invalid referenced field (%3) configured in relation %4" ).arg( layer->name(), fieldName, relation.fieldPairs().at( 0 ).second, relation.name() )
165 );
166 return QVariant();
167 }
168
169 QgsExpression expr( referencedLayer->displayExpression() );
170
171 QgsFeatureRequest request;
173 QgsAttributeList requiredAttributes = qgis::setToList( expr.referencedAttributeIndexes( referencedLayer->fields() ) );
174 requiredAttributes << referencedFieldIdx;
175 request.setSubsetOfAttributes( requiredAttributes );
176 QgsFeature feature;
177 auto iterator = referencedLayer->getFeatures( request );
178
180
181 expr.prepare( &context );
182
183 while ( iterator.nextFeature( feature ) )
184 {
185 context.setFeature( feature );
186 QString title = expr.evaluate( &context ).toString();
187
188 if ( expr.hasEvalError() )
189 {
190 title = feature.attribute( referencedFieldIdx ).toString();
191 }
192
193 cache.insert( feature.attribute( referencedFieldIdx ), title );
194 }
195
196 return QVariant::fromValue<QMap<QVariant, QString>>( cache );
197}
198
199
200QList<QgsVectorLayerRef> QgsRelationReferenceFieldFormatter::layerDependencies( const QVariantMap &config ) const
201{
202 // Old projects, create before the weak relations were introduced and stored with the
203 // widget configuration do not have the referenced layer details but only the "Relation" id,
204 // for these projects automatic loading of broken references is not supported.
205 if ( config.value( u"ReferencedLayerId"_s ).toString().isEmpty() )
206 {
207 return {};
208 }
209
210 const QList<QgsVectorLayerRef> result { { QgsVectorLayerRef(
211 config.value( u"ReferencedLayerId"_s ).toString(),
212 config.value( u"ReferencedLayerName"_s ).toString(),
213 config.value( u"ReferencedLayerDataSource"_s ).toString(),
214 config.value( u"ReferencedLayerProviderKey"_s ).toString()
215 ) } };
216 return result;
217}
218
219QVariantList QgsRelationReferenceFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
220{
221 QVariantList values;
222 if ( auto *lProject = context.project() )
223 {
224 const QgsVectorLayer *referencedLayer = lProject->relationManager()->relation( config[u"Relation"_s].toString() ).referencedLayer();
225 if ( referencedLayer )
226 {
227 const int fieldIndex = lProject->relationManager()->relation( config[u"Relation"_s].toString() ).referencedFields().first();
228 values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) );
229 }
230 }
231 return values;
232}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2330
A vector of attributes.
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 setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
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.
bool hasEvalError() const
Returns true if an error occurred when evaluating last input.
QVariant evaluate()
Evaluate the feature and return the result.
QSet< int > referencedAttributeIndexes(const QgsFields &fields) const
Returns a list of field name indexes obtained from the provided fields.
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.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
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.
Flags flags() const
Returns the flags.
void setFlags(const Flags &flags)
Sets the flags.
QString name
Definition qgsfield.h:65
int count
Definition qgsfields.h:50
int size() const
Returns number of items.
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.
QString name
Definition qgsmaplayer.h:87
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).
QgsRelationManager * relationManager
Definition qgsproject.h:125
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
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...
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.
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...
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
QString id() const override
Returns a unique id for this field formatter.
QgsRelationReferenceFieldFormatter()
Default constructor of field formatter for a relation reference field.
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.
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QString name
Definition qgsrelation.h:52
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
Represents a vector layer which manages a vector based dataset.
QString displayExpression
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.
QList< int > QgsAttributeList
Definition qgsfield.h:30
_LayerRef< QgsVectorLayer > QgsVectorLayerRef