QGIS API Documentation 3.41.0-Master (3440c17df1d)
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#include "qgsvectorlayer.h"
18#include "moc_qgspolymorphicrelation.cpp"
19#include "qgslogger.h"
20#include "qgsproject.h"
22
23#include <QApplication>
24
26 : d( new QgsPolymorphicRelationPrivate() )
27{
28}
29
31 : d( new QgsPolymorphicRelationPrivate() )
32 , mContext( context )
33{
34}
35
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
52{
53 Q_UNUSED( context );
54 QDomElement elem = node.toElement();
55
56 if ( elem.tagName() != QLatin1String( "relation" ) )
57 {
58 QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
59 }
60
61 QgsPolymorphicRelation relation( relationContext );
62
63 QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
64 QString referencedLayerField = elem.attribute( QStringLiteral( "referencedLayerField" ) );
65 QString referencedLayerExpression = elem.attribute( QStringLiteral( "referencedLayerExpression" ) );
66 QString id = elem.attribute( QStringLiteral( "id" ) );
67 QString name = elem.attribute( QStringLiteral( "name" ) );
68 QString relationStrength = elem.attribute( QStringLiteral( "relationStrength" ) );
69 QStringList referencedLayerIds = elem.attribute( QStringLiteral( "referencedLayerIds" ) ).split( "," );
70
71 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
72
73 relation.d->mReferencingLayerId = referencingLayerId;
74 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] );
75 relation.d->mReferencedLayerField = referencedLayerField;
76 relation.d->mReferencedLayerExpression = referencedLayerExpression;
77 relation.d->mReferencedLayerIds = referencedLayerIds;
78 relation.d->mRelationId = id;
79 relation.d->mRelationName = name;
80 relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( relationStrength, Qgis::RelationshipStrength::Association );
81
82 QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
83 for ( int i = 0; i < references.size(); ++i )
84 {
85 QDomElement refEl = references.at( i ).toElement();
86
87 QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
88 QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
89
90 relation.addFieldPair( referencingField, referencedField );
91 }
92
93 relation.updateRelationStatus();
94
95 return relation;
96}
97
98void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
99{
100 QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
101 elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
102 elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
103 elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
104 elem.setAttribute( QStringLiteral( "referencedLayerField" ), d->mReferencedLayerField );
105 elem.setAttribute( QStringLiteral( "referencedLayerExpression" ), d->mReferencedLayerExpression );
106 elem.setAttribute( QStringLiteral( "referencedLayerIds" ), d->mReferencedLayerIds.join( "," ) );
107 elem.setAttribute( QStringLiteral( "relationStrength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
108
109 // 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
110 for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) )
111 Q_ASSERT( ! layerId.contains( "," ) );
112
113 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
114 {
115 QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
116 referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
117 referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
118 elem.appendChild( referenceElem );
119 }
120
121 node.appendChild( elem );
122}
123
124void QgsPolymorphicRelation::setId( const QString &id )
125{
126 if ( d->mRelationId == id )
127 return;
128
129 d.detach();
130 d->mRelationId = id;
131
133}
134
136{
137 d.detach();
138 d->mReferencingLayerId = id;
139
141}
142
143void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
144{
145 d.detach();
146 d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField );
148}
149
151{
152 d.detach();
153 d->mFieldPairs << fieldPair;
155}
156
158{
159 return d->mRelationId;
160}
161
163{
164 d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
165 .arg( referencingLayerId(),
166 d->mFieldPairs.at( 0 ).referencingField(),
168 d->mFieldPairs.at( 0 ).referencedField() );
170}
171
173{
174 return d->mReferencingLayerId;
175}
176
178{
179 return d->mReferencingLayer;
180}
181
182QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const
183{
184 return d->mFieldPairs;
185}
186
188{
189 QgsAttributeList attrs;
190
191 if ( d->mReferencedLayerIds.contains( layerId ) )
192 {
193 QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) ); // skip-keyword-check
194
195 if ( vl && vl->isValid() )
196 {
197 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
198 {
199 attrs << vl->fields().lookupField( pair.second );
200 }
201 }
202 }
203
204 return attrs;
205}
206
208{
209 QgsAttributeList attrs;
210
211 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
212 {
213 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
214 }
215 return attrs;
216
217}
218
220{
221 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull();
222}
223
225{
226 return d->mReferencedLayerField == other.d->mReferencedLayerField
227 && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression
228 && d->mReferencingLayerId == other.d->mReferencingLayerId
229 && d->mFieldPairs == other.d->mFieldPairs;
230}
231
233{
234 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
235
236 d->mValid = true;
237 d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId )
238 ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] )
239 : nullptr;
240 d->mReferencedLayersMap.clear();
241
242 if ( d->mRelationId.isEmpty() )
243 {
244 QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
245 d->mValid = false;
246 return;
247 }
248
249 if ( !d->mReferencingLayer )
250 {
251 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %1" ).arg( d->mReferencingLayerId ), 4 );
252 d->mValid = false;
253 return;
254 }
255
256 if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 )
257 {
258 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2" ).arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 );
259 d->mValid = false;
260 return;
261 }
262
263 if ( d->mReferencedLayerExpression.trimmed().isNull() )
264 {
265 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer expression \"%1\" is missing" ).arg( d->mReferencedLayerExpression ), 4 );
266 d->mValid = false;
267 return;
268 }
269
270 const QStringList referencedLayerIds = d->mReferencedLayerIds;
271 for ( const QString &referencedLayerId : referencedLayerIds )
272 {
273 d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) );
274
275 if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() )
276 {
277 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 );
278 d->mValid = false;
279 return;
280 }
281 }
282
283 if ( d->mFieldPairs.count() == 0 )
284 {
285 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
286 d->mValid = false;
287 return;
288 }
289
290 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
291 {
292 if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 )
293 {
294 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
295 d->mValid = false;
296 return;
297 }
298
299 for ( const QString &referencedLayerId : referencedLayerIds )
300 {
301 if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 )
302 {
303 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) );
304 d->mValid = false;
305 return;
306 }
307 }
308 }
309}
310
311void QgsPolymorphicRelation::setName( const QString &name )
312{
313 if ( d->mRelationName == name && !name.isEmpty() )
314 return;
315
316 d.detach();
317 d->mRelationName = name;
319}
320
322{
323 if ( d->mRelationName.isEmpty() )
324 return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : QStringLiteral( "<NO LAYER>" ) );
325
326 return d->mRelationName;
327}
328
329void QgsPolymorphicRelation::setReferencedLayerField( const QString &referencedLayerField )
330{
331 d.detach();
332 d->mReferencedLayerField = referencedLayerField;
334}
335
337{
338 return d->mReferencedLayerField;
339}
340
341void QgsPolymorphicRelation::setReferencedLayerExpression( const QString &referencedLayerExpression )
342{
343 d.detach();
344 d->mReferencedLayerExpression = referencedLayerExpression;
346}
347
349{
350 return d->mReferencedLayerExpression;
351}
352
353void QgsPolymorphicRelation::setReferencedLayerIds( const QStringList &referencedLayerIds )
354{
355 d.detach();
356 d->mReferencedLayerIds = referencedLayerIds;
358}
359
361{
362 return d->mReferencedLayerIds;
363}
364
366{
367 return d->mRelationStrength;
368}
369
371{
372 d.detach();
373 d->mRelationStrength = relationStrength;
375}
376
378{
379 QList<QgsRelation> relations;
380
381 if ( !isValid() )
382 return relations;
383
384 const QStringList referencedLayerIds = d->mReferencedLayerIds;
385
386 for ( const QString &referencedLayerId : referencedLayerIds )
387 {
388 QgsRelation relation;
389 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
390
391 relation.setId( QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId ) );
392 relation.setReferencedLayer( referencedLayerId );
393 relation.setReferencingLayer( d->mReferencingLayerId );
394 relation.setName( QStringLiteral( "Generated for \"%1\"" ).arg( referencedLayerName ) );
395 relation.setPolymorphicRelationId( d->mRelationId );
396 relation.setStrength( d->mRelationStrength );
397
398 const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs();
399 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
400 relation.addFieldPair( fieldPair );
401
402 if ( !relation.isValid() )
403 continue;
404
405 relations << relation;
406 }
407
408 return relations;
409}
410
411QString QgsPolymorphicRelation::upgradeGeneratedRelationId( const QString &oldRelationId ) const
412{
413 if ( !isValid() )
414 return QString();
415
416 const QStringList referencedLayerIds = d->mReferencedLayerIds;
417 for ( const QString &referencedLayerId : referencedLayerIds )
418 {
419 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
420 if ( oldRelationId == QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerName ) )
421 {
422 return QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId );
423 }
424 }
425
426 return QString();
427}
428
430{
431 if ( !layer || !layer->isValid() )
432 return QString();
433
435 QgsExpression expr( d->mReferencedLayerExpression );
436
437 return expr.evaluate( &context ).toString();
438}
RelationshipStrength
Relationship strength.
Definition qgis.h:4108
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Class for 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.
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
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.
The class is used as a container of context for various read/write operations on other 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:69
Represents a relationship between two vector layers.
Definition qgsrelation.h:44
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.
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 data sets.
QgsExpressionContext createExpressionContext() const FINAL
This method needs to be reimplemented in all classes which implement this interface and return an exp...
QList< int > QgsAttributeList
Definition qgsfield.h:27
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38