QGIS API Documentation 3.39.0-Master (699c004a5df)
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 "qgslogger.h"
18#include "qgsproject.h"
19#include "qgsvectorlayer.h"
21
22#include <QApplication>
23
25 : d( new QgsPolymorphicRelationPrivate() )
26{
27}
28
30 : d( new QgsPolymorphicRelationPrivate() )
31 , mContext( context )
32{
33}
34
36
38 : d( other.d )
39 , mContext( other.mContext )
40{
41}
42
44{
45 d = other.d;
46 mContext = other.mContext;
47 return *this;
48}
49
51{
52 Q_UNUSED( context );
53 QDomElement elem = node.toElement();
54
55 if ( elem.tagName() != QLatin1String( "relation" ) )
56 {
57 QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
58 }
59
60 QgsPolymorphicRelation relation( relationContext );
61
62 QString referencingLayerId = elem.attribute( QStringLiteral( "referencingLayer" ) );
63 QString referencedLayerField = elem.attribute( QStringLiteral( "referencedLayerField" ) );
64 QString referencedLayerExpression = elem.attribute( QStringLiteral( "referencedLayerExpression" ) );
65 QString id = elem.attribute( QStringLiteral( "id" ) );
66 QString name = elem.attribute( QStringLiteral( "name" ) );
67 QString relationStrength = elem.attribute( QStringLiteral( "relationStrength" ) );
68 QStringList referencedLayerIds = elem.attribute( QStringLiteral( "referencedLayerIds" ) ).split( "," );
69
70 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
71
72 relation.d->mReferencingLayerId = referencingLayerId;
73 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] );
74 relation.d->mReferencedLayerField = referencedLayerField;
75 relation.d->mReferencedLayerExpression = referencedLayerExpression;
76 relation.d->mReferencedLayerIds = referencedLayerIds;
77 relation.d->mRelationId = id;
78 relation.d->mRelationName = name;
79 relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( relationStrength, Qgis::RelationshipStrength::Association );
80
81 QDomNodeList references = elem.elementsByTagName( QStringLiteral( "fieldRef" ) );
82 for ( int i = 0; i < references.size(); ++i )
83 {
84 QDomElement refEl = references.at( i ).toElement();
85
86 QString referencingField = refEl.attribute( QStringLiteral( "referencingField" ) );
87 QString referencedField = refEl.attribute( QStringLiteral( "referencedField" ) );
88
89 relation.addFieldPair( referencingField, referencedField );
90 }
91
92 relation.updateRelationStatus();
93
94 return relation;
95}
96
97void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
98{
99 QDomElement elem = doc.createElement( QStringLiteral( "relation" ) );
100 elem.setAttribute( QStringLiteral( "id" ), d->mRelationId );
101 elem.setAttribute( QStringLiteral( "name" ), d->mRelationName );
102 elem.setAttribute( QStringLiteral( "referencingLayer" ), d->mReferencingLayerId );
103 elem.setAttribute( QStringLiteral( "referencedLayerField" ), d->mReferencedLayerField );
104 elem.setAttribute( QStringLiteral( "referencedLayerExpression" ), d->mReferencedLayerExpression );
105 elem.setAttribute( QStringLiteral( "referencedLayerIds" ), d->mReferencedLayerIds.join( "," ) );
106 elem.setAttribute( QStringLiteral( "relationStrength" ), qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
107
108 // 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
109 for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) )
110 Q_ASSERT( ! layerId.contains( "," ) );
111
112 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
113 {
114 QDomElement referenceElem = doc.createElement( QStringLiteral( "fieldRef" ) );
115 referenceElem.setAttribute( QStringLiteral( "referencingField" ), pair.first );
116 referenceElem.setAttribute( QStringLiteral( "referencedField" ), pair.second );
117 elem.appendChild( referenceElem );
118 }
119
120 node.appendChild( elem );
121}
122
123void QgsPolymorphicRelation::setId( const QString &id )
124{
125 if ( d->mRelationId == id )
126 return;
127
128 d.detach();
129 d->mRelationId = id;
130
132}
133
135{
136 d.detach();
137 d->mReferencingLayerId = id;
138
140}
141
142void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
143{
144 d.detach();
145 d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField );
147}
148
150{
151 d.detach();
152 d->mFieldPairs << fieldPair;
154}
155
157{
158 return d->mRelationId;
159}
160
162{
163 d->mRelationId = QStringLiteral( "%1_%2_%3_%4" )
164 .arg( referencingLayerId(),
165 d->mFieldPairs.at( 0 ).referencingField(),
167 d->mFieldPairs.at( 0 ).referencedField() );
169}
170
172{
173 return d->mReferencingLayerId;
174}
175
177{
178 return d->mReferencingLayer;
179}
180
181QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const
182{
183 return d->mFieldPairs;
184}
185
187{
188 QgsAttributeList attrs;
189
190 if ( d->mReferencedLayerIds.contains( layerId ) )
191 {
192 QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) );
193
194 if ( vl && vl->isValid() )
195 {
196 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
197 {
198 attrs << vl->fields().lookupField( pair.second );
199 }
200 }
201 }
202
203 return attrs;
204}
205
207{
208 QgsAttributeList attrs;
209
210 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
211 {
212 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
213 }
214 return attrs;
215
216}
217
219{
220 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull();
221}
222
224{
225 return d->mReferencedLayerField == other.d->mReferencedLayerField
226 && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression
227 && d->mReferencingLayerId == other.d->mReferencingLayerId
228 && d->mFieldPairs == other.d->mFieldPairs;
229}
230
232{
233 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
234
235 d->mValid = true;
236 d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId )
237 ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] )
238 : nullptr;
239 d->mReferencedLayersMap.clear();
240
241 if ( d->mRelationId.isEmpty() )
242 {
243 QgsDebugError( QStringLiteral( "Invalid relation: no ID" ) );
244 d->mValid = false;
245 return;
246 }
247
248 if ( !d->mReferencingLayer )
249 {
250 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referencing layer does not exist. ID: %1" ).arg( d->mReferencingLayerId ), 4 );
251 d->mValid = false;
252 return;
253 }
254
255 if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 )
256 {
257 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2" ).arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 );
258 d->mValid = false;
259 return;
260 }
261
262 if ( d->mReferencedLayerExpression.trimmed().isNull() )
263 {
264 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer expression \"%1\" is missing" ).arg( d->mReferencedLayerExpression ), 4 );
265 d->mValid = false;
266 return;
267 }
268
269 const QStringList referencedLayerIds = d->mReferencedLayerIds;
270 for ( const QString &referencedLayerId : referencedLayerIds )
271 {
272 d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) );
273
274 if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() )
275 {
276 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: referenced layer does not exist. ID: %1" ).arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 );
277 d->mValid = false;
278 return;
279 }
280 }
281
282 if ( d->mFieldPairs.count() == 0 )
283 {
284 QgsDebugMsgLevel( QStringLiteral( "Invalid relation: no pair of field is specified." ), 4 );
285 d->mValid = false;
286 return;
287 }
288
289 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
290 {
291 if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 )
292 {
293 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referencing layer %2" ).arg( pair.first, d->mReferencingLayer->name() ) );
294 d->mValid = false;
295 return;
296 }
297
298 for ( const QString &referencedLayerId : referencedLayerIds )
299 {
300 if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 )
301 {
302 QgsDebugError( QStringLiteral( "Invalid relation: field %1 does not exist in referenced layer %2" ).arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) );
303 d->mValid = false;
304 return;
305 }
306 }
307 }
308}
309
310void QgsPolymorphicRelation::setName( const QString &name )
311{
312 if ( d->mRelationName == name && !name.isEmpty() )
313 return;
314
315 d.detach();
316 d->mRelationName = name;
318}
319
321{
322 if ( d->mRelationName.isEmpty() )
323 return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : QStringLiteral( "<NO LAYER>" ) );
324
325 return d->mRelationName;
326}
327
328void QgsPolymorphicRelation::setReferencedLayerField( const QString &referencedLayerField )
329{
330 d.detach();
331 d->mReferencedLayerField = referencedLayerField;
333}
334
336{
337 return d->mReferencedLayerField;
338}
339
340void QgsPolymorphicRelation::setReferencedLayerExpression( const QString &referencedLayerExpression )
341{
342 d.detach();
343 d->mReferencedLayerExpression = referencedLayerExpression;
345}
346
348{
349 return d->mReferencedLayerExpression;
350}
351
352void QgsPolymorphicRelation::setReferencedLayerIds( const QStringList &referencedLayerIds )
353{
354 d.detach();
355 d->mReferencedLayerIds = referencedLayerIds;
357}
358
360{
361 return d->mReferencedLayerIds;
362}
363
365{
366 return d->mRelationStrength;
367}
368
370{
371 d.detach();
372 d->mRelationStrength = relationStrength;
374}
375
377{
378 QList<QgsRelation> relations;
379
380 if ( !isValid() )
381 return relations;
382
383 const QStringList referencedLayerIds = d->mReferencedLayerIds;
384
385 for ( const QString &referencedLayerId : referencedLayerIds )
386 {
387 QgsRelation relation;
388 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
389
390 relation.setId( QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId ) );
391 relation.setReferencedLayer( referencedLayerId );
392 relation.setReferencingLayer( d->mReferencingLayerId );
393 relation.setName( QStringLiteral( "Generated for \"%1\"" ).arg( referencedLayerName ) );
394 relation.setPolymorphicRelationId( d->mRelationId );
395 relation.setStrength( d->mRelationStrength );
396
397 const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs();
398 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
399 relation.addFieldPair( fieldPair );
400
401 if ( !relation.isValid() )
402 continue;
403
404 relations << relation;
405 }
406
407 return relations;
408}
409
410QString QgsPolymorphicRelation::upgradeGeneratedRelationId( const QString &oldRelationId ) const
411{
412 if ( !isValid() )
413 return QString();
414
415 const QStringList referencedLayerIds = d->mReferencedLayerIds;
416 for ( const QString &referencedLayerId : referencedLayerIds )
417 {
418 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
419 if ( oldRelationId == QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerName ) )
420 {
421 return QStringLiteral( "%1_%2" ).arg( d->mRelationId, referencedLayerId );
422 }
423 }
424
425 return QString();
426}
427
429{
430 if ( !layer || !layer->isValid() )
431 return QString();
432
434 QgsExpression expr( d->mReferencedLayerExpression );
435
436 return expr.evaluate( &context ).toString();
437}
RelationshipStrength
Relationship strength.
Definition qgis.h:4080
@ 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