QGIS API Documentation  3.27.0-Master (aef1b1ec20)
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 #include "qgspolymorphicrelation.h"
25 #include "qgsrelationmanager.h"
26 
28  : d( new QgsRelationPrivate() )
29 {
30 }
31 
33  : d( new QgsRelationPrivate() )
34  , mContext( context )
35 {
36 }
37 
38 QgsRelation::~QgsRelation() = default;
39 
41  : d( other.d )
42  , mContext( other.mContext )
43 {
44 }
45 
47 {
48  d = other.d;
49  mContext = other.mContext;
50  return *this;
51 }
52 
53 QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
54 {
55  QDomElement elem = node.toElement();
56 
57  if ( elem.tagName() != QLatin1String( "relation" ) )
58  {
59  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
60  }
61 
62  QgsRelation relation( relationContext );
63 
64  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
65  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
66  QString id = elem.attribute( QStringLiteral( "id" ) );
67  QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
68  QString strength = elem.attribute( QStringLiteral( "strength" ) );
69 
70  QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
71 
74 
75  if ( !referencingLayer )
76  {
77  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
78  }
80  {
81  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
82  }
83 
84  if ( !referencedLayer )
85  {
86  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
87  }
89  {
90  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
91  }
92 
93  relation.d->mReferencingLayerId = referencingLayerId;
94  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
95  relation.d->mReferencedLayerId = referencedLayerId;
96  relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
97  relation.d->mRelationId = id;
98  relation.d->mRelationName = name;
99  relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( strength, RelationStrength::Association );
100 
101  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
102  for ( int i = 0; i < references.size(); ++i )
103  {
104  QDomElement refEl = references.at( i ).toElement();
105 
106  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
107  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
108 
109  relation.addFieldPair( referencingField, referencedField );
110  }
111 
112  relation.updateRelationStatus();
113 
114  return relation;
115 }
116 
117 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
118 {
119  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
120  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
121  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
122  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
123  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
124  elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<RelationStrength>( d->mRelationStrength ) );
125 
126  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
127  {
128  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
129  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
130  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
131  elem.appendChild( referenceElem );
132  }
133 
134  node.appendChild( elem );
135 }
136 
137 void QgsRelation::setId( const QString &id )
138 {
139  d.detach();
140  d->mRelationId = id;
141 
143 }
144 
145 void QgsRelation::setName( const QString &name )
146 {
147  d.detach();
148  d->mRelationName = name;
149 }
150 
151 
153 {
154  d.detach();
155  d->mRelationStrength = strength;
156 }
157 
158 void QgsRelation::setReferencingLayer( const QString &id )
159 {
160  d.detach();
161  d->mReferencingLayerId = id;
162 
164 }
165 
166 void QgsRelation::setReferencedLayer( const QString &id )
167 {
168  d.detach();
169  d->mReferencedLayerId = id;
170 
172 }
173 
174 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
175 {
176  d.detach();
177  d->mFieldPairs << FieldPair( referencingField, referencedField );
179 }
180 
181 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
182 {
183  d.detach();
184  d->mFieldPairs << fieldPair;
186 }
187 
189 {
191 }
192 
194 {
195  QString filter = getRelatedFeaturesFilter( feature );
196  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
197 
198  QgsFeatureRequest myRequest;
199  myRequest.setFilterExpression( filter );
200  return myRequest;
201 }
202 
203 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
204 {
205  QStringList conditions;
206 
207  if ( ! d->mPolymorphicRelationId.isEmpty() )
208  {
210  if ( polyRel.isValid() )
211  {
213  }
214  else
215  {
216  QgsDebugMsg( "The polymorphic relation is invalid" );
217  conditions << QStringLiteral( " FALSE " );
218  }
219  }
220 
221  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
222  {
223  QVariant val( feature.attribute( pair.referencedField() ) );
224  int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
225  if ( referencingIdx >= 0 )
226  {
227  QVariant::Type fieldType = referencingLayer()->fields().at( referencingIdx ).type();
228  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val, fieldType );
229  }
230  else
231  {
232  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
233  }
234  }
235 
236  return conditions.join( QLatin1String( " AND " ) );
237 }
238 
240 {
241  QStringList conditions;
242 
243  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
244  {
245  int referencedIdx = referencedLayer()->fields().lookupField( pair.referencedField() );
246  int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
247  if ( referencedIdx >= 0 )
248  {
249  QVariant::Type fieldType = referencedLayer()->fields().at( referencedIdx ).type();
250  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ), fieldType );
251  }
252  else
253  {
254  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
255  }
256  }
257 
258  QgsFeatureRequest myRequest;
259 
260  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
261 
262  myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
263 
264  return myRequest;
265 }
266 
268 {
269  return getReferencedFeatureRequest( feature.attributes() );
270 }
271 
273 {
274  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
275 
276  QgsFeature f;
277  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
278  return f;
279 }
280 
281 QString QgsRelation::name() const
282 {
283  return d->mRelationName;
284 }
285 
287 {
288  return d->mRelationStrength;
289 }
290 
291 QString QgsRelation::id() const
292 {
293  return d->mRelationId;
294 }
295 
297 {
298  if ( !d->mFieldPairs.isEmpty() )
299  {
300  const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 );
301  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
302  .arg( referencingLayerId(),
303  fieldPair.referencingField(),
305  fieldPair.referencedField() );
306  }
308 }
309 
311 {
312  return d->mReferencingLayerId;
313 }
314 
316 {
317  return d->mReferencingLayer;
318 }
319 
321 {
322  return d->mReferencedLayerId;
323 }
324 
326 {
327  return d->mReferencedLayer;
328 }
329 
330 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
331 {
332  return d->mFieldPairs;
333 }
334 
336 {
337  QgsAttributeList attrs;
338  attrs.reserve( d->mFieldPairs.size() );
339  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
340  {
341  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
342  }
343  return attrs;
344 }
345 
347 {
348  QgsAttributeList attrs;
349 
350  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
351  {
352  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
353  }
354  return attrs;
355 
356 }
357 
359 {
360  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
361 }
362 
364 {
365  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
366 }
367 
368 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
369 {
370  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
371  {
372  if ( pair.first == referencingField )
373  return pair.second;
374  }
375  return QString();
376 }
377 
378 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
379 {
380  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
381  {
382  if ( pair.second == referencedField )
383  return pair.first;
384  }
385  return QString();
386 }
387 
389 {
390  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
391 
392  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
393  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
394 
395  d->mValid = true;
396 
397  if ( d->mRelationId.isEmpty() )
398  {
399  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
400  d->mValid = false;
401  }
402  else
403  {
404  if ( !d->mReferencedLayer )
405  {
406  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
407  d->mValid = false;
408  }
409  else if ( !d->mReferencingLayer )
410  {
411  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
412  d->mValid = false;
413  }
414  else
415  {
416  if ( d->mFieldPairs.count() < 1 )
417  {
418  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
419  d->mValid = false;
420  }
421 
422  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
423  {
424  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
425  {
426  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
427  d->mValid = false;
428  break;
429  }
430  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
431  {
432  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
433  d->mValid = false;
434  break;
435  }
436  }
437  }
438 
439  }
440 }
441 
442 void QgsRelation::setPolymorphicRelationId( const QString &polymorphicRelationId )
443 {
444  d.detach();
445  d->mPolymorphicRelationId = polymorphicRelationId;
446 }
447 
449 {
450  return d->mPolymorphicRelationId;
451 }
452 
454 {
455  if ( ! mContext.project() || ! mContext.project()->relationManager() )
456  return QgsPolymorphicRelation();
457 
458  return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
459 }
460 
462 {
463  if ( d->mPolymorphicRelationId.isNull() )
464  return QgsRelation::Normal;
465  else
466  return QgsRelation::Generated;
467 }
A vector of attributes.
Definition: qgsattributes.h:58
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QVariant::Type fieldType=QVariant::Type::Invalid)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:320
QVariant::Type type
Definition: qgsfield.h:58
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
Base class for all map layer types.
Definition: qgsmaplayer.h:73
QgsMapLayerType type
Definition: qgsmaplayer.h:80
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
QString layerRepresentation(const QgsVectorLayer *layer) const
Returns layer representation as evaluated string.
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.
QgsRelationManager * relationManager
Definition: qgsproject.h:114
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
The class is used as a container of context for various read/write operations on other objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
Context for relations.
const QgsProject * project() const
Gets the associated project.
QgsPolymorphicRelation polymorphicRelation(const QString &polymorphicRelationId) const
Returns the list of relations associated with a polymorphic relation.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:89
QString referencingField() const
Gets the name of the referencing (child) field.
Definition: qgsrelation.h:99
QString referencedField() const
Gets the name of the referenced (parent) field.
Definition: qgsrelation.h:101
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
QString name
Definition: qgsrelation.h:49
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
void setId(const QString &id)
Set an id for this relation.
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:27
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
void setPolymorphicRelationId(const QString &polymorphicRelationId)
Sets the parent polymorphic relation id.
QgsRelation & operator=(const QgsRelation &other)
Copies a relation.
Definition: qgsrelation.cpp:46
void generateId()
Generate a (project-wide) unique id for this relation.
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 QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:53
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
QString polymorphicRelationId
Definition: qgsrelation.h:51
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
void addFieldPair(const QString &referencingField, const QString &referencedField)
Add a field pair which is part of this relation The first element of each pair are the field names of...
RelationType
Enum holding the relations type.
Definition: qgsrelation.h:60
@ Generated
A generated relation is a child of a polymorphic relation.
Definition: qgsrelation.h:62
@ Normal
A normal relation.
Definition: qgsrelation.h:61
void setReferencingLayer(const QString &id)
Set the referencing (child) 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...
Q_GADGET QString id
Definition: qgsrelation.h:46
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:71
RelationType type() const
Returns the type of the relation.
QgsPolymorphicRelation polymorphicRelation
Definition: qgsrelation.h:52
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
void setStrength(RelationStrength strength)
Set a strength for this relation.
bool isValid
Definition: qgsrelation.h:50
QString referencedLayerId() const
Access the referenced (parent) layer's id.
void setName(const QString &name)
Set a name for this relation.
RelationStrength strength() const
Returns the relation strength as a string.
QString referencingLayerId() const
Access the referencing (child) layer's id This is the layer which has the field(s) which point to ano...
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
void updateRelationStatus()
Updates the validity status of this relation.
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
@ VectorLayer
Vector layer.
QList< int > QgsAttributeList
Definition: qgsfield.h:26
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38