QGIS API Documentation 3.29.0-Master (ade4f0cf0f)
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"
25#include "qgsrelationmanager.h"
26
28 : d( new QgsRelationPrivate() )
29{
30}
31
33 : d( new QgsRelationPrivate() )
34 , mContext( context )
35{
36}
37
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
53QgsRelation 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<Qgis::RelationshipStrength>( strength, Qgis::RelationshipStrength::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
117void 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<Qgis::RelationshipStrength>( 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
137void QgsRelation::setId( const QString &id )
138{
139 d.detach();
140 d->mRelationId = id;
141
143}
144
145void 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
158void QgsRelation::setReferencingLayer( const QString &id )
159{
160 d.detach();
161 d->mReferencingLayerId = id;
162
164}
165
166void QgsRelation::setReferencedLayer( const QString &id )
167{
168 d.detach();
169 d->mReferencedLayerId = id;
170
172}
173
174void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
175{
176 d.detach();
177 d->mFieldPairs << FieldPair( referencingField, referencedField );
179}
180
181void 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
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{
275
276 QgsFeature f;
277 d->mReferencedLayer->getFeatures( request ).nextFeature( f );
278 return f;
279}
280
281QString QgsRelation::name() const
282{
283 return d->mRelationName;
284}
285
287{
288 return d->mRelationStrength;
289}
290
291QString 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
330QList<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 if ( isValid() )
366 return QString();
367
368 if ( d->mReferencingLayer.isNull() )
369 {
370 if ( d->mReferencingLayerId.isEmpty() )
371 return QObject::tr( "Referencing layer not set" );
372 else
373 return QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
374 }
375 else if ( !d->mReferencingLayer.data()->isValid() )
376 return QObject::tr( "Referencing layer %1 is not valid" ).arg( d->mReferencingLayerId );
377 else if ( d->mReferencedLayer.isNull() )
378 {
379 if ( d->mReferencedLayerId.isEmpty() )
380 return QObject::tr( "Referenced layer not set" );
381 else
382 return QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
383 }
384 else if ( !d->mReferencedLayer.data()->isValid() )
385 return QObject::tr( "Referenced layer %1 is not valid" ).arg( d->mReferencedLayerId );
386 else
387 return d->mValidationError;
388}
389
391{
392 return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
393}
394
395QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
396{
397 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
398 {
399 if ( pair.first == referencingField )
400 return pair.second;
401 }
402 return QString();
403}
404
405QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
406{
407 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
408 {
409 if ( pair.second == referencedField )
410 return pair.first;
411 }
412 return QString();
413}
414
416{
417 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
418
419 d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
420 d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
421
422 d->mValid = true;
423
424 if ( d->mRelationId.isEmpty() )
425 {
426 QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
427 d->mValidationError = QObject::tr( "Relationship has no ID" );
428 d->mValid = false;
429 }
430 else
431 {
432 if ( !d->mReferencedLayer )
433 {
434 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
435 d->mValidationError = QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
436 d->mValid = false;
437 }
438 else if ( !d->mReferencingLayer )
439 {
440 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
441 d->mValidationError = QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
442 d->mValid = false;
443 }
444 else
445 {
446 if ( d->mFieldPairs.count() < 1 )
447 {
448 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
449 d->mValidationError = QObject::tr( "No fields specified for relationship" );
450 d->mValid = false;
451 }
452
453 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
454 {
455 if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
456 {
457 QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
458 d->mValidationError = QObject::tr( "Field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() );
459 d->mValid = false;
460 break;
461 }
462 else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
463 {
464 QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
465 d->mValidationError = QObject::tr( "Field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() );
466 d->mValid = false;
467 break;
468 }
469 }
470 }
471
472 }
473}
474
475void QgsRelation::setPolymorphicRelationId( const QString &polymorphicRelationId )
476{
477 d.detach();
478 d->mPolymorphicRelationId = polymorphicRelationId;
479}
480
482{
483 return d->mPolymorphicRelationId;
484}
485
487{
488 if ( ! mContext.project() || ! mContext.project()->relationManager() )
489 return QgsPolymorphicRelation();
490
491 return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
492}
493
495{
496 if ( d->mPolymorphicRelationId.isNull() )
498 else
500}
501
503{
504 switch ( cardinality )
505 {
507 return QObject::tr( "One-to-one" );
509 return QObject::tr( "One-to-many" );
511 return QObject::tr( "Many-to-one" );
513 return QObject::tr( "Many-to-many" );
514 }
516}
517
519{
520 switch ( strength )
521 {
523 return QObject::tr( "Association" );
525 return QObject::tr( "Composition" );
526 }
528}
RelationshipStrength
Relationship strength.
Definition: qgis.h:2435
@ 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:2421
@ Generated
A generated relation is a child of a polymorphic relation.
@ Normal
A normal relation.
RelationshipCardinality
Relationship cardinality.
Definition: qgis.h:2447
@ 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:338
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:115
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: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
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.
@ VectorLayer
Vector layer.
#define BUILTIN_UNREACHABLE
Definition: qgis.h:3453
QList< int > QgsAttributeList
Definition: qgsfield.h:26
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38