Estoy agregando ciertos campos de un tipo de contenido a un formulario personalizado usando field_attach_form (). Cuando se envía el formulario, estoy procesando esos campos llamando a field_attach_form_validate () y field_attach_submit () desde #validate y #submit callbacks.
En ese momento, quiero comparar el objeto de nodo preparado posterior al envío con el nodo original y solo molestarme en node_save () si alguno de los campos ha cambiado. Por lo tanto, empiezo cargando el nodo original usando entity_load_unchanged()
.
Desafortunadamente, las matrices de campo en el objeto de nodo original no coinciden con las matrices de campo en el objeto de nodo preparado que está esperando ser guardado, incluso si no se han realizado cambios en los campos, por lo que un simple "$ old_field == $ new_field "La comparación es imposible. Por ejemplo, un campo de texto simple aparece así en el original:
$old_node->field_text['und'][0] = array(
'value' => 'Test',
'format' => NULL,
'safe_value' => 'Test',
);
Mientras que en el nodo preparado aparece así.
$node->field_text['und'][0] = array(
'value' => 'Test',
);
Puede pensar en comparar la clave de "valor", pero luego se encuentra con campos formados por otros elementos que no tienen claves de "valor". Por ejemplo, veamos un campo de dirección donde no hay una clave de 'valor' y hay claves en los nodos antiguos y preparados que no tienen contrapartes.
Nodo antiguo
$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,
);
Nodo 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 los campos vacíos, hay otra discrepancia.
Nodo antiguo
$old_node->field_text = array();
Nodo preparado
$node->field_text = array(
'und' => array(),
);
¿Puedo comparar genéricamente el valor antiguo y el nuevo de cualquier campo para detectar si ha cambiado o no?
¿Es esto solo una imposibilidad?
_field_invoke()
o algo relacionado para preparar la estructura de campo completa del nodo "preparado", renderizar ambos campos y simplemente comparar estas cadenas HTML. Solo una idea.Respuestas:
Esto, por fin, debería funcionar como una solución genérica. Gracias a Clive y morbiD por todos los aportes.
Pase ambas versiones del nodo a la siguiente función. Va a:
Extraiga todos los campos editables del tipo de contenido detectado y sus columnas editables (es decir, elementos que podrían aparecer en el formulario personalizado) de la base de datos en una sola consulta.
Ignora los campos y columnas que están completamente vacíos en ambas versiones.
Trate un campo que tenga un número diferente de valores entre las dos versiones como un cambio.
Itere a través de cada campo, valor y columna y compare las dos versiones.
Compare elementos no idénticamente (! =) Si son numéricos e idénticamente (! ==) si son algo más.
Inmediatamente devuelve VERDADERO en el primer cambio que detecta (ya que un cambio es suficiente para saber que necesitamos volver a guardar el nodo).
Devuelve FALSE si no se detecta ningún cambio después de comparar todos los valores.
Compare recursivamente las colecciones de campo al cargarlas y su esquema y pasar los resultados a sí mismo. Esto DEBE incluso permitirle comparar colecciones de campos anidados. El código NO debería tener ninguna dependencia en el módulo Field Collection.
Avíseme si hay más errores o errores tipográficos en este código.
A veces le interesa saber qué campos han cambiado. Para saber eso, puede usar esta versión de la función:
A veces es posible que desee hacer que cambiar ciertos campos de un nodo no haga que se actualice la marca de tiempo "modificada" de ese nodo. Esto podría implementarse de la siguiente manera:
EDITAR ( 30/07/2013 ) Se mejoró el soporte de recolección de campo. Se agregó soporte para campos con múltiples valores.
EDITAR (31/07/2015) Se agregó una versión de la función que devuelve qué campos han cambiado, y un caso de uso de ejemplo.
fuente
Aquí hay otro enfoque más simple que evita las complejas comparaciones de valores del lado del servidor y funcionaría con cualquier forma:
Puede usar un complemento de formulario sucio jQuery como https://github.com/codedance/jquery.AreYouSure
Aunque otros que le permiten escuchar la forma cambiada / estado sucio también funcionarían.
Agregue un oyente para establecer el valor de un elemento de formulario oculto:
Establezca el elemento de formulario oculto en un valor predeterminado de 'cambiado' para guardarlo de forma predeterminada para aquellos usuarios con JavaScript deshabilitado (~ 2%).
p.ej:
Luego puede verificar el valor del elemento oculto
if ($form_state['values']['hidden_indicator'] == 'changed') { /* node_save($node) */ }
en su formulario validar / enviar controladores.
fuente
Drupal.behaviors.formUpdated
tal vezval()
podría vincularse con eso, aunque parece que se disparará sin que el valor realmente cambie (por ejemplo, incluye un evento de clic), mientras que los complementos dedicados son mejores para detectar los valores reales de los formularios cambiados.No estoy seguro de que sea perfecto, pero ¿por qué no tomarlo al revés, comparando las formas en lugar de los objetos de nodo ?
No estoy seguro de que lo esté si está estrictamente en forma de nodo, pero de todos modos puede representar el formulario con su nodo anterior y su nuevo nodo:
Compara tus formularios ...
Espero que sea una buena pista ... hágamelo saber.
fuente
Aquí hay un método que usa hook_node_presave ($ node). Es solo una maqueta, si crees que ayuda, pruébalo y mejóralo según tus necesidades.
Supongo que, para cada valor de campo, las instancias que se definen en $ node deben definirse y ser iguales en $ node_before. No me interesan los campos de valor de campo que están en $ node_before y no están en $ node, supongo que permanecen igual.
fuente
Este es solo un código que improvisé. Todo el crédito debe ir a @eclecto para hacer todo el trabajo de piernas. Esta es solo una variación (igualmente no probada) que toma los objetos del nodo directamente, reduce un poco los golpes de la base de datos y se encarga de la negociación del lenguaje.
fuente
La respuesta proporcionada es excelente y me ayudó, pero hay algo que tuve que corregir.
En el
foreach()
bucle, tuve que cambiar de$new_field
a$old_field
. No sé si esta es una nueva versión de Drupal o solo mi código (podría deberse a otro código en otro lugar), pero no tengo acceso$new_field['entity']
.fuente
Gracias por la publicación, realmente me ahorró mucho tiempo. Arreglé un montón de advertencias y avisos que la función estaba generando:
fuente