Ir para conteúdo
kania

Função genérica para atualizar chave em JSON duplicada usando o PHP

Recommended Posts

Tenho a seguinte situação.
Recebo vários JSONs podendo ou não ser multidimensional, preciso atualizar uma determinada chave deste JSON, o problema é que algumas chaves podem ser duplicadas, bem como seus respectivos valores. Como estou tentando criar uma função genérica para navegar em qualquer JSON e modificar a chave em si, com estas duplicidades de chaves, estou tendo dificuldades em dizer ao código qual é chave que devo alterar.

 

Arquivo JSON Exemplo

 

{
      "CREDITOR": {
        "TAX": {
          "TAC": 0.7
        },
        "ENABLE": "true",
        "PRODUCTION": {
          "email": "email@dominio.com",
          "senha": "12457895",
          "BASE_URL": "https://domino.com"
        },
        "HOMOLOGATION": {
          "email": "email@dominio.com",
          "senha": "12457895",
          "BASE_URL": "https://domino.com"
        },
        "TARGET_VALUES": 5000000
      }
    }

 

Converto o JSON para array

 

$json = json_decode($json_string, true);


Função que criei até aqui

 

   

/**
     * Encontra a chave correspondente dentro do JSON
     *
     * @param array $jsonArray - JSON a ser verificado
     * @param string $keyFather - chave de entrada
     * @param string $keyUpdate - chave que modificar
     * @param string $valueUpdate - novo valor da chave
     * @return string
     *
     */
    public static function searchKeyJson(array $jsonArray, string $keyFather, string $keyUpdate = null, $valueUpdate = null)
    {
        foreach ($jsonArray as $key => $value) {
            if ($key == $keyFather && $keyUpdate == null) {
                $jsonArray[$key] =  $valueUpdate;
                return $jsonArray;
            }
            if ($key == $keyFather && $keyUpdate != null) {
                $jsonArray[$keyFather][$keyUpdate] = $valueUpdate;
                return $jsonArray;
            }
            if (is_array($value)) {
                if (($result = self::searchKeyJson($value, $keyFather, $keyUpdate, $valueUpdate)) !== false) {
                    if ($keyUpdate == null) {
                        return $result;
                    } else {
                        return $result;
                    }
                }
            }
        }
        return false;
    }


Até modifico o valor, mais no final para salvar, ele não monta o JSON como original, ele caba ignorando a chave inicial no JSON de exmeplo "CREDITOR": {}

 

Retorno da função (notem que esta forma do padrão da original)

 

=> [
         "TAX" => [
           "TAC" => 0.7,
         ],
         "ENABLE" => "true",
         "PRODUCTION" => [
           "email" => "teste",
           "senha" => "12457895",
           "BASE_URL" => "https://domino.com",
         ],
         "HOMOLOGATION" => [
           "email" => "email@dominio.com",
           "senha" => "12457895",
           "BASE_URL" => "https://domino.com",
         ],
         "TARGET_VALUES" => 5000000,
       ]

 

Se eu percorrer o array e tentar modificar a chave em questão, ele muda todas as as chaves que tiverem no JSON porque tem duplicidade.

Como posso resolver isto com uma função global que sirva para qualquer padrão de JSON que eu tiver?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá amigo, tudo bem?

 

Vou tentar ajudar você, mas não entendi alguns pontos:

 

No seu exemplo houve alteração no e-mail apenas de PRODUCTION, mas não em HOMOLOGATION. É esse o comportamento esperado?

Desculpe, mas não entendi muito bem seu objetivo: seria varrer todo o array pela chave 'email' (por exemplo) e substituir todas as ocorrências ou apenas a primeira encontrada (ou ainda outra coisa)?

 

Apenas para ilustrar melhor, como você fez a chamada da sua função para que ela gerasse o resultado que você colou no seu post? Digo, quais parâmetros você passou pra dentro dela?

 

Apenas uma observação:

                    if ($keyUpdate == null) {
                        return $result;
                    } else {
                        return $result;
                    }

É o mesmo que simplesmente:

return $result;

(Provavelmente foi apenas falta de atenção, mas não custa mencionar...)

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá noite Mateus, obrigado por responder.

 

A citação do erro estava correta, dupliquei o retorno do return $result (hehehe)

 

Vamos lá, vou detalhar melhor o que estou tentando fazer.

 

A aplicação Backend esta em uma API em Laravel 9, com banco de dados Mysql e o Front em Vue.js 3.

No banco de dados, existem várias tabelas com colunas do tipo jsonb.

 

O que preciso fazer é manipular este campo jsonb no PHP, para isto pensei no seguinte.

 

- Ter uma função genérica já que estes campos jsonb não tem um json igual em cada;

- A função precisa poder percorrer o json (já convertido em array para trabalhar no php) e encontrara chave especifica para poder modifica-la

 

No exemplo acima, eu cheguei a conseguir modificar a chave email da chave pai PRODUCTION efetuando chamada

searchKeyJson($jsonArray, , , "VALOR MODIFICADO")

Sendo que o parâmetro $keyUpdate é facultativo, já que posso precisar alterar um chave que esta em primeiro nível no json.

 

Funcionou a modificação para este exemplo de json/array, mais não funciona em todos os casos, pois como monto as chaves na $jsonArray[$keyFather][$keyUpdate] = $valueUpdate; ele se perde quando não encontra as referencias conforme a profundidade do array.


A ideia inicial era que eu pudesse passar apenas a chave em si que quero manipular (exemplo: email), mais não funcionaria porque como no json do exemplo, eu tenho duas chaves e-mail, uma no PRODUCTION e outra no HOMOLOGATION, logo ou vai modificar todas as chaves, ou vai modificar apenas a primeira que encontrar.

 

Ai estou pesquisando para ver como posso resolver este pepinão (hehehe), tendo em vista que estes campos jsonb do banco tem configuração do sistema importantes que precisam ser manipulados em um front, não da para ficar ajustando eles não mão sempre que precisar.

 

Exemplo da Função completa ate o momento, com 2 json e duas chamadas para testar, ambas funcionam, porem como mencionei, dependendo da profundidade da chave não consegue fazer a atualização.

 

<?php

// $json_string = '{"COMISSAO":{"EXTRA":0.5,"AGENTE":0.5,"ESCRITORIO":1},"PROPOSTA":{"API":[4,5,6,7,8,9,10,11],"JUROS":[1,2],"PRAZO":[1,2,3,4],"MAX_JUROS":2}}';

$json_string = '{"CREDITOR":{"TAX":{"TAC":0.7},"ENABLE":"true","PRODUCTION":{"email":"email@dominio.com","senha":"12457895","BASE_URL":"https://domino.com"},"HOMOLOGATION":{"email":"email@dominio.com","senha":"12457895","BASE_URL":"https://domino.com"},"TARGET_VALUES":5000000}}';

$json = json_decode($json_string, true);

function searchKeyJson(array &$jsonArray, string $keyFather, string $keyUpdate = null, $valueUpdate = null)
{
    foreach ($jsonArray as $key => $value) {
        if ($key == $keyFather && $keyUpdate == null) {
            $jsonArray[$key] =  $valueUpdate;
            return $jsonArray;
        }
        if ($key == $keyFather && $keyUpdate != null) {
            $jsonArray[$keyFather][$keyUpdate] =  $valueUpdate;
            return $jsonArray;
        }
        if (is_array($value)) {
            if (($result = searchKeyJson($jsonArray[$key], $keyFather, $keyUpdate, $valueUpdate)) !== false) {
                return $jsonArray;
            }
        }
    }
    return false;
}

// Chamada da função sem passar a chave de resultado
$returnWithoutKey = searchKeyJson($json, "PRODUCTION", "email", "TESTE UPDATE");
// $returnWithoutKey = searchKeyJson($json, "PROPOSTA", "API", [1,2,5,8]);

// Exemplo de valor a ser modificado dentro do JSON
echo "RETORNO".PHP_EOL;
echo "========================".PHP_EOL;
print_r($returnWithoutKey);


?>

 

Editado por kania
Adição de conteúdo necessário

Compartilhar este post


Link para o post
Compartilhar em outros sites

Certo, acho que agora entendi melhor, mas infelizmente acredito que essa necessidade seja oriunda de um modelo de dados que poderia ser otimizado no futuro.

 

Dito isso, elaborei uma pequena solução que espero que lhe ajude. Veja:

 

1 - Primeiro pegamos o caminho até o subarray desejado:

function getSubArrayPath( $array, $wantedKey ) {
    $mode = RecursiveIteratorIterator::SELF_FIRST;
    $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ), $mode );
    
    foreach ( $iterator as $key => $value )
        if ( $wantedKey === $key ) {
            $keys[] = $key;
            for ( $i = $iterator->getDepth()-1; $i >= 0 ; --$i ) {
                $keys[] = $iterator->getSubIterator( $i )->key();
            }
            
            return $keys;
        }
    
    return [];
}

Essa função, que faz uso das classes nativas RecursiveIteratorIterator e RecursiveArrayIterator tem a simples tarefa de receber um array (como o que o json_decode nos entregou) e dizer qual o caminho deve ser feito para chegarmos até a chave desejada (no caso PRODUCTION).

Ela deve se comportar da seguinte maneira:

$path = getSubArrayPath( $array, $key );

Retorna isso:
Array
(
    [0] => PRODUCTION
    [1] => CREDITOR
)

 

 

2 - Agora que temos o caminho dinâmico que estava enraizado na nossa árvore de dados, podemos encontrar a referência do subarray que queremos manipular. Para isso criamos a seguinte função:

function &getSubArrayRef( &$array, $key ) {
    $path = getSubArrayPath( $array, $key );
    $ref =& $array;
    
    while ( $node = array_pop( $path ) )
        $ref =& $ref[ $node ];
    
    return $ref;
}

Essa função é simples: enviamos os dados e dizemos a chave que ela deve encontrar recursivamente. Ela vai internamente chamar a "getSubArrayPath()" para determinar o caminho e então retornar a referência do subarray que desejamos.

 

 

Se reunirmos tudo teríamos esse código:

<?php
$json = '{"CREDITOR":{"TAX":{"TAC":0.7},"ENABLE":"true","PRODUCTION":{"email":"email@dominio.com","senha":"12457895","BASE_URL":"https://domino.com"},"HOMOLOGATION":{"email":"email@dominio.com","senha":"12457895","BASE_URL":"https://domino.com"},"TARGET_VALUES":5000000}}';
$data = json_decode( $json, true );

function getSubArrayPath( $array, $wantedKey ) {
    $mode = RecursiveIteratorIterator::SELF_FIRST;
    $iterator = new RecursiveIteratorIterator( new RecursiveArrayIterator( $array ), $mode );
    
    foreach ( $iterator as $key => $value )
        if ( $wantedKey === $key ) {
            $keys[] = $key;
            for ( $i = $iterator->getDepth()-1; $i >= 0 ; --$i ) {
                $keys[] = $iterator->getSubIterator( $i )->key();
            }
            
            return $keys;
        }
    
    return [];
}

function &getSubArrayRef( &$array, $key ) {
    $path = getSubArrayPath( $array, $key );
    $ref =& $array;
    
    while ( $node = array_pop( $path ) )
        $ref =& $ref[ $node ];
    
    return $ref;
}

$ref = &getSubArrayRef( $data, 'PRODUCTION' );
$ref[ 'email' ] = 'novo e-mail';
$ref[ 'senha' ] = 'nova senha';
$ref[ 'atributo_novo' ] = 'AAAAAAAAAA';

print_r( $data );

 

E o retorno disso seria esse:

Array
(
    [CREDITOR] => Array
        (
            [TAX] => Array
                (
                    [TAC] => 0.7
                )

            [ENABLE] => true
            [PRODUCTION] => Array
                (
                    [email] => novo e-mail
                    [senha] => nova senha
                    [BASE_URL] => https://domino.com
                    [atributo_novo] => AAAAAAAAAA
                )

            [HOMOLOGATION] => Array
                (
                    [email] => email@dominio.com
                    [senha] => 12457895
                    [BASE_URL] => https://domino.com
                )

            [TARGET_VALUES] => 5000000
        )

)

 

Achei melhor dividir as responsabilidades e trazer as manipulações para fora da função, mas se você preferir fazer tudo numa chamada só, basta modificar o valor de $ref após o while da nossa função "getSubArrayRef()". Dessa forma você poderia enviar o valor desejado e já sair com o array modificado de dentro da função (como você estava fazendo antes).

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olá Matheus, tudo bom?

Bacana sua ideia, funciona bem quando passamos bem definida as referencias, mais acaba no mesmo problema que eu ainda não consegui resolver, a função não fica genérica para tratar qualquer json passando um parent como referencia para alterar um children.

 

Por exemplo, vamos dizer que o mesmo json foi modificado em algum momento e agora preciso modificar a chave TAC, eu precisaria passar como paramento principal a chave TESTE ou CREDITOR, e nas referencias todas as chaves em cadeia para modificar o item correto, isto? Estou tentando facilitar a vida do Front em não ter que passar todas estas referencias para a API, mais não sei se tem como.

 

$ref = &getSubArrayRef( $data, 'TESTE' );
$ref[ 'TAX' ]['TAC'] = 'nova tac';

 

JSON MODIFICADO COM A CHAVE [TESTE]

{
  "TESTE": {
    "TAX": {
      "TAC": 0.7
    }
  },
  "CREDITOR": {
    "TAX": {
      "TAC": 0.7
    },
    "ENABLE": "true",
    "PRODUCTION": {
      "email": "TESTE 5 tregbg",
      "senha": "aL1VM8Z6qYsc",
      "BASE_URL": "https://api.bancarizaao.fiduciascm.digital/"
    },
    "HOMOLOGATION": {
      "email": "integracao@selectinvestimentos.com",
      "senha": "1234",
      "BASE_URL": "https://api.americanohoml.fiduciascm.digital/"
    },
    "TARGET_VALUES": 5000000
  }
}

 

Outra opção que estou vendo se seria viável é usar spred operator, ai o front continuaria tendo que passar as referencia de toda  a arvore que vai ter que ser modificada, contudo só passaria um lista de string, mais ainda não testei para ver se ficaria viável.

 

function teste(...$param){
    foreach($param as $key => $val){
        echo '["'.$val.'"]';
    }
}

return teste("CREDITOR", "PRODUCTION", "email");

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisar ser um membro para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar Agora

  • Conteúdo Similar

    • Por landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, ativo. Nela tem cadastrado vários itens. No campo ativo eu coloco a letra "S" para informar que este item está ativo no sistema. Por exemplo: 1, casa, S 2, mesa, S 3, cama, S 4, moto S 5, rádio O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "produtos" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, mesa, moto 2, mesa, casa, cama 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela produtos da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "mesa". Preciso fazer com que o php me liste todos os registros da tabela "produtos" que contenham a palavra "mesa". Até aqui tudo bem eu consigo listar. Estou fazendo assim: <?php $item = "mesa" $sql = mysqli_query($conn, "SELECT * FROM produtos WHERE item1 LIKE '$item' OR item2 LIKE '$item' OR item3 LIKE '$item' LIMIT 10"); while($aux = mysqli_fetch_assoc($sql)) { $id = $aux["id"]; $item1 = $aux["item1"]; $item2 = $aux["item2"]; $item3 = $aux["item3"]; echo $id . " - " . $item1 . ", " . $item2 . ", " $item3 . "<br>"; } ?> O problema é que está listando todos os registros que contém o item mesa. Eu preciso que o php verifique os demais item e me liste somente os registro em que todos os registros estejam ativos no sistema. No exemplo acima ele não deveria listar o registro 3. pois nesse registro contém o item "radio" e este item não está ativo no sistema. Ou seja, o registro "radio" na tabela itens não possui um "S" na coluna "ativo". Alguém sabe como resolver isso?
    • Por ILR master
      Fala galera.
      Espero que todos estejam bem.
      Seguinte: Tenho um arquivo xml onde alguns campos estão com : (dois pontos), como o exemplo abaixo:
       
      <item>
      <title>
      d sa dsad sad sadasdas
      </title>
      <link>
      dsadas dsa sad asd as dsada
      </link>
      <pubDate>sadasdasdsa as</pubDate>
      <dc:creator>
      d sad sad sa ad as das
      </dc:creator>
      </item>
       
      Meu código:
       
      $link = "noticias.xml"; 
      $xml = simplexml_load_file($link); 
      foreach($xml -> channel as $ite) {     
           $titulo = $ite -> item->title;
           $urltitulo = $ite -> item->link;
           print $urltitulo = $ite -> item->dc:creator;
      } //fim do foreach
      ?>
       
      Esse campo dc:creator eu não consigo ler. Como faço?
       
      Agradeço quem puder me ajudar.
       
      Abs
       
       
    • Por First
      Olá a todos!
       
      Eu estou criando um sistema do zero mas estou encontnrando algumas dificuldades e não estou sabendo resolver, então vim recorrer ajuda de vocês.
      Aqui está todo o meu código: https://github.com/PauloJagata/aprendizado/
       
      Eu fiz um sistema de rotas mas só mostra o conteúdo da '/' não sei porque, quando eu tento acessar o register nada muda.
      E eu também quero que se não estiver liberado na rota mostra o erro de 404, mas quando eu tento acessar um link inválido, nada acontece.
      Alguém pode me ajudar com isso? E se tiver algumas sugestão para melhoria do código também estou aceitando.
       
       
      Desde já, obrigado.
    • Por landerbadi
      Olá pessoal, boa tarde
       
      Tenho uma tabela chamada "produtos" com os seguintes campos (id, produto) e outra tabela chamada "itens" com os seguintes campos (id, prod_01, prod_02, prod_03, prod_04).
       
      Na tabela produtos eu tenho cadastrado os seguintes produtos: laranja, maçã, uva, goiaba, arroz, feijão, macarrão, etc.
       
      Na tabela itens eu tenho cadastrado os itens da seguinte maneira:
       
      1, laranja, uva, arroz, feijão;
      2, maçã, macarrão, goiaba, uva;
      3, arroz, feijão, maçã, azeite
       
      Meu problema é o seguinte: 
      Eu escolho um produto da tabela "produtos", por exemplo "uva".  Preciso fazer uma consulta na tabela "itens" para ser listado todos os registros que contenham o produto "uva" e que todos os demais produtos estejam cadastrados na tabela "produtos".
       
      No exemplo acima seria listado apenas dois registros, pois o terceiro registro não contém o produto "uva". 
       
      Alguém pode me ajudar? Pois estou quebrando a cabeça a vários dias e não consigo achar uma solução.
    • Por landerbadi
      Boa tarde pessoal. Estou tentado fazer uma consulta no banco de dados porém estou tendo dificuldades. Tenho uma tabela chamada "itens" com os seguintes campos: id, item, plural, ativo. Nela tem cadastrado vários itens e seu respectivo plural. No campo ativo eu coloco a letra "S" para informar que esta palavra está ativa no sistema. Por exemplo: 1, casa, casas, S 2, mesa, mesas, S 3, cama, camas, S 4, moto, motos, S 5, rádio, rádios O quinto registro "radio" não está ativo no sistema pois não tem um "S" no campo ativo. E outra tabela chamada "variações" com os seguintes campos (id, item1, item2, item3) com os seguintes registros: 1, casa, camas, moto 2, mesas, casas, radio 3, rádio, cama, mesa Eu preciso fazer uma busca na tabela variações da seguinte maneira: Eu escolho um registro na tabela "itens", por exemplo "casa". Preciso fazer com que o php me liste todos os registros da tabela "variações" que contenham a palavra "casa". Porém se tiver algum registro com a palavra "casas" também tem que ser listado. Neste caso ele irá encontrar dois registros. Agora eu preciso que o php verifique os demais itens e faça a listagem apenas dos item que estão ativos (que contenham um "S" no campo ativo. Neste caso ele irá encontrar apenas um registro, pois o segundo registro contém a palavra "rádio". E "rádio" não está ativo na tabela itens. Como faço isso?
×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.