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