QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
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
17
18#include "qgslogger.h"
20#include "qgsproject.h"
21#include "qgsvectorlayer.h"
22
23#include <QApplication>
24
25#include "moc_qgspolymorphicrelation.cpp"
26
28 : d( new QgsPolymorphicRelationPrivate() )
29{
30}
31
33 : d( new QgsPolymorphicRelationPrivate() )
34 , mContext( context )
35{
36}
37
39
41 : d( other.d )
42 , mContext( other.mContext )
43{
44}
45
47 : d( std::move( other.d ) )
48 , mContext( std::move( other.mContext ) )
49{
50}
51
53{
54 if ( &other == this )
55 return *this;
56
57 d = other.d;
58 mContext = other.mContext;
59 return *this;
60}
61
63{
64 if ( &other == this )
65 return *this;
66
67 d = std::move( other.d );
68 mContext = std::move( other.mContext );
69 return *this;
70}
71
73{
74 Q_UNUSED( context );
75 QDomElement elem = node.toElement();
76
77 if ( elem.tagName() != QLatin1String( "relation" ) )
78 {
79 QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
80 }
81
82 QgsPolymorphicRelation relation( relationContext );
83
84 QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
85 QString referencedLayerField = elem.attribute( QStringLiteral( "referencedLayerField" ) );
86 QString referencedLayerExpression = elem.attribute( QStringLiteral( "referencedLayerExpression" ) );
87 QString id = elem.attribute( QStringLiteral( "id" ) );
88 QString name = elem.attribute( QStringLiteral( "name" ) );
89 QString relationStrength = elem.attribute( QStringLiteral( "relationStrength" ) );
90 QStringList referencedLayerIds = elem.attribute( QStringLiteral( "referencedLayerIds" ) ).split( "," );
91
92 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
93
94 relation.d->mReferencingLayerId = referencingLayerId;
95 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] );
96 relation.d->mReferencedLayerField = referencedLayerField;
97 relation.d->mReferencedLayerExpression = referencedLayerExpression;
98 relation.d->mReferencedLayerIds = referencedLayerIds;
99 relation.d->mRelationId = id;
100 relation.d->mRelationName = name;
101 relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( relationStrength, Qgis::RelationshipStrength::Association );
102
103 QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
104 for ( int i = 0; i < references.size(); ++i )
105 {
106 QDomElement refEl = references.at( i ).toElement();
107
108 QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
109 QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
110
111 relation.addFieldPair( referencingField, referencedField );
112 }
113
114 relation.updateRelationStatus();
115
116 return relation;
117}
118
119void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
120{
121 QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
122 elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
123 elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
124 elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
125 elem.setAttribute( QStringLiteral( "referencedLayerField" ), d->mReferencedLayerField );
126 elem.setAttribute( QStringLiteral( "referencedLayerExpression" ), d->mReferencedLayerExpression );
127 elem.setAttribute( QStringLiteral( "referencedLayerIds" ), d->mReferencedLayerIds.join( "," ) );
128 elem.setAttribute( QStringLiteral( "relationStrength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
129
130 // 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
131 for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) )
132 Q_ASSERT( ! layerId.contains( "," ) );
133
134 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
135 {
136 QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
137 referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
138 referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
139 elem.appendChild( referenceElem );
140 }
141
142 node.appendChild( elem );
143}
144
145void QgsPolymorphicRelation::setId( const QString &id )
146{
147 if ( d->mRelationId == id )
148 return;
149
150 d.detach();
151 d->mRelationId = id;
152
154}
155
157{
158 d.detach();
159 d->mReferencingLayerId = id;
160
162}
163
164void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
165{
166 d.detach();
167 d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField );
169}
170
172{
173 d.detach();
174 d->mFieldPairs << fieldPair;
176}
177
179{
180 return d->mRelationId;
181}
182
184{
185 d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
186 .arg( referencingLayerId(),
187 d->mFieldPairs.at( 0 ).referencingField(),
189 d->mFieldPairs.at( 0 ).referencedField() );
191}
192
194{
195 return d->mReferencingLayerId;
196}
197
199{
200 return d->mReferencingLayer;
201}
202
203QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const
204{
205 return d->mFieldPairs;
206}
207
209{
210 QgsAttributeList attrs;
211
212 if ( d->mReferencedLayerIds.contains( layerId ) )
213 {
214 QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) ); // skip-keyword-check
215
216 if ( vl && vl->isValid() )
217 {
218 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
219 {
220 attrs << vl->fields().lookupField( pair.second );
221 }
222 }
223 }
224
225 return attrs;
226}
227
229{
230 QgsAttributeList attrs;
231
232 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
233 {
234 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
235 }
236 return attrs;
237
238}
239
241{
242 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull();
243}
244
246{
247 return d->mReferencedLayerField == other.d->mReferencedLayerField
248 && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression
249 && d->mReferencingLayerId == other.d->mReferencingLayerId
250 && d->mFieldPairs == other.d->mFieldPairs;
251}
252
254{
255 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
256
257 d->mValid = true;
258 d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId )
259 ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] )
260 : nullptr;
261 d->mReferencedLayersMap.clear();
262
263 if ( d->mRelationId.isEmpty() )
264 {
265 QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
266 d->mValid = false;
267 return;
268 }
269
270 if ( !d->mReferencingLayer )
271 {
272 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %1" ).arg( d->mReferencingLayerId ), 4 );
273 d->mValid = false;
274 return;
275 }
276
277 if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 )
278 {
279 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2" ).arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 );
280 d->mValid = false;
281 return;
282 }
283
284 if ( d->mReferencedLayerExpression.trimmed().isNull() )
285 {
286 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer expression \"%1\" is missing" ).arg( d->mReferencedLayerExpression ), 4 );
287 d->mValid = false;
288 return;
289 }
290
291 const QStringList referencedLayerIds = d->mReferencedLayerIds;
292 for ( const QString &referencedLayerId : referencedLayerIds )
293 {
294 d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) );
295
296 if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() )
297 {
298 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 );
299 d->mValid = false;
300 return;
301 }
302 }
303
304 if ( d->mFieldPairs.count() == 0 )
305 {
306 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
307 d->mValid = false;
308 return;
309 }
310
311 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
312 {
313 if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 )
314 {
315 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
316 d->mValid = false;
317 return;
318 }
319
320 for ( const QString &referencedLayerId : referencedLayerIds )
321 {
322 if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 )
323 {
324 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) );
325 d->mValid = false;
326 return;
327 }
328 }
329 }
330}
331
333{
334 if ( d->mRelationName == name && !name.isEmpty() )
335 return;
336
337 d.detach();
338 d->mRelationName = name;
340}
341
343{
344 if ( d->mRelationName.isEmpty() )
345 return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : QStringLiteral( "<NO LAYER>" ) );
346
347 return d->mRelationName;
348}
349
351{
352 d.detach();
353 d->mReferencedLayerField = referencedLayerField;
355}
356
358{
359 return d->mReferencedLayerField;
360}
361
363{
364 d.detach();
365 d->mReferencedLayerExpression = referencedLayerExpression;
367}
368
370{
371 return d->mReferencedLayerExpression;
372}
373
375{
376 d.detach();
377 d->mReferencedLayerIds = referencedLayerIds;
379}
380
382{
383 return d->mReferencedLayerIds;
384}
385
387{
388 return d->mRelationStrength;
389}
390
392{
393 d.detach();
394 d->mRelationStrength = relationStrength;
396}
397
399{
400 QList<QgsRelation> relations;
401
402 if ( !isValid() )
403 return relations;
404
405 const QStringList referencedLayerIds = d->mReferencedLayerIds;
406
407 for ( const QString &referencedLayerId : referencedLayerIds )
408 {
409 QgsRelation relation;
410 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
411
412 relation.setId( QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId ) );
413 relation.setReferencedLayer( referencedLayerId );
414 relation.setReferencingLayer( d->mReferencingLayerId );
415 relation.setName( QStringLiteral( "Generated for \"%1\"" ).arg( referencedLayerName ) );
416 relation.setPolymorphicRelationId( d->mRelationId );
417 relation.setStrength( d->mRelationStrength );
418
419 const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs();
420 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
421 relation.addFieldPair( fieldPair );
422
423 if ( !relation.isValid() )
424 continue;
425
426 relations << relation;
427 }
428
429 return relations;
430}
431
432QString QgsPolymorphicRelation::upgradeGeneratedRelationId( const QString &oldRelationId ) const
433{
434 if ( !isValid() )
435 return QString();
436
437 const QStringList referencedLayerIds = d->mReferencedLayerIds;
438 for ( const QString &referencedLayerId : referencedLayerIds )
439 {
440 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
441 if ( oldRelationId == QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerName ) )
442 {
443 return QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId );
444 }
445 }
446
447 return QString();
448}
449
451{
452 if ( !layer || !layer->isValid() )
453 return QString();
454
456 QgsExpression expr( d->mReferencedLayerExpression );
457
458 return expr.evaluate( &context ).toString();
459}
RelationshipStrength
Relationship strength.
Definition qgis.h:4405
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
Definition qgis.h:4406
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Handles parsing and evaluation of expressions (formerly called "search strings").
QVariant evaluate()
Evaluate the feature and return the result.
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.
QgsAttributeList referencedFields(const QString &layerId) const
Returns a list of attributes used to form the referenced fields (most likely primary key) on the refe...
QString layerRepresentation(const QgsVectorLayer *layer) const
Returns layer representation as evaluated string.
QList< QgsRelation > generateRelations() const
Returns a list of generated relations, based on the currently set referencedLayerIds().
void setReferencedLayerIds(const QStringList &childRelationIds)
Sets a list of layer ids to be used as potential referenced layers.
void setReferencedLayerExpression(const QString &expression)
Sets the expression to identify the parent layer.
void setReferencedLayerField(const QString &referencedLayerField)
Sets the field in the referencing layer where the referenced layer identifier is stored.
void generateId()
Generate a (project-wide) unique id for this relation.
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...
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
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...
static QgsPolymorphicRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
void setRelationStrength(Qgis::RelationshipStrength relationStrength)
Sets the relation strength for all the generated normal relations.
bool hasEqualDefinition(const QgsPolymorphicRelation &other) const
Compares the two QgsRelation, ignoring the name and the ID.
QString referencingLayerId() const
Access the referencing (child) layer's id This is the layer which has the field(s) which point to ano...
void updateRelationStatus()
Updates the validity status of this relation.
Qgis::RelationshipStrength strength() const
Returns the relation strength for all the generated normal relations.
QStringList referencedLayerIds() const
Returns a list of layer ids to be used as potential referenced layers.
QgsPolymorphicRelation()
Default constructor.
QgsAttributeList referencingFields() const
Returns a list of attributes used to form the referencing fields (foreign key) on the referencing (ch...
void setName(const QString &name)
Set a name for this relation.
void setId(const QString &id)
Set an id for this relation.
QgsPolymorphicRelation & operator=(const QgsPolymorphicRelation &other)
Copies a relation.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
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.
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
Represents a relationship between two vector layers.
Definition qgsrelation.h:42
QString name
Definition qgsrelation.h:52
void setId(const QString &id)
Set an id for this relation.
void setReferencedLayer(const QString &id)
Set the referenced (parent) layer id.
void setPolymorphicRelationId(const QString &polymorphicRelationId)
Sets the parent polymorphic relation id.
void setStrength(Qgis::RelationshipStrength strength)
Set a strength for this relation.
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.
void setName(const QString &name)
Set a name for this relation.
Represents a vector layer which manages a vector based dataset.
QgsExpressionContext createExpressionContext() const final
This method needs to be reimplemented in all classes which implement this interface and return an exp...
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
QList< int > QgsAttributeList
Definition qgsfield.h:28
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57