QGIS API Documentation  3.12.1-BucureČ™ti (121cc00ff0)
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  }
77  else if ( QgsMapLayerType::VectorLayer != referencingLayer->type() )
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  }
86  else if ( QgsMapLayerType::VectorLayer != referencedLayer->type() )
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  if ( strength == QLatin1String( "Composition" ) )
98  {
99  relation.d->mRelationStrength = RelationStrength::Composition;
100  }
101  else
102  {
103  relation.d->mRelationStrength = RelationStrength::Association;
104  }
105 
106  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
107  for ( int i = 0; i < references.size(); ++i )
108  {
109  QDomElement refEl = references.at( i ).toElement();
110 
111  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
112  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
113 
114  relation.addFieldPair( referencingField, referencedField );
115  }
116 
117  relation.updateRelationStatus();
118 
119  return relation;
120 }
121 
122 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
123 {
124  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
125  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
126  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
127  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
128  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
129  if ( d->mRelationStrength == RelationStrength::Composition )
130  {
131  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Composition" ) );
132  }
133  else
134  {
135  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Association" ) );
136  }
137 
138  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
139  {
140  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
141  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
142  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
143  elem.appendChild( referenceElem );
144  }
145 
146  node.appendChild( elem );
147 }
148 
149 void QgsRelation::setId( const QString &id )
150 {
151  d.detach();
152  d->mRelationId = id;
153 
155 }
156 
157 void QgsRelation::setName( const QString &name )
158 {
159  d.detach();
160  d->mRelationName = name;
161 }
162 
163 
165 {
166  d.detach();
167  d->mRelationStrength = strength;
168 }
169 
170 void QgsRelation::setReferencingLayer( const QString &id )
171 {
172  d.detach();
173  d->mReferencingLayerId = id;
174 
176 }
177 
178 void QgsRelation::setReferencedLayer( const QString &id )
179 {
180  d.detach();
181  d->mReferencedLayerId = id;
182 
184 }
185 
186 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
187 {
188  d.detach();
189  d->mFieldPairs << FieldPair( referencingField, referencedField );
191 }
192 
193 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
194 {
195  d.detach();
196  d->mFieldPairs << fieldPair;
198 }
199 
201 {
203 }
204 
206 {
207  QString filter = getRelatedFeaturesFilter( feature );
208  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ) );
209 
210  QgsFeatureRequest myRequest;
211  myRequest.setFilterExpression( filter );
212  return myRequest;
213 }
214 
215 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
216 {
217  QStringList conditions;
218 
219  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
220  {
221  QVariant val( feature.attribute( pair.referencedField() ) );
222  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
223  }
224 
225  return conditions.join( QStringLiteral( " AND " ) );
226 }
227 
229 {
230  QStringList conditions;
231 
232  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
233  {
234  int referencingIdx = referencingLayer()->fields().indexFromName( pair.referencingField() );
235  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
236  }
237 
238  QgsFeatureRequest myRequest;
239 
240  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
241 
242  myRequest.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
243 
244  return myRequest;
245 }
246 
248 {
249  return getReferencedFeatureRequest( feature.attributes() );
250 }
251 
253 {
254  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
255 
256  QgsFeature f;
257  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
258  return f;
259 }
260 
261 QString QgsRelation::name() const
262 {
263  return d->mRelationName;
264 }
265 
267 {
268  return d->mRelationStrength;
269 }
270 
271 QString QgsRelation::id() const
272 {
273  return d->mRelationId;
274 }
275 
277 {
278  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
279  .arg( referencingLayerId(),
280  d->mFieldPairs.at( 0 ).referencingField(),
282  d->mFieldPairs.at( 0 ).referencedField() );
284 }
285 
287 {
288  return d->mReferencingLayerId;
289 }
290 
292 {
293  return d->mReferencingLayer;
294 }
295 
297 {
298  return d->mReferencedLayerId;
299 }
300 
302 {
303  return d->mReferencedLayer;
304 }
305 
306 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
307 {
308  return d->mFieldPairs;
309 }
310 
312 {
313  QgsAttributeList attrs;
314 
315  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
316  {
317  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
318  }
319  return attrs;
320 }
321 
323 {
324  QgsAttributeList attrs;
325 
326  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
327  {
328  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
329  }
330  return attrs;
331 
332 }
333 
334 bool QgsRelation::isValid() const
335 {
336  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
337 }
338 
340 {
341  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
342 }
343 
344 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
345 {
346  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
347  {
348  if ( pair.first == referencingField )
349  return pair.second;
350  }
351  return QString();
352 }
353 
354 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
355 {
356  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
357  {
358  if ( pair.second == referencedField )
359  return pair.first;
360  }
361  return QString();
362 }
363 
365 {
366  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
367 
368  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
369  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
370 
371  d->mValid = true;
372 
373  if ( d->mRelationId.isEmpty() )
374  {
375  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
376  d->mValid = false;
377  }
378  else
379  {
380  if ( !d->mReferencedLayer )
381  {
382  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
383  d->mValid = false;
384  }
385  else if ( !d->mReferencingLayer )
386  {
387  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
388  d->mValid = false;
389  }
390  else
391  {
392  if ( d->mFieldPairs.count() < 1 )
393  {
394  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
395  d->mValid = false;
396  }
397 
398  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
399  {
400  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
401  {
402  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
403  d->mValid = false;
404  break;
405  }
406  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
407  {
408  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
409  d->mValid = false;
410  break;
411  }
412  }
413  }
414 
415  }
416 }
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.
const QgsProject * project() const
Gets the associated project.
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:122
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
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
Context for relations.
#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...
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:51
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QList< int > QgsAttributeList
Definition: qgsfield.h:26
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:44
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.