QGIS API Documentation  3.37.0-Master (a5b4d9743e8)
qgsrelationmanager.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsrelationmanager.cpp
3  --------------------------------------
4  Date : 1.3.2013
5  Copyright : (C) 2013 Matthias Kuhn
6  Email : matthias 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 
16 #include "qgsrelationmanager.h"
17 
18 #include "qgslogger.h"
19 #include "qgsproject.h"
20 #include "qgsvectordataprovider.h"
21 #include "qgsvectorlayer.h"
22 
24  : QObject( project )
25  , mProject( project )
26 {
27  if ( project )
28  {
29  // TODO: QGIS 4 remove: relations are now stored with the layer style
30  connect( project, &QgsProject::readProjectWithContext, this, &QgsRelationManager::readProject );
31  // TODO: QGIS 4 remove: relations are now stored with the layer style
32  connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );
33 
34  connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
35  }
36 }
37 
39 {
40  return QgsRelationContext( mProject );
41 }
42 
43 void QgsRelationManager::setRelations( const QList<QgsRelation> &relations )
44 {
45  mRelations.clear();
46  for ( const QgsRelation &rel : std::as_const( relations ) )
47  {
48  addRelation( rel );
49  }
50  emit changed();
51 }
52 
53 QMap<QString, QgsRelation> QgsRelationManager::relations() const
54 {
55  return mRelations;
56 }
57 
59 {
60  // Do not add relations to layers that do not exist
62  return;
63 
64  mRelations.insert( relation.id(), relation );
65  if ( mProject )
66  {
67  mProject->setDirty( true );
68  }
69  emit changed();
70 }
71 
72 
74 {
75  for ( auto relationIt = mRelations.begin(); relationIt != mRelations.end(); ++relationIt )
76  {
77  relationIt->updateRelationStatus();
78  }
79 }
80 
81 
82 void QgsRelationManager::removeRelation( const QString &id )
83 {
84  mRelations.remove( id );
85  emit changed();
86 }
87 
89 {
90  mRelations.remove( relation.id() );
91  emit changed();
92 }
93 
94 QgsRelation QgsRelationManager::relation( const QString &id ) const
95 {
96  return mRelations.value( id );
97 }
98 
99 QList<QgsRelation> QgsRelationManager::relationsByName( const QString &name ) const
100 {
101  QList<QgsRelation> relations;
102 
103  for ( const QgsRelation &rel : std::as_const( mRelations ) )
104  {
105  if ( QString::compare( rel.name(), name, Qt::CaseInsensitive ) == 0 )
106  relations << rel;
107  }
108 
109  return relations;
110 }
111 
113 {
114  mRelations.clear();
115  emit changed();
116 }
117 
118 QList<QgsRelation> QgsRelationManager::referencingRelations( const QgsVectorLayer *layer, int fieldIdx ) const
119 {
120  if ( !layer )
121  {
122  return mRelations.values();
123  }
124 
125  QList<QgsRelation> relations;
126 
127  for ( const QgsRelation &rel : std::as_const( mRelations ) )
128  {
129  if ( rel.referencingLayer() == layer )
130  {
131  if ( fieldIdx != -2 )
132  {
133  bool containsField = false;
134  const auto constFieldPairs = rel.fieldPairs();
135  for ( const QgsRelation::FieldPair &fp : constFieldPairs )
136  {
137  if ( fieldIdx == layer->fields().lookupField( fp.referencingField() ) )
138  {
139  containsField = true;
140  break;
141  }
142  }
143 
144  if ( !containsField )
145  {
146  continue;
147  }
148  }
149  relations.append( rel );
150  }
151  }
152 
153  return relations;
154 }
155 
156 QList<QgsRelation> QgsRelationManager::referencedRelations( const QgsVectorLayer *layer ) const
157 {
158  if ( !layer )
159  {
160  return mRelations.values();
161  }
162 
163  QList<QgsRelation> relations;
164 
165  for ( const QgsRelation &rel : std::as_const( mRelations ) )
166  {
167  if ( rel.referencedLayer() == layer )
168  {
169  relations.append( rel );
170  }
171  }
172 
173  return relations;
174 }
175 
176 void QgsRelationManager::readProject( const QDomDocument &doc, QgsReadWriteContext &context )
177 {
178  mRelations.clear();
179  mPolymorphicRelations.clear();
180 
181  QDomNodeList relationNodes = doc.elementsByTagName( QStringLiteral( "relations" ) );
182  if ( relationNodes.count() )
183  {
184  QgsRelationContext relcontext( mProject );
185 
186  QDomNode node = relationNodes.item( 0 );
187  QDomNodeList relationNodes = node.childNodes();
188  int relCount = relationNodes.count();
189  for ( int i = 0; i < relCount; ++i )
190  {
191  addRelation( QgsRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
192  }
193  }
194  else
195  {
196  QgsDebugMsgLevel( QStringLiteral( "No relations data present in this document" ), 2 );
197  }
198 
199  QDomNodeList polymorphicRelationNodes = doc.elementsByTagName( QStringLiteral( "polymorphicRelations" ) );
200  if ( polymorphicRelationNodes.count() )
201  {
202  QgsRelationContext relcontext( mProject );
203 
204  QDomNode node = polymorphicRelationNodes.item( 0 );
205  QDomNodeList relationNodes = node.childNodes();
206  int relCount = relationNodes.count();
207  for ( int i = 0; i < relCount; ++i )
208  {
209  addPolymorphicRelation( QgsPolymorphicRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
210  }
211  }
212  else
213  {
214  QgsDebugMsgLevel( QStringLiteral( "No polymorphic relations data present in this document" ), 3 );
215  }
216 
217  emit relationsLoaded();
218  emit changed();
219 }
220 
221 void QgsRelationManager::writeProject( QDomDocument &doc )
222 {
223  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
224  if ( !nl.count() )
225  {
226  QgsDebugError( QStringLiteral( "Unable to find qgis element in project file" ) );
227  return;
228  }
229  QDomNode qgisNode = nl.item( 0 ); // there should only be one
230 
231  QDomElement relationsNode = doc.createElement( QStringLiteral( "relations" ) );
232  qgisNode.appendChild( relationsNode );
233 
234  for ( const QgsRelation &relation : std::as_const( mRelations ) )
235  {
236  // the generated relations for polymorphic relations should be ignored,
237  // they are generated every time when a polymorphic relation is added
238  switch ( relation.type() )
239  {
241  continue;
243  break;
244  }
245 
246  relation.writeXml( relationsNode, doc );
247  }
248 
249  QDomElement polymorphicRelationsNode = doc.createElement( QStringLiteral( "polymorphicRelations" ) );
250  qgisNode.appendChild( polymorphicRelationsNode );
251 
252  for ( const QgsPolymorphicRelation &relation : std::as_const( mPolymorphicRelations ) )
253  {
254  relation.writeXml( polymorphicRelationsNode, doc );
255  }
256 }
257 
258 void QgsRelationManager::layersRemoved( const QStringList &layers )
259 {
260  bool relationsChanged = false;
261  for ( const QString &layer : std::as_const( layers ) )
262  {
263  QMapIterator<QString, QgsRelation> it( mRelations );
264 
265  while ( it.hasNext() )
266  {
267  it.next();
268 
269  if ( it.value().referencedLayerId() == layer
270  || it.value().referencingLayerId() == layer )
271  {
272  mRelations.remove( it.key() );
273  relationsChanged = true;
274  }
275  }
276  }
277  if ( relationsChanged )
278  {
279  emit changed();
280  }
281 }
282 
283 static bool hasRelationWithEqualDefinition( const QList<QgsRelation> &existingRelations, const QgsRelation &relation )
284 {
285  for ( const QgsRelation &cur : std::as_const( existingRelations ) )
286  {
287  if ( cur.hasEqualDefinition( relation ) ) return true;
288  }
289  return false;
290 }
291 
292 QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelation> &existingRelations, const QList<QgsVectorLayer *> &layers )
293 {
294  QList<QgsRelation> result;
295  for ( const QgsVectorLayer *layer : std::as_const( layers ) )
296  {
297  if ( const QgsVectorDataProvider *provider = layer->dataProvider() )
298  {
299  const auto constDiscoverRelations = provider->discoverRelations( layer, layers );
300  for ( const QgsRelation &relation : constDiscoverRelations )
301  {
302  if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
303  {
304  result.append( relation );
305  }
306  }
307  }
308  }
309  return result;
310 }
311 
312 QMap<QString, QgsPolymorphicRelation> QgsRelationManager::polymorphicRelations() const
313 {
314  return mPolymorphicRelations;
315 }
316 
317 QgsPolymorphicRelation QgsRelationManager::polymorphicRelation( const QString &polymorphicRelationId ) const
318 {
319  return mPolymorphicRelations.value( polymorphicRelationId );
320 }
321 
323 {
325  return;
326 
327  mPolymorphicRelations.insert( polymorphicRelation.id(), polymorphicRelation );
328 
329  const QList<QgsRelation> generatedRelations = polymorphicRelation.generateRelations();
330  for ( const QgsRelation &generatedRelation : generatedRelations )
331  addRelation( generatedRelation );
332 }
333 
334 void QgsRelationManager::removePolymorphicRelation( const QString &polymorphicRelationId )
335 {
336  QgsPolymorphicRelation relation = mPolymorphicRelations.take( polymorphicRelationId );
337 
338  const QList<QgsRelation> generatedRelations = relation.generateRelations();
339  for ( const QgsRelation &generatedRelation : generatedRelations )
340  removeRelation( generatedRelation.id() );
341 }
342 
343 void QgsRelationManager::setPolymorphicRelations( const QList<QgsPolymorphicRelation> &relations )
344 {
345  const QList<QgsPolymorphicRelation> oldRelations = polymorphicRelations().values();
346  for ( const QgsPolymorphicRelation &oldRelation : oldRelations )
347  removePolymorphicRelation( oldRelation.id() );
348 
349  for ( const QgsPolymorphicRelation &newRelation : relations )
350  addPolymorphicRelation( newRelation );
351 }
@ Generated
A generated relation is a child of a polymorphic relation.
@ Normal
A normal relation.
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
A polymorphic relation consists of the same properties like a normal relation except for the referenc...
QList< QgsRelation > generateRelations() const
Returns a list of generated relations, based on the currently set referencedLayerIds()
static QgsPolymorphicRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
QgsVectorLayer * referencingLayer
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
void writeProject(QDomDocument &)
Emitted when the project is being written.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:599
The class is used as a container of context for various read/write operations on other objects.
Context for relations.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
static QList< QgsRelation > discoverRelations(const QList< QgsRelation > &existingRelations, const QList< QgsVectorLayer * > &layers)
Discover all the relations available from the current layers.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
void addPolymorphicRelation(const QgsPolymorphicRelation &polymorphicRelation)
Adds a new polymorphic relation.
QgsPolymorphicRelation polymorphicRelation(const QString &polymorphicRelationId) const
Returns the list of relations associated with a polymorphic relation.
void setPolymorphicRelations(const QList< QgsPolymorphicRelation > &relations)
Sets the specified polymorphic relations and removes any polymorphic relations currently set.
QMap< QString, QgsPolymorphicRelation > polymorphicRelations() const
Returns all the polymorphic relations.
QgsRelationManager(QgsProject *project=nullptr)
Constructor for QgsRelationManager.
QList< QgsRelation > referencingRelations(const QgsVectorLayer *layer=nullptr, int fieldIdx=-2) const
Gets all relations where the specified layer (and field) is the referencing part (i....
void clear()
Remove any relation managed by this class.
void changed()
Emitted when relations are added or removed to the manager.
QgsRelationContext context() const
Gets the relation context.
void updateRelationsStatus()
Updates relations status.
void removeRelation(const QString &id)
Remove a relation.
void setRelations(const QList< QgsRelation > &relations)
Will set the specified relations and remove any relation currently set.
void removePolymorphicRelation(const QString &polymorphicRelationId)
Removes an existing polymorphic relation and it's generated relations.
void addRelation(const QgsRelation &relation)
Add a relation.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
void relationsLoaded()
Emitted when the relations were loaded after reading a project.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:67
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:54
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:47
Qgis::RelationshipType type() const
Returns the type of the relation.
Q_GADGET QString id
Definition: qgsrelation.h:45
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:46
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
This is the base class for vector data providers.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugError(str)
Definition: qgslogger.h:38