QGIS API Documentation  2.99.0-Master (d55fa22)
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 
25  : mReferencingLayer( nullptr )
26  , mReferencedLayer( nullptr )
27  , mValid( false )
28 {
29 }
30 
31 QgsRelation QgsRelation::createFromXml( const QDomNode &node )
32 {
33  QDomElement elem = node.toElement();
34 
35  if ( elem.tagName() != QLatin1String( "relation" ) )
36  {
37  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
38  }
39 
40  QgsRelation relation;
41 
42  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
43  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
44  QString id = elem.attribute( QStringLiteral( "id" ) );
45  QString name = elem.attribute( QStringLiteral( "name" ) );
46 
47  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
48 
51 
52  if ( !referencingLayer )
53  {
54  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
55  }
56  else if ( QgsMapLayer::VectorLayer != referencingLayer->type() )
57  {
58  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
59  }
60 
61  if ( !referencedLayer )
62  {
63  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
64  }
65  else if ( QgsMapLayer::VectorLayer != referencedLayer->type() )
66  {
67  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
68  }
69 
70  relation.mReferencingLayerId = referencingLayerId;
71  relation.mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
72  relation.mReferencedLayerId = referencedLayerId;
73  relation.mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
74  relation.mRelationId = id;
75  relation.mRelationName = name;
76 
77  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
78  for ( int i = 0; i < references.size(); ++i )
79  {
80  QDomElement refEl = references.at( i ).toElement();
81 
82  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
83  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
84 
85  relation.addFieldPair( referencingField, referencedField );
86  }
87 
88  relation.updateRelationStatus();
89 
90  return relation;
91 }
92 
93 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
94 {
95  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
96  elem.setAttribute( QStringLiteral( "id" ), mRelationId );
97  elem.setAttribute( QStringLiteral( "name" ), mRelationName );
98  elem.setAttribute( QStringLiteral( "referencingLayer" ), mReferencingLayerId );
99  elem.setAttribute( QStringLiteral( "referencedLayer" ), mReferencedLayerId );
100 
101  Q_FOREACH ( const FieldPair &fields, mFieldPairs )
102  {
103  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
104  referenceElem.setAttribute( QStringLiteral( "referencingField" ), fields.first );
105  referenceElem.setAttribute( QStringLiteral( "referencedField" ), fields.second );
106  elem.appendChild( referenceElem );
107  }
108 
109  node.appendChild( elem );
110 }
111 
112 void QgsRelation::setId( const QString &id )
113 {
114  mRelationId = id;
115 
116  updateRelationStatus();
117 }
118 
119 void QgsRelation::setName( const QString &name )
120 {
121  mRelationName = name;
122 }
123 
124 void QgsRelation::setReferencingLayer( const QString &id )
125 {
126  mReferencingLayerId = id;
127 
128  updateRelationStatus();
129 }
130 
131 void QgsRelation::setReferencedLayer( const QString &id )
132 {
133  mReferencedLayerId = id;
134 
135  updateRelationStatus();
136 }
137 
138 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
139 {
140  mFieldPairs << FieldPair( referencingField, referencedField );
141  updateRelationStatus();
142 }
143 
144 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
145 {
146  mFieldPairs << fieldPair;
147  updateRelationStatus();
148 }
149 
151 {
153 }
154 
156 {
157  QString filter = getRelatedFeaturesFilter( feature );
158  QgsDebugMsg( QString( "Filter conditions: '%1'" ).arg( filter ) );
159 
160  QgsFeatureRequest myRequest;
161  myRequest.setFilterExpression( filter );
162  return myRequest;
163 }
164 
165 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
166 {
167  QStringList conditions;
168 
169  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mFieldPairs )
170  {
171  int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );
172  QgsField referencingField = referencingLayer()->fields().at( referencingIdx );
173 
174  QVariant val( feature.attribute( fieldPair.referencedField() ) );
175 
176  if ( val.isNull() )
177  {
178  conditions << QStringLiteral( "\"%1\" IS NULL" ).arg( fieldPair.referencingField() );
179  }
180  else if ( referencingField.type() == QVariant::String )
181  {
182  // Use quotes
183  conditions << QStringLiteral( "\"%1\" = '%2'" ).arg( fieldPair.referencingField(), val.toString() );
184  }
185  else
186  {
187  // No quotes
188  conditions << QStringLiteral( "\"%1\" = %2" ).arg( fieldPair.referencingField(), val.toString() );
189  }
190  }
191 
192  return conditions.join( QStringLiteral( " AND " ) );
193 }
194 
196 {
197  QStringList conditions;
198 
199  Q_FOREACH ( const QgsRelation::FieldPair &fieldPair, mFieldPairs )
200  {
201  int referencedIdx = referencedLayer()->fields().indexFromName( fieldPair.referencedField() );
202  int referencingIdx = referencingLayer()->fields().indexFromName( fieldPair.referencingField() );
203 
204  QgsField referencedField = referencedLayer()->fields().at( referencedIdx );
205 
206  if ( referencedField.type() == QVariant::String )
207  {
208  // Use quotes
209  conditions << QStringLiteral( "\"%1\" = '%2'" ).arg( fieldPair.referencedField(), attributes.at( referencingIdx ).toString() );
210  }
211  else
212  {
213  // No quotes
214  conditions << QStringLiteral( "\"%1\" = %2" ).arg( fieldPair.referencedField(), attributes.at( referencingIdx ).toString() );
215  }
216  }
217 
218  QgsFeatureRequest myRequest;
219 
220  QgsDebugMsg( QString( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
221 
222  myRequest.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
223 
224  return myRequest;
225 }
226 
228 {
229  return getReferencedFeatureRequest( feature.attributes() );
230 }
231 
233 {
234  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
235 
236  QgsFeature f;
237  mReferencedLayer->getFeatures( request ).nextFeature( f );
238  return f;
239 }
240 
241 QString QgsRelation::name() const
242 {
243  return mRelationName;
244 }
245 
246 QString QgsRelation::id() const
247 {
248  return mRelationId;
249 }
250 
252 {
253  mRelationId = QStringLiteral( "%1_%2_%3_%4" )
254  .arg( referencingLayerId(),
255  mFieldPairs.at( 0 ).referencingField(),
257  mFieldPairs.at( 0 ).referencedField() );
258  updateRelationStatus();
259 }
260 
262 {
263  return mReferencingLayerId;
264 }
265 
267 {
268  return mReferencingLayer;
269 }
270 
272 {
273  return mReferencedLayerId;
274 }
275 
277 {
278  return mReferencedLayer;
279 }
280 
281 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
282 {
283  return mFieldPairs;
284 }
285 
287 {
288  QgsAttributeList attrs;
289 
290  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
291  {
292  attrs << mReferencedLayer->fields().lookupField( pair.second );
293  }
294  return attrs;
295 }
296 
298 {
299  QgsAttributeList attrs;
300 
301  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
302  {
303  attrs << mReferencingLayer->fields().lookupField( pair.first );
304  }
305  return attrs;
306 
307 }
308 
309 bool QgsRelation::isValid() const
310 {
311  return mValid;
312 }
313 
315 {
316  return mReferencedLayerId == other.mReferencedLayerId && mReferencingLayerId == other.mReferencingLayerId && mFieldPairs == other.mFieldPairs;
317 }
318 
319 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
320 {
321  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
322  {
323  if ( pair.first == referencingField )
324  return pair.second;
325  }
326  return QString();
327 }
328 
329 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
330 {
331  Q_FOREACH ( const FieldPair &pair, mFieldPairs )
332  {
333  if ( pair.second == referencedField )
334  return pair.first;
335  }
336  return QString();
337 }
338 
339 void QgsRelation::updateRelationStatus()
340 {
341  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
342 
343  mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[mReferencingLayerId] );
344  mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[mReferencedLayerId] );
345 
346  mValid = true;
347 
348  if ( mRelationId.isEmpty() )
349  {
350  QgsDebugMsg( "Invalid relation: no ID" );
351  mValid = false;
352  }
353  else
354  {
355  if ( !mReferencedLayer )
356  {
357  QgsDebugMsg( QString( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( mReferencedLayerId ) );
358  mValid = false;
359  }
360  else if ( !mReferencingLayer )
361  {
362  QgsDebugMsg( QString( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( mReferencingLayerId ) );
363  mValid = false;
364  }
365  else
366  {
367  if ( mFieldPairs.count() < 1 )
368  {
369  QgsDebugMsg( "Invalid relation: no pair of field is specified." );
370  mValid = false;
371  }
372 
373  Q_FOREACH ( const FieldPair &fieldPair, mFieldPairs )
374  {
375  if ( -1 == mReferencingLayer->fields().lookupField( fieldPair.first ) )
376  {
377  QgsDebugMsg( QString( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( fieldPair.first, mReferencingLayer->name() ) );
378  mValid = false;
379  break;
380  }
381  else if ( -1 == mReferencedLayer->fields().lookupField( fieldPair.second ) )
382  {
383  QgsDebugMsg( QString( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( fieldPair.second, mReferencedLayer->name() ) );
384  mValid = false;
385  break;
386  }
387  }
388  }
389 
390  }
391 }
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:289
Wrapper for iterator of features from vector data provider or vector layer.
bool isValid() const
Returns the validity of this relation.
Base class for all map layer types.
Definition: qgsmaplayer.h:54
void generateId()
Generate a (project-wide) unique id for this relation.
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:124
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:24
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...
void setName(const QString &name)
Set a name for this relation.
QMap< QString, QgsMapLayer * > mapLayers() const
Returns a map of all registered layers by layer ID.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
QgsMapLayer::LayerType type() const
Returns the type of the layer.
QString id() const
A (project-wide) unique id for this relation.
void setId(const QString &id)
Set an id for this relation.
QgsFields fields() const
Returns the list of fields of this layer.
QgsField at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:135
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:174
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:60
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Get the referenced field counterpart given a referencing field.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsVectorLayer * referencedLayer() const
Access the referenced (parent) layer.
static QgsRelation createFromXml(const QDomNode &node)
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:31
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Q_INVOKABLE QString resolveReferencingField(const QString &referencedField) const
Get the referencing field counterpart given a referenced field.
bool hasEqualDefinition(const QgsRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
QString referencingLayerId() const
Access the referencing (child) layer&#39;s id This is the layer which has the field(s) which point to ano...
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:377
QString referencedField() const
Get the name of the referenced (parent) field.
Definition: qgsrelation.h:74
QString name
Definition: qgsmaplayer.h:58
QList< int > QgsAttributeList
Definition: qgsfield.h:28
bool nextFeature(QgsFeature &f)
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:94
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:255
QString referencedLayerId() const
Access the referenced (parent) layer&#39;s id.
QString referencingField() const
Get the name of the referencing (child) field.
Definition: qgsrelation.h:72
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Definition: qgsrelation.cpp:93
QgsAttributes attributes
Definition: qgsfeature.h:71
QString name() const
Returns a human readable name for this relation.