QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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"
17
18#include "qgsfeatureiterator.h"
19#include "qgslogger.h"
21#include "qgsproject.h"
22#include "qgsrelation_p.h"
23#include "qgsrelationmanager.h"
24#include "qgsvectorlayer.h"
25
26#include <QApplication>
27
28#include "moc_qgsrelation.cpp"
29
31 : d( new QgsRelationPrivate() )
32{
33}
34
36 : d( new QgsRelationPrivate() )
37 , mContext( context )
38{
39}
40
42
44 : d( other.d )
45 , mContext( other.mContext )
46{
47}
48
50 : d( std::move( other.d ) )
51 , mContext( std::move( other.mContext ) )
52{}
53
55{
56 if ( &other == this )
57 return *this;
58
59 d = other.d;
60 mContext = other.mContext;
61 return *this;
62}
63
65{
66 if ( &other == this )
67 return *this;
68
69 d = std::move( other.d );
70 mContext = std::move( other.mContext );
71 return *this;
72}
73
74QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
75{
76 QDomElement elem = node.toElement();
77
78 if ( elem.tagName() != QLatin1String( "relation" ) )
79 {
80 QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
81 }
82
83 QgsRelation relation( relationContext );
84
85 QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
86 QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
87 QString id = elem.attribute( QStringLiteral( "id" ) );
88 QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
89 QString strength = elem.attribute( QStringLiteral( "strength" ) );
90
91 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
92
95
96 if ( !referencingLayer )
97 {
98 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
99 }
100 else if ( Qgis::LayerType::Vector != referencingLayer->type() )
101 {
102 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
103 }
104
105 if ( !referencedLayer )
106 {
107 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
108 }
109 else if ( Qgis::LayerType::Vector != referencedLayer->type() )
110 {
111 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
112 }
113
114 relation.d->mReferencingLayerId = referencingLayerId;
115 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
116 relation.d->mReferencedLayerId = referencedLayerId;
117 relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
118 relation.d->mRelationId = id;
119 relation.d->mRelationName = name;
121
122 QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
123 for ( int i = 0; i < references.size(); ++i )
124 {
125 QDomElement refEl = references.at( i ).toElement();
126
127 QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
128 QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
129
130 relation.addFieldPair( referencingField, referencedField );
131 }
132
133 relation.updateRelationStatus();
134
135 return relation;
136}
137
138void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
139{
140 QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
141 elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
142 elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
143 elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
144 elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
145 elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
146
147 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
148 {
149 QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
150 referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
151 referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
152 elem.appendChild( referenceElem );
153 }
154
155 node.appendChild( elem );
156}
157
158void QgsRelation::setId( const QString &id )
159{
160 d.detach();
161 d->mRelationId = id;
162
164}
165
166void QgsRelation::setName( const QString &name )
167{
168 d.detach();
169 d->mRelationName = name;
170}
171
172
174{
175 d.detach();
176 d->mRelationStrength = strength;
177}
178
179void QgsRelation::setReferencingLayer( const QString &id )
180{
181 d.detach();
182 d->mReferencingLayerId = id;
183
185}
186
187void QgsRelation::setReferencedLayer( const QString &id )
188{
189 d.detach();
190 d->mReferencedLayerId = id;
191
193}
194
195void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
196{
197 d.detach();
198 d->mFieldPairs << FieldPair( referencingField, referencedField );
200}
201
202void QgsRelation::addFieldPair( const FieldPair &fieldPair )
203{
204 d.detach();
205 d->mFieldPairs << fieldPair;
207}
208
213
215{
216 QString filter = getRelatedFeaturesFilter( feature );
217 QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
218
219 QgsFeatureRequest myRequest;
220 myRequest.setFilterExpression( filter );
221 return myRequest;
222}
223
225{
226 QStringList conditions;
227
228 if ( ! d->mPolymorphicRelationId.isEmpty() )
229 {
231 if ( polyRel.isValid() )
232 {
234 }
235 else
236 {
237 QgsDebugError( "The polymorphic relation is invalid" );
238 conditions << QStringLiteral( " FALSE " );
239 }
240 }
241
242 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
243 {
244 QVariant val( feature.attribute( pair.referencedField() ) );
245 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
246 if ( referencingIdx >= 0 )
247 {
248 QMetaType::Type fieldType = referencingLayer()->fields().at( referencingIdx ).type();
249 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val, fieldType );
250 }
251 else
252 {
253 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
254 }
255 }
256
257 return conditions.join( QLatin1String( " AND " ) );
258}
259
261{
262 QStringList conditions;
263
264 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
265 {
266 int referencedIdx = referencedLayer()->fields().lookupField( pair.referencedField() );
267 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
268 if ( referencedIdx >= 0 )
269 {
270 QMetaType::Type fieldType = referencedLayer()->fields().at( referencedIdx ).type();
271 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ), fieldType );
272 }
273 else
274 {
275 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
276 }
277 }
278
279 QgsFeatureRequest myRequest;
280
281 QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
282
283 myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
284
285 return myRequest;
286}
287
292
294{
296
297 QgsFeature f;
298 ( void )d->mReferencedLayer->getFeatures( request ).nextFeature( f );
299 return f;
300}
301
302QString QgsRelation::name() const
303{
304 return d->mRelationName;
305}
306
308{
309 return d->mRelationStrength;
310}
311
312QString QgsRelation::id() const
313{
314 return d->mRelationId;
315}
316
318{
319 if ( !d->mFieldPairs.isEmpty() )
320 {
321 const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 );
322 d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
323 .arg( referencingLayerId(),
324 fieldPair.referencingField(),
326 fieldPair.referencedField() );
327 }
329}
330
332{
333 return d->mReferencingLayerId;
334}
335
337{
338 return d->mReferencingLayer;
339}
340
342{
343 return d->mReferencedLayerId;
344}
345
347{
348 return d->mReferencedLayer;
349}
350
351QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
352{
353 return d->mFieldPairs;
354}
355
357{
358 QgsAttributeList attrs;
359 attrs.reserve( d->mFieldPairs.size() );
360 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
361 {
362 attrs << d->mReferencedLayer->fields().lookupField( pair.second );
363 }
364 return attrs;
365}
366
368{
369 QgsAttributeList attrs;
370
371 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
372 {
373 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
374 }
375 return attrs;
376
377}
378
380{
381 if ( ! referencingLayer() )
382 {
383 return false;
384 }
385
386 const auto fields = referencingFields();
387
388 return std::find_if( fields.constBegin(), fields.constEnd(), [&]( const auto & fieldIdx )
389 {
390 if ( !referencingLayer()->fields().exists( fieldIdx ) )
391 {
392 return false;
393 }
394 const QgsField field = referencingLayer()->fields().field( fieldIdx );
395 return field.constraints().constraints().testFlag( QgsFieldConstraints::Constraint::ConstraintNotNull );
396 } ) == fields.constEnd();
397}
398
400{
401 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
402}
403
405{
406 if ( isValid() )
407 return QString();
408
409 if ( d->mReferencingLayer.isNull() )
410 {
411 if ( d->mReferencingLayerId.isEmpty() )
412 return QObject::tr( "Referencing layer not set" );
413 else
414 return QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
415 }
416 else if ( !d->mReferencingLayer.data()->isValid() )
417 return QObject::tr( "Referencing layer %1 is not valid" ).arg( d->mReferencingLayerId );
418 else if ( d->mReferencedLayer.isNull() )
419 {
420 if ( d->mReferencedLayerId.isEmpty() )
421 return QObject::tr( "Referenced layer not set" );
422 else
423 return QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
424 }
425 else if ( !d->mReferencedLayer.data()->isValid() )
426 return QObject::tr( "Referenced layer %1 is not valid" ).arg( d->mReferencedLayerId );
427 else
428 return d->mValidationError;
429}
430
432{
433 return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
434}
435
436QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
437{
438 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
439 {
440 if ( pair.first == referencingField )
441 return pair.second;
442 }
443 return QString();
444}
445
446QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
447{
448 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
449 {
450 if ( pair.second == referencedField )
451 return pair.first;
452 }
453 return QString();
454}
455
457{
458 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
459
460 d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
461 d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
462
463 d->mValid = true;
464
465 if ( d->mRelationId.isEmpty() )
466 {
467 QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
468 d->mValidationError = QObject::tr( "Relationship has no ID" );
469 d->mValid = false;
470 }
471 else
472 {
473 if ( !d->mReferencedLayer )
474 {
475 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
476 d->mValidationError = QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
477 d->mValid = false;
478 }
479 else if ( !d->mReferencingLayer )
480 {
481 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
482 d->mValidationError = QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
483 d->mValid = false;
484 }
485 else
486 {
487 if ( d->mFieldPairs.count() < 1 )
488 {
489 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
490 d->mValidationError = QObject::tr( "No fields specified for relationship" );
491 d->mValid = false;
492 }
493
494 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
495 {
496 if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
497 {
498 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
499 d->mValidationError = QObject::tr( "Field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() );
500 d->mValid = false;
501 break;
502 }
503 else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
504 {
505 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
506 d->mValidationError = QObject::tr( "Field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() );
507 d->mValid = false;
508 break;
509 }
510 }
511 }
512
513 }
514}
515
517{
518 d.detach();
519 d->mPolymorphicRelationId = polymorphicRelationId;
520}
521
523{
524 return d->mPolymorphicRelationId;
525}
526
528{
529 if ( ! mContext.project() || ! mContext.project()->relationManager() )
530 return QgsPolymorphicRelation();
531
532 return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
533}
534
536{
537 if ( d->mPolymorphicRelationId.isNull() )
539 else
541}
542
544{
545 switch ( cardinality )
546 {
548 return QObject::tr( "One-to-one" );
550 return QObject::tr( "One-to-many" );
552 return QObject::tr( "Many-to-one" );
554 return QObject::tr( "Many-to-many" );
555 }
557}
558
560{
561 switch ( strength )
562 {
564 return QObject::tr( "Association" );
566 return QObject::tr( "Composition" );
567 }
569}
RelationshipStrength
Relationship strength.
Definition qgis.h:4405
@ Composition
Fix relation, related elements are part of the parent and a parent copy will copy any children or del...
Definition qgis.h:4407
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
Definition qgis.h:4406
RelationshipType
Relationship types.
Definition qgis.h:4391
@ Generated
A generated relation is a child of a polymorphic relation.
Definition qgis.h:4393
@ Normal
A normal relation.
Definition qgis.h:4392
@ Vector
Vector layer.
Definition qgis.h:191
RelationshipCardinality
Relationship cardinality.
Definition qgis.h:4417
@ ManyToMany
Many to many relationship.
Definition qgis.h:4421
@ ManyToOne
Many to one relationship.
Definition qgis.h:4420
@ OneToOne
One to one relationship.
Definition qgis.h:4418
@ OneToMany
One to many relationship.
Definition qgis.h:4419
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.
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:54
QMetaType::Type type
Definition qgsfield.h:61
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:80
A relation where the referenced (parent) layer is calculated based on fields from the referencing (ch...
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.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
Context for relations.
const QgsProject * project() const
Gets the associated project.
Defines a relation between matching fields of the two involved tables of a relation.
Definition qgsrelation.h:72
QString referencingField() const
Gets the name of the referencing (child) field.
Definition qgsrelation.h:82
QString referencedField() const
Gets the name of the referenced (parent) field.
Definition qgsrelation.h:84
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QList< int > referencedFields
Definition qgsrelation.h:51
static QString cardinalityToDisplayString(Qgis::RelationshipCardinality cardinality)
Returns a user-friendly translated string representing a relationship cardinality.
QString name
Definition qgsrelation.h:52
Q_INVOKABLE 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:55
QgsVectorLayer * referencedLayer
Definition qgsrelation.h:50
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:45
Q_INVOKABLE 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...
QString referencedLayerId
Definition qgsrelation.h:49
QgsPolymorphicRelation polymorphicRelation
Definition qgsrelation.h:56
Qgis::RelationshipStrength strength
Definition qgsrelation.h:54
QgsVectorLayer * referencingLayer
Definition qgsrelation.h:47
QString referencingLayerId
Definition qgsrelation.h:46
bool referencingFieldsAllowNull() const
Returns true if none of the referencing fields has a NOT NULL constraint.
void setName(const QString &name)
Set a name for this relation.
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Q_INVOKABLE 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.
QList< int > referencingFields
Definition qgsrelation.h:48
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 dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
Definition qgis.h:6817
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6798
#define BUILTIN_UNREACHABLE
Definition qgis.h:7208
QList< int > QgsAttributeList
Definition qgsfield.h:28
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57