QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
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
18#include "qgsmessagelog.h"
19#include "qgsrelation.h"
21#include "qgsproject.h"
22#include "qgsrelationmanager.h"
23#include "qgsvectorlayer.h"
25
27{
28 setFlags( flags() | QgsFieldFormatter::CanProvideAvailableValues );
29}
30
32{
33 return QStringLiteral( "RelationReference" );
34}
35
36QString QgsRelationReferenceFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
37{
38 if ( cache.isValid() )
39 {
40 return cache.value<QMap<QVariant, QString>>().value( value );
41 }
42
43 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
44
45 // Some sanity checks
46 if ( !config.contains( QStringLiteral( "Relation" ) ) )
47 {
48 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
49 return value.toString();
50 }
51
52 const QString relationName = config[QStringLiteral( "Relation" )].toString();
53 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationName );
54 if ( !relation.isValid() )
55 {
56 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
57 return value.toString();
58 }
59 QgsVectorLayer *referencingLayer = relation.referencingLayer();
60 if ( layer != referencingLayer )
61 {
62 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
63 return value.toString();
64 }
65 const int referencingFieldIdx = referencingLayer->fields().lookupField( relation.fieldPairs().at( 0 ).first );
66 if ( referencingFieldIdx != fieldIndex )
67 {
68 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent fieldIndex parameter w.r.t relation referencingFieldIdx" ).arg( layer->name(), fieldName ) );
69 return value.toString();
70 }
71 QgsVectorLayer *referencedLayer = relation.referencedLayer();
72 if ( !referencedLayer )
73 {
74 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
75 return value.toString();
76 }
77
78 // Attributes from the referencing layer
79 QgsAttributes attrs = QgsAttributes( layer->fields().count() );
80 // Set the value on the foreign key field of the referencing record
81 attrs[ referencingFieldIdx ] = value;
82
83 const QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs );
84 QgsFeature feature;
85 referencedLayer->getFeatures( request ).nextFeature( feature );
86 if ( !feature.isValid() )
87 return value.toString();
88
89 QgsExpression expr( referencedLayer->displayExpression() );
91 context.setFeature( feature );
92 QString title = expr.evaluate( &context ).toString();
93 if ( expr.hasEvalError() )
94 {
95 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
96 title = feature.attribute( referencedFieldIdx ).toString();
97 }
98 return title;
99}
100
101QVariant QgsRelationReferenceFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
102{
103 return representValue( layer, fieldIndex, config, cache, value );
104}
105
106QVariant QgsRelationReferenceFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
107{
108 Q_UNUSED( fieldIndex )
109 QMap<QVariant, QString> cache;
110
111 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
112
113 // Some sanity checks
114 if ( !config.contains( QStringLiteral( "Relation" ) ) )
115 {
116 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
117 return QVariant();
118 }
119 const QString relationName = config[QStringLiteral( "Relation" )].toString();
120 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() );
121 if ( !relation.isValid() )
122 {
123 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
124 return QVariant();
125 }
126 QgsVectorLayer *referencingLayer = relation.referencingLayer();
127 if ( layer != referencingLayer )
128 {
129 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
130 return QVariant();
131 }
132 QgsVectorLayer *referencedLayer = relation.referencedLayer();
133 if ( !referencedLayer )
134 {
135 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
136 return QVariant();
137 }
138
139 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
140 if ( referencedFieldIdx == -1 )
141 {
142 QgsMessageLog::logMessage( 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() ) );
143 return QVariant();
144 }
145
146 QgsExpression expr( referencedLayer->displayExpression() );
147
148 QgsFeatureRequest request;
150 QgsAttributeList requiredAttributes = qgis::setToList( expr.referencedAttributeIndexes( referencedLayer->fields() ) );
151 requiredAttributes << referencedFieldIdx;
152 request.setSubsetOfAttributes( requiredAttributes );
153 QgsFeature feature;
154 auto iterator = referencedLayer->getFeatures( request );
155
157
158 expr.prepare( &context );
159
160 while ( iterator.nextFeature( feature ) )
161 {
162 context.setFeature( feature );
163 QString title = expr.evaluate( &context ).toString();
164
165 if ( expr.hasEvalError() )
166 {
167 title = feature.attribute( referencedFieldIdx ).toString();
168 }
169
170 cache.insert( feature.attribute( referencedFieldIdx ), title );
171 }
172
173 return QVariant::fromValue<QMap<QVariant, QString>>( cache );
174}
175
176
177QList<QgsVectorLayerRef> QgsRelationReferenceFieldFormatter::layerDependencies( const QVariantMap &config ) const
178{
179 // Old projects, create before the weak relations were introduced and stored with the
180 // widget configuration do not have the referenced layer details but only the "Relation" id,
181 // for these projects automatic loading of broken references is not supported.
182 if ( config.value( QStringLiteral( "ReferencedLayerId" ) ).toString().isEmpty() )
183 {
184 return {};
185 }
186
187 const QList<QgsVectorLayerRef> result {{
189 config.value( QStringLiteral( "ReferencedLayerId" ) ).toString(),
190 config.value( QStringLiteral( "ReferencedLayerName" ) ).toString(),
191 config.value( QStringLiteral( "ReferencedLayerDataSource" ) ).toString(),
192 config.value( QStringLiteral( "ReferencedLayerProviderKey" ) ).toString() )
193 }};
194 return result;
195}
196
197QVariantList QgsRelationReferenceFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
198{
199 QVariantList values;
200 if ( auto *lProject = context.project() )
201 {
202 const QgsVectorLayer *referencedLayer = lProject->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() ).referencedLayer();
203 if ( referencedLayer )
204 {
205 const int fieldIndex = lProject->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() ).referencedFields().first();
206 values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) );
207 }
208 }
209 return values;
210}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
A vector of attributes.
Definition: qgsattributes.h:59
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.
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.
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.
This class 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:56
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.
void setFlags(const Flags &flags)
Sets the flags.
QString name
Definition: qgsfield.h:62
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
QString name
Definition: qgsmaplayer.h:78
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).
QgsRelationManager * relationManager
Definition: qgsproject.h:117
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:481
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
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...
QList< QgsVectorLayerRef > layerDependencies(const QVariantMap &config) const override SIP_SKIP
Returns a list of weak layer references to other layers required by this formatter for the given conf...
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.
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:48
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
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:46
bool isValid
Definition: qgsrelation.h:49
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.
QString displayExpression
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:27
_LayerRef< QgsVectorLayer > QgsVectorLayerRef