QGIS API Documentation  3.14.0-Pi (9f7028fd23)
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  }
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  }
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  if ( strength == QLatin1String( "Composition" ) )
98  {
99  relation.d->mRelationStrength = RelationStrength::Composition;
100  }
101  else
102  {
103  relation.d->mRelationStrength = RelationStrength::Association;
104  }
105 
106  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
107  for ( int i = 0; i < references.size(); ++i )
108  {
109  QDomElement refEl = references.at( i ).toElement();
110 
111  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
112  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
113 
114  relation.addFieldPair( referencingField, referencedField );
115  }
116 
117  relation.updateRelationStatus();
118 
119  return relation;
120 }
121 
122 void QgsRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
123 {
124  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
125  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
126  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
127  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
128  elem.setAttribute( QStringLiteral( "referencedLayer" ), d->mReferencedLayerId );
129  if ( d->mRelationStrength == RelationStrength::Composition )
130  {
131  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Composition" ) );
132  }
133  else
134  {
135  elem.setAttribute( QStringLiteral( "strength" ), QStringLiteral( "Association" ) );
136  }
137 
138  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
139  {
140  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
141  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
142  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
143  elem.appendChild( referenceElem );
144  }
145 
146  node.appendChild( elem );
147 }
148 
149 void QgsRelation::setId( const QString &id )
150 {
151  d.detach();
152  d->mRelationId = id;
153 
155 }
156 
157 void QgsRelation::setName( const QString &name )
158 {
159  d.detach();
160  d->mRelationName = name;
161 }
162 
163 
165 {
166  d.detach();
167  d->mRelationStrength = strength;
168 }
169 
170 void QgsRelation::setReferencingLayer( const QString &id )
171 {
172  d.detach();
173  d->mReferencingLayerId = id;
174 
176 }
177 
178 void QgsRelation::setReferencedLayer( const QString &id )
179 {
180  d.detach();
181  d->mReferencedLayerId = id;
182 
184 }
185 
186 void QgsRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
187 {
188  d.detach();
189  d->mFieldPairs << FieldPair( referencingField, referencedField );
191 }
192 
193 void QgsRelation::addFieldPair( const FieldPair &fieldPair )
194 {
195  d.detach();
196  d->mFieldPairs << fieldPair;
198 }
199 
201 {
203 }
204 
206 {
207  QString filter = getRelatedFeaturesFilter( feature );
208  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( filter ) );
209 
210  QgsFeatureRequest myRequest;
211  myRequest.setFilterExpression( filter );
212  return myRequest;
213 }
214 
215 QString QgsRelation::getRelatedFeaturesFilter( const QgsFeature &feature ) const
216 {
217  QStringList conditions;
218 
219  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
220  {
221  QVariant val( feature.attribute( pair.referencedField() ) );
222  conditions << QgsExpression::createFieldEqualityExpression( pair.referencingField(), val );
223  }
224 
225  return conditions.join( QStringLiteral( " AND " ) );
226 }
227 
229 {
230  QStringList conditions;
231 
232  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
233  {
234  int referencingIdx = referencingLayer()->fields().indexFromName( pair.referencingField() );
235  conditions << QgsExpression::createFieldEqualityExpression( pair.referencedField(), attributes.at( referencingIdx ) );
236  }
237 
238  QgsFeatureRequest myRequest;
239 
240  QgsDebugMsg( QStringLiteral( "Filter conditions: '%1'" ).arg( conditions.join( " AND " ) ) );
241 
242  myRequest.setFilterExpression( conditions.join( QStringLiteral( " AND " ) ) );
243 
244  return myRequest;
245 }
246 
248 {
249  return getReferencedFeatureRequest( feature.attributes() );
250 }
251 
253 {
254  QgsFeatureRequest request = getReferencedFeatureRequest( feature );
255 
256  QgsFeature f;
257  d->mReferencedLayer->getFeatures( request ).nextFeature( f );
258  return f;
259 }
260 
261 QString QgsRelation::name() const
262 {
263  return d->mRelationName;
264 }
265 
267 {
268  return d->mRelationStrength;
269 }
270 
271 QString QgsRelation::id() const
272 {
273  return d->mRelationId;
274 }
275 
277 {
278  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
279  .arg( referencingLayerId(),
280  d->mFieldPairs.at( 0 ).referencingField(),
282  d->mFieldPairs.at( 0 ).referencedField() );
284 }
285 
287 {
288  return d->mReferencingLayerId;
289 }
290 
292 {
293  return d->mReferencingLayer;
294 }
295 
297 {
298  return d->mReferencedLayerId;
299 }
300 
302 {
303  return d->mReferencedLayer;
304 }
305 
306 QList<QgsRelation::FieldPair> QgsRelation::fieldPairs() const
307 {
308  return d->mFieldPairs;
309 }
310 
312 {
313  QgsAttributeList attrs;
314 
315  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
316  {
317  attrs << d->mReferencedLayer->fields().lookupField( pair.second );
318  }
319  return attrs;
320 }
321 
323 {
324  QgsAttributeList attrs;
325 
326  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
327  {
328  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
329  }
330  return attrs;
331 
332 }
333 
334 bool QgsRelation::isValid() const
335 {
336  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayer.isNull() && d->mReferencingLayer.data()->isValid() && d->mReferencedLayer.data()->isValid();
337 }
338 
340 {
341  return d->mReferencedLayerId == other.d->mReferencedLayerId && d->mReferencingLayerId == other.d->mReferencingLayerId && d->mFieldPairs == other.d->mFieldPairs;
342 }
343 
344 QString QgsRelation::resolveReferencedField( const QString &referencingField ) const
345 {
346  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
347  {
348  if ( pair.first == referencingField )
349  return pair.second;
350  }
351  return QString();
352 }
353 
354 QString QgsRelation::resolveReferencingField( const QString &referencedField ) const
355 {
356  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
357  {
358  if ( pair.second == referencedField )
359  return pair.first;
360  }
361  return QString();
362 }
363 
365 {
366  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
367 
368  d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] );
369  d->mReferencedLayer = qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencedLayerId] );
370 
371  d->mValid = true;
372 
373  if ( d->mRelationId.isEmpty() )
374  {
375  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
376  d->mValid = false;
377  }
378  else
379  {
380  if ( !d->mReferencedLayer )
381  {
382  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayerId ), 4 );
383  d->mValid = false;
384  }
385  else if ( !d->mReferencingLayer )
386  {
387  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %2" ).arg( d->mReferencingLayerId ), 4 );
388  d->mValid = false;
389  }
390  else
391  {
392  if ( d->mFieldPairs.count() < 1 )
393  {
394  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
395  d->mValid = false;
396  }
397 
398  for ( const FieldPair &pair : qgis::as_const( d->mFieldPairs ) )
399  {
400  if ( -1 == d->mReferencingLayer->fields().lookupField( pair.first ) )
401  {
402  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
403  d->mValid = false;
404  break;
405  }
406  else if ( -1 == d->mReferencedLayer->fields().lookupField( pair.second ) )
407  {
408  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencedg layer %2" ).arg( pair.second, d->mReferencedLayer->name() ) );
409  d->mValid = false;
410  break;
411  }
412  }
413  }
414 
415  }
416 }
QgsVectorLayer::getFeatures
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
Definition: qgsvectorlayer.cpp:993
QgsRelation::referencingFields
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
Definition: qgsrelation.cpp:322
QgsRelation::QgsRelation
QgsRelation()
Default constructor.
Definition: qgsrelation.cpp:25
QgsReadWriteContext
Definition: qgsreadwritecontext.h:34
QgsMapLayerType::VectorLayer
@ VectorLayer
QgsDebugMsgLevel
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QgsProject::mapLayers
QMap< QString, QgsMapLayer * > mapLayers(const bool validOnly=false) const
Returns a map of all registered layers by layer ID.
Definition: qgsproject.cpp:3347
QgsRelation::updateRelationStatus
void updateRelationStatus()
Updates the validity status of this relation.
Definition: qgsrelation.cpp:364
QgsRelation::name
QString name
Definition: qgsrelation.h:48
QgsRelation::addFieldPair
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...
Definition: qgsrelation.cpp:186
qgsfeatureiterator.h
QgsRelation::referencedFields
QgsAttributeList referencedFields() const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
Definition: qgsrelation.cpp:311
QgsRelation::strength
RelationStrength strength() const
Returns the relation strength as a string.
Definition: qgsrelation.cpp:266
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsRelation::setStrength
void setStrength(RelationStrength strength)
Set a strength for this relation.
Definition: qgsrelation.cpp:164
QgsRelationContext::project
const QgsProject * project() const
Gets the associated project.
Definition: qgsrelationcontext.cpp:36
qgsrelation_p.h
QgsRelation::setReferencingLayer
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
Definition: qgsrelation.cpp:170
QgsRelation::referencingLayer
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
QgsFeatureRequest::setFilterExpression
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
Definition: qgsfeaturerequest.cpp:129
QgsRelation::resolveReferencedField
Q_INVOKABLE QString resolveReferencedField(const QString &referencingField) const
Gets the referenced field counterpart given a referencing field.
Definition: qgsrelation.cpp:344
QgsRelation::id
QString id
Definition: qgsrelation.h:45
qgsapplication.h
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3280
QgsFeatureRequest
Definition: qgsfeaturerequest.h:75
QgsRelation::setName
void setName(const QString &name)
Set a name for this relation.
Definition: qgsrelation.cpp:157
QgsReadWriteContext::projectTranslator
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
Definition: qgsreadwritecontext.h:108
QgsRelation::referencedLayer
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:47
QgsRelation::setId
void setId(const QString &id)
Set an id for this relation.
Definition: qgsrelation.cpp:149
QgsRelation::resolveReferencingField
Q_INVOKABLE QString resolveReferencingField(const QString &referencedField) const
Gets the referencing field counterpart given a referenced field.
Definition: qgsrelation.cpp:354
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsFeature::attribute
QVariant attribute(const QString &name) const
Lookup attribute value from attribute name.
Definition: qgsfeature.cpp:262
QgsRelation::getReferencedFeatureRequest
QgsFeatureRequest getReferencedFeatureRequest(const QgsAttributes &attributes) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Definition: qgsrelation.cpp:228
qgsrelation.h
QgsRelation::FieldPair
Definition: qgsrelation.h:74
QgsFeature::attributes
QgsAttributes attributes
Definition: qgsfeature.h:69
qgsvectorlayer.h
QgsRelation::fieldPairs
QList< QgsRelation::FieldPair > fieldPairs() const
Returns the field pairs which form this relation The first element of each pair are the field names o...
Definition: qgsrelation.cpp:306
QgsRelation::getRelatedFeatures
QgsFeatureIterator getRelatedFeatures(const QgsFeature &feature) const
Creates an iterator which returns all the features on the referencing (child) layer which have a fore...
Definition: qgsrelation.cpp:200
QgsRelation::getRelatedFeaturesRequest
QgsFeatureRequest getRelatedFeaturesRequest(const QgsFeature &feature) const
Creates a request to return all the features on the referencing (child) layer which have a foreign ke...
Definition: qgsrelation.cpp:205
QgsRelation::~QgsRelation
~QgsRelation()
QgsExpression::createFieldEqualityExpression
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value)
Create an expression allowing to evaluate if a field is equal to a value.
Definition: qgsexpression.cpp:1062
QgsRelation::hasEqualDefinition
bool hasEqualDefinition(const QgsRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
Definition: qgsrelation.cpp:339
QgsVectorLayer
Definition: qgsvectorlayer.h:385
QgsMapLayer
Definition: qgsmaplayer.h:81
QgsRelation::setReferencedLayer
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
Definition: qgsrelation.cpp:178
QgsRelation::isValid
bool isValid
Definition: qgsrelation.h:49
QgsRelation::operator=
QgsRelation & operator=(const QgsRelation &other)
Copies a relation.
Definition: qgsrelation.cpp:44
QgsRelation::createFromXml
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:51
QgsRelation::referencingLayerId
QString referencingLayerId() const
Access the referencing (child) layer's id This is the layer which has the field(s) which point to ano...
Definition: qgsrelation.cpp:286
QgsRelation
Definition: qgsrelation.h:41
QgsAttributes
Definition: qgsattributes.h:57
QgsRelation::RelationStrength
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:57
QgsFeature
Definition: qgsfeature.h:55
QgsRelation::getRelatedFeaturesFilter
QString getRelatedFeaturesFilter(const QgsFeature &feature) const
Returns a filter expression which returns all the features on the referencing (child) layer which hav...
Definition: qgsrelation.cpp:215
QgsRelationContext
Definition: qgsrelationcontext.h:31
qgslogger.h
QgsRelation::writeXml
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Definition: qgsrelation.cpp:122
QgsFeatureIterator
Definition: qgsfeatureiterator.h:263
QgsRelation::generateId
void generateId()
Generate a (project-wide) unique id for this relation.
Definition: qgsrelation.cpp:276
QgsMapLayer::type
QgsMapLayerType type() const
Returns the type of the layer.
Definition: qgsmaplayer.cpp:129
QgsRelation::getReferencedFeature
QgsFeature getReferencedFeature(const QgsFeature &feature) const
Creates a request to return the feature on the referenced (parent) layer which is referenced by the p...
Definition: qgsrelation.cpp:252
qgsproject.h
QgsFields::indexFromName
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsRelation::referencedLayerId
QString referencedLayerId() const
Access the referenced (parent) layer's id.
Definition: qgsrelation.cpp:296
QgsProjectTranslator::translate
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.