QGIS API Documentation  3.21.0-Master (56b4176581)
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 "qgsapplication.h"
19 #include "qgslogger.h"
20 #include "qgsproject.h"
21 #include "qgsvectordataprovider.h"
22 #include "qgsvectorlayer.h"
23 
25  : QObject( project )
26  , mProject( project )
27 {
28  if ( project )
29  {
30  // TODO: QGIS 4 remove: relations are now stored with the layer style
31  connect( project, &QgsProject::readProjectWithContext, this, &QgsRelationManager::readProject );
32  // TODO: QGIS 4 remove: relations are now stored with the layer style
33  connect( project, &QgsProject::writeProject, this, &QgsRelationManager::writeProject );
34 
35  connect( project, &QgsProject::layersRemoved, this, &QgsRelationManager::layersRemoved );
36  }
37 }
38 
40 {
41  return QgsRelationContext( mProject );
42 }
43 
44 void QgsRelationManager::setRelations( const QList<QgsRelation> &relations )
45 {
46  mRelations.clear();
47  for ( const QgsRelation &rel : std::as_const( relations ) )
48  {
49  addRelation( rel );
50  }
51  emit changed();
52 }
53 
54 QMap<QString, QgsRelation> QgsRelationManager::relations() const
55 {
56  return mRelations;
57 }
58 
60 {
61  // Do not add relations to layers that do not exist
63  return;
64 
65  mRelations.insert( relation.id(), relation );
66  if ( mProject )
67  {
68  mProject->setDirty( true );
69  }
70  emit changed();
71 }
72 
73 
75 {
76  for ( auto relation : mRelations )
77  {
79  }
80 }
81 
82 
83 void QgsRelationManager::removeRelation( const QString &id )
84 {
85  mRelations.remove( id );
86  emit changed();
87 }
88 
90 {
91  mRelations.remove( relation.id() );
92  emit changed();
93 }
94 
95 QgsRelation QgsRelationManager::relation( const QString &id ) const
96 {
97  return mRelations.value( id );
98 }
99 
100 QList<QgsRelation> QgsRelationManager::relationsByName( const QString &name ) const
101 {
102  QList<QgsRelation> relations;
103 
104  for ( const QgsRelation &rel : std::as_const( mRelations ) )
105  {
106  if ( QString::compare( rel.name(), name, Qt::CaseInsensitive ) == 0 )
107  relations << rel;
108  }
109 
110  return relations;
111 }
112 
114 {
115  mRelations.clear();
116  emit changed();
117 }
118 
119 QList<QgsRelation> QgsRelationManager::referencingRelations( const QgsVectorLayer *layer, int fieldIdx ) const
120 {
121  if ( !layer )
122  {
123  return mRelations.values();
124  }
125 
126  QList<QgsRelation> relations;
127 
128  for ( const QgsRelation &rel : std::as_const( mRelations ) )
129  {
130  if ( rel.referencingLayer() == layer )
131  {
132  if ( fieldIdx != -2 )
133  {
134  bool containsField = false;
135  const auto constFieldPairs = rel.fieldPairs();
136  for ( const QgsRelation::FieldPair &fp : constFieldPairs )
137  {
138  if ( fieldIdx == layer->fields().lookupField( fp.referencingField() ) )
139  {
140  containsField = true;
141  break;
142  }
143  }
144 
145  if ( !containsField )
146  {
147  continue;
148  }
149  }
150  relations.append( rel );
151  }
152  }
153 
154  return relations;
155 }
156 
157 QList<QgsRelation> QgsRelationManager::referencedRelations( const QgsVectorLayer *layer ) const
158 {
159  if ( !layer )
160  {
161  return mRelations.values();
162  }
163 
164  QList<QgsRelation> relations;
165 
166  for ( const QgsRelation &rel : std::as_const( mRelations ) )
167  {
168  if ( rel.referencedLayer() == layer )
169  {
170  relations.append( rel );
171  }
172  }
173 
174  return relations;
175 }
176 
177 void QgsRelationManager::readProject( const QDomDocument &doc, QgsReadWriteContext &context )
178 {
179  mRelations.clear();
180  mPolymorphicRelations.clear();
181 
182  QDomNodeList relationNodes = doc.elementsByTagName( QStringLiteral( "relations" ) );
183  if ( relationNodes.count() )
184  {
185  QgsRelationContext relcontext( mProject );
186 
187  QDomNode node = relationNodes.item( 0 );
188  QDomNodeList relationNodes = node.childNodes();
189  int relCount = relationNodes.count();
190  for ( int i = 0; i < relCount; ++i )
191  {
192  addRelation( QgsRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
193  }
194  }
195  else
196  {
197  QgsDebugMsg( QStringLiteral( "No relations data present in this document" ) );
198  }
199 
200  QDomNodeList polymorphicRelationNodes = doc.elementsByTagName( QStringLiteral( "polymorphicRelations" ) );
201  if ( polymorphicRelationNodes.count() )
202  {
203  QgsRelationContext relcontext( mProject );
204 
205  QDomNode node = polymorphicRelationNodes.item( 0 );
206  QDomNodeList relationNodes = node.childNodes();
207  int relCount = relationNodes.count();
208  for ( int i = 0; i < relCount; ++i )
209  {
210  addPolymorphicRelation( QgsPolymorphicRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
211  }
212  }
213  else
214  {
215  QgsDebugMsgLevel( QStringLiteral( "No polymorphic relations data present in this document" ), 3 );
216  }
217 
218  emit relationsLoaded();
219  emit changed();
220 }
221 
222 void QgsRelationManager::writeProject( QDomDocument &doc )
223 {
224  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
225  if ( !nl.count() )
226  {
227  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
228  return;
229  }
230  QDomNode qgisNode = nl.item( 0 ); // there should only be one
231 
232  QDomElement relationsNode = doc.createElement( QStringLiteral( "relations" ) );
233  qgisNode.appendChild( relationsNode );
234 
235  for ( const QgsRelation &relation : std::as_const( mRelations ) )
236  {
237  // the generated relations for polymorphic relations should be ignored,
238  // they are generated every time when a polymorphic relation is added
240  continue;
241 
242  relation.writeXml( relationsNode, doc );
243  }
244 
245  QDomElement polymorphicRelationsNode = doc.createElement( QStringLiteral( "polymorphicRelations" ) );
246  qgisNode.appendChild( polymorphicRelationsNode );
247 
248  for ( const QgsPolymorphicRelation &relation : std::as_const( mPolymorphicRelations ) )
249  {
250  relation.writeXml( polymorphicRelationsNode, doc );
251  }
252 }
253 
254 void QgsRelationManager::layersRemoved( const QStringList &layers )
255 {
256  bool relationsChanged = false;
257  for ( const QString &layer : std::as_const( layers ) )
258  {
259  QMapIterator<QString, QgsRelation> it( mRelations );
260 
261  while ( it.hasNext() )
262  {
263  it.next();
264 
265  if ( it.value().referencedLayerId() == layer
266  || it.value().referencingLayerId() == layer )
267  {
268  mRelations.remove( it.key() );
269  relationsChanged = true;
270  }
271  }
272  }
273  if ( relationsChanged )
274  {
275  emit changed();
276  }
277 }
278 
279 static bool hasRelationWithEqualDefinition( const QList<QgsRelation> &existingRelations, const QgsRelation &relation )
280 {
281  for ( const QgsRelation &cur : std::as_const( existingRelations ) )
282  {
283  if ( cur.hasEqualDefinition( relation ) ) return true;
284  }
285  return false;
286 }
287 
288 QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelation> &existingRelations, const QList<QgsVectorLayer *> &layers )
289 {
290  QList<QgsRelation> result;
291  for ( const QgsVectorLayer *layer : std::as_const( layers ) )
292  {
293  if ( const QgsVectorDataProvider *provider = layer->dataProvider() )
294  {
295  const auto constDiscoverRelations = provider->discoverRelations( layer, layers );
296  for ( const QgsRelation &relation : constDiscoverRelations )
297  {
298  if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
299  {
300  result.append( relation );
301  }
302  }
303  }
304  }
305  return result;
306 }
307 
308 QMap<QString, QgsPolymorphicRelation> QgsRelationManager::polymorphicRelations() const
309 {
310  return mPolymorphicRelations;
311 }
312 
313 QgsPolymorphicRelation QgsRelationManager::polymorphicRelation( const QString &polymorphicRelationId ) const
314 {
315  return mPolymorphicRelations.value( polymorphicRelationId );
316 }
317 
319 {
321  return;
322 
323  mPolymorphicRelations.insert( polymorphicRelation.id(), polymorphicRelation );
324 
325  const QList<QgsRelation> generatedRelations = polymorphicRelation.generateRelations();
326  for ( const QgsRelation &generatedRelation : generatedRelations )
327  addRelation( generatedRelation );
328 }
329 
330 void QgsRelationManager::removePolymorphicRelation( const QString &polymorphicRelationId )
331 {
332  QgsPolymorphicRelation relation = mPolymorphicRelations.take( polymorphicRelationId );
333 
334  const QList<QgsRelation> generatedRelations = relation.generateRelations();
335  for ( const QgsRelation &generatedRelation : generatedRelations )
336  removeRelation( generatedRelation.id() );
337 }
338 
339 void QgsRelationManager::setPolymorphicRelations( const QList<QgsPolymorphicRelation> &relations )
340 {
341  const QList<QgsPolymorphicRelation> oldRelations = polymorphicRelations().values();
342  for ( const QgsPolymorphicRelation &oldRelation : oldRelations )
343  removePolymorphicRelation( oldRelation.id() );
344 
345  for ( const QgsPolymorphicRelation &newRelation : relations )
346  addPolymorphicRelation( newRelation );
347 }
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:344
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:101
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:518
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:89
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:53
QgsVectorLayer * referencedLayer
Definition: qgsrelation.h:48
@ Generated
A generated relation is a child of a polymorphic relation.
Definition: qgsrelation.h:62
Q_GADGET QString id
Definition: qgsrelation.h:46
RelationType type() const
Returns the type of the relation.
QgsVectorLayer * referencingLayer
Definition: qgsrelation.h:47
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
void updateRelationStatus()
Updates the validity status of this relation.
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 QgsDebugMsg(str)
Definition: qgslogger.h:38