QGIS API Documentation 3.99.0-Master (d270888f95f)
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#include <QString>
25
26#include "moc_qgspolymorphicrelation.cpp"
27
28using namespace Qt::StringLiterals;
29
31 : d( new QgsPolymorphicRelationPrivate() )
32{
33}
34
36 : d( new QgsPolymorphicRelationPrivate() )
37 , mContext( context )
38{
39}
40
42
44 : d( other.d )
45 , mContext( other.mContext )
46{
47}
48
50 : d( std::move( other.d ) )
51 , mContext( std::move( other.mContext ) )
52{
53}
54
56{
57 if ( &other == this )
58 return *this;
59
60 d = other.d;
61 mContext = other.mContext;
62 return *this;
63}
64
66{
67 if ( &other == this )
68 return *this;
69
70 d = std::move( other.d );
71 mContext = std::move( other.mContext );
72 return *this;
73}
74
76{
77 Q_UNUSED( context );
78 QDomElement elem = node.toElement();
79
80 if ( elem.tagName() != "relation"_L1 )
81 {
82 QgsLogger::warning( QApplication::translate( "QgsPolymorphicRelation", "Cannot create relation. Unexpected tag '%1'" ).arg( elem.tagName() ) );
83 }
84
85 QgsPolymorphicRelation relation( relationContext );
86
87 QString referencingLayerId = elem.attribute( u"referencingLayer"_s );
88 QString referencedLayerField = elem.attribute( u"referencedLayerField"_s );
89 QString referencedLayerExpression = elem.attribute( u"referencedLayerExpression"_s );
90 QString id = elem.attribute( u"id"_s );
91 QString name = elem.attribute( u"name"_s );
92 QString relationStrength = elem.attribute( u"relationStrength"_s );
93 QStringList referencedLayerIds = elem.attribute( u"referencedLayerIds"_s ).split( "," );
94
95 QMap<QString, QgsMapLayer *> mapLayers = relationContext.project()->mapLayers();
96
97 relation.d->mReferencingLayerId = referencingLayerId;
98 relation.d->mReferencingLayer = qobject_cast<QgsVectorLayer *>( mapLayers[referencingLayerId] );
99 relation.d->mReferencedLayerField = referencedLayerField;
100 relation.d->mReferencedLayerExpression = referencedLayerExpression;
101 relation.d->mReferencedLayerIds = referencedLayerIds;
102 relation.d->mRelationId = id;
103 relation.d->mRelationName = name;
104 relation.d->mRelationStrength = qgsEnumKeyToValue<Qgis::RelationshipStrength>( relationStrength, Qgis::RelationshipStrength::Association );
105
106 QDomNodeList references = elem.elementsByTagName( u"fieldRef"_s );
107 for ( int i = 0; i < references.size(); ++i )
108 {
109 QDomElement refEl = references.at( i ).toElement();
110
111 QString referencingField = refEl.attribute( u"referencingField"_s );
112 QString referencedField = refEl.attribute( u"referencedField"_s );
113
114 relation.addFieldPair( referencingField, referencedField );
115 }
116
117 relation.updateRelationStatus();
118
119 return relation;
120}
121
122void QgsPolymorphicRelation::writeXml( QDomNode &node, QDomDocument &doc ) const
123{
124 QDomElement elem = doc.createElement( u"relation"_s );
125 elem.setAttribute( u"id"_s, d->mRelationId );
126 elem.setAttribute( u"name"_s, d->mRelationName );
127 elem.setAttribute( u"referencingLayer"_s, d->mReferencingLayerId );
128 elem.setAttribute( u"referencedLayerField"_s, d->mReferencedLayerField );
129 elem.setAttribute( u"referencedLayerExpression"_s, d->mReferencedLayerExpression );
130 elem.setAttribute( u"referencedLayerIds"_s, d->mReferencedLayerIds.join( "," ) );
131 elem.setAttribute( u"relationStrength"_s, qgsEnumValueToKey<Qgis::RelationshipStrength>( d->mRelationStrength ) );
132
133 // 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
134 for ( const QString &layerId : std::as_const( d->mReferencedLayerIds ) )
135 Q_ASSERT( ! layerId.contains( "," ) );
136
137 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
138 {
139 QDomElement referenceElem = doc.createElement( u"fieldRef"_s );
140 referenceElem.setAttribute( u"referencingField"_s, pair.first );
141 referenceElem.setAttribute( u"referencedField"_s, pair.second );
142 elem.appendChild( referenceElem );
143 }
144
145 node.appendChild( elem );
146}
147
148void QgsPolymorphicRelation::setId( const QString &id )
149{
150 if ( d->mRelationId == id )
151 return;
152
153 d.detach();
154 d->mRelationId = id;
155
157}
158
160{
161 d.detach();
162 d->mReferencingLayerId = id;
163
165}
166
167void QgsPolymorphicRelation::addFieldPair( const QString &referencingField, const QString &referencedField )
168{
169 d.detach();
170 d->mFieldPairs << QgsRelation::FieldPair( referencingField, referencedField );
172}
173
175{
176 d.detach();
177 d->mFieldPairs << fieldPair;
179}
180
182{
183 return d->mRelationId;
184}
185
187{
188 d->mRelationId = u"%1_%2_%3_%4"_s
189 .arg( referencingLayerId(),
190 d->mFieldPairs.at( 0 ).referencingField(),
192 d->mFieldPairs.at( 0 ).referencedField() );
194}
195
197{
198 return d->mReferencingLayerId;
199}
200
202{
203 return d->mReferencingLayer;
204}
205
206QList<QgsRelation::FieldPair> QgsPolymorphicRelation::fieldPairs() const
207{
208 return d->mFieldPairs;
209}
210
212{
213 QgsAttributeList attrs;
214
215 if ( d->mReferencedLayerIds.contains( layerId ) )
216 {
217 QgsVectorLayer *vl = static_cast<QgsVectorLayer *>( QgsProject::instance()->mapLayer( layerId ) ); // skip-keyword-check
218
219 if ( vl && vl->isValid() )
220 {
221 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
222 {
223 attrs << vl->fields().lookupField( pair.second );
224 }
225 }
226 }
227
228 return attrs;
229}
230
232{
233 QgsAttributeList attrs;
234
235 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
236 {
237 attrs << d->mReferencingLayer->fields().lookupField( pair.first );
238 }
239 return attrs;
240
241}
242
244{
245 return d->mValid && !d->mReferencingLayer.isNull() && !d->mReferencedLayerField.isNull() && d->mReferencingLayer.data()->isValid() && !d->mReferencedLayerExpression.isNull();
246}
247
249{
250 return d->mReferencedLayerField == other.d->mReferencedLayerField
251 && d->mReferencedLayerExpression == other.d->mReferencedLayerExpression
252 && d->mReferencingLayerId == other.d->mReferencingLayerId
253 && d->mFieldPairs == other.d->mFieldPairs;
254}
255
257{
258 const QMap<QString, QgsMapLayer *> &mapLayers = mContext.project()->mapLayers();
259
260 d->mValid = true;
261 d->mReferencingLayer = mapLayers.contains( d->mReferencingLayerId )
262 ? qobject_cast<QgsVectorLayer *>( mapLayers[d->mReferencingLayerId] )
263 : nullptr;
264 d->mReferencedLayersMap.clear();
265
266 if ( d->mRelationId.isEmpty() )
267 {
268 QgsDebugError( u"Invalid relation: no ID"_s );
269 d->mValid = false;
270 return;
271 }
272
273 if ( !d->mReferencingLayer )
274 {
275 QgsDebugMsgLevel( u"Invalid relation: referencing layer does not exist. ID: %1"_s.arg( d->mReferencingLayerId ), 4 );
276 d->mValid = false;
277 return;
278 }
279
280 if ( d->mReferencingLayer->fields().lookupField( d->mReferencedLayerField ) == -1 )
281 {
282 QgsDebugMsgLevel( u"Invalid relation: referenced layer field \"%1\" does not exist in layer with ID: %2"_s.arg( d->mReferencedLayerField, d->mReferencingLayerId ), 4 );
283 d->mValid = false;
284 return;
285 }
286
287 if ( d->mReferencedLayerExpression.trimmed().isNull() )
288 {
289 QgsDebugMsgLevel( u"Invalid relation: referenced layer expression \"%1\" is missing"_s.arg( d->mReferencedLayerExpression ), 4 );
290 d->mValid = false;
291 return;
292 }
293
294 const QStringList referencedLayerIds = d->mReferencedLayerIds;
295 for ( const QString &referencedLayerId : referencedLayerIds )
296 {
297 d->mReferencedLayersMap.insert( referencedLayerId, qobject_cast<QgsVectorLayer *>( mapLayers[referencedLayerId] ) );
298
299 if ( !d->mReferencedLayersMap[referencedLayerId] || !d->mReferencedLayersMap[referencedLayerId]->isValid() )
300 {
301 QgsDebugMsgLevel( u"Invalid relation: referenced layer does not exist. ID: %1"_s.arg( d->mReferencedLayersMap[referencedLayerId]->id() ), 4 );
302 d->mValid = false;
303 return;
304 }
305 }
306
307 if ( d->mFieldPairs.count() == 0 )
308 {
309 QgsDebugMsgLevel( u"Invalid relation: no pair of field is specified."_s, 4 );
310 d->mValid = false;
311 return;
312 }
313
314 for ( const QgsRelation::FieldPair &pair : std::as_const( d->mFieldPairs ) )
315 {
316 if ( d->mReferencingLayer->fields().lookupField( pair.first ) == -1 )
317 {
318 QgsDebugError( u"Invalid relation: field %1 does not exist in referencing layer %2"_s.arg( pair.first, d->mReferencingLayer->name() ) );
319 d->mValid = false;
320 return;
321 }
322
323 for ( const QString &referencedLayerId : referencedLayerIds )
324 {
325 if ( d->mReferencedLayersMap[referencedLayerId]->fields().lookupField( pair.second ) == -1 )
326 {
327 QgsDebugError( u"Invalid relation: field %1 does not exist in referenced layer %2"_s.arg( pair.second, d->mReferencedLayersMap[referencedLayerId]->name() ) );
328 d->mValid = false;
329 return;
330 }
331 }
332 }
333}
334
336{
337 if ( d->mRelationName == name && !name.isEmpty() )
338 return;
339
340 d.detach();
341 d->mRelationName = name;
343}
344
346{
347 if ( d->mRelationName.isEmpty() )
348 return QObject::tr( "Polymorphic relations for \"%1\"" ).arg( d->mReferencingLayer ? d->mReferencingLayer->name() : u"<NO LAYER>"_s );
349
350 return d->mRelationName;
351}
352
354{
355 d.detach();
356 d->mReferencedLayerField = referencedLayerField;
358}
359
361{
362 return d->mReferencedLayerField;
363}
364
366{
367 d.detach();
368 d->mReferencedLayerExpression = referencedLayerExpression;
370}
371
373{
374 return d->mReferencedLayerExpression;
375}
376
378{
379 d.detach();
380 d->mReferencedLayerIds = referencedLayerIds;
382}
383
385{
386 return d->mReferencedLayerIds;
387}
388
390{
391 return d->mRelationStrength;
392}
393
395{
396 d.detach();
397 d->mRelationStrength = relationStrength;
399}
400
402{
403 QList<QgsRelation> relations;
404
405 if ( !isValid() )
406 return relations;
407
408 const QStringList referencedLayerIds = d->mReferencedLayerIds;
409
410 for ( const QString &referencedLayerId : referencedLayerIds )
411 {
412 QgsRelation relation;
413 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
414
415 relation.setId( u"%1_%2"_s.arg( d->mRelationId, referencedLayerId ) );
416 relation.setReferencedLayer( referencedLayerId );
417 relation.setReferencingLayer( d->mReferencingLayerId );
418 relation.setName( u"Generated for \"%1\""_s.arg( referencedLayerName ) );
419 relation.setPolymorphicRelationId( d->mRelationId );
420 relation.setStrength( d->mRelationStrength );
421
422 const QList<QgsRelation::FieldPair> constFieldPairs = fieldPairs();
423 for ( const QgsRelation::FieldPair &fieldPair : constFieldPairs )
424 relation.addFieldPair( fieldPair );
425
426 if ( !relation.isValid() )
427 continue;
428
429 relations << relation;
430 }
431
432 return relations;
433}
434
435QString QgsPolymorphicRelation::upgradeGeneratedRelationId( const QString &oldRelationId ) const
436{
437 if ( !isValid() )
438 return QString();
439
440 const QStringList referencedLayerIds = d->mReferencedLayerIds;
441 for ( const QString &referencedLayerId : referencedLayerIds )
442 {
443 const QString referencedLayerName = d->mReferencedLayersMap[referencedLayerId]->name();
444 if ( oldRelationId == u"%1_%2"_s.arg( d->mRelationId, referencedLayerName ) )
445 {
446 return u"%1_%2"_s.arg( d->mRelationId, referencedLayerId );
447 }
448 }
449
450 return QString();
451}
452
454{
455 if ( !layer || !layer->isValid() )
456 return QString();
457
459 QgsExpression expr( d->mReferencedLayerExpression );
460
461 return expr.evaluate( &context ).toString();
462}
RelationshipStrength
Relationship strength.
Definition qgis.h:4464
@ Association
Loose relation, related elements are not part of the parent and a parent copy will not copy any child...
Definition qgis.h:4465
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:7110
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7091
QList< int > QgsAttributeList
Definition qgsfield.h:30
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59