QGIS API Documentation 3.99.0-Master (d270888f95f)
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 return cache.value<QMap<QVariant, QString>>().value( value );
45 }
46
47 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
48
49 // Some sanity checks
50 if ( !config.contains( u"Relation"_s ) )
51 {
52 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
53 return value.toString();
54 }
55
56 const QString relationName = config[u"Relation"_s].toString();
57 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationName ); // skip-keyword-check
58 if ( !relation.isValid() )
59 {
60 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
61 return value.toString();
62 }
63 QgsVectorLayer *referencingLayer = relation.referencingLayer();
64 if ( layer != referencingLayer )
65 {
66 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
67 return value.toString();
68 }
69 const int referencingFieldIdx = referencingLayer->fields().lookupField( relation.fieldPairs().at( 0 ).first );
70 if ( referencingFieldIdx != fieldIndex )
71 {
72 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent fieldIndex parameter w.r.t relation referencingFieldIdx" ).arg( layer->name(), fieldName ) );
73 return value.toString();
74 }
75 QgsVectorLayer *referencedLayer = relation.referencedLayer();
76 if ( !referencedLayer )
77 {
78 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
79 return value.toString();
80 }
81
82 // Attributes from the referencing layer
83 QgsAttributes attrs = QgsAttributes( layer->fields().count() );
84 // Set the value on the foreign key field of the referencing record
85 attrs[ referencingFieldIdx ] = value;
86
87 const QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs );
88 QgsFeature feature;
89 referencedLayer->getFeatures( request ).nextFeature( feature );
90 if ( !feature.isValid() )
91 return value.toString();
92
93 QgsExpression expr( referencedLayer->displayExpression() );
95 context.setFeature( feature );
96 QString title = expr.evaluate( &context ).toString();
97 if ( expr.hasEvalError() )
98 {
99 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
100 title = feature.attribute( referencedFieldIdx ).toString();
101 }
102 return title;
103}
104
105QVariant QgsRelationReferenceFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
106{
107 return representValue( layer, fieldIndex, config, cache, value );
108}
109
110QVariant QgsRelationReferenceFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
111{
112 Q_UNUSED( fieldIndex )
113 QMap<QVariant, QString> cache;
114
115 const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" );
116
117 // Some sanity checks
118 if ( !config.contains( u"Relation"_s ) )
119 {
120 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) );
121 return QVariant();
122 }
123 const QString relationName = config[u"Relation"_s].toString();
124 const QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[u"Relation"_s].toString() ); // skip-keyword-check
125 if ( !relation.isValid() )
126 {
127 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) );
128 return QVariant();
129 }
130 QgsVectorLayer *referencingLayer = relation.referencingLayer();
131 if ( layer != referencingLayer )
132 {
133 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) );
134 return QVariant();
135 }
136 QgsVectorLayer *referencedLayer = relation.referencedLayer();
137 if ( !referencedLayer )
138 {
139 QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) );
140 return QVariant();
141 }
142
143 const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
144 if ( referencedFieldIdx == -1 )
145 {
146 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() ) );
147 return QVariant();
148 }
149
150 QgsExpression expr( referencedLayer->displayExpression() );
151
152 QgsFeatureRequest request;
154 QgsAttributeList requiredAttributes = qgis::setToList( expr.referencedAttributeIndexes( referencedLayer->fields() ) );
155 requiredAttributes << referencedFieldIdx;
156 request.setSubsetOfAttributes( requiredAttributes );
157 QgsFeature feature;
158 auto iterator = referencedLayer->getFeatures( request );
159
161
162 expr.prepare( &context );
163
164 while ( iterator.nextFeature( feature ) )
165 {
166 context.setFeature( feature );
167 QString title = expr.evaluate( &context ).toString();
168
169 if ( expr.hasEvalError() )
170 {
171 title = feature.attribute( referencedFieldIdx ).toString();
172 }
173
174 cache.insert( feature.attribute( referencedFieldIdx ), title );
175 }
176
177 return QVariant::fromValue<QMap<QVariant, QString>>( cache );
178}
179
180
181QList<QgsVectorLayerRef> QgsRelationReferenceFieldFormatter::layerDependencies( const QVariantMap &config ) const
182{
183 // Old projects, create before the weak relations were introduced and stored with the
184 // widget configuration do not have the referenced layer details but only the "Relation" id,
185 // for these projects automatic loading of broken references is not supported.
186 if ( config.value( u"ReferencedLayerId"_s ).toString().isEmpty() )
187 {
188 return {};
189 }
190
191 const QList<QgsVectorLayerRef> result {{
193 config.value( u"ReferencedLayerId"_s ).toString(),
194 config.value( u"ReferencedLayerName"_s ).toString(),
195 config.value( u"ReferencedLayerDataSource"_s ).toString(),
196 config.value( u"ReferencedLayerProviderKey"_s ).toString() )
197 }};
198 return result;
199}
200
201QVariantList QgsRelationReferenceFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const
202{
203 QVariantList values;
204 if ( auto *lProject = context.project() )
205 {
206 const QgsVectorLayer *referencedLayer = lProject->relationManager()->relation( config[u"Relation"_s].toString() ).referencedLayer();
207 if ( referencedLayer )
208 {
209 const int fieldIndex = lProject->relationManager()->relation( config[u"Relation"_s].toString() ).referencedFields().first();
210 values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) );
211 }
212 }
213 return values;
214}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
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())
Adds a message to the log instance (and creates it if necessary).
QgsRelationManager * relationManager
Definition qgsproject.h:123
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.
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