QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsrelation.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelation.cpp
3  --------------------------------------
4  Date : 29.4.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias at opengis dot ch
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsrelation.h"
17 
18 #include "qgsapplication.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgslogger.h"
21 #include "qgsproject.h"
22 #include "qgsvectorlayer.h"
23 #include "qgsrelation_p.h"
24 
26  : d( new QgsRelationPrivate() )
27 {
28 }
29 
31  : d( new QgsRelationPrivate() )
32  , mContext( context )
33 {
34 }
35 
36 QgsRelation::~QgsRelation() = default;
37 
39  : d( other.d )
40  , mContext( other.mContext )
41 {
42 }
43 
45 {
46  d = other.d;
47  mContext = other.mContext;
48  return *this;
49 }
50 
51 QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
52 {
53  QDomElement elem = node.toElement();
54 
55  if ( elem.tagName() != QLatin1String( "relation" ) )
56  {
57  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
58  }
59 
60  QgsRelation relation( relationContext );
61 
62  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
63  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
64  QString id = elem.attribute( QStringLiteral( "id" ) );
65  QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
66  QString strength = elem.attribute( QStringLiteral( "strength" ) );
67 
68  QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
69 
72 
73  if ( !referencingLayer )
74  {
75  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
76  }
78  {
79  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
80  }
81 
82  if ( !referencedLayer )
83  {
84  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
85  }
87  {
88  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
89  }
90 
91  relation.d->mReferencingLayerId = referencingLayerId;
92  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
93  relation.d->mReferencedLayerId = referencedLayerId;
94  relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
95  relation.d->mRelationId = id;
96  relation.d->mRelationName = name;
97  relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( strength, RelationStrength::Association );
98 
99  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
100  for ( int i = 0; i < references.size(); ++i )
101  {
102  QDomElement refEl = references.at( i ).toElement();
103 
104  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
105  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
106 
107  relation.addFieldPair( referencingField, referencedField );
108  }
109 
110  relation.updateRelationStatus();
111 
112  return relation;
113 }
114 
115 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
116 {
117  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
118  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
119  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
120  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
121  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
122  elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<RelationStrength>( d->mRelationStrength ) );
123 
124  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
125  {
126  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
127  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
128  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
129  elem.appendChild( referenceElem );
130  }
131 
132  node.appendChild( elem );
133 }
134 
135 void QgsRelation::setId( const QString &id )
136 {
137  d.detach();
138  d->mRelationId = id;
139 
141 }
142 
143 void QgsRelation::setName( const QString &name )
144 {
145  d.detach();
146  d->mRelationName = name;
147 }
148 
149 
151 {
152  d.detach();
153  d->mRelationStrength = strength;
154 }
155 
156 void QgsRelation::setReferencingLayer( const QString &id )
157 {
158  d.detach();
159  d->mReferencingLayerId = id;
160 
162 }
163 
164 void QgsRelation::setReferencedLayer( const QString &id )
165 {
166  d.detach();
167  d->mReferencedLayerId = id;
168 
170 }
171 
172 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
173 {
174  d.detach();
175  d->mFieldPairs << FieldPair( referencingField, referencedField );
177 }
178 
179 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
180 {
181  d.detach();
182  d->mFieldPairs << fieldPair;
184 }
185 
187 {
189 }
190 
192 {
193  QString filter = getRelatedFeaturesFilter( feature );
194  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
195 
196  QgsFeatureRequest myRequest;
197  myRequest.setFilterExpression( filter );
198  return myRequest;
199 }
200 
201 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
202 {
203  QStringList conditions;
204 
205  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
206  {
207  QVariant val( feature.attribute( pair.referencedField() ) );
208  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
209  }
210 
211  return conditions.join( QLatin1String( " AND " ) );
212 }
213 
215 {
216  QStringList conditions;
217 
218  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
219  {
220  int referencingIdx = referencingLayer()->fields().indexFromName( pair.referencingField() );
221  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
222  }
223 
224  QgsFeatureRequest myRequest;
225 
226  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
227 
228  myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
229 
230  return myRequest;
231 }
232 
234 {
235  return getReferencedFeatureRequest( feature.attributes() );
236 }
237 
239 {
240  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
241 
242  QgsFeature f;
243  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
244  return f;
245 }
246 
247 QString QgsRelation::name() const
248 {
249  return d->mRelationName;
250 }
251 
253 {
254  return d->mRelationStrength;
255 }
256 
257 QString QgsRelation::id() const
258 {
259  return d->mRelationId;
260 }
261 
263 {
264  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
265  .arg( referencingLayerId(),
266  d->mFieldPairs.at( 0 ).referencingField(),
268  d->mFieldPairs.at( 0 ).referencedField() );
270 }
271 
273 {
274  return d->mReferencingLayerId;
275 }
276 
278 {
279  return d->mReferencingLayer;
280 }
281 
283 {
284  return d->mReferencedLayerId;
285 }
286 
288 {
289  return d->mReferencedLayer;
290 }
291 
292 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
293 {
294  return d->mFieldPairs;
295 }
296 
298 {
299  QgsAttributeList attrs;
300 
301  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
302  {
303  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
304  }
305  return attrs;
306 }
307 
309 {
310  QgsAttributeList attrs;
311 
312  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
313  {
314  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
315  }
316  return attrs;
317 
318 }
319 
321 {
322  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
323 }
324 
326 {
327  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
328 }
329 
330 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
331 {
332  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
333  {
334  if ( pair.first == referencingField )
335  return pair.second;
336  }
337  return QString();
338 }
339 
340 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
341 {
342  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
343  {
344  if ( pair.second == referencedField )
345  return pair.first;
346  }
347  return QString();
348 }
349 
351 {
352  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
353 
354  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
355  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
356 
357  d->mValid = true;
358 
359  if ( d->mRelationId.isEmpty() )
360  {
361  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
362  d->mValid = false;
363  }
364  else
365  {
366  if ( !d->mReferencedLayer )
367  {
368  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
369  d->mValid = false;
370  }
371  else if ( !d->mReferencingLayer )
372  {
373  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
374  d->mValid = false;
375  }
376  else
377  {
378  if ( d->mFieldPairs.count() < 1 )
379  {
380  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
381  d->mValid = false;
382  }
383 
384  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
385  {
386  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
387  {
388  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
389  d->mValid = false;
390  break;
391  }
392  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
393  {
394  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
395  d->mValid = false;
396  break;
397  }
398  }
399  }
400 
401  }
402 }
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsRelation::referencingFields
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
Definition: qgsrelation.cpp:308
QgsRelation::QgsRelation
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:25
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:35
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsProject::mapLayers
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Definition: qgsproject.cpp:3436
QgsRelation::updateRelationStatus
void updateRelationStatus()
Updates the validity status of this relation.
Definition: qgsrelation.cpp:350
QgsRelation::name
QString name
Definition: qgsrelation.h:48
QgsRelation::addFieldPair
void addFieldPair(const QString &referencingField, const QString &referencedField)
Add a field pairs which is part of this relation The first element of each pair are the field names o...
Definition: qgsrelation.cpp:172
qgsfeatureiterator.h
QgsRelation::id
Q_GADGET QString id
Definition: qgsrelation.h:45
QgsRelation::referencedFields
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
Definition: qgsrelation.cpp:297
QgsRelation::strength
RelationStrength strength() const
Returns the relation strength as a string.
Definition: qgsrelation.cpp:252
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsRelation::setStrength
void setStrength(RelationStrength strength)
Set a strength for this relation.
Definition: qgsrelation.cpp:150
QgsRelationContext::project
const QgsProject * project() const
Gets the associated project.
Definition: qgsrelationcontext.cpp:36
qgsrelation_p.h
QgsRelation::setReferencingLayer
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
Definition: qgsrelation.cpp:156
QgsRelation::referencingLayer
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:46
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:124
QgsRelation::resolveReferencedField
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
Definition: qgsrelation.cpp:330
qgsapplication.h
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3283
QgsFeatureRequest
This class wraps a request for features to a vector layer (or directly its vector data provider).
Definition: qgsfeaturerequest.h:76
QgsRelation::setName
void setName(const QString &name)
Set a name for this relation.
Definition: qgsrelation.cpp:143
QgsReadWriteContext::projectTranslator
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
Definition: qgsreadwritecontext.h:108
QgsRelation::referencedLayer
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
QgsRelation::setId
void setId(const QString &id)
Set an id for this relation.
Definition: qgsrelation.cpp:135
QgsRelation::resolveReferencingField
Q_INVOKABLE QString resolveReferencingField(const QString &referencedField) const
Gets the referencing field counterpart given a referenced field.
Definition: qgsrelation.cpp:340
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsRelation::getReferencedFeatureRequest
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Definition: qgsrelation.cpp:214
qgsrelation.h
QgsRelation::FieldPair
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:75
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:65
qgsvectorlayer.h
QgsRelation::fieldPairs
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
Definition: qgsrelation.cpp:292
QgsRelation::getRelatedFeatures
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
Definition: qgsrelation.cpp:186
QgsRelation::getRelatedFeaturesRequest
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Definition: qgsrelation.cpp:191
QgsRelation::~QgsRelation
~QgsRelation()
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1079
QgsRelation::hasEqualDefinition
bool hasEqualDefinition(const QgsRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
Definition: qgsrelation.cpp:325
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:387
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsRelation::setReferencedLayer
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
Definition: qgsrelation.cpp:164
QgsRelation::isValid
bool isValid
Definition: qgsrelation.h:49
QgsRelation::operator=
QgsRelation & operator=(const QgsRelation &other)
Copies a relation.
Definition: qgsrelation.cpp:44
QgsRelation::createFromXml
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:51
QgsRelation::referencingLayerId
QString referencingLayerId() const
Access the referencing (child) layer's id This is the layer which has the field(s) which point to ano...
Definition: qgsrelation.cpp:272
QgsRelation
Definition: qgsrelation.h:42
QgsAttributes
A vector of attributes.
Definition: qgsattributes.h:58
QgsRelation::RelationStrength
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:58
QgsFeature
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:56
QgsRelation::getRelatedFeaturesFilter
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
Definition: qgsrelation.cpp:201
QgsRelationContext
Context for relations.
Definition: qgsrelationcontext.h:32
qgslogger.h
QgsRelation::writeXml
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Definition: qgsrelation.cpp:115
QgsFeatureIterator
Wrapper for iterator of features from vector data provider or vector layer.
Definition: qgsfeatureiterator.h:265
QgsRelation::generateId
void generateId()
Generate a (project-wide) unique id for this relation.
Definition: qgsrelation.cpp:262
QgsRelation::getReferencedFeature
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Definition: qgsrelation.cpp:238
qgsproject.h
QgsFields::indexFromName
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsRelation::referencedLayerId
QString referencedLayerId() const
Access the referenced (parent) layer's id.
Definition: qgsrelation.cpp:282
QgsProjectTranslator::translate
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
The derived translate() translates with QTranslator and qm file the sourceText.
QgsMapLayer::type
QgsMapLayerType type
Definition: qgsmaplayer.h:90