QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
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 "qgsfeatureiterator.h"
19 #include "qgslogger.h"
20 #include "qgsproject.h"
21 #include "qgsvectorlayer.h"
22 #include "qgsrelation_p.h"
23 #include "qgspolymorphicrelation.h"
24 #include "qgsrelationmanager.h"
25 
26 #include <QApplication>
27 
29  : d( new QgsRelationPrivate() )
30 {
31 }
32 
34  : d( new QgsRelationPrivate() )
35  , mContext( context )
36 {
37 }
38 
39 QgsRelation::~QgsRelation() = default;
40 
42  : d( other.d )
43  , mContext( other.mContext )
44 {
45 }
46 
48 {
49  d = other.d;
50  mContext = other.mContext;
51  return *this;
52 }
53 
54 QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
55 {
56  QDomElement elem = node.toElement();
57 
58  if ( elem.tagName() != QLatin1String( "relation" ) )
59  {
60  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
61  }
62 
63  QgsRelation relation( relationContext );
64 
65  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
66  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
67  QString id = elem.attribute( QStringLiteral( "id" ) );
68  QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
69  QString strength = elem.attribute( QStringLiteral( "strength" ) );
70 
71  QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
72 
75 
76  if ( !referencingLayer )
77  {
78  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
79  }
81  {
82  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
83  }
84 
85  if ( !referencedLayer )
86  {
87  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
88  }
90  {
91  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
92  }
93 
94  relation.d->mReferencingLayerId = referencingLayerId;
95  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
96  relation.d->mReferencedLayerId = referencedLayerId;
97  relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
98  relation.d->mRelationId = id;
99  relation.d->mRelationName = name;
100  relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( strength, Qgis::RelationshipStrength::Association );
101 
102  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
103  for ( int i = 0; i < references.size(); ++i )
104  {
105  QDomElement refEl = references.at( i ).toElement();
106 
107  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
108  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
109 
110  relation.addFieldPair( referencingField, referencedField );
111  }
112 
113  relation.updateRelationStatus();
114 
115  return relation;
116 }
117 
118 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
119 {
120  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
121  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
122  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
123  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
124  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
125  elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
126 
127  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
128  {
129  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
130  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
131  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
132  elem.appendChild( referenceElem );
133  }
134 
135  node.appendChild( elem );
136 }
137 
138 void QgsRelation::setId( const QString &id )
139 {
140  d.detach();
141  d->mRelationId = id;
142 
144 }
145 
146 void QgsRelation::setName( const QString &name )
147 {
148  d.detach();
149  d->mRelationName = name;
150 }
151 
152 
154 {
155  d.detach();
156  d->mRelationStrength = strength;
157 }
158 
159 void QgsRelation::setReferencingLayer( const QString &id )
160 {
161  d.detach();
162  d->mReferencingLayerId = id;
163 
165 }
166 
167 void QgsRelation::setReferencedLayer( const QString &id )
168 {
169  d.detach();
170  d->mReferencedLayerId = id;
171 
173 }
174 
175 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
176 {
177  d.detach();
178  d->mFieldPairs << FieldPair( referencingField, referencedField );
180 }
181 
182 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
183 {
184  d.detach();
185  d->mFieldPairs << fieldPair;
187 }
188 
190 {
192 }
193 
195 {
196  QString filter = getRelatedFeaturesFilter( feature );
197  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
198 
199  QgsFeatureRequest myRequest;
200  myRequest.setFilterExpression( filter );
201  return myRequest;
202 }
203 
204 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
205 {
206  QStringList conditions;
207 
208  if ( ! d->mPolymorphicRelationId.isEmpty() )
209  {
211  if ( polyRel.isValid() )
212  {
214  }
215  else
216  {
217  QgsDebugError( "The polymorphic relation is invalid" );
218  conditions << QStringLiteral( " FALSE " );
219  }
220  }
221 
222  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
223  {
224  QVariant val( feature.attribute( pair.referencedField() ) );
225  int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
226  if ( referencingIdx >= 0 )
227  {
228  QVariant::Type fieldType = referencingLayer()->fields().at( referencingIdx ).type();
229  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val, fieldType );
230  }
231  else
232  {
233  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
234  }
235  }
236 
237  return conditions.join( QLatin1String( " AND " ) );
238 }
239 
241 {
242  QStringList conditions;
243 
244  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
245  {
246  int referencedIdx = referencedLayer()->fields().lookupField( pair.referencedField() );
247  int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
248  if ( referencedIdx >= 0 )
249  {
250  QVariant::Type fieldType = referencedLayer()->fields().at( referencedIdx ).type();
251  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ), fieldType );
252  }
253  else
254  {
255  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
256  }
257  }
258 
259  QgsFeatureRequest myRequest;
260 
261  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
262 
263  myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
264 
265  return myRequest;
266 }
267 
269 {
270  return getReferencedFeatureRequest( feature.attributes() );
271 }
272 
274 {
275  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
276 
277  QgsFeature f;
278  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
279  return f;
280 }
281 
282 QString QgsRelation::name() const
283 {
284  return d->mRelationName;
285 }
286 
288 {
289  return d->mRelationStrength;
290 }
291 
292 QString QgsRelation::id() const
293 {
294  return d->mRelationId;
295 }
296 
298 {
299  if ( !d->mFieldPairs.isEmpty() )
300  {
301  const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 );
302  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
303  .arg( referencingLayerId(),
304  fieldPair.referencingField(),
306  fieldPair.referencedField() );
307  }
309 }
310 
312 {
313  return d->mReferencingLayerId;
314 }
315 
317 {
318  return d->mReferencingLayer;
319 }
320 
322 {
323  return d->mReferencedLayerId;
324 }
325 
327 {
328  return d->mReferencedLayer;
329 }
330 
331 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
332 {
333  return d->mFieldPairs;
334 }
335 
337 {
338  QgsAttributeList attrs;
339  attrs.reserve( d->mFieldPairs.size() );
340  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
341  {
342  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
343  }
344  return attrs;
345 }
346 
348 {
349  QgsAttributeList attrs;
350 
351  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
352  {
353  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
354  }
355  return attrs;
356 
357 }
358 
360 {
361  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
362 }
363 
365 {
366  if ( isValid() )
367  return QString();
368 
369  if ( d->mReferencingLayer.isNull() )
370  {
371  if ( d->mReferencingLayerId.isEmpty() )
372  return QObject::tr( "Referencing layer not set" );
373  else
374  return QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
375  }
376  else if ( !d->mReferencingLayer.data()->isValid() )
377  return QObject::tr( "Referencing layer %1 is not valid" ).arg( d->mReferencingLayerId );
378  else if ( d->mReferencedLayer.isNull() )
379  {
380  if ( d->mReferencedLayerId.isEmpty() )
381  return QObject::tr( "Referenced layer not set" );
382  else
383  return QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
384  }
385  else if ( !d->mReferencedLayer.data()->isValid() )
386  return QObject::tr( "Referenced layer %1 is not valid" ).arg( d->mReferencedLayerId );
387  else
388  return d->mValidationError;
389 }
390 
392 {
393  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
394 }
395 
396 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
397 {
398  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
399  {
400  if ( pair.first == referencingField )
401  return pair.second;
402  }
403  return QString();
404 }
405 
406 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
407 {
408  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
409  {
410  if ( pair.second == referencedField )
411  return pair.first;
412  }
413  return QString();
414 }
415 
417 {
418  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
419 
420  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
421  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
422 
423  d->mValid = true;
424 
425  if ( d->mRelationId.isEmpty() )
426  {
427  QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
428  d->mValidationError = QObject::tr( "Relationship has no ID" );
429  d->mValid = false;
430  }
431  else
432  {
433  if ( !d->mReferencedLayer )
434  {
435  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
436  d->mValidationError = QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
437  d->mValid = false;
438  }
439  else if ( !d->mReferencingLayer )
440  {
441  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
442  d->mValidationError = QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
443  d->mValid = false;
444  }
445  else
446  {
447  if ( d->mFieldPairs.count() < 1 )
448  {
449  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
450  d->mValidationError = QObject::tr( "No fields specified for relationship" );
451  d->mValid = false;
452  }
453 
454  for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
455  {
456  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
457  {
458  QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
459  d->mValidationError = QObject::tr( "Field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() );
460  d->mValid = false;
461  break;
462  }
463  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
464  {
465  QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
466  d->mValidationError = QObject::tr( "Field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() );
467  d->mValid = false;
468  break;
469  }
470  }
471  }
472 
473  }
474 }
475 
476 void QgsRelation::setPolymorphicRelationId( const QString &polymorphicRelationId )
477 {
478  d.detach();
479  d->mPolymorphicRelationId = polymorphicRelationId;
480 }
481 
483 {
484  return d->mPolymorphicRelationId;
485 }
486 
488 {
489  if ( ! mContext.project() || ! mContext.project()->relationManager() )
490  return QgsPolymorphicRelation();
491 
492  return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
493 }
494 
496 {
497  if ( d->mPolymorphicRelationId.isNull() )
499  else
501 }
502 
504 {
505  switch ( cardinality )
506  {
508  return QObject::tr( "One-to-one" );
510  return QObject::tr( "One-to-many" );
512  return QObject::tr( "Many-to-one" );
514  return QObject::tr( "Many-to-many" );
515  }
517 }
518 
520 {
521  switch ( strength )
522  {
524  return QObject::tr( "Association" );
526  return QObject::tr( "Composition" );
527  }
529 }
RelationshipStrength
Relationship strength.
Definition: qgis.h:3611
@ Composition
Fix relation, related elements are part of the parent and a parent copy will copy any children or del...
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
RelationshipType
Relationship types.
Definition: qgis.h:3597
@ Generated
A generated relation is a child of a polymorphic relation.
@ Normal
A normal relation.
@ Vector
Vector layer.
RelationshipCardinality
Relationship cardinality.
Definition: qgis.h:3623
@ ManyToMany
Many to many relationship.
@ ManyToOne
Many to one relationship.
@ OneToOne
One to one relationship.
@ OneToMany
One to many relationship.
A vector of attributes.
Definition: qgsattributes.h:59
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:335
QVariant::Type type
Definition: qgsfield.h:60
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:359
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:131
Base class for all map layer types.
Definition: qgsmaplayer.h:75
Qgis::LayerType type
Definition: qgsmaplayer.h:82
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:117
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:67
QString referencingField() const
Gets the name of the referencing (child) field.
Definition: qgsrelation.h:77
QString referencedField() const
Gets the name of the referenced (parent) field.
Definition: qgsrelation.h:79
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...
static QString cardinalityToDisplayString(Qgis::RelationshipCardinality cardinality)
Returns a user-friendly translated string representing a relationship cardinality.
QString name
Definition: qgsrelation.h:48
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:28
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:47
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:54
QString validationError() const
Returns a user-friendly explanation for why the relationship is invalid.
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
QString polymorphicRelationId
Definition: qgsrelation.h:50
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
Qgis::RelationshipType type() const
Returns the type of the relation.
void setStrength(Qgis::RelationshipStrength strength)
Set a strength for this relation.
static QString strengthToDisplayString(Qgis::RelationshipStrength strength)
Returns a user-friendly translated string representing a relationship strength.
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...
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:45
QgsPolymorphicRelation polymorphicRelation
Definition: qgsrelation.h:51
Qgis::RelationshipStrength strength() const
Returns the relation strength as a string.
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:46
bool isValid
Definition: qgsrelation.h:49
QString referencedLayerId() const
Access the referenced (parent) layer's id.
void setName(const QString &name)
Set a name for this relation.
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.
#define BUILTIN_UNREACHABLE
Definition: qgis.h:5818
QList< int > QgsAttributeList
Definition: qgsfield.h:27
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38