Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsrelationreferencefieldformatter.cpp - QgsRelationReferenceFieldFormatter 3 : : 4 : : --------------------- 5 : : begin : 3.12.2016 6 : : copyright : (C) 2016 by Matthias Kuhn 7 : : email : matthias@opengis.ch 8 : : *************************************************************************** 9 : : * * 10 : : * This program is free software; you can redistribute it and/or modify * 11 : : * it under the terms of the GNU General Public License as published by * 12 : : * the Free Software Foundation; either version 2 of the License, or * 13 : : * (at your option) any later version. * 14 : : * * 15 : : ***************************************************************************/ 16 : : #include "qgsrelationreferencefieldformatter.h" 17 : : 18 : : #include "qgsmessagelog.h" 19 : : #include "qgsrelation.h" 20 : : #include "qgsexpressioncontext.h" 21 : : #include "qgsproject.h" 22 : : #include "qgsrelationmanager.h" 23 : : #include "qgsvectorlayer.h" 24 : : #include "qgsexpressioncontextutils.h" 25 : : 26 : 5 : QgsRelationReferenceFieldFormatter::QgsRelationReferenceFieldFormatter() 27 : 10 : { 28 : 5 : setFlags( flags() | QgsFieldFormatter::CanProvideAvailableValues ); 29 : 5 : } 30 : : 31 : 5 : QString QgsRelationReferenceFieldFormatter::id() const 32 : : { 33 : 10 : return QStringLiteral( "RelationReference" ); 34 : : } 35 : : 36 : 0 : QString QgsRelationReferenceFieldFormatter::representValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const 37 : : { 38 : 0 : if ( cache.isValid() ) 39 : : { 40 : 0 : return cache.value<QMap<QVariant, QString>>().value( value ); 41 : : } 42 : : 43 : 0 : const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" ); 44 : : 45 : : // Some sanity checks 46 : 0 : if ( !config.contains( QStringLiteral( "Relation" ) ) ) 47 : : { 48 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) ); 49 : 0 : return value.toString(); 50 : : } 51 : : 52 : 0 : const QString relationName = config[QStringLiteral( "Relation" )].toString(); 53 : 0 : QgsRelation relation = QgsProject::instance()->relationManager()->relation( relationName ); 54 : 0 : if ( !relation.isValid() ) 55 : : { 56 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) ); 57 : 0 : return value.toString(); 58 : : } 59 : 0 : QgsVectorLayer *referencingLayer = relation.referencingLayer(); 60 : 0 : if ( layer != referencingLayer ) 61 : : { 62 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) ); 63 : 0 : return value.toString(); 64 : : } 65 : 0 : int referencingFieldIdx = referencingLayer->fields().lookupField( relation.fieldPairs().at( 0 ).first ); 66 : 0 : if ( referencingFieldIdx != fieldIndex ) 67 : : { 68 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent fieldIndex parameter w.r.t relation referencingFieldIdx" ).arg( layer->name(), fieldName ) ); 69 : 0 : return value.toString(); 70 : : } 71 : 0 : QgsVectorLayer *referencedLayer = relation.referencedLayer(); 72 : 0 : if ( !referencedLayer ) 73 : : { 74 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) ); 75 : 0 : return value.toString(); 76 : : } 77 : : 78 : : // Attributes from the referencing layer 79 : 0 : QgsAttributes attrs = QgsAttributes( layer->fields().count() ); 80 : : // Set the value on the foreign key field of the referencing record 81 : 0 : attrs[ referencingFieldIdx ] = value; 82 : : 83 : 0 : QgsFeatureRequest request = relation.getReferencedFeatureRequest( attrs ); 84 : 0 : QgsFeature feature; 85 : 0 : referencedLayer->getFeatures( request ).nextFeature( feature ); 86 : 0 : if ( !feature.isValid() ) 87 : 0 : return value.toString(); 88 : : 89 : 0 : QgsExpression expr( referencedLayer->displayExpression() ); 90 : 0 : QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( referencedLayer ) ); 91 : 0 : context.setFeature( feature ); 92 : 0 : QString title = expr.evaluate( &context ).toString(); 93 : 0 : if ( expr.hasEvalError() ) 94 : : { 95 : 0 : int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second ); 96 : 0 : title = feature.attribute( referencedFieldIdx ).toString(); 97 : 0 : } 98 : 0 : return title; 99 : 0 : } 100 : : 101 : 0 : QVariant QgsRelationReferenceFieldFormatter::sortValue( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value ) const 102 : : { 103 : 0 : return representValue( layer, fieldIndex, config, cache, value ); 104 : 0 : } 105 : : 106 : 0 : QVariant QgsRelationReferenceFieldFormatter::createCache( QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config ) const 107 : : { 108 : : Q_UNUSED( fieldIndex ) 109 : 0 : QMap<QVariant, QString> cache; 110 : : 111 : 0 : const QString fieldName = fieldIndex < layer->fields().size() ? layer->fields().at( fieldIndex ).name() : QObject::tr( "<unknown>" ); 112 : : 113 : : // Some sanity checks 114 : 0 : if ( !config.contains( QStringLiteral( "Relation" ) ) ) 115 : : { 116 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Missing Relation in configuration" ).arg( layer->name(), fieldName ) ); 117 : 0 : return QVariant(); 118 : : } 119 : 0 : const QString relationName = config[QStringLiteral( "Relation" )].toString(); 120 : 0 : QgsRelation relation = QgsProject::instance()->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() ); 121 : 0 : if ( !relation.isValid() ) 122 : : { 123 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid relation %3" ).arg( layer->name(), fieldName, relationName ) ); 124 : 0 : return QVariant(); 125 : : } 126 : 0 : QgsVectorLayer *referencingLayer = relation.referencingLayer(); 127 : 0 : if ( layer != referencingLayer ) 128 : : { 129 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: representValue() with inconsistent layer parameter w.r.t relation referencingLayer" ).arg( layer->name(), fieldName ) ); 130 : 0 : return QVariant(); 131 : : } 132 : 0 : QgsVectorLayer *referencedLayer = relation.referencedLayer(); 133 : 0 : if ( !referencedLayer ) 134 : : { 135 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Cannot find referenced layer" ).arg( layer->name(), fieldName ) ); 136 : 0 : return QVariant(); 137 : : } 138 : : 139 : 0 : const int referencedFieldIdx = referencedLayer->fields().lookupField( relation.fieldPairs().at( 0 ).second ); 140 : 0 : if ( referencedFieldIdx == -1 ) 141 : : { 142 : 0 : QgsMessageLog::logMessage( QObject::tr( "Layer %1, field %2: Invalid referenced field (%3) configured in relation %4" ).arg( layer->name(), fieldName, relation.fieldPairs().at( 0 ).second, relation.name() ) ); 143 : 0 : return QVariant(); 144 : : } 145 : : 146 : 0 : QgsExpression expr( referencedLayer->displayExpression() ); 147 : : 148 : 0 : QgsFeatureRequest request; 149 : 0 : request.setFlags( QgsFeatureRequest::NoGeometry ); 150 : 0 : QgsAttributeList requiredAttributes = qgis::setToList( expr.referencedAttributeIndexes( referencedLayer->fields() ) ); 151 : 0 : requiredAttributes << referencedFieldIdx; 152 : 0 : request.setSubsetOfAttributes( requiredAttributes ); 153 : 0 : QgsFeature feature; 154 : 0 : auto iterator = referencedLayer->getFeatures( request ); 155 : : 156 : 0 : QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( referencedLayer ) ); 157 : : 158 : 0 : expr.prepare( &context ); 159 : : 160 : 0 : while ( iterator.nextFeature( feature ) ) 161 : : { 162 : 0 : context.setFeature( feature ); 163 : 0 : QString title = expr.evaluate( &context ).toString(); 164 : : 165 : 0 : if ( expr.hasEvalError() ) 166 : : { 167 : 0 : title = feature.attribute( referencedFieldIdx ).toString(); 168 : 0 : } 169 : : 170 : 0 : cache.insert( feature.attribute( referencedFieldIdx ), title ); 171 : 0 : } 172 : : 173 : 0 : return QVariant::fromValue<QMap<QVariant, QString>>( cache ); 174 : 0 : } 175 : : 176 : : 177 : 0 : QList<QgsVectorLayerRef> QgsRelationReferenceFieldFormatter::layerDependencies( const QVariantMap &config ) const 178 : : { 179 : : // Old projects, create before the weak relations were introduced and stored with the 180 : : // widget configuration do not have the referenced layer details but only the "Relation" id, 181 : : // for these projects automatic loading of broken references is not supported. 182 : 0 : if ( config.value( QStringLiteral( "ReferencedLayerId" ) ).toString().isEmpty() ) 183 : : { 184 : 0 : return {}; 185 : : } 186 : : 187 : 0 : const QList<QgsVectorLayerRef> result {{ 188 : 0 : QgsVectorLayerRef( 189 : 0 : config.value( QStringLiteral( "ReferencedLayerId" ) ).toString(), 190 : 0 : config.value( QStringLiteral( "ReferencedLayerName" ) ).toString(), 191 : 0 : config.value( QStringLiteral( "ReferencedLayerDataSource" ) ).toString(), 192 : 0 : config.value( QStringLiteral( "ReferencedLayerProviderKey" ) ).toString() ) 193 : : }}; 194 : 0 : return result; 195 : 0 : } 196 : : 197 : 0 : QVariantList QgsRelationReferenceFieldFormatter::availableValues( const QVariantMap &config, int countLimit, const QgsFieldFormatterContext &context ) const 198 : : { 199 : 0 : QVariantList values; 200 : 0 : if ( auto *lProject = context.project() ) 201 : : { 202 : 0 : const QgsVectorLayer *referencedLayer = lProject->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() ).referencedLayer(); 203 : 0 : if ( referencedLayer ) 204 : : { 205 : 0 : int fieldIndex = lProject->relationManager()->relation( config[QStringLiteral( "Relation" )].toString() ).referencedFields().first(); 206 : 0 : values = qgis::setToList( referencedLayer->uniqueValues( fieldIndex, countLimit ) ); 207 : 0 : } 208 : 0 : } 209 : 0 : return values; 210 : 0 : }