QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 {
28  return QStringLiteral( "RelationReference" );
29 }
30 
31 QString QgsRelationReferenceFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
32 {
33  if ( cache.isValid() )
34  {
35  return cache.value<QMap<QVariant, QString>>().value( value );
36  }
37 
38  // Some sanity checks
39  if ( !config.contains( QStringLiteral( "Relation" ) ) )
40  {
41  QgsMessageLog::logMessage( QObject::tr( "Missing Relation in configuration" ) );
42  return value.toString();
43  }
44  QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() );
45  if ( !relation.isValid() )
46  {
47  QgsMessageLog::logMessage( QObject::tr( "Invalid relation" ) );
48  return value.toString();
49  }
50  QgsVectorLayer *referencingLayer = relation.referencingLayer();
51  if ( layer != referencingLayer )
52  {
53  QgsMessageLog::logMessage( QObject::tr( "representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ) );
54  return value.toString();
55  }
56  int referencingFieldIdx = referencingLayer->fields().lookupField( relation.fieldPairs().at( 0 ).first );
57  if ( referencingFieldIdx != fieldIndex )
58  {
59  QgsMessageLog::logMessage( QObject::tr( "representValue() with inconsistent fieldIndex parameter w.r.t relation referencingFieldIdx" ) );
60  return value.toString();
61  }
62  QgsVectorLayer *referencedLayer = relation.referencedLayer();
63  if ( !referencedLayer )
64  {
65  QgsMessageLog::logMessage( QObject::tr( "Cannot find referenced layer" ) );
66  return value.toString();
67  }
68 
69  // Attributes from the referencing layer
70  QgsAttributes attrs = QgsAttributes( layer->fields().count() );
71  // Set the value on the foreign key field of the referencing record
72  attrs[ referencingFieldIdx ] = value;
73 
74  QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs );
75  QgsFeature feature;
76  referencedLayer->getFeatures( request ).nextFeature( feature );
77  if ( !feature.isValid() )
78  return value.toString();
79 
80  QgsExpression expr( referencedLayer->displayExpression() );
82  context.setFeature( feature );
83  QString title = expr.evaluate( &context ).toString();
84  if ( expr.hasEvalError() )
85  {
86  int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
87  title = feature.attribute( referencedFieldIdx ).toString();
88  }
89  return title;
90 }
91 
92 QVariant QgsRelationReferenceFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const
93 {
94  return representValue( layer, fieldIndex, config, cache, value );
95 }
96 
97 QVariant QgsRelationReferenceFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const
98 {
99  Q_UNUSED( fieldIndex )
100  QMap<QVariant, QString> cache;
101 
102  // Some sanity checks
103  if ( !config.contains( QStringLiteral( "Relation" ) ) )
104  {
105  QgsMessageLog::logMessage( QObject::tr( "Missing Relation in configuration" ) );
106  return QVariant();
107  }
108  QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() );
109  if ( !relation.isValid() )
110  {
111  QgsMessageLog::logMessage( QObject::tr( "Invalid relation" ) );
112  return QVariant();
113  }
114  QgsVectorLayer *referencingLayer = relation.referencingLayer();
115  if ( layer != referencingLayer )
116  {
117  QgsMessageLog::logMessage( QObject::tr( "representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ) );
118  return QVariant();
119  }
120  QgsVectorLayer *referencedLayer = relation.referencedLayer();
121  if ( !referencedLayer )
122  {
123  QgsMessageLog::logMessage( QObject::tr( "Cannot find referenced layer" ) );
124  return QVariant();
125  }
126  int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
127  if ( referencedFieldIdx == -1 )
128  {
129  QgsMessageLog::logMessage( QObject::tr( "Invalid referenced field (%1) configured in relation %2" ).arg( relation.fieldPairs().at( 0 ).second, relation.name() ) );
130  return QVariant();
131  }
132 
133  QgsExpression expr( referencedLayer->displayExpression() );
134 
135  QgsFeatureRequest request;
137  QgsAttributeList requiredAttributes = expr.referencedAttributeIndexes( referencedLayer->fields() ).toList();
138  requiredAttributes << referencedFieldIdx;
139  request.setSubsetOfAttributes( requiredAttributes );
140  QgsFeature feature;
141  auto iterator = referencedLayer->getFeatures( request );
142 
144 
145  expr.prepare( &context );
146 
147  while ( iterator.nextFeature( feature ) )
148  {
149  context.setFeature( feature );
150  QString title = expr.evaluate( &context ).toString();
151 
152  if ( expr.hasEvalError() )
153  {
154  int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second );
155  title = feature.attribute( referencedFieldIdx ).toString();
156  }
157 
158  cache.insert( feature.attribute( referencedFieldIdx ), title );
159  }
160 
161  return QVariant::fromValue<QMap<QVariant, QString>>( cache );
162 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
Class for parsing and evaluation of expressions (formerly called "search strings").
QString name
Definition: qgsrelation.h:48
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
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
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:100
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
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:438
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
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:262
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 & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.