QGIS API Documentation  3.17.0-Master (a035f434f4)
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"
24 
26  : d( new QgsRelationPrivate() )
27 {
28 }
29 
31  : d( new QgsRelationPrivate() )
32  , mContext( context )
33 {
34 }
35 
36 QgsRelation::~QgsRelation() = default;
37 
39  : d( other.d )
40  , mContext( other.mContext )
41 {
42 }
43 
45 {
46  d = other.d;
47  mContext = other.mContext;
48  return *this;
49 }
50 
51 QgsRelation QgsRelation::createFromXml( const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext )
52 {
53  QDomElement elem = node.toElement();
54 
55  if ( elem.tagName() != QLatin1String( "relation" ) )
56  {
57  QgsLogger::warning( QApplication::translate( "QgsRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
58  }
59 
60  QgsRelation relation( relationContext );
61 
62  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
63  QString referencedLayerId = elem.attribute( QStringLiteral( "referencedLayer" ) );
64  QString id = elem.attribute( QStringLiteral( "id" ) );
65  QString name = context.projectTranslator()->translate( QStringLiteral( "project:relations" ), elem.attribute( QStringLiteral( "name" ) ) );
66  QString strength = elem.attribute( QStringLiteral( "strength" ) );
67 
68  QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
69 
72 
73  if ( !referencingLayer )
74  {
75  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencingLayerId ) );
76  }
77  else if ( QgsMapLayerType::VectorLayer != referencingLayer->type() )
78  {
79  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencingLayerId ) );
80  }
81 
82  if ( !referencedLayer )
83  {
84  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which does not exist." ).arg( referencedLayerId ) );
85  }
86  else if ( QgsMapLayerType::VectorLayer != referencedLayer->type() )
87  {
88  QgsLogger::warning( QApplication::translate( "QgsRelation", "Relation defined for layer '%1' which is not of type VectorLayer." ).arg( referencedLayerId ) );
89  }
90 
91  relation.d->mReferencingLayerId = referencingLayerId;
92  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( referencingLayer );
93  relation.d->mReferencedLayerId = referencedLayerId;
94  relation.d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( referencedLayer );
95  relation.d->mRelationId = id;
96  relation.d->mRelationName = name;
97  relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( strength, RelationStrength::Association );
98 
99  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
100  for ( int i = 0; i < references.size(); ++i )
101  {
102  QDomElement refEl = references.at( i ).toElement();
103 
104  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
105  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
106 
107  relation.addFieldPair( referencingField, referencedField );
108  }
109 
110  relation.updateRelationStatus();
111 
112  return relation;
113 }
114 
115 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
116 {
117  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
118  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
119  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
120  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
121  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
122  elem.setAttribute( QStringLiteral( "strength" ), qgsEnumValueToKey<RelationStrength>( d->mRelationStrength ) );
123 
124  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
125  {
126  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
127  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
128  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
129  elem.appendChild( referenceElem );
130  }
131 
132  node.appendChild( elem );
133 }
134 
135 void QgsRelation::setId( const QString &id )
136 {
137  d.detach();
138  d->mRelationId = id;
139 
141 }
142 
143 void QgsRelation::setName( const QString &name )
144 {
145  d.detach();
146  d->mRelationName = name;
147 }
148 
149 
151 {
152  d.detach();
153  d->mRelationStrength = strength;
154 }
155 
156 void QgsRelation::setReferencingLayer( const QString &id )
157 {
158  d.detach();
159  d->mReferencingLayerId = id;
160 
162 }
163 
164 void QgsRelation::setReferencedLayer( const QString &id )
165 {
166  d.detach();
167  d->mReferencedLayerId = id;
168 
170 }
171 
172 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
173 {
174  d.detach();
175  d->mFieldPairs << FieldPair( referencingField, referencedField );
177 }
178 
179 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
180 {
181  d.detach();
182  d->mFieldPairs << fieldPair;
184 }
185 
187 {
189 }
190 
192 {
193  QString filter = getRelatedFeaturesFilter( feature );
194  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ), 2 );
195 
196  QgsFeatureRequest myRequest;
197  myRequest.setFilterExpression( filter );
198  return myRequest;
199 }
200 
201 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
202 {
203  QStringList conditions;
204 
205  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
206  {
207  QVariant val( feature.attribute( pair.referencedField() ) );
208  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
209  }
210 
211  return conditions.join( QLatin1String( " AND " ) );
212 }
213 
215 {
216  QStringList conditions;
217 
218  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
219  {
220  int referencingIdx = referencingLayer()->fields().indexFromName( pair.referencingField() );
221  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
222  }
223 
224  QgsFeatureRequest myRequest;
225 
226  QgsDebugMsgLevel( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ), 2 );
227 
228  myRequest.setFilterExpression( conditions.join( QLatin1String( " AND " ) ) );
229 
230  return myRequest;
231 }
232 
234 {
235  return getReferencedFeatureRequest( feature.attributes() );
236 }
237 
239 {
240  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
241 
242  QgsFeature f;
243  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
244  return f;
245 }
246 
247 QString QgsRelation::name() const
248 {
249  return d->mRelationName;
250 }
251 
253 {
254  return d->mRelationStrength;
255 }
256 
257 QString QgsRelation::id() const
258 {
259  return d->mRelationId;
260 }
261 
263 {
264  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
265  .arg( referencingLayerId(),
266  d->mFieldPairs.at( 0 ).referencingField(),
268  d->mFieldPairs.at( 0 ).referencedField() );
270 }
271 
273 {
274  return d->mReferencingLayerId;
275 }
276 
278 {
279  return d->mReferencingLayer;
280 }
281 
283 {
284  return d->mReferencedLayerId;
285 }
286 
288 {
289  return d->mReferencedLayer;
290 }
291 
292 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
293 {
294  return d->mFieldPairs;
295 }
296 
298 {
299  QgsAttributeList attrs;
300 
301  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
302  {
303  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
304  }
305  return attrs;
306 }
307 
309 {
310  QgsAttributeList attrs;
311 
312  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
313  {
314  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
315  }
316  return attrs;
317 
318 }
319 
320 bool QgsRelation::isValid() const
321 {
322  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
323 }
324 
326 {
327  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
328 }
329 
330 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
331 {
332  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
333  {
334  if ( pair.first == referencingField )
335  return pair.second;
336  }
337  return QString();
338 }
339 
340 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
341 {
342  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
343  {
344  if ( pair.second == referencedField )
345  return pair.first;
346  }
347  return QString();
348 }
349 
351 {
352  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
353 
354  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
355  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
356 
357  d->mValid = true;
358 
359  if ( d->mRelationId.isEmpty() )
360  {
361  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
362  d->mValid = false;
363  }
364  else
365  {
366  if ( !d->mReferencedLayer )
367  {
368  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
369  d->mValid = false;
370  }
371  else if ( !d->mReferencingLayer )
372  {
373  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
374  d->mValid = false;
375  }
376  else
377  {
378  if ( d->mFieldPairs.count() < 1 )
379  {
380  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
381  d->mValid = false;
382  }
383 
384  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
385  {
386  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
387  {
388  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
389  d->mValid = false;
390  break;
391  }
392  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
393  {
394  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
395  d->mValid = false;
396  break;
397  }
398  }
399  }
400 
401  }
402 }
The class is used as a container of context for various read/write operations on other objects...
Wrapper for iterator of features from vector data provider or vector layer.
bool isValid() const
Returns the validity of this relation.
Base class for all map layer types.
Definition: qgsmaplayer.h:83
void generateId()
Generate a (project-wide) unique id for this relation.
const QgsProject * project() const
Gets the associated project.
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:25
void addFieldPair(const QString &referencingField, const QString &referencedField)
Add a field pairs which is part of this relation The first element of each pair are the field names o...
void setName(const QString &name)
Set a name for this relation.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
QString id() const
A (project-wide) unique id for this relation.
void setId(const QString &id)
Set an id for this relation.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
void setStrength(RelationStrength strength)
Set a strength for this relation.
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
RelationStrength strength() const
Returns the relation strength as a string.
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:74
Context for relations.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsVectorLayer * referencedLayer() const
Access the referenced (parent) layer.
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
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 QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
void setReferencedLayer(const QString &id)
Set the referenced (parent) 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...
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.
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
QString referencingLayerId() const
Access the referencing (child) layer&#39;s id This is the layer which has the field(s) which point to ano...
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:57
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:51
QgsVectorLayer * referencingLayer() const
Access the referencing (child) layer This is the layer which has the field(s) which point to another ...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsMapLayerType type
Definition: qgsmaplayer.h:91
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:264
QgsRelation & operator=(const QgsRelation &other)
Copies a relation.
Definition: qgsrelation.cpp:44
QString referencedLayerId() const
Access the referenced (parent) layer&#39;s id.
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
QgsAttributes attributes
Definition: qgsfeature.h:65
QString name() const
Returns a human readable name for this relation.
void updateRelationStatus()
Updates the validity status of this relation.