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