QGIS API Documentation 3.99.0-Master (8e76e220402)
Loading...
Searching...
No Matches
qgsvectorlayerjoinbuffer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayerjoinbuffer.cpp
3 ----------------------------
4 begin : Feb 09, 2011
5 copyright : (C) 2011 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
20#include "qgsauxiliarystorage.h"
21#include "qgsfeatureiterator.h"
22#include "qgslogger.h"
23#include "qgsproject.h"
25
26#include <QDomElement>
27#include <QString>
28
29#include "moc_qgsvectorlayerjoinbuffer.cpp"
30
31using namespace Qt::StringLiterals;
32
37
38static QList<QgsVectorLayer *> _outEdges( QgsVectorLayer *vl )
39{
40 QList<QgsVectorLayer *> lst;
41 const auto constVectorJoins = vl->vectorJoins();
42 for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
43 {
44 if ( QgsVectorLayer *joinVl = info.joinLayer() )
45 lst << joinVl;
46 }
47 return lst;
48}
49
50static bool _hasCycleDFS( QgsVectorLayer *n, QHash<QgsVectorLayer *, int> &mark )
51{
52 if ( mark.value( n ) == 1 ) // temporary
53 return true;
54 if ( mark.value( n ) == 0 ) // not visited
55 {
56 mark[n] = 1; // temporary
57 const auto outEdges { _outEdges( n ) };
58 for ( QgsVectorLayer *m : outEdges )
59 {
60 if ( _hasCycleDFS( m, mark ) )
61 return true;
62 }
63 mark[n] = 2; // permanent
64 }
65 return false;
66}
67
68
70{
71 QMutexLocker locker( &mMutex );
72 mVectorJoins.push_back( joinInfo );
73
74 // run depth-first search to detect cycles in the graph of joins between layers.
75 // any cycle would cause infinite recursion when updating fields
76 QHash<QgsVectorLayer *, int> markDFS;
77 if ( mLayer && _hasCycleDFS( mLayer, markDFS ) )
78 {
79 // we have to reject this one
80 mVectorJoins.pop_back();
81 return false;
82 }
83
84 // Wait for notifications about changed fields in joined layer to propagate them.
85 // During project load the joined layers possibly do not exist yet so the connection will not be created,
86 // but then QgsProject makes sure to call createJoinCaches() which will do the connection.
87 // Unique connection makes sure we do not respond to one layer's update more times (in case of multiple join)
88 if ( QgsVectorLayer *vl = joinInfo.joinLayer() )
89 {
90 connectJoinedLayer( vl );
91 }
92
93 if ( mLayer )
94 {
95 locker.unlock();
96 mLayer->updateFields();
97 locker.relock();
98 }
99
100 //cache joined layer to virtual memory if specified by user
101 if ( mLayer && joinInfo.isUsingMemoryCache() )
102 {
103 cacheJoinLayer( mVectorJoins.last() );
104 }
105
106 locker.unlock();
107
108 return true;
109}
110
111
112bool QgsVectorLayerJoinBuffer::removeJoin( const QString &joinLayerId )
113{
114 bool res = false;
115 {
116 QMutexLocker locker( &mMutex );
117 for ( int i = 0; i < mVectorJoins.size(); ++i )
118 {
119 if ( mVectorJoins.at( i ).joinLayerId() == joinLayerId )
120 {
121 if ( QgsVectorLayer *vl = mVectorJoins.at( i ).joinLayer() )
122 {
123 disconnect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields );
124 }
125
126 mVectorJoins.removeAt( i );
127 res = true;
128 }
129 }
130 }
131
132 emit joinedFieldsChanged();
133 return res;
134}
135
136void QgsVectorLayerJoinBuffer::cacheJoinLayer( QgsVectorLayerJoinInfo &joinInfo )
137{
138 //memory cache not required or already done
139 if ( !joinInfo.isUsingMemoryCache() || !joinInfo.cacheDirty )
140 {
141 return;
142 }
143
144 QgsVectorLayer *cacheLayer = joinInfo.joinLayer();
145 if ( cacheLayer )
146 {
147 int joinFieldIndex = cacheLayer->fields().indexFromName( joinInfo.joinFieldName() );
148
149 if ( joinFieldIndex < 0 || joinFieldIndex >= cacheLayer->fields().count() )
150 return;
151
152 joinInfo.cachedAttributes.clear();
153
154 QgsFeatureRequest request;
156 // maybe user requested just a subset of layer's attributes
157 // so we do not have to cache everything
158 QVector<int> subsetIndices;
159 if ( joinInfo.hasSubset() )
160 {
161 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( joinInfo );
162 subsetIndices = joinSubsetIndices( cacheLayer, subsetNames );
163
164 // we need just subset of attributes - but make sure to include join field name
165 QgsAttributeList cacheLayerAttrs = subsetIndices.toList();
166 if ( !cacheLayerAttrs.contains( joinFieldIndex ) )
167 cacheLayerAttrs.append( joinFieldIndex );
168 request.setSubsetOfAttributes( cacheLayerAttrs );
169 }
170
171 QgsFeatureIterator fit = cacheLayer->getFeatures( request );
172 QgsFeature f;
173 while ( fit.nextFeature( f ) )
174 {
175 QgsAttributes attrs = f.attributes();
176 QString key = attrs.at( joinFieldIndex ).toString();
177 if ( joinInfo.hasSubset() )
178 {
179 QgsAttributes subsetAttrs( subsetIndices.count() );
180 for ( int i = 0; i < subsetIndices.count(); ++i )
181 subsetAttrs[i] = attrs.at( subsetIndices.at( i ) );
182 joinInfo.cachedAttributes.insert( key, subsetAttrs );
183 }
184 else
185 {
186 QgsAttributes attributesCache;
187 for ( int i = 0; i < attrs.size(); i++ )
188 {
189 if ( i == joinFieldIndex )
190 continue;
191
192 QString joinInfoPrefix = joinInfo.prefix();
193 if ( joinInfoPrefix.isNull() ) // Default prefix 'layerName_' used
194 joinInfoPrefix = QString( "%1_" ).arg( cacheLayer->name() );
195
196 // Joined field name
197 const QString joinFieldName = joinInfoPrefix + cacheLayer->fields().names().at( i );
198
199 // Check for name collisions
200 int fieldIndex = mLayer->fields().indexFromName( joinFieldName );
201 if ( fieldIndex >= 0
202 && mLayer->fields().fieldOrigin( fieldIndex ) != Qgis::FieldOrigin::Join )
203 continue;
204
205 attributesCache.append( attrs.at( i ) );
206 }
207 joinInfo.cachedAttributes.insert( key, attributesCache );
208 }
209 }
210 joinInfo.cacheDirty = false;
211 }
212}
213
214
215QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset )
216{
217 return joinSubsetIndices( joinLayer->fields(), joinFieldsSubset );
218}
219
220QVector<int> QgsVectorLayerJoinBuffer::joinSubsetIndices( const QgsFields &joinLayerFields, const QStringList &joinFieldsSubset )
221{
222 QVector<int> subsetIndices;
223 for ( int i = 0; i < joinFieldsSubset.count(); ++i )
224 {
225 QString joinedFieldName = joinFieldsSubset.at( i );
226 int index = joinLayerFields.lookupField( joinedFieldName );
227 if ( index != -1 )
228 {
229 subsetIndices.append( index );
230 }
231 else
232 {
233 QgsDebugError( "Join layer subset field not found: " + joinedFieldName );
234 }
235 }
236
237 return subsetIndices;
238}
239
241{
242 QString prefix;
243
244 QList< QgsVectorLayerJoinInfo>::const_iterator joinIt = mVectorJoins.constBegin();
245 for ( int joinIdx = 0; joinIt != mVectorJoins.constEnd(); ++joinIt, ++joinIdx )
246 {
247 QgsVectorLayer *joinLayer = joinIt->joinLayer();
248 if ( !joinLayer )
249 {
250 continue;
251 }
252
253 const QgsFields &joinFields = joinLayer->fields();
254 QString joinFieldName = joinIt->joinFieldName();
255
256 QSet<QString> subset;
257 if ( joinIt->hasSubset() )
258 {
259 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
260 subset = qgis::listToSet( subsetNames );
261 }
262
263 if ( joinIt->prefix().isNull() )
264 {
265 prefix = joinLayer->name() + '_';
266 }
267 else
268 {
269 prefix = joinIt->prefix();
270 }
271
272 for ( int idx = 0; idx < joinFields.count(); ++idx )
273 {
274 // if using just a subset of fields, filter some of them out
275 if ( joinIt->hasSubset() && !subset.contains( joinFields.at( idx ).name() ) )
276 continue;
277
278 //skip the join field to avoid double field names (fields often have the same name)
279 // when using subset of field, use all the selected fields
280 if ( joinIt->hasSubset() || joinFields.at( idx ).name() != joinFieldName )
281 {
282 QgsField f = joinFields.at( idx );
283 f.setName( prefix + f.name() );
284 fields.append( f, Qgis::FieldOrigin::Join, idx + ( joinIdx * 1000 ) );
285 }
286 }
287 }
288}
289
291{
292 QMutexLocker locker( &mMutex );
293 QList< QgsVectorLayerJoinInfo >::iterator joinIt = mVectorJoins.begin();
294 for ( ; joinIt != mVectorJoins.end(); ++joinIt )
295 {
296 if ( joinIt->isUsingMemoryCache() && joinIt->cacheDirty )
297 cacheJoinLayer( *joinIt );
298 }
299}
300
301
302void QgsVectorLayerJoinBuffer::writeXml( QDomNode &layer_node, QDomDocument &document ) const
303{
304 QDomElement vectorJoinsElem = document.createElement( u"vectorjoins"_s );
305 layer_node.appendChild( vectorJoinsElem );
306 QList< QgsVectorLayerJoinInfo >::const_iterator joinIt = mVectorJoins.constBegin();
307 for ( ; joinIt != mVectorJoins.constEnd(); ++joinIt )
308 {
309 if ( isAuxiliaryJoin( *joinIt ) )
310 continue;
311
312 QDomElement joinElem = document.createElement( u"join"_s );
313
314 joinElem.setAttribute( u"targetFieldName"_s, joinIt->targetFieldName() );
315
316 joinElem.setAttribute( u"joinLayerId"_s, joinIt->joinLayerId() );
317 joinElem.setAttribute( u"joinFieldName"_s, joinIt->joinFieldName() );
318
319 joinElem.setAttribute( u"memoryCache"_s, joinIt->isUsingMemoryCache() );
320 joinElem.setAttribute( u"dynamicForm"_s, joinIt->isDynamicFormEnabled() );
321 joinElem.setAttribute( u"editable"_s, joinIt->isEditable() );
322 joinElem.setAttribute( u"upsertOnEdit"_s, joinIt->hasUpsertOnEdit() );
323 joinElem.setAttribute( u"cascadedDelete"_s, joinIt->hasCascadedDelete() );
324
325 if ( joinIt->hasSubset() )
326 {
327 QDomElement subsetElem = document.createElement( u"joinFieldsSubset"_s );
328 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( *joinIt );
329
330 const auto constSubsetNames = subsetNames;
331 for ( const QString &fieldName : constSubsetNames )
332 {
333 QDomElement fieldElem = document.createElement( u"field"_s );
334 fieldElem.setAttribute( u"name"_s, fieldName );
335 subsetElem.appendChild( fieldElem );
336 }
337
338 joinElem.appendChild( subsetElem );
339 }
340
341 if ( !joinIt->prefix().isNull() )
342 {
343 joinElem.setAttribute( u"customPrefix"_s, joinIt->prefix() );
344 joinElem.setAttribute( u"hasCustomPrefix"_s, 1 );
345 }
346
347 vectorJoinsElem.appendChild( joinElem );
348 }
349}
350
351void QgsVectorLayerJoinBuffer::readXml( const QDomNode &layer_node )
352{
353 mVectorJoins.clear();
354 QDomElement vectorJoinsElem = layer_node.firstChildElement( u"vectorjoins"_s );
355 if ( !vectorJoinsElem.isNull() )
356 {
357 QDomNodeList joinList = vectorJoinsElem.elementsByTagName( u"join"_s );
358 for ( int i = 0; i < joinList.size(); ++i )
359 {
360 QDomElement infoElem = joinList.at( i ).toElement();
362 info.setJoinFieldName( infoElem.attribute( u"joinFieldName"_s ) );
363 // read layer ID - to turn it into layer object, caller will need to call resolveReferences() later
364 info.setJoinLayerId( infoElem.attribute( u"joinLayerId"_s ) );
365 info.setTargetFieldName( infoElem.attribute( u"targetFieldName"_s ) );
366 info.setUsingMemoryCache( infoElem.attribute( u"memoryCache"_s ).toInt() );
367 info.setDynamicFormEnabled( infoElem.attribute( u"dynamicForm"_s ).toInt() );
368 info.setEditable( infoElem.attribute( u"editable"_s ).toInt() );
369 info.setUpsertOnEdit( infoElem.attribute( u"upsertOnEdit"_s ).toInt() );
370 info.setCascadedDelete( infoElem.attribute( u"cascadedDelete"_s ).toInt() );
371
372 QDomElement subsetElem = infoElem.firstChildElement( u"joinFieldsSubset"_s );
373 if ( !subsetElem.isNull() )
374 {
375 QStringList *fieldNames = new QStringList;
376 QDomNodeList fieldNodes = infoElem.elementsByTagName( u"field"_s );
377 fieldNames->reserve( fieldNodes.count() );
378 for ( int i = 0; i < fieldNodes.count(); ++i )
379 *fieldNames << fieldNodes.at( i ).toElement().attribute( u"name"_s );
380 info.setJoinFieldNamesSubset( fieldNames );
381 }
382
383 if ( infoElem.attribute( u"hasCustomPrefix"_s ).toInt() )
384 info.setPrefix( infoElem.attribute( u"customPrefix"_s ) );
385 else
386 info.setPrefix( QString() );
387
388 addJoin( info );
389 }
390 }
391}
392
394{
395 bool resolved = false;
396 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
397 {
398 if ( it->joinLayer() )
399 continue; // already resolved
400
401 if ( QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( project->mapLayer( it->joinLayerId() ) ) )
402 {
403 it->setJoinLayer( joinedLayer );
404 connectJoinedLayer( joinedLayer );
405 resolved = true;
406 }
407 }
408
409 if ( resolved )
410 emit joinedFieldsChanged();
411}
412
414{
415 if ( !info )
416 return -1;
417
418 int joinIndex = mVectorJoins.indexOf( *info );
419 if ( joinIndex == -1 )
420 return -1;
421
422 for ( int i = 0; i < fields.count(); ++i )
423 {
424 if ( fields.fieldOrigin( i ) != Qgis::FieldOrigin::Join )
425 continue;
426
427 if ( fields.fieldOriginIndex( i ) / 1000 == joinIndex )
428 return i;
429 }
430 return -1;
431}
432
433const QgsVectorLayerJoinInfo *QgsVectorLayerJoinBuffer::joinForFieldIndex( int index, const QgsFields &fields, int &sourceFieldIndex ) const
434{
435 if ( fields.fieldOrigin( index ) != Qgis::FieldOrigin::Join )
436 return nullptr;
437
438 int originIndex = fields.fieldOriginIndex( index );
439 int sourceJoinIndex = originIndex / 1000;
440 sourceFieldIndex = originIndex % 1000;
441
442 if ( sourceJoinIndex < 0 || sourceJoinIndex >= mVectorJoins.count() )
443 return nullptr;
444
445 return &( mVectorJoins[sourceJoinIndex] );
446}
447
448QList<const QgsVectorLayerJoinInfo *> QgsVectorLayerJoinBuffer::joinsWhereFieldIsId( const QgsField &field ) const
449{
450 QList<const QgsVectorLayerJoinInfo *> infos;
451
452 const auto constMVectorJoins = mVectorJoins;
453 for ( const QgsVectorLayerJoinInfo &info : constMVectorJoins )
454 {
455 if ( infos.contains( &info ) )
456 continue;
457
458 if ( info.targetFieldName() == field.name() )
459 infos.append( &info );
460 }
461
462 return infos;
463}
464
466{
467 QgsFeature joinedFeature;
468
469 if ( info->joinLayer() )
470 {
471 joinedFeature.initAttributes( info->joinLayer()->fields().count() );
472 joinedFeature.setFields( info->joinLayer()->fields() );
473
474 QString joinFieldName = info->joinFieldName();
475 const QVariant targetValue = feature.attribute( info->targetFieldName() );
476 QString filter = QgsExpression::createFieldEqualityExpression( joinFieldName, targetValue );
477
478 QgsFeatureRequest request;
479 request.setFilterExpression( filter );
480 request.setLimit( 1 );
481
482 QgsFeatureIterator it = info->joinLayer()->getFeatures( request );
483 it.nextFeature( joinedFeature );
484 }
485
486 return joinedFeature;
487}
488
490{
491 QgsFeature targetedFeature;
492
493 if ( info->joinLayer() )
494 {
495 const QVariant targetValue = feature.attribute( info->joinFieldName() );
496 const QString filter = QgsExpression::createFieldEqualityExpression( info->targetFieldName(), targetValue );
497
498 QgsFeatureRequest request;
499 request.setFilterExpression( filter );
500 request.setLimit( 1 );
501
502 QgsFeatureIterator it = mLayer->getFeatures( request );
503 it.nextFeature( targetedFeature );
504 }
505
506 return targetedFeature;
507}
508
510{
512 cloned->mVectorJoins = mVectorJoins;
513 return cloned;
514}
515
516void QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields()
517{
518 // TODO - check - this whole method is probably not needed anymore,
519 // since the cache handling is covered by joinedLayerModified()
520
521 QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
522 Q_ASSERT( joinedLayer );
523
524 // recache the joined layer
525 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
526 {
527 if ( joinedLayer == it->joinLayer() )
528 {
529 it->cachedAttributes.clear();
530 cacheJoinLayer( *it );
531 }
532 }
533
534 emit joinedFieldsChanged();
535}
536
537void QgsVectorLayerJoinBuffer::joinedLayerModified()
538{
539 QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
540 Q_ASSERT( joinedLayer );
541
542 // recache the joined layer
543 for ( QgsVectorJoinList::iterator it = mVectorJoins.begin(); it != mVectorJoins.end(); ++it )
544 {
545 if ( joinedLayer == it->joinLayer() )
546 {
547 it->cacheDirty = true;
548 }
549 }
550}
551
552void QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted()
553{
554 QgsVectorLayer *joinedLayer = qobject_cast<QgsVectorLayer *>( sender() );
555 Q_ASSERT( joinedLayer );
556
557 removeJoin( joinedLayer->id() );
558}
559
560void QgsVectorLayerJoinBuffer::connectJoinedLayer( QgsVectorLayer *vl )
561{
562 connect( vl, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerJoinBuffer::joinedLayerUpdatedFields, Qt::UniqueConnection );
563 connect( vl, &QgsVectorLayer::layerModified, this, &QgsVectorLayerJoinBuffer::joinedLayerModified, Qt::UniqueConnection );
564 connect( vl, &QgsVectorLayer::willBeDeleted, this, &QgsVectorLayerJoinBuffer::joinedLayerWillBeDeleted, Qt::UniqueConnection );
565}
566
568{
569 if ( !containsJoins() )
570 return false;
571
572 // try to add/update a feature in each joined layer
573 const QgsVectorJoinList joins = vectorJoins();
574 for ( const QgsVectorLayerJoinInfo &info : joins )
575 {
576 QgsVectorLayer *joinLayer = info.joinLayer();
577
578 if ( joinLayer && joinLayer->isEditable() && info.isEditable() && info.hasUpsertOnEdit() )
579 {
580 QgsFeatureList joinFeatures;
581
582 for ( const QgsFeature &feature : std::as_const( features ) )
583 {
584 const QgsFeature joinFeature = info.extractJoinedFeature( feature );
585
586 // we don't want to add a new feature in joined layer when the id
587 // column value yet exist, we just want to update the existing one
588 const QVariant idFieldValue = feature.attribute( info.targetFieldName() );
589 const QString filter = QgsExpression::createFieldEqualityExpression( info.joinFieldName(), idFieldValue.toString() );
590
591 QgsFeatureRequest request;
593 request.setNoAttributes();
594 request.setFilterExpression( filter );
595 request.setLimit( 1 );
596
597 QgsFeatureIterator it = info.joinLayer()->getFeatures( request );
598 QgsFeature existingFeature;
599 it.nextFeature( existingFeature );
600
601 if ( existingFeature.isValid() )
602 {
603 if ( info.hasSubset() )
604 {
605 const QStringList subsetNames = QgsVectorLayerJoinInfo::joinFieldNamesSubset( info );
606 const auto constSubsetNames = subsetNames;
607 for ( const QString &field : constSubsetNames )
608 {
609 QVariant newValue = joinFeature.attribute( field );
610 int fieldIndex = joinLayer->fields().indexOf( field );
611 joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
612 }
613 }
614 else
615 {
616 const QgsFields joinFields = joinFeature.fields();
617 for ( const auto &field : joinFields )
618 {
619 QVariant newValue = joinFeature.attribute( field.name() );
620 int fieldIndex = joinLayer->fields().indexOf( field.name() );
621 joinLayer->changeAttributeValue( existingFeature.id(), fieldIndex, newValue );
622 }
623 }
624 }
625 else
626 {
627 // joined feature is added only if one of its field is not null
628 bool notNullFields = false;
629 const QgsFields joinFields = joinFeature.fields();
630 for ( const auto &field : joinFields )
631 {
632 if ( field.name() == info.joinFieldName() )
633 continue;
634
635 if ( !QgsVariantUtils::isNull( joinFeature.attribute( field.name() ) ) )
636 {
637 notNullFields = true;
638 break;
639 }
640 }
641
642 if ( notNullFields )
643 joinFeatures << joinFeature;
644 }
645 }
646
647 joinLayer->addFeatures( joinFeatures );
648 }
649 }
650
651 return true;
652}
653
654bool QgsVectorLayerJoinBuffer::changeAttributeValue( QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue )
655{
656 if ( mLayer->fields().fieldOrigin( field ) != Qgis::FieldOrigin::Join )
657 return false;
658
659 int srcFieldIndex;
660 const QgsVectorLayerJoinInfo *info = joinForFieldIndex( field, mLayer->fields(), srcFieldIndex );
661 if ( info && info->joinLayer() && info->isEditable() )
662 {
663 QgsFeature feature = mLayer->getFeature( fid );
664
665 if ( !feature.isValid() )
666 return false;
667
668 const QgsFeature joinFeature = joinedFeatureOf( info, feature );
669
670 if ( joinFeature.isValid() )
671 return info->joinLayer()->changeAttributeValue( joinFeature.id(), srcFieldIndex, newValue, oldValue );
672 else
673 {
674 feature.setAttribute( field, newValue );
675 return addFeatures( QgsFeatureList() << feature );
676 }
677 }
678 else
679 return false;
680}
681
683{
684 bool success = true;
685
686 for ( auto it = newValues.constBegin(); it != newValues.constEnd(); ++it )
687 {
688 const int field = it.key();
689 const QVariant newValue = it.value();
690 QVariant oldValue;
691
692 if ( oldValues.contains( field ) )
693 oldValue = oldValues[field];
694
695 success &= changeAttributeValue( fid, field, newValue, oldValue );
696 }
697
698 return success;
699}
700
702{
703 return deleteFeatures( QgsFeatureIds() << fid, context );
704}
705
707{
708 if ( !containsJoins() )
709 return false;
710
711 const auto constFids = fids;
712 for ( const QgsFeatureId &fid : constFids )
713 {
714 const auto constVectorJoins = vectorJoins();
715 for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
716 {
717 if ( info.isEditable() && info.hasCascadedDelete() )
718 {
719 const QgsFeature joinFeature = joinedFeatureOf( &info, mLayer->getFeature( fid ) );
720 if ( joinFeature.isValid() )
721 info.joinLayer()->deleteFeature( joinFeature.id(), context );
722 }
723 }
724 }
725
726 return true;
727}
728
730{
731 const QgsAuxiliaryLayer *al = mLayer->auxiliaryLayer();
732
733 return al && al->id() == info.joinLayerId();
734}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
@ Join
Field originates from a joined layer.
Definition qgis.h:1765
Allows managing the auxiliary storage for a vector layer.
static QString createFieldEqualityExpression(const QString &fieldName, const QVariant &value, QMetaType::Type fieldType=QMetaType::Type::UnknownType)
Create an expression allowing to evaluate if a field is equal to a value.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setLimit(long long limit)
Set the maximum number of features to request.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
QgsFeatureRequest & setFilterExpression(const QString &expression)
Set the filter expression.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
QFlags< Flag > Flags
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
Q_INVOKABLE bool setAttribute(int field, const QVariant &attr)
Sets an attribute's value by field index.
QgsAttributes attributes
Definition qgsfeature.h:69
QgsFields fields
Definition qgsfeature.h:70
void initAttributes(int fieldCount)
Initialize this feature with the given number of fields.
QgsFeatureId id
Definition qgsfeature.h:68
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
bool isValid() const
Returns the validity of this feature.
Q_INVOKABLE QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QString name
Definition qgsfield.h:65
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:233
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:76
int count
Definition qgsfields.h:50
Q_INVOKABLE int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Q_INVOKABLE int indexOf(const QString &fieldName) const
Gets the field index from the field name.
Qgis::FieldOrigin fieldOrigin(int fieldIdx) const
Returns the field's origin (value from an enumeration).
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
int fieldOriginIndex(int fieldIdx) const
Returns the field's origin index (its meaning is specific to each type of origin).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QStringList names
Definition qgsfields.h:51
QString name
Definition qgsmaplayer.h:87
QString id
Definition qgsmaplayer.h:86
void willBeDeleted()
Emitted in the destructor when the layer is about to be deleted, but it is still in a perfectly valid...
void layerModified()
Emitted when modifications has been done on layer.
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition qgsproject.h:113
Q_INVOKABLE QgsMapLayer * mapLayer(const QString &layerId) const
Retrieve a pointer to a registered layer by layer ID.
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
void resolveReferences(QgsProject *project)
Resolves layer IDs of joined layers using given project's available layers.
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
bool addJoin(const QgsVectorLayerJoinInfo &joinInfo)
Joins another vector layer to this layer.
void readXml(const QDomNode &layer_node)
Reads joins from project file.
QgsVectorLayerJoinBuffer(QgsVectorLayer *layer=nullptr)
void writeXml(QDomNode &layer_node, QDomDocument &document) const
Saves mVectorJoins to xml under the layer node.
int joinedFieldsOffset(const QgsVectorLayerJoinInfo *info, const QgsFields &fields)
Find out what is the first index of the join within fields.
const QgsVectorLayerJoinInfo * joinForFieldIndex(int index, const QgsFields &fields, int &sourceFieldIndex) const
Finds the vector join for a layer field index.
bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant())
Changes attribute value in joined layers.
bool deleteFeatures(const QgsFeatureIds &fids, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a list of features from joined layers.
QList< const QgsVectorLayerJoinInfo * > joinsWhereFieldIsId(const QgsField &field) const
Returns joins where the field of a target layer is considered as an id.
bool removeJoin(const QString &joinLayerId)
Removes a vector layer join.
bool containsJoins() const
Quick way to test if there is any join at all.
bool changeAttributeValues(QgsFeatureId fid, const QgsAttributeMap &newValues, const QgsAttributeMap &oldValues=QgsAttributeMap())
Changes attributes' values in joined layers.
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) override
Adds a list of features in joined layers.
QgsVectorLayerJoinBuffer * clone() const
Create a copy of the join buffer.
void joinedFieldsChanged()
Emitted whenever the list of joined fields changes (e.g.
void createJoinCaches()
Calls cacheJoinLayer() for all vector joins.
void updateFields(QgsFields &fields)
Updates field map with joined attributes.
static QVector< int > joinSubsetIndices(QgsVectorLayer *joinLayer, const QStringList &joinFieldsSubset)
Returns a vector of indices for use in join based on field names from the layer.
bool deleteFeature(QgsFeatureId fid, QgsVectorLayer::DeleteContext *context=nullptr) const
Deletes a feature from joined layers.
QgsFeature joinedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the joined feature corresponding to the feature.
const QgsVectorJoinList & vectorJoins() const
bool isAuxiliaryJoin(const QgsVectorLayerJoinInfo &info) const
Returns true if the join information is about auxiliary layer, false otherwise.
Defines left outer join from our vector layer to some other vector layer.
QStringList * joinFieldNamesSubset() const
Returns the subset of fields to be used from joined layer.
void setDynamicFormEnabled(bool enabled)
Sets whether the form has to be dynamically updated with joined fields when a feature is being create...
void setUsingMemoryCache(bool enabled)
Sets whether values from the joined layer should be cached in memory to speed up lookups.
void setJoinLayerId(const QString &layerId)
Sets ID of the joined layer. It will need to be overwritten by setJoinLayer() to a reference to real ...
void setEditable(bool enabled)
Sets whether the form of the target layer allows editing joined fields.
bool cacheDirty
True if the cached join attributes need to be updated.
bool isEditable() const
Returns whether joined fields may be edited through the form of the target layer.
void setCascadedDelete(bool enabled)
Sets whether a feature deleted on the target layer has to impact the joined layer by deleting the cor...
void setJoinFieldName(const QString &fieldName)
Sets name of the field of joined layer that will be used for join.
bool isUsingMemoryCache() const
Returns whether values from the joined layer should be cached in memory to speed up lookups.
QString prefix() const
Returns prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
void setTargetFieldName(const QString &fieldName)
Sets name of the field of our layer that will be used for join.
QString joinFieldName() const
Returns name of the field of joined layer that will be used for join.
void setUpsertOnEdit(bool enabled)
Sets whether a feature created on the target layer has to impact the joined layer by creating a new f...
QString targetFieldName() const
Returns name of the field of our layer that will be used for join.
QString joinLayerId() const
ID of the joined layer - may be used to resolve reference to the joined layer.
void setPrefix(const QString &prefix)
Sets prefix of fields from the joined layer. If nullptr, joined layer's name will be used.
bool hasSubset(bool blocklisted=true) const
Returns true if blocklisted fields is not empty or if a subset of names has been set.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be nullptr if the reference was set by layer ID and not resolved yet).
QHash< QString, QgsAttributes > cachedAttributes
Cache for joined attributes to provide fast lookup (size is 0 if no memory caching).
void setJoinFieldNamesSubset(QStringList *fieldNamesSubset)
Sets the subset of fields to be used from joined layer.
Represents a vector layer which manages a vector based dataset.
bool isEditable() const final
Returns true if the provider is in editing mode.
Q_INVOKABLE bool changeAttributeValue(QgsFeatureId fid, int field, const QVariant &newValue, const QVariant &oldValue=QVariant(), bool skipDefaultValues=false, QgsVectorLayerToolsContext *context=nullptr)
Changes an attribute value for a feature (but does not immediately commit the changes).
QgsAuxiliaryLayer * auxiliaryLayer()
Returns the current auxiliary layer.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
bool addFeatures(QgsFeatureList &features, QgsFeatureSink::Flags flags=QgsFeatureSink::Flags()) final
Adds a list of features to the sink.
QMap< int, QVariant > QgsAttributeMap
QList< QgsFeature > QgsFeatureList
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:30
#define QgsDebugError(str)
Definition qgslogger.h:59
QList< QgsVectorLayerJoinInfo > QgsVectorJoinList
Context for cascade delete features.