QGIS API Documentation  3.17.0-Master (a035f434f4)
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  const auto constRelations = relations;
48  for ( const QgsRelation &rel : constRelations )
49  {
50  addRelation( rel );
51  }
52  emit changed();
53 }
54 
55 QMap<QString, QgsRelation> QgsRelationManager::relations() const
56 {
57  return mRelations;
58 }
59 
61 {
62  // Do not add relations to layers that do not exist
63  if ( !( relation.referencingLayer() && relation.referencedLayer() ) )
64  return;
65 
66  mRelations.insert( relation.id(), relation );
67  if ( mProject )
68  {
69  mProject->setDirty( true );
70  }
71  emit changed();
72 }
73 
74 
76 {
77  for ( auto relation : mRelations )
78  {
80  }
81 }
82 
83 
84 void QgsRelationManager::removeRelation( const QString &id )
85 {
86  mRelations.remove( id );
87  emit changed();
88 }
89 
91 {
92  mRelations.remove( relation.id() );
93  emit changed();
94 }
95 
96 QgsRelation QgsRelationManager::relation( const QString &id ) const
97 {
98  return mRelations.value( id );
99 }
100 
101 QList<QgsRelation> QgsRelationManager::relationsByName( const QString &name ) const
102 {
103  QList<QgsRelation> relations;
104 
105  const auto constMRelations = mRelations;
106  for ( const QgsRelation &rel : constMRelations )
107  {
108  if ( QString::compare( rel.name(), name, Qt::CaseInsensitive ) == 0 )
109  relations << rel;
110  }
111 
112  return relations;
113 }
114 
116 {
117  mRelations.clear();
118  emit changed();
119 }
120 
121 QList<QgsRelation> QgsRelationManager::referencingRelations( const QgsVectorLayer *layer, int fieldIdx ) const
122 {
123  if ( !layer )
124  {
125  return mRelations.values();
126  }
127 
128  QList<QgsRelation> relations;
129 
130  const auto constMRelations = mRelations;
131  for ( const QgsRelation &rel : constMRelations )
132  {
133  if ( rel.referencingLayer() == layer )
134  {
135  if ( fieldIdx != -2 )
136  {
137  bool containsField = false;
138  const auto constFieldPairs = rel.fieldPairs();
139  for ( const QgsRelation::FieldPair &fp : constFieldPairs )
140  {
141  if ( fieldIdx == layer->fields().lookupField( fp.referencingField() ) )
142  {
143  containsField = true;
144  break;
145  }
146  }
147 
148  if ( !containsField )
149  {
150  continue;
151  }
152  }
153  relations.append( rel );
154  }
155  }
156 
157  return relations;
158 }
159 
160 QList<QgsRelation> QgsRelationManager::referencedRelations( const QgsVectorLayer *layer ) const
161 {
162  if ( !layer )
163  {
164  return mRelations.values();
165  }
166 
167  QList<QgsRelation> relations;
168 
169  const auto constMRelations = mRelations;
170  for ( const QgsRelation &rel : constMRelations )
171  {
172  if ( rel.referencedLayer() == layer )
173  {
174  relations.append( rel );
175  }
176  }
177 
178  return relations;
179 }
180 
181 void QgsRelationManager::readProject( const QDomDocument &doc, QgsReadWriteContext &context )
182 {
183  mRelations.clear();
184 
185  QDomNodeList nodes = doc.elementsByTagName( QStringLiteral( "relations" ) );
186  if ( nodes.count() )
187  {
188  QgsRelationContext relcontext( mProject );
189 
190  QDomNode node = nodes.item( 0 );
191  QDomNodeList relationNodes = node.childNodes();
192  int relCount = relationNodes.count();
193  for ( int i = 0; i < relCount; ++i )
194  {
195  addRelation( QgsRelation::createFromXml( relationNodes.at( i ), context, relcontext ) );
196  }
197  }
198  else
199  {
200  QgsDebugMsg( QStringLiteral( "No relations data present in this document" ) );
201  }
202 
203  emit relationsLoaded();
204  emit changed();
205 }
206 
207 void QgsRelationManager::writeProject( QDomDocument &doc )
208 {
209  QDomNodeList nl = doc.elementsByTagName( QStringLiteral( "qgis" ) );
210  if ( !nl.count() )
211  {
212  QgsDebugMsg( QStringLiteral( "Unable to find qgis element in project file" ) );
213  return;
214  }
215  QDomNode qgisNode = nl.item( 0 ); // there should only be one
216 
217  QDomElement relationsNode = doc.createElement( QStringLiteral( "relations" ) );
218  qgisNode.appendChild( relationsNode );
219 
220  const auto constMRelations = mRelations;
221  for ( const QgsRelation &relation : constMRelations )
222  {
223  relation.writeXml( relationsNode, doc );
224  }
225 }
226 
227 void QgsRelationManager::layersRemoved( const QStringList &layers )
228 {
229  bool relationsChanged = false;
230  const auto constLayers = layers;
231  for ( const QString &layer : constLayers )
232  {
233  QMapIterator<QString, QgsRelation> it( mRelations );
234 
235  while ( it.hasNext() )
236  {
237  it.next();
238 
239  if ( it.value().referencedLayerId() == layer
240  || it.value().referencingLayerId() == layer )
241  {
242  mRelations.remove( it.key() );
243  relationsChanged = true;
244  }
245  }
246  }
247  if ( relationsChanged )
248  {
249  emit changed();
250  }
251 }
252 
253 static bool hasRelationWithEqualDefinition( const QList<QgsRelation> &existingRelations, const QgsRelation &relation )
254 {
255  const auto constExistingRelations = existingRelations;
256  for ( const QgsRelation &cur : constExistingRelations )
257  {
258  if ( cur.hasEqualDefinition( relation ) ) return true;
259  }
260  return false;
261 }
262 
263 QList<QgsRelation> QgsRelationManager::discoverRelations( const QList<QgsRelation> &existingRelations, const QList<QgsVectorLayer *> &layers )
264 {
265  QList<QgsRelation> result;
266  const auto constLayers = layers;
267  for ( const QgsVectorLayer *layer : constLayers )
268  {
269  const auto constDiscoverRelations = layer->dataProvider()->discoverRelations( layer, layers );
270  for ( const QgsRelation &relation : constDiscoverRelations )
271  {
272  if ( !hasRelationWithEqualDefinition( existingRelations, relation ) )
273  {
274  result.append( relation );
275  }
276  }
277  }
278  return result;
279 }
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:344
QgsRelationContext context() const
Gets the relation context.
void setDirty(bool b=true)
Flag the project as dirty (modified).
Definition: qgsproject.cpp:520
The class is used as a container of context for various read/write operations on other objects...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
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.e.
void layersRemoved(const QStringList &layerIds)
Emitted after one or more layers were removed from the registry.
QgsVectorLayer referencingLayer
Definition: qgsrelation.h:46
QString id
Definition: qgsrelation.h:45
void updateRelationsStatus()
Updates relations status.
QList< QgsRelation > referencedRelations(const QgsVectorLayer *layer=nullptr) const
Gets all relations where this layer is the referenced part (i.e.
void clear()
Remove any relation managed by this class.
void setRelations(const QList< QgsRelation > &relations)
Will set the specified relations and remove any relation currently set.
void readProjectWithContext(const QDomDocument &, QgsReadWriteContext &context)
Emitted when a project is being read.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
Defines a relation between matching fields of the two involved tables of a relation.
Definition: qgsrelation.h:74
Context for relations.
QList< QgsRelation > relationsByName(const QString &name) const
Returns a list of relations with matching names.
QMap< QString, QgsRelation > relations() const
Gets access to the relations managed by this class.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc.
Definition: qgsproject.h:94
QgsVectorLayer referencedLayer
Definition: qgsrelation.h:47
void relationsLoaded()
Emitted when the relations were loaded after reading a project.
void writeProject(QDomDocument &)
Emitted when the project is being written.
void changed()
Emitted when relations are added or removed to the manager.
static QgsRelation createFromXml(const QDomNode &node, QgsReadWriteContext &context, const QgsRelationContext &relationContext=QgsRelationContext())
Creates a relation from an XML structure.
Definition: qgsrelation.cpp:51
static QList< QgsRelation > discoverRelations(const QList< QgsRelation > &existingRelations, const QList< QgsVectorLayer *> &layers)
Discover all the relations available from the current layers.
void removeRelation(const QString &id)
Remove a relation.
QgsRelationManager(QgsProject *project=nullptr)
Constructor for QgsRelationManager.
Q_INVOKABLE QgsRelation relation(const QString &id) const
Gets access to a relation by its id.
Represents a vector layer which manages a vector based data sets.
void writeXml(QDomNode &node, QDomDocument &doc) const
Writes a relation to an XML structure.
void updateRelationStatus()
Updates the validity status of this relation.
void addRelation(const QgsRelation &relation)
Add a relation.