QGIS API Documentation 3.41.0-Master (af5edcb665c)
Loading...
Searching...
No Matches
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"
18
19#include "qgsfeatureiterator.h"
20#include "qgslogger.h"
21#include "qgsproject.h"
22#include "qgsvectorlayer.h"
23#include "qgsrelation_p.h"
24#include "qgsrelationmanager.h"
25#include "moc_qgsrelation.cpp"
26
27#include <QApplication>
28
30 : d( new QgsRelationPrivate() )
31{
32}
33
35 : d( new QgsRelationPrivate() )
36 , mContext( context )
37{
38}
39
41
43 : d( other.d )
44 , mContext( other.mContext )
45{
46}
47
49{
50 d = other.d;
51 mContext = other.mContext;
52 return *this;
53}
54
55QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
56{
57 QDomElement elem = node.toElement();
58
59 if ( elem.tagName() != QLatin1String( "relation" ) )
60 {
61 QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
62 }
63
64 QgsRelation relation( relationContext );
65
66 QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
67 QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
68 QString id = elem.attribute( QStringLiteral( "id" ) );
69 QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
70 QString strength = elem.attribute( QStringLiteral( "strength" ) );
71
72 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
73
76
77 if ( !referencingLayer )
78 {
79 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
80 }
82 {
83 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
84 }
85
86 if ( !referencedLayer )
87 {
88 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
89 }
91 {
92 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
93 }
94
95 relation.d->mReferencingLayerId = referencingLayerId;
96 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
97 relation.d->mReferencedLayerId = referencedLayerId;
98 relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
99 relation.d->mRelationId = id;
100 relation.d->mRelationName = name;
101 relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( strength, Qgis::RelationshipStrength::Association );
102
103 QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
104 for ( int i = 0; i < references.size(); ++i )
105 {
106 QDomElement refEl = references.at( i ).toElement();
107
108 QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
109 QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
110
111 relation.addFieldPair( referencingField, referencedField );
112 }
113
114 relation.updateRelationStatus();
115
116 return relation;
117}
118
119void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
120{
121 QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
122 elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
123 elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
124 elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
125 elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
126 elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
127
128 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
129 {
130 QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
131 referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
132 referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
133 elem.appendChild( referenceElem );
134 }
135
136 node.appendChild( elem );
137}
138
139void QgsRelation::setId( const QString &id )
140{
141 d.detach();
142 d->mRelationId = id;
143
145}
146
147void QgsRelation::setName( const QString &name )
148{
149 d.detach();
150 d->mRelationName = name;
151}
152
153
155{
156 d.detach();
157 d->mRelationStrength = strength;
158}
159
160void QgsRelation::setReferencingLayer( const QString &id )
161{
162 d.detach();
163 d->mReferencingLayerId = id;
164
166}
167
168void QgsRelation::setReferencedLayer( const QString &id )
169{
170 d.detach();
171 d->mReferencedLayerId = id;
172
174}
175
176void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
177{
178 d.detach();
179 d->mFieldPairs << FieldPair( referencingField, referencedField );
181}
182
183void QgsRelation::addFieldPair( const FieldPair &fieldPair )
184{
185 d.detach();
186 d->mFieldPairs << fieldPair;
188}
189
194
196{
197 QString filter = getRelatedFeaturesFilter( feature );
198 QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
199
200 QgsFeatureRequest myRequest;
201 myRequest.setFilterExpression( filter );
202 return myRequest;
203}
204
206{
207 QStringList conditions;
208
209 if ( ! d->mPolymorphicRelationId.isEmpty() )
210 {
212 if ( polyRel.isValid() )
213 {
215 }
216 else
217 {
218 QgsDebugError( "The polymorphic relation is invalid" );
219 conditions << QStringLiteral( " FALSE " );
220 }
221 }
222
223 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
224 {
225 QVariant val( feature.attribute( pair.referencedField() ) );
226 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
227 if ( referencingIdx >= 0 )
228 {
229 QMetaType::Type fieldType = referencingLayer()->fields().at( referencingIdx ).type();
230 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val, fieldType );
231 }
232 else
233 {
234 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
235 }
236 }
237
238 return conditions.join( QLatin1String( " AND " ) );
239}
240
242{
243 QStringList conditions;
244
245 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
246 {
247 int referencedIdx = referencedLayer()->fields().lookupField( pair.referencedField() );
248 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
249 if ( referencedIdx >= 0 )
250 {
251 QMetaType::Type fieldType = referencedLayer()->fields().at( referencedIdx ).type();
252 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ), fieldType );
253 }
254 else
255 {
256 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
257 }
258 }
259
260 QgsFeatureRequest myRequest;
261
262 QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
263
264 myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
265
266 return myRequest;
267}
268
273
275{
277
278 QgsFeature f;
279 ( void )d->mReferencedLayer->getFeatures( request ).nextFeature( f );
280 return f;
281}
282
283QString QgsRelation::name() const
284{
285 return d->mRelationName;
286}
287
289{
290 return d->mRelationStrength;
291}
292
293QString QgsRelation::id() const
294{
295 return d->mRelationId;
296}
297
299{
300 if ( !d->mFieldPairs.isEmpty() )
301 {
302 const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 );
303 d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
304 .arg( referencingLayerId(),
305 fieldPair.referencingField(),
307 fieldPair.referencedField() );
308 }
310}
311
313{
314 return d->mReferencingLayerId;
315}
316
318{
319 return d->mReferencingLayer;
320}
321
323{
324 return d->mReferencedLayerId;
325}
326
328{
329 return d->mReferencedLayer;
330}
331
332QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
333{
334 return d->mFieldPairs;
335}
336
338{
339 QgsAttributeList attrs;
340 attrs.reserve( d->mFieldPairs.size() );
341 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
342 {
343 attrs << d->mReferencedLayer->fields().lookupField( pair.second );
344 }
345 return attrs;
346}
347
349{
350 QgsAttributeList attrs;
351
352 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
353 {
354 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
355 }
356 return attrs;
357
358}
359
361{
362 if ( ! referencingLayer() )
363 {
364 return false;
365 }
366
367 const auto fields = referencingFields();
368
369 return std::find_if( fields.constBegin(), fields.constEnd(), [&]( const auto & fieldIdx )
370 {
371 if ( !referencingLayer()->fields().exists( fieldIdx ) )
372 {
373 return false;
374 }
375 const QgsField field = referencingLayer()->fields().field( fieldIdx );
376 return field.constraints().constraints().testFlag( QgsFieldConstraints::Constraint::ConstraintNotNull );
377 } ) == fields.constEnd();
378}
379
381{
382 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
383}
384
386{
387 if ( isValid() )
388 return QString();
389
390 if ( d->mReferencingLayer.isNull() )
391 {
392 if ( d->mReferencingLayerId.isEmpty() )
393 return QObject::tr( "Referencing layer not set" );
394 else
395 return QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
396 }
397 else if ( !d->mReferencingLayer.data()->isValid() )
398 return QObject::tr( "Referencing layer %1 is not valid" ).arg( d->mReferencingLayerId );
399 else if ( d->mReferencedLayer.isNull() )
400 {
401 if ( d->mReferencedLayerId.isEmpty() )
402 return QObject::tr( "Referenced layer not set" );
403 else
404 return QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
405 }
406 else if ( !d->mReferencedLayer.data()->isValid() )
407 return QObject::tr( "Referenced layer %1 is not valid" ).arg( d->mReferencedLayerId );
408 else
409 return d->mValidationError;
410}
411
413{
414 return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
415}
416
417QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
418{
419 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
420 {
421 if ( pair.first == referencingField )
422 return pair.second;
423 }
424 return QString();
425}
426
427QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
428{
429 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
430 {
431 if ( pair.second == referencedField )
432 return pair.first;
433 }
434 return QString();
435}
436
438{
439 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
440
441 d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
442 d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
443
444 d->mValid = true;
445
446 if ( d->mRelationId.isEmpty() )
447 {
448 QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
449 d->mValidationError = QObject::tr( "Relationship has no ID" );
450 d->mValid = false;
451 }
452 else
453 {
454 if ( !d->mReferencedLayer )
455 {
456 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
457 d->mValidationError = QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
458 d->mValid = false;
459 }
460 else if ( !d->mReferencingLayer )
461 {
462 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
463 d->mValidationError = QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
464 d->mValid = false;
465 }
466 else
467 {
468 if ( d->mFieldPairs.count() < 1 )
469 {
470 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
471 d->mValidationError = QObject::tr( "No fields specified for relationship" );
472 d->mValid = false;
473 }
474
475 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
476 {
477 if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
478 {
479 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
480 d->mValidationError = QObject::tr( "Field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() );
481 d->mValid = false;
482 break;
483 }
484 else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
485 {
486 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
487 d->mValidationError = QObject::tr( "Field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() );
488 d->mValid = false;
489 break;
490 }
491 }
492 }
493
494 }
495}
496
497void QgsRelation::setPolymorphicRelationId( const QString &polymorphicRelationId )
498{
499 d.detach();
500 d->mPolymorphicRelationId = polymorphicRelationId;
501}
502
504{
505 return d->mPolymorphicRelationId;
506}
507
509{
510 if ( ! mContext.project() || ! mContext.project()->relationManager() )
511 return QgsPolymorphicRelation();
512
513 return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
514}
515
517{
518 if ( d->mPolymorphicRelationId.isNull() )
520 else
522}
523
525{
526 switch ( cardinality )
527 {
529 return QObject::tr( "One-to-one" );
531 return QObject::tr( "One-to-many" );
533 return QObject::tr( "Many-to-one" );
535 return QObject::tr( "Many-to-many" );
536 }
538}
539
541{
542 switch ( strength )
543 {
545 return QObject::tr( "Association" );
547 return QObject::tr( "Composition" );
548 }
550}
RelationshipStrength
Relationship strength.
Definition qgis.h:4171
@ 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:4157
@ Generated
A generated relation is a child of a polymorphic relation.
@ Normal
A normal relation.
@ Vector
Vector layer.
RelationshipCardinality
Relationship cardinality.
Definition qgis.h:4183
@ ManyToMany
Many to many relationship.
@ ManyToOne
Many to one relationship.
@ OneToOne
One to one relationship.
@ OneToMany
One to many relationship.
A vector of attributes.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
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:58
QgsAttributes attributes
Definition qgsfeature.h:67
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
@ ConstraintNotNull
Field may not be null.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static void warning(const QString &msg)
Goes to qWarning.
Base class for all map layer types.
Definition qgsmaplayer.h:76
Qgis::LayerType type
Definition qgsmaplayer.h:86
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
Translates a string using the Qt QTranslator mechanism.
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:69
QString referencingField() const
Gets the name of the referencing (child) field.
Definition qgsrelation.h:79
QString referencedField() const
Gets the name of the referenced (parent) field.
Definition qgsrelation.h:81
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
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:50
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.
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.
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.
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:52
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:49
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.
QString id
Definition qgsrelation.h:47
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...
QgsPolymorphicRelation polymorphicRelation
Definition qgsrelation.h:53
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:48
bool referencingFieldsAllowNull() const
Returns true if none of the referencing fields has a NOT NULL constraint.
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.
#define BUILTIN_UNREACHABLE
Definition qgis.h:6720
QList< int > QgsAttributeList
Definition qgsfield.h:27
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38