QGIS API Documentation  3.17.0-Master (8af46bc54f)
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  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  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  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  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  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  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 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:344
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:185
Class for parsing and evaluation of expressions (formerly called "search strings").
QString name
Definition: qgsrelation.h:48
int size() const
Returns number of items.
Definition: qgsfields.cpp:138
QString name
Definition: qgsfield.h:59
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...
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
A context for field formatter containing information like the project.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
int count() const
Returns number of items.
Definition: qgsfields.cpp:133
void setFlags(const Flags &flags)
Sets the flags.
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QgsRelationReferenceFieldFormatter()
Default constructor of field formatter for a relation reference field.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:47
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
QgsRelationManager relationManager
Definition: qgsproject.h:105
QVariant createCache(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config) const override
Create a cache for a given field.
QString displayExpression
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QString id() const override
Returns a unique id for this field formatter.
bool isValid
Definition: qgsrelation.h:49
QSet< QVariant > uniqueValues(int fieldIndex, int limit=-1) const FINAL
Calculates a list of unique values contained within an attribute in the layer.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:469
_LayerRef< QgsVectorLayer > QgsVectorLayerRef
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QString name
Definition: qgsmaplayer.h:87
QList< int > QgsAttributeList
Definition: qgsfield.h:26
bool nextFeature(QgsFeature &f)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsProject * project() const
Returns the project used in field formatter.
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...
Flags flags() const
Returns the flags.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
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...