QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgspolymorphicrelation.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgspolymorphicrelation.h
3  --------------------------------------
4  Date : December 2020
5  Copyright : (C) 2020 Ivan Ivanov
6  Email : ivan 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 "qgspolymorphicrelation.h"
17 
18 #include "qgsapplication.h"
19 #include "qgsfeatureiterator.h"
20 #include "qgslogger.h"
21 #include "qgsproject.h"
22 #include "qgsvectorlayer.h"
25 
27  : d( new QgsPolymorphicRelationPrivate() )
28 {
29 }
30 
32  : d( new QgsPolymorphicRelationPrivate() )
33  , mContext( context )
34 {
35 }
36 
38 
40  : d( other.d )
41  , mContext( other.mContext )
42 {
43 }
44 
46 {
47  d = other.d;
48  mContext = other.mContext;
49  return *this;
50 }
51 
53 {
54  Q_UNUSED( context );
55  QDomElement elem = node.toElement();
56 
57  if ( elem.tagName() != QLatin1String( "relation" ) )
58  {
59  QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
60  }
61 
62  QgsPolymorphicRelation relation( relationContext );
63 
64  QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
65  QString referencedLayerField = elem.attribute( QStringLiteral( "referencedLayerField" ) );
66  QString referencedLayerExpression = elem.attribute( QStringLiteral( "referencedLayerExpression" ) );
67  QString id = elem.attribute( QStringLiteral( "id" ) );
68  QString name = elem.attribute( QStringLiteral( "name" ) );
69  QString relationStrength = elem.attribute( QStringLiteral( "relationStrength" ) );
70  QStringList referencedLayerIds = elem.attribute( QStringLiteral( "referencedLayerIds" ) ).split( "," );
71 
72  QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
73 
74  relation.d->mReferencingLayerId = referencingLayerId;
75  relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] );
76  relation.d->mReferencedLayerField = referencedLayerField;
77  relation.d->mReferencedLayerExpression = referencedLayerExpression;
78  relation.d->mReferencedLayerIds = referencedLayerIds;
79  relation.d->mRelationId = id;
80  relation.d->mRelationName = name;
81  relation.d->mRelationStrength = qgsEnumKeyToValue<QgsRelation::RelationStrength>( relationStrength, QgsRelation::RelationStrength::Association );
82 
83  QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
84  for ( int i = 0; i < references.size(); ++i )
85  {
86  QDomElement refEl = references.at( i ).toElement();
87 
88  QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
89  QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
90 
91  relation.addFieldPair( referencingField, referencedField );
92  }
93 
94  relation.updateRelationStatus();
95 
96  return relation;
97 }
98 
99 void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
100 {
101  QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
102  elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
103  elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
104  elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
105  elem.setAttribute( QStringLiteral( "referencedLayerField" ), d->mReferencedLayerField );
106  elem.setAttribute( QStringLiteral( "referencedLayerExpression" ), d->mReferencedLayerExpression );
107  elem.setAttribute( QStringLiteral( "referencedLayerIds" ), d->mReferencedLayerIds.join( "," ) );
108  elem.setAttribute( QStringLiteral( "relationStrength" ), qgsEnumValueToKey<QgsRelation::RelationStrength>( d->mRelationStrength ) );
109 
110  // note that a layer id can store a comma in theory. Luckyly, this is not easy to achieve, e.g. you need to modify the .qgs file manually
111  for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) )
112  Q_ASSERT( ! layerId.contains( "," ) );
113 
114  for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
115  {
116  QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
117  referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
118  referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
119  elem.appendChild( referenceElem );
120  }
121 
122  node.appendChild( elem );
123 }
124 
125 void QgsPolymorphicRelation::setId( const QString &id )
126 {
127  if ( d->mRelationId == id )
128  return;
129 
130  d.detach();
131  d->mRelationId = id;
132 
134 }
135 
137 {
138  d.detach();
139  d->mReferencingLayerId = id;
140 
142 }
143 
144 void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
145 {
146  d.detach();
147  d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField );
149 }
150 
152 {
153  d.detach();
154  d->mFieldPairs << fieldPair;
156 }
157 
158 QString QgsPolymorphicRelation::id() const
159 {
160  return d->mRelationId;
161 }
162 
164 {
165  d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
166  .arg( referencingLayerId(),
167  d->mFieldPairs.at( 0 ).referencingField(),
169  d->mFieldPairs.at( 0 ).referencedField() );
171 }
172 
174 {
175  return d->mReferencingLayerId;
176 }
177 
179 {
180  return d->mReferencingLayer;
181 }
182 
183 QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const
184 {
185  return d->mFieldPairs;
186 }
187 
189 {
190  QgsAttributeList attrs;
191 
192  if ( d->mReferencedLayerIds.contains( layerId ) )
193  {
194  QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
195 
196  if ( vl && vl->isValid() )
197  {
198  for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
199  {
200  attrs << vl->fields().lookupField( pair.second );
201  }
202  }
203  }
204 
205  return attrs;
206 }
207 
209 {
210  QgsAttributeList attrs;
211 
212  for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
213  {
214  attrs << d->mReferencingLayer->fields().lookupField( pair.first );
215  }
216  return attrs;
217 
218 }
219 
221 {
222  return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull();
223 }
224 
226 {
227  return d->mReferencedLayerField == other.d->mReferencedLayerField
228  && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression
229  && d->mReferencingLayerId == other.d->mReferencingLayerId
230  && d->mFieldPairs == other.d->mFieldPairs;
231 }
232 
234 {
235  const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
236 
237  d->mValid = true;
238  d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId )
239  ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] )
240  : nullptr;
241  d->mReferencedLayersMap.clear();
242 
243  if ( d->mRelationId.isEmpty() )
244  {
245  QgsDebugMsg( QStringLiteral( "Invalid relation: no ID" ) );
246  d->mValid = false;
247  return;
248  }
249 
250  if ( !d->mReferencingLayer )
251  {
252  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %1" ).arg( d->mReferencingLayerId ), 4 );
253  d->mValid = false;
254  return;
255  }
256 
257  if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 )
258  {
259  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2" ).arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 );
260  d->mValid = false;
261  return;
262  }
263 
264  if ( d->mReferencedLayerExpression.trimmed().isNull() )
265  {
266  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer expression \"%1\" is missing" ).arg( d->mReferencedLayerExpression ), 4 );
267  d->mValid = false;
268  return;
269  }
270 
271  const QStringList referencedLayerIds = d->mReferencedLayerIds;
272  for ( const QString &referencedLayerId : referencedLayerIds )
273  {
274  d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) );
275 
276  if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() )
277  {
278  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 );
279  d->mValid = false;
280  return;
281  }
282  }
283 
284  if ( d->mFieldPairs.count() == 0 )
285  {
286  QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
287  d->mValid = false;
288  return;
289  }
290 
291  for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
292  {
293  if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 )
294  {
295  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
296  d->mValid = false;
297  return;
298  }
299 
300  for ( const QString &referencedLayerId : referencedLayerIds )
301  {
302  if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 )
303  {
304  QgsDebugMsg( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) );
305  d->mValid = false;
306  return;
307  }
308  }
309  }
310 }
311 
312 void QgsPolymorphicRelation::setName( const QString &name )
313 {
314  if ( d->mRelationName == name && !name.isEmpty() )
315  return;
316 
317  d.detach();
318  d->mRelationName = name;
320 }
321 
322 QString QgsPolymorphicRelation::name() const
323 {
324  if ( d->mRelationName.isEmpty() )
325  return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : QStringLiteral( "<NO LAYER>" ) );
326 
327  return d->mRelationName;
328 }
329 
330 void QgsPolymorphicRelation::setReferencedLayerField( const QString &referencedLayerField )
331 {
332  d.detach();
333  d->mReferencedLayerField = referencedLayerField;
335 }
336 
338 {
339  return d->mReferencedLayerField;
340 }
341 
342 void QgsPolymorphicRelation::setReferencedLayerExpression( const QString &referencedLayerExpression )
343 {
344  d.detach();
345  d->mReferencedLayerExpression = referencedLayerExpression;
347 }
348 
350 {
351  return d->mReferencedLayerExpression;
352 }
353 
354 void QgsPolymorphicRelation::setReferencedLayerIds( const QStringList &referencedLayerIds )
355 {
356  d.detach();
357  d->mReferencedLayerIds = referencedLayerIds;
359 }
360 
362 {
363  return d->mReferencedLayerIds;
364 }
365 
367 {
368  return d->mRelationStrength;
369 }
370 
372 {
373  d.detach();
374  d->mRelationStrength = relationStrength;
376 }
377 
379 {
380  QList<QgsRelation> relations;
381 
382  if ( !isValid() )
383  return relations;
384 
385  const QStringList referencedLayerIds = d->mReferencedLayerIds;
386 
387  for ( const QString &referencedLayerId : referencedLayerIds )
388  {
389  QgsRelation relation;
390  QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
391 
392  relation.setId( QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerName ) );
393  relation.setReferencedLayer( referencedLayerId );
394  relation.setReferencingLayer( d->mReferencingLayerId );
395  relation.setName( QStringLiteral( "Generated for \"%1\"" ).arg( referencedLayerName ) );
396  relation.setPolymorphicRelationId( d->mRelationId );
397  relation.setStrength( d->mRelationStrength );
398 
399  const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs();
400  for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
401  relation.addFieldPair( fieldPair );
402 
403  if ( !relation.isValid() )
404  continue;
405 
406  relations << relation;
407  }
408 
409  return relations;
410 }
411 
413 {
414  if ( !layer || !layer->isValid() )
415  return QString();
416 
418  QgsExpression expr( d->mReferencedLayerExpression );
419 
420  return expr.evaluate( &context ).toString();
421 }
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
qgsexpressioncontextutils.h
QgsPolymorphicRelation
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
Definition: qgspolymorphicrelation.h:49
QgsReadWriteContext
The class is used as a container of context for various read/write operations on other objects.
Definition: qgsreadwritecontext.h:34
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:3955
QgsRelation::addFieldPair
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...
Definition: qgsrelation.cpp:174
qgsfeatureiterator.h
QgsProject::instance
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:480
QgsPolymorphicRelation::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: qgspolymorphicrelation.cpp:173
QgsPolymorphicRelation::operator=
QgsPolymorphicRelation & operator=(const QgsPolymorphicRelation &other)
Copies a relation.
Definition: qgspolymorphicrelation.cpp:45
QgsPolymorphicRelation::createFromXml
static QgsPolymorphicRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgspolymorphicrelation.cpp:52
QgsMapLayer::isValid
bool isValid
Definition: qgsmaplayer.h:81
QgsDebugMsg
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
QgsProject::mapLayer
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
Definition: qgsproject.cpp:3680
QgsAttributeList
QList< int > QgsAttributeList
Definition: qgsfield.h:26
QgsRelation::setStrength
void setStrength(RelationStrength strength)
Set a strength for this relation.
Definition: qgsrelation.cpp:152
QgsRelationContext::project
const QgsProject * project() const
Gets the associated project.
Definition: qgsrelationcontext.cpp:36
QgsRelation::setReferencingLayer
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
Definition: qgsrelation.cpp:158
qgsapplication.h
QgsVectorLayer::fields
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Definition: qgsvectorlayer.cpp:3436
QgsPolymorphicRelation::setReferencedLayerIds
void setReferencedLayerIds(const QStringList &childRelationIds)
Sets a list of layer ids to be used as potential referenced layers.
Definition: qgspolymorphicrelation.cpp:354
QgsRelation::setName
void setName(const QString &name)
Set a name for this relation.
Definition: qgsrelation.cpp:145
QgsPolymorphicRelation::referencingFields
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
Definition: qgspolymorphicrelation.cpp:208
QgsPolymorphicRelation::referencedFields
QgsAttributeList referencedFields(const QString &layerId) const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
Definition: qgspolymorphicrelation.cpp:188
QgsPolymorphicRelation::name
QString name
Definition: qgspolymorphicrelation.h:57
QgsPolymorphicRelation::updateRelationStatus
void updateRelationStatus()
Updates the validity status of this relation.
Definition: qgspolymorphicrelation.cpp:233
QgsPolymorphicRelation::setReferencingLayer
void setReferencingLayer(const QString &id)
Set the referencing (child) layer id.
Definition: qgspolymorphicrelation.cpp:136
QgsRelation::setId
void setId(const QString &id)
Set an id for this relation.
Definition: qgsrelation.cpp:137
QgsPolymorphicRelation::writeXml
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
Definition: qgspolymorphicrelation.cpp:99
QgsLogger::warning
static void warning(const QString &msg)
Goes to qWarning.
Definition: qgslogger.cpp:122
QgsPolymorphicRelation::layerRepresentation
QString layerRepresentation(const QgsVectorLayer *layer) const
Returns layer representation as evaluated string.
Definition: qgspolymorphicrelation.cpp:412
QgsPolymorphicRelation::setReferencedLayerExpression
void setReferencedLayerExpression(const QString &expression)
Sets the expression to identify the parent layer.
Definition: qgspolymorphicrelation.cpp:342
QgsRelation::FieldPair
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:88
QgsExpression::evaluate
QVariant evaluate()
Evaluate the feature and return the result.
Definition: qgsexpression.cpp:350
QgsPolymorphicRelation::setId
void setId(const QString &id)
Set an id for this relation.
Definition: qgspolymorphicrelation.cpp:125
QgsPolymorphicRelation::setReferencedLayerField
void setReferencedLayerField(const QString &referencedLayerField)
Sets the field in the referencing layer where the referenced layer identifier is stored.
Definition: qgspolymorphicrelation.cpp:330
qgsvectorlayer.h
QgsPolymorphicRelation::id
QString id
Definition: qgspolymorphicrelation.h:53
QgsPolymorphicRelation::referencingLayer
QgsVectorLayer referencingLayer
Definition: qgspolymorphicrelation.h:54
qgspolymorphicrelation_p.h
QgsPolymorphicRelation::referencedLayerExpression
QString referencedLayerExpression
Definition: qgspolymorphicrelation.h:56
QgsPolymorphicRelation::isValid
bool isValid
Definition: qgspolymorphicrelation.h:58
QgsVectorLayer
Represents a vector layer which manages a vector based data sets.
Definition: qgsvectorlayer.h:391
QgsPolymorphicRelation::setRelationStrength
void setRelationStrength(QgsRelation::RelationStrength relationStrength)
Sets the relation strength for all the generated normal relations.
Definition: qgspolymorphicrelation.cpp:371
QgsPolymorphicRelation::addFieldPair
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...
Definition: qgspolymorphicrelation.cpp:144
QgsRelation::setReferencedLayer
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
Definition: qgsrelation.cpp:166
QgsRelation::isValid
bool isValid
Definition: qgsrelation.h:50
QgsPolymorphicRelation::generateRelations
QList< QgsRelation > generateRelations() const
Returns a list of generated relations, based on the currently set referencedLayerIds()
Definition: qgspolymorphicrelation.cpp:378
QgsRelation
Definition: qgsrelation.h:42
qgspolymorphicrelation.h
QgsPolymorphicRelation::hasEqualDefinition
bool hasEqualDefinition(const QgsPolymorphicRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
Definition: qgspolymorphicrelation.cpp:225
QgsPolymorphicRelation::generateId
void generateId()
Generate a (project-wide) unique id for this relation.
Definition: qgspolymorphicrelation.cpp:163
QgsRelation::RelationStrength
RelationStrength
enum for the relation strength Association, Composition
Definition: qgsrelation.h:70
QgsPolymorphicRelation::referencedLayerField
QString referencedLayerField
Definition: qgspolymorphicrelation.h:55
QgsPolymorphicRelation::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: qgspolymorphicrelation.cpp:183
QgsRelationContext
Context for relations. Used to resolve layers from projects.
Definition: qgsrelationcontext.h:31
qgslogger.h
QgsPolymorphicRelation::setName
void setName(const QString &name)
Set a name for this relation.
Definition: qgspolymorphicrelation.cpp:312
QgsPolymorphicRelation::referencedLayerIds
QStringList referencedLayerIds() const
Returns a list of layer ids to be used as potential referenced layers.
Definition: qgspolymorphicrelation.cpp:361
QgsFields::lookupField
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsVectorLayer::createExpressionContext
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
Definition: qgsvectorlayer.cpp:5203
QgsExpression
Class for parsing and evaluation of expressions (formerly called "search strings")....
Definition: qgsexpression.h:102
qgsproject.h
QgsPolymorphicRelation::~QgsPolymorphicRelation
~QgsPolymorphicRelation()
QgsRelation::setPolymorphicRelationId
void setPolymorphicRelationId(const QString &polymorphicRelationId)
Sets the parent polymorphic relation id.
Definition: qgsrelation.cpp:442
QgsPolymorphicRelation::strength
QgsRelation::RelationStrength strength() const
Returns the relation strength for all the generated normal relations.
Definition: qgspolymorphicrelation.cpp:366
QgsPolymorphicRelation::QgsPolymorphicRelation
QgsPolymorphicRelation()
Default constructor.
Definition: qgspolymorphicrelation.cpp:26