QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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
7  email : [email protected]
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"
20 #include "qgsexpressioncontext.h"
21 #include "qgsproject.h"
22 #include "qgsrelationmanager.h"
23 #include "qgsvectorlayer.h"
25 
27 {
29 }
30 
32 {
33  return QStringLiteral( "RelationReference" );
34 }
35 
36 QString 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 
101 QVariant 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 
106 QVariant 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 
177 QList<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 
197 QVariantList 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 }
A vector of attributes.
Definition: qgsattributes.h:58
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)
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.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
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:209
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
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.
QString name
Definition: qgsfield.h:60
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:349
QString name
Definition: qgsmaplayer.h:76
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:111
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:470
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:49
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
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
bool isValid
Definition: qgsrelation.h:50
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:26
_LayerRef< QgsVectorLayer > QgsVectorLayerRef