QGIS API Documentation 4.3.0-Master (0de80482b60)
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#include <QString>
28
29#include "moc_qgsrelation.cpp"
30
31using namespace Qt::StringLiterals;
32
34 : d( new QgsRelationPrivate() )
35 , mContext( nullptr )
36{}
37
39 : d( new QgsRelationPrivate() )
40 , mContext( context )
41{}
42
44
46 : d( other.d )
47 , mContext( other.mContext )
48{}
49
51 : d( std::move( other.d ) )
52 , mContext( std::move( other.mContext ) )
53{}
54
56{
57 if ( &other == this )
58 return *this;
59
60 d = other.d;
61 mContext = other.mContext;
62 return *this;
63}
64
66{
67 if ( &other == this )
68 return *this;
69
70 d = std::move( other.d );
71 mContext = std::move( other.mContext );
72 return *this;
73}
74
75// TODO QGIS 5.0 -- Remove the deprecated createFromXml method without the relationContext parameter
77{
78 return createFromXml( node, context, QgsRelationContext( QgsProject::instance() ) ); // skip-keyword-check
79}
80
81QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
82{
83 QDomElement elem = node.toElement();
84
85 if ( elem.tagName() != "relation"_L1 )
86 {
87 QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
88 }
89
90 QgsRelation relation( relationContext );
91
92 QString referencingLayerId = elem.attribute( u"referencingLayer"_s );
93 QString referencedLayerId = elem.attribute( u"referencedLayer"_s );
94 QString id = elem.attribute( u"id"_s );
95 QString name = context.projectTranslator()->translate( u"project:relations"_s, elem.attribute( u"name"_s ) );
96 QString strength = elem.attribute( u"strength"_s );
97
98 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
99
102
103 if ( !referencingLayer )
104 {
105 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
106 }
107 else if ( Qgis::LayerType::Vector != referencingLayer->type() )
108 {
109 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
110 }
111
112 if ( !referencedLayer )
113 {
114 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
115 }
116 else if ( Qgis::LayerType::Vector != referencedLayer->type() )
117 {
118 QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
119 }
120
121 relation.d->mReferencingLayerId = referencingLayerId;
122 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
123 relation.d->mReferencedLayerId = referencedLayerId;
124 relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
125 relation.d->mRelationId = id;
126 relation.d->mRelationName = name;
128
129 QDomNodeList references = elem.elementsByTagName( u"fieldRef"_s );
130 for ( int i = 0; i < references.size(); ++i )
131 {
132 QDomElement refEl = references.at( i ).toElement();
133
134 QString referencingField = refEl.attribute( u"referencingField"_s );
135 QString referencedField = refEl.attribute( u"referencedField"_s );
136
137 relation.addFieldPair( referencingField, referencedField );
138 }
139
140 relation.updateRelationStatus();
141
142 return relation;
143}
144
145void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
146{
147 QDomElement elem = doc.createElement( u"relation"_s );
148 elem.setAttribute( u"id"_s, d->mRelationId );
149 elem.setAttribute( u"name"_s, d->mRelationName );
150 elem.setAttribute( u"referencingLayer"_s, d->mReferencingLayerId );
151 elem.setAttribute( u"referencedLayer"_s, d->mReferencedLayerId );
152 elem.setAttribute( u"strength"_s, qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
153
154 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
155 {
156 QDomElement referenceElem = doc.createElement( u"fieldRef"_s );
157 referenceElem.setAttribute( u"referencingField"_s, pair.first );
158 referenceElem.setAttribute( u"referencedField"_s, pair.second );
159 elem.appendChild( referenceElem );
160 }
161
162 node.appendChild( elem );
163}
164
165void QgsRelation::setId( const QString &id )
166{
167 d.detach();
168 d->mRelationId = id;
169
171}
172
173void QgsRelation::setName( const QString &name )
174{
175 d.detach();
176 d->mRelationName = name;
177}
178
179
181{
182 d.detach();
183 d->mRelationStrength = strength;
184}
185
186void QgsRelation::setReferencingLayer( const QString &id )
187{
188 d.detach();
189 d->mReferencingLayerId = id;
190
192}
193
194void QgsRelation::setReferencedLayer( const QString &id )
195{
196 d.detach();
197 d->mReferencedLayerId = id;
198
200}
201
202void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
203{
204 d.detach();
205 d->mFieldPairs << FieldPair( referencingField, referencedField );
207}
208
209void QgsRelation::addFieldPair( const FieldPair &fieldPair )
210{
211 d.detach();
212 d->mFieldPairs << fieldPair;
214}
215
220
222{
223 QString filter = getRelatedFeaturesFilter( feature );
224 QgsDebugMsgLevel( u"Filter conditions: '%1'"_s.arg( filter ), 2 );
225
226 QgsFeatureRequest myRequest;
227 myRequest.setFilterExpression( filter );
228 return myRequest;
229}
230
232{
233 QStringList conditions;
234
235 if ( !d->mPolymorphicRelationId.isEmpty() )
236 {
238 if ( polyRel.isValid() )
239 {
241 }
242 else
243 {
244 QgsDebugError( "The polymorphic relation is invalid" );
245 conditions << u" FALSE "_s;
246 }
247 }
248
249 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
250 {
251 QVariant val( feature.attribute( pair.referencedField() ) );
252 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
253 if ( referencingIdx >= 0 )
254 {
255 QMetaType::Type fieldType = referencingLayer()->fields().at( referencingIdx ).type();
256 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val, fieldType );
257 }
258 else
259 {
260 conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
261 }
262 }
263
264 return conditions.join( " AND "_L1 );
265}
266
268{
269 QStringList conditions;
270
271 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
272 {
273 int referencedIdx = referencedLayer()->fields().lookupField( pair.referencedField() );
274 int referencingIdx = referencingLayer()->fields().lookupField( pair.referencingField() );
275 if ( referencedIdx >= 0 )
276 {
277 QMetaType::Type fieldType = referencedLayer()->fields().at( referencedIdx ).type();
278 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ), fieldType );
279 }
280 else
281 {
282 conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
283 }
284 }
285
286 QgsFeatureRequest myRequest;
287
288 QgsDebugMsgLevel( u"Filter conditions: '%1'"_s.arg( conditions.join( " AND " ) ), 2 );
289
290 myRequest.setFilterExpression( conditions.join( " AND "_L1 ) );
291
292 return myRequest;
293}
294
299
301{
303
304 QgsFeature f;
305 ( void ) d->mReferencedLayer->getFeatures( request ).nextFeature( f );
306 return f;
307}
308
309QString QgsRelation::name() const
310{
311 return d->mRelationName;
312}
313
315{
316 return d->mRelationStrength;
317}
318
319QString QgsRelation::id() const
320{
321 return d->mRelationId;
322}
323
325{
326 if ( !d->mFieldPairs.isEmpty() )
327 {
328 const QgsRelation::FieldPair fieldPair = d->mFieldPairs.at( 0 );
329 d->mRelationId = u"%1_%2_%3_%4"_s.arg( referencingLayerId(), fieldPair.referencingField(), referencedLayerId(), fieldPair.referencedField() );
330 }
332}
333
335{
336 return d->mReferencingLayerId;
337}
338
340{
341 return d->mReferencingLayer;
342}
343
345{
346 return d->mReferencedLayerId;
347}
348
350{
351 return d->mReferencedLayer;
352}
353
354QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
355{
356 return d->mFieldPairs;
357}
358
360{
361 QgsAttributeList attrs;
362 attrs.reserve( d->mFieldPairs.size() );
363 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
364 {
365 attrs << d->mReferencedLayer->fields().lookupField( pair.second );
366 }
367 return attrs;
368}
369
371{
372 QgsAttributeList attrs;
373
374 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
375 {
376 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
377 }
378 return attrs;
379}
380
382{
383 if ( !referencingLayer() )
384 {
385 return false;
386 }
387
388 const auto fields = referencingFields();
389
390 return std::find_if(
391 fields.constBegin(),
392 fields.constEnd(),
393 [&]( const auto &fieldIdx ) {
394 if ( !referencingLayer()->fields().exists( fieldIdx ) )
395 {
396 return false;
397 }
398 const QgsField field = referencingLayer()->fields().field( fieldIdx );
399 return field.constraints().constraints().testFlag( QgsFieldConstraints::Constraint::ConstraintNotNull );
400 }
401 )
402 == fields.constEnd();
403}
404
406{
407 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
408}
409
411{
412 if ( isValid() )
413 return QString();
414
415 if ( d->mReferencingLayer.isNull() )
416 {
417 if ( d->mReferencingLayerId.isEmpty() )
418 return QObject::tr( "Referencing layer not set" );
419 else
420 return QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
421 }
422 else if ( !d->mReferencingLayer.data()->isValid() )
423 return QObject::tr( "Referencing layer %1 is not valid" ).arg( d->mReferencingLayerId );
424 else if ( d->mReferencedLayer.isNull() )
425 {
426 if ( d->mReferencedLayerId.isEmpty() )
427 return QObject::tr( "Referenced layer not set" );
428 else
429 return QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
430 }
431 else if ( !d->mReferencedLayer.data()->isValid() )
432 return QObject::tr( "Referenced layer %1 is not valid" ).arg( d->mReferencedLayerId );
433 else
434 return d->mValidationError;
435}
436
438{
439 return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
440}
441
442QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
443{
444 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
445 {
446 if ( pair.first == referencingField )
447 return pair.second;
448 }
449 return QString();
450}
451
452QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
453{
454 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
455 {
456 if ( pair.second == referencedField )
457 return pair.first;
458 }
459 return QString();
460}
461
463{
464 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
465
466 d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
467 d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
468
469 d->mValid = true;
470
471 if ( d->mRelationId.isEmpty() )
472 {
473 QgsDebugError( u"Invalid relation: no ID"_s );
474 d->mValidationError = QObject::tr( "Relationship has no ID" );
475 d->mValid = false;
476 }
477 else
478 {
479 if ( !d->mReferencedLayer )
480 {
481 QgsDebugMsgLevel( u"Invalid relation: referenced layer does not exist. ID: %1"_s.arg( d->mReferencedLayerId ), 4 );
482 d->mValidationError = QObject::tr( "Referenced layer %1 does not exist" ).arg( d->mReferencedLayerId );
483 d->mValid = false;
484 }
485 else if ( !d->mReferencingLayer )
486 {
487 QgsDebugMsgLevel( u"Invalid relation: referencing layer does not exist. ID: %2"_s.arg( d->mReferencingLayerId ), 4 );
488 d->mValidationError = QObject::tr( "Referencing layer %1 does not exist" ).arg( d->mReferencingLayerId );
489 d->mValid = false;
490 }
491 else
492 {
493 if ( d->mFieldPairs.count() < 1 )
494 {
495 QgsDebugMsgLevel( u"Invalid relation: no pair of field is specified."_s, 4 );
496 d->mValidationError = QObject::tr( "No fields specified for relationship" );
497 d->mValid = false;
498 }
499
500 for ( const FieldPair &pair : std::as_const( d->mFieldPairs ) )
501 {
502 if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
503 {
504 QgsDebugError( u"Invalid relation: field %1 does not exist in referencing layer %2"_s.arg( pair.first, d->mReferencingLayer->name() ) );
505 d->mValidationError = QObject::tr( "Field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() );
506 d->mValid = false;
507 break;
508 }
509 else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
510 {
511 QgsDebugError( u"Invalid relation: field %1 does not exist in referenced layer %2"_s.arg( pair.second, d->mReferencedLayer->name() ) );
512 d->mValidationError = QObject::tr( "Field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayer->name() );
513 d->mValid = false;
514 break;
515 }
516 }
517 }
518 }
519}
520
522{
523 d.detach();
524 d->mPolymorphicRelationId = polymorphicRelationId;
525}
526
528{
529 return d->mPolymorphicRelationId;
530}
531
533{
534 if ( !mContext.project() || !mContext.project()->relationManager() )
535 return QgsPolymorphicRelation();
536
537 return mContext.project()->relationManager()->polymorphicRelation( d->mPolymorphicRelationId );
538}
539
541{
542 if ( d->mPolymorphicRelationId.isNull() )
544 else
546}
547
549{
550 switch ( cardinality )
551 {
553 return QObject::tr( "One-to-one" );
555 return QObject::tr( "One-to-many" );
557 return QObject::tr( "Many-to-one" );
559 return QObject::tr( "Many-to-many" );
560 }
562}
563
565{
566 switch ( strength )
567 {
569 return QObject::tr( "Association" );
571 return QObject::tr( "Composition" );
572 }
574}
RelationshipStrength
Relationship strength.
Definition qgis.h:4849
@ Composition
Fix relation, related elements are part of the parent and a parent copy will copy any children or del...
Definition qgis.h:4851
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
Definition qgis.h:4850
RelationshipType
Relationship types.
Definition qgis.h:4835
@ Generated
A generated relation is a child of a polymorphic relation.
Definition qgis.h:4837
@ Normal
A normal relation.
Definition qgis.h:4836
@ Vector
Vector layer.
Definition qgis.h:207
RelationshipCardinality
Relationship cardinality.
Definition qgis.h:4861
@ ManyToMany
Many to many relationship.
Definition qgis.h:4865
@ ManyToOne
Many to one relationship.
Definition qgis.h:4864
@ OneToOne
One to one relationship.
Definition qgis.h:4862
@ OneToMany
One to many relationship.
Definition qgis.h:4863
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:60
QgsAttributes attributes
Definition qgsfeature.h:64
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:56
QMetaType::Type type
Definition qgsfield.h:63
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:83
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.
static QgsProject * instance()
Returns the QgsProject singleton instance.
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:71
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.
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.
static Q_DECL_DEPRECATED QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context)
Creates a relation from an XML structure.
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:7850
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7831
#define BUILTIN_UNREACHABLE
Definition qgis.h:8229
QList< int > QgsAttributeList
Definition qgsfield.h:30
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71