QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 
30 QgsRelation::~QgsRelation() = default;
31 
33  : d( other.d )
34 {
35 }
36 
38 {
39  d = other.d;
40  return *this;
41 }
42 
44 {
45  QDomElement elem = node.toElement();
46 
47  if ( elem.tagName() != QLatin1String( "relation" ) )
48  {
49  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
50  }
51 
52  QgsRelation relation;
53 
54  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
55  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
56  QString id = elem.attribute( QStringLiteral( "id" ) );
57  QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
58  QString strength = elem.attribute( QStringLiteral( "strength" ) );
59 
60  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
61 
64 
65  if ( !referencingLayer )
66  {
67  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
68  }
69  else if ( QgsMapLayerType::VectorLayer != referencingLayer->type() )
70  {
71  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
72  }
73 
74  if ( !referencedLayer )
75  {
76  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
77  }
78  else if ( QgsMapLayerType::VectorLayer != referencedLayer->type() )
79  {
80  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
81  }
82 
83  relation.d->mReferencingLayerId = referencingLayerId;
84  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
85  relation.d->mReferencedLayerId = referencedLayerId;
86  relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
87  relation.d->mRelationId = id;
88  relation.d->mRelationName = name;
89  if ( strength == QLatin1String( "Composition" ) )
90  {
91  relation.d->mRelationStrength = RelationStrength::Composition;
92  }
93  else
94  {
95  relation.d->mRelationStrength = RelationStrength::Association;
96  }
97 
98  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
99  for ( int i = 0; i < references.size(); ++i )
100  {
101  QDomElement refEl = references.at( i ).toElement();
102 
103  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
104  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
105 
106  relation.addFieldPair( referencingField, referencedField );
107  }
108 
109  relation.updateRelationStatus();
110 
111  return relation;
112 }
113 
114 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
115 {
116  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
117  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
118  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
119  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
120  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
121  if ( d->mRelationStrength == RelationStrength::Composition )
122  {
123  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Composition" ) );
124  }
125  else
126  {
127  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Association" ) );
128  }
129 
130  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
131  {
132  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
133  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
134  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
135  elem.appendChild( referenceElem );
136  }
137 
138  node.appendChild( elem );
139 }
140 
141 void QgsRelation::setId( const QString &id )
142 {
143  d.detach();
144  d->mRelationId = id;
145 
147 }
148 
149 void QgsRelation::setName( const QString &name )
150 {
151  d.detach();
152  d->mRelationName = name;
153 }
154 
155 
157 {
158  d.detach();
159  d->mRelationStrength = strength;
160 }
161 
162 void QgsRelation::setReferencingLayer( const QString &id )
163 {
164  d.detach();
165  d->mReferencingLayerId = id;
166 
168 }
169 
170 void QgsRelation::setReferencedLayer( const QString &id )
171 {
172  d.detach();
173  d->mReferencedLayerId = id;
174 
176 }
177 
178 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
179 {
180  d.detach();
181  d->mFieldPairs << FieldPair( referencingField, referencedField );
183 }
184 
185 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
186 {
187  d.detach();
188  d->mFieldPairs << fieldPair;
190 }
191 
193 {
195 }
196 
198 {
199  QString filter = getRelatedFeaturesFilter( feature );
200  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ) );
201 
202  QgsFeatureRequest myRequest;
203  myRequest.setFilterExpression( filter );
204  return myRequest;
205 }
206 
207 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
208 {
209  QStringList conditions;
210 
211  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
212  {
213  QVariant val( feature.attribute( pair.referencedField() ) );
214  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
215  }
216 
217  return conditions.join( QStringLiteral( " AND " ) );
218 }
219 
221 {
222  QStringList conditions;
223 
224  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
225  {
226  int referencingIdx = referencingLayer()->fields().indexFromName( pair.referencingField() );
227  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
228  }
229 
230  QgsFeatureRequest myRequest;
231 
232  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
233 
234  myRequest.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
235 
236  return myRequest;
237 }
238 
240 {
241  return getReferencedFeatureRequest( feature.attributes() );
242 }
243 
245 {
246  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
247 
248  QgsFeature f;
249  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
250  return f;
251 }
252 
253 QString QgsRelation::name() const
254 {
255  return d->mRelationName;
256 }
257 
259 {
260  return d->mRelationStrength;
261 }
262 
263 QString QgsRelation::id() const
264 {
265  return d->mRelationId;
266 }
267 
269 {
270  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
271  .arg( referencingLayerId(),
272  d->mFieldPairs.at( 0 ).referencingField(),
274  d->mFieldPairs.at( 0 ).referencedField() );
276 }
277 
279 {
280  return d->mReferencingLayerId;
281 }
282 
284 {
285  return d->mReferencingLayer;
286 }
287 
289 {
290  return d->mReferencedLayerId;
291 }
292 
294 {
295  return d->mReferencedLayer;
296 }
297 
298 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
299 {
300  return d->mFieldPairs;
301 }
302 
304 {
305  QgsAttributeList attrs;
306 
307  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
308  {
309  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
310  }
311  return attrs;
312 }
313 
315 {
316  QgsAttributeList attrs;
317 
318  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
319  {
320  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
321  }
322  return attrs;
323 
324 }
325 
326 bool QgsRelation::isValid() const
327 {
328  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
329 }
330 
332 {
333  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
334 }
335 
336 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
337 {
338  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
339  {
340  if ( pair.first == referencingField )
341  return pair.second;
342  }
343  return QString();
344 }
345 
346 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
347 {
348  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
349  {
350  if ( pair.second == referencedField )
351  return pair.first;
352  }
353  return QString();
354 }
355 
357 {
358  const QMap<QString, QgsMapLayer *> &mapLayers = QgsProject::instance()->mapLayers();
359 
360  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
361  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
362 
363  d->mValid = true;
364 
365  if ( d->mRelationId.isEmpty() )
366  {
367  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
368  d->mValid = false;
369  }
370  else
371  {
372  if ( !d->mReferencedLayer )
373  {
374  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
375  d->mValid = false;
376  }
377  else if ( !d->mReferencingLayer )
378  {
379  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
380  d->mValid = false;
381  }
382  else
383  {
384  if ( d->mFieldPairs.count() < 1 )
385  {
386  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
387  d->mValid = false;
388  }
389 
390  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
391  {
392  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
393  {
394  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
395  d->mValid = false;
396  break;
397  }
398  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
399  {
400  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
401  d->mValid = false;
402  break;
403  }
404  }
405  }
406 
407  }
408 }
The class is used as a container of context for various read/write operations on other objects...
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:79
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...
QgsMapLayerType type() const
Returns the type of the layer.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:121
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context)
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:43
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:25
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QString id() const
A (project-wide) unique id for this relation.
void setId(const QString &id)
Set an id for this relation.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
void setStrength(RelationStrength strength)
Set a strength for this relation.
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
Gets the field index from the field name.
Definition: qgsfields.cpp:202
RelationStrength strength() const
Returns the relation strength as a string.
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.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:74
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets 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.
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
Gets the referencing field counterpart given a referenced field.
bool hasEqualDefinition(const QgsRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer 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...
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.
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...
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:57
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:442
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
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 attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsRelation & operator=(const QgsRelation &other)
Copies a relation.
Definition: qgsrelation.cpp:37
QString referencedLayerId() const
Access the referenced (parent) layer&#39;s id.
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
QgsAttributes attributes
Definition: qgsfeature.h:65
QString name() const
Returns a human readable name for this relation.
void updateRelationStatus()
Updates the validity status of this relation.