carrierRepository = $carrierRepository; $this->logger = $logger; $this->carrierOptionsFactory = $carrierOptionsFactory; $this->wpAdapter = $wpAdapter; } /** * Validates data from API. * * @param non-empty-array> $carriers * * @return bool */ public function validate_carrier_data( array $carriers ): bool { foreach ( $carriers as $carrier ) { if ( ! isset( $carrier['id'], $carrier['name'], $carrier['country'], $carrier['currency'], $carrier['pickupPoints'], $carrier['apiAllowed'], $carrier['separateHouseNumber'], $carrier['customsDeclarations'], $carrier['requiresEmail'], $carrier['requiresPhone'], $carrier['requiresSize'], $carrier['disallowsCod'], $carrier['maxWeight'] ) ) { return false; } } return true; } /** * Maps input data to storage structure. * * @param array> $carriers $carriers Validated data retrieved from API. * * @return array> data to store in db */ private function carriers_mapper( array $carriers ): array { $mappedData = array(); $carrierBooleanParams = array( 'is_pickup_points' => 'pickupPoints', 'has_carrier_direct_label' => 'apiAllowed', 'separate_house_number' => 'separateHouseNumber', 'customs_declarations' => 'customsDeclarations', 'requires_email' => 'requiresEmail', 'requires_phone' => 'requiresPhone', 'requires_size' => 'requiresSize', 'disallows_cod' => 'disallowsCod', 'available' => 'available', ); foreach ( $carriers as $carrier ) { $carrierId = (int) $carrier['id']; $carrierData = array( 'name' => $carrier['name'], 'country' => $carrier['country'], 'currency' => $carrier['currency'], 'max_weight' => (float) $carrier['maxWeight'], 'deleted' => false, ); foreach ( $carrierBooleanParams as $columnName => $paramName ) { $carrierData[ $columnName ] = ( $carrier[ $paramName ] === 'true' ); } $mappedData[ $carrierId ] = $carrierData; } return $mappedData; } /** * Saves carriers. * * @param array> $carriers Validated data retrieved from API. */ public function save( array $carriers ): void { $mappedData = $this->carriers_mapper( $carriers ); $carriersInDb = $this->carrierRepository->getAllRawIndexed(); foreach ( $mappedData as $carrierId => $carrier ) { if ( isset( $carriersInDb[ $carrierId ] ) ) { $this->carrierRepository->update( $carrier, $carrierId ); $differences = $this->getArrayDifferences( $carriersInDb[ $carrierId ], $carrier ); if ( isset( $differences['available'] ) ) { $carrierOptions = $this->carrierOptionsFactory->createByCarrierId( (string) $carrierId ); if ( $carrierOptions->isActive() ) { $this->wpAdapter->updateOption( $carrierOptions->getOptionId(), array_merge( $carrierOptions->toArray(), [ 'active' => false ] ) ); } } if ( count( $differences ) > 0 ) { $this->addLogEntry( // translators: %s is carrier name. sprintf( __( 'Carrier parameters changed for carrier "%s".', 'packeta' ), $carrier['name'] ) . ' ' . __( 'New parameters', 'packeta' ) . ': ' . implode( ', ', $differences ) ); } unset( $carriersInDb[ $carrierId ] ); } else { $carrier['id'] = $carrierId; $updatedRowCount = $this->carrierRepository->insert( $carrier ); if ( $updatedRowCount === false ) { // translators: %s is a carrier name. $message = sprintf( (string) $this->wpAdapter->__( 'An error occurred while saving carrier "%s". More details in WC log.', 'packeta' ), $carrier['name'] ); $this->logMessages[] = $message; $record = new Record(); $record->action = Record::ACTION_CARRIER_LIST_UPDATE; $record->status = Record::STATUS_ERROR; $record->title = $message; $record->params = []; $this->logger->add( $record ); } else { $this->addLogEntry( // translators: %s is a carrier name. sprintf( (string) $this->wpAdapter->__( 'A new carrier "%s" has been added.', 'packeta' ), $carrier['name'] ) ); } } } if ( count( $carriersInDb ) > 0 ) { $this->carrierRepository->set_as_deleted( array_keys( $carriersInDb ) ); foreach ( $carriersInDb as $deletedCarrier ) { if ( (bool) $deletedCarrier['deleted'] === true ) { continue; } $this->addLogEntry( // translators: %s is carrier name. sprintf( __( 'Carrier "%s" has been removed.', 'packeta' ), $deletedCarrier['name'] ) ); } } if ( count( $this->logMessages ) > 0 ) { set_transient( Transients::CARRIER_CHANGES, true ); } else { delete_transient( Transients::CARRIER_CHANGES ); } } /** * Gets array changes as array of strings. * * @param array $oldData Previous version. * @param array $newData New version. * * @return array */ private function getArrayDifferences( array $oldData, array $newData ): array { $differences = []; $columnSettings = $this->getColumnSettings(); foreach ( $oldData as $key => $oldValue ) { if ( $key === 'id' ) { continue; } if ( $key === 'deleted' ) { if ( (string) $oldValue === '1' && isset( $newData['name'] ) ) { $differences[ $key ] = __( 'carrier was re-enabled', 'packeta' ); } continue; } $newValue = (string) $newData[ $key ]; if ( $columnSettings[ $key ]['isBoolean'] === true ) { $newValue = ( $newValue === '1' ? $newValue : '0' ); $oldValue = ( (string) $oldValue === '1' ? (string) $oldValue : '0' ); } if ( (string) $oldValue === $newValue ) { continue; } if ( $columnSettings[ $key ]['isBoolean'] === true ) { $newValue = ( $newValue === '1' ? __( 'yes', 'packeta' ) : __( 'no', 'packeta' ) ); $oldValue = ( $oldValue === '1' ? __( 'yes', 'packeta' ) : __( 'no', 'packeta' ) ); } $differences[ $key ] = $columnSettings[ $key ]['label'] . ': ' . $oldValue . ' => ' . $newValue; } return $differences; } /** * Adds log entry. * * @param string $message Message. * * @return void */ private function addLogEntry( string $message ): void { $this->logMessages[] = $message; $record = new Record(); $record->action = Record::ACTION_CARRIER_LIST_UPDATE; $record->status = Record::STATUS_SUCCESS; $record->title = $message; $record->params = []; $this->logger->add( $record ); } /** * Gets translations and isBoolean properties for column names. * * @return array> */ private function getColumnSettings(): array { return [ 'name' => [ 'label' => __( 'name', 'packeta' ), 'isBoolean' => false, ], 'is_pickup_points' => [ 'label' => __( 'offers own pickup points', 'packeta' ), 'isBoolean' => true, ], 'has_carrier_direct_label' => [ 'label' => __( 'supports direct labels', 'packeta' ), 'isBoolean' => true, ], 'separate_house_number' => [ 'label' => __( 'requires separate house number', 'packeta' ), 'isBoolean' => true, ], 'customs_declarations' => [ 'label' => __( 'requires completion of customs declarations', 'packeta' ), 'isBoolean' => true, ], 'requires_email' => [ 'label' => __( 'requires email', 'packeta' ), 'isBoolean' => true, ], 'requires_phone' => [ 'label' => __( 'requires phone number', 'packeta' ), 'isBoolean' => true, ], 'requires_size' => [ 'label' => __( 'requires package size', 'packeta' ), 'isBoolean' => true, ], 'disallows_cod' => [ 'label' => __( 'disallows COD', 'packeta' ), 'isBoolean' => true, ], 'country' => [ 'label' => __( 'country', 'packeta' ), 'isBoolean' => false, ], 'currency' => [ 'label' => __( 'currency', 'packeta' ), 'isBoolean' => false, ], 'max_weight' => [ 'label' => __( 'maximum weight (kg)', 'packeta' ), 'isBoolean' => false, ], 'available' => [ 'label' => __( 'available', 'packeta' ), 'isBoolean' => true, ], ]; } }