Estou adicionando certos campos de um tipo de conteúdo a um formulário personalizado usando field_attach_form (). Quando o formulário é enviado, estou processando esses campos chamando field_attach_form_validate () e field_attach_submit () de #validate e #submit retornos de chamada.
Nesse ponto, desejo comparar o objeto de nó preparado pós-envio com o nó original e me preocupar apenas com node_save () se algum dos campos tiver sido alterado. Portanto, começo carregando o nó original usando entity_load_unchanged()
.
Infelizmente, as matrizes de campo no objeto de nó original não correspondem às matrizes de campo no objeto de nó preparado que está aguardando para ser salvo, mesmo que nenhuma alteração tenha sido feita nos campos, portanto, um simples "$ old_field == $ new_field "comparação é impossível. Por exemplo, um campo de texto simples aparece assim no original:
$old_node->field_text['und'][0] = array(
'value' => 'Test',
'format' => NULL,
'safe_value' => 'Test',
);
Enquanto no nó preparado aparece assim.
$node->field_text['und'][0] = array(
'value' => 'Test',
);
Você pode apenas comparar a chave 'value', mas depois encontra campos compostos de outros elementos que não possuem chaves 'value'. Por exemplo, vejamos um campo de endereço em que não há chave de 'valor' e há chaves nos nós antigos e preparados que não possuem contrapartes.
Nó antigo
$old_node->field_address['und'][0] = array(
'country' => 'GB',
'administrative_area' => 'Test',
'sub_administrative_area' => NULL,
'locality' => 'Test',
'dependent_locality' => NULL,
'postal_code' => 'Test',
'thoroughfare' => 'Test',
'premise' => 'Test',
'sub_premise' => NULL,
'organisation_name' => 'Test',
'name_line' => 'Test',
'first_name' => NULL,
'last_name' => NULL,
'data' => NULL,
);
Nó preparado
$node->field_address['und'][0] = array(
'element_key' => 'node|page|field_address|und|0',
'thoroughfare' => 'Test',
'premise' => 'Test',
'locality' => 'Test',
'administrative_area' => 'Test',
'postal_code' => 'Test',
'country' => 'GB',
'organisation_name' => 'Test',
'name_line' => 'Test',
);
Para campos vazios, há outra discrepância.
Nó antigo
$old_node->field_text = array();
Nó preparado
$node->field_text = array(
'und' => array(),
);
Posso comparar genericamente o valor antigo e o novo de qualquer campo para detectar se ele foi alterado ou não?
Isso é apenas uma impossibilidade?
_field_invoke()
ou algo relacionado para preparar a estrutura de campo completa do nó "preparado", renderizar os dois campos e simplesmente comparar essas seqüências de caracteres HTML. Apenas uma ideia.Respostas:
Por fim, isso deve funcionar como uma solução genérica. Obrigado a Clive e morbiD por toda a entrada.
Passe as duas versões do nó para a seguinte função. Será:
Puxe todos os campos editáveis do tipo de conteúdo detectado e suas colunas editáveis (ou seja, itens que podem aparecer no formulário personalizado) do banco de dados em uma única consulta.
Ignore campos e colunas completamente vazios nas duas versões.
Trate um campo que tenha um número diferente de valores entre as duas versões como uma alteração.
Repita todos os campos, valores e colunas e compare as duas versões.
Compare itens de forma não idêntica (! =) Se eles forem numéricos e idênticos (! ==) se forem outros itens.
Retorne imediatamente TRUE na primeira alteração que detectar (já que uma alteração é suficiente para saber que precisamos salvar novamente o nó).
Retorne FALSE se nenhuma alteração for detectada depois que todos os valores forem comparados.
Compare recursivamente as coleções de campos carregando-as e seu esquema e passando os resultados para si. Isso DEVE mesmo permitir comparar coleções de campos aninhados. O código NÃO deve ter nenhuma dependência no módulo Field Collection.
Informe-me se houver mais erros ou erros de digitação neste código.
Às vezes você está interessado em saber quais campos foram alterados. Para saber isso, você pode usar esta versão da função:
Às vezes, convém fazer com que a alteração de certos campos de um nó não faça com que o carimbo de data e hora "alterado" desse nó seja atualizado. Isso pode ser implementado da seguinte maneira:
EDIT (30/7/2013) Suporte reforçado à coleta de campo. Adicionado suporte para campos com vários valores.
EDIT (31/07/2015) Adicionada versão da função que retorna quais campos foram alterados e exemplo de caso de uso.
fonte
Aqui está outra abordagem mais simples que evita comparações complexas de valores no servidor e funcionaria de qualquer forma:
Você pode usar um plugin de formulário sujo do jQuery, como https://github.com/codedance/jquery.AreYouSure
Embora outros que permitem ouvir o status alterado / sujo do formulário também funcionem.
Adicione um ouvinte para definir o valor de um elemento oculto do formulário:
Defina o elemento oculto do formulário como um valor padrão de 'alterado' para salvar por padrão para os usuários com o javascript desativado (~ 2%).
por exemplo:
Você pode então verificar o valor do elemento oculto
if ($form_state['values']['hidden_indicator'] == 'changed') { /* node_save($node) */ }
no seu formulário, valide / envie manipuladores.
fonte
Drupal.behaviors.formUpdated
talvezval()
possa estar ligada a isso, embora pareça que será acionada sem que o valor seja realmente alterado (por exemplo, inclui evento click), enquanto os plug-ins dedicados são melhores para detectar valores reais de formulário alterados.Não tenho certeza de que seja perfeito, mas por que não fazer o contrário, comparando os formulários em vez dos objetos do nó ?
Não tenho certeza se você está estritamente em um formulário de nó, mas mesmo assim você pode renderizar o formulário com seu nó antigo e seu novo nó:
Compare seus formulários ...
Espero que seja uma boa pista ... me avise.
fonte
Aqui está um método usando hook_node_presave ($ node). É apenas uma maquete, se você acha que ajuda, teste e melhore para as suas necessidades!
Suponho que, para cada valor de campo, as instâncias definidas em $ node devem ser definidas e iguais em $ node_before. Eu não ligo para os campos do valor do campo que estão em $ node_before e não estão em $ node, suponho que eles permaneçam iguais.
fonte
Este é apenas um código que juntei. Todo o crédito deve ir para @eclecto por fazer todo o trabalho das pernas. Essa é apenas uma variação (igualmente não testada) que leva os objetos do nó diretamente, reduz um pouco as ocorrências do banco de dados e cuida da negociação da linguagem.
fonte
A resposta fornecida é ótima e me ajudou, mas há algo que precisei corrigir.
No
foreach()
loop, eu tive que mudar de$new_field
para$old_field
. Não sei se esta é uma nova versão do Drupal ou apenas meu código (pode ser devido a outro código em outro lugar), mas não tenho acesso$new_field['entity']
.fonte
Obrigado pelo post, realmente me salvou muito tempo. Corrigi vários avisos e avisos de que a função estava sendo exibida:
fonte