QGIS API Documentation  2.99.0-Master (69af2f5)
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.
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
QgsFields fields() const override
Returns the list of fields of this layer.
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:46
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 getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
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:27
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.