Dodam pewne pola z typu zawartości do formularza niestandardowego za pomocą field_attach_form (). Kiedy formularz jest przesyłany, przetwarzam te pola, wywołując field_attach_form_validate () i field_attach_submit () z # callidvalidate i #submit.
W tym momencie chcę porównać obiekt po przesłaniu, przygotowany obiekt węzła z pierwotnym węzłem i zawracać sobie głowę węzłem_save (), jeśli którekolwiek z pól uległo zmianie. Dlatego zaczynam od załadowania oryginalnego węzła za pomocą entity_load_unchanged()
.
Niestety tablice pól w oryginalnym obiekcie węzła nie pasują do tablic pól w przygotowanym obiekcie węzła, który czeka na zapisanie, nawet jeśli nie wprowadzono żadnych zmian w polach, więc proste „$ old_field == $ new_field „porównanie jest niemożliwe. Na przykład proste pole tekstowe wygląda tak jak w oryginale:
$old_node->field_text['und'][0] = array(
'value' => 'Test',
'format' => NULL,
'safe_value' => 'Test',
);
Natomiast w przygotowanym węźle wygląda to tak.
$node->field_text['und'][0] = array(
'value' => 'Test',
);
Możesz po prostu porównać klucz „wartość”, ale potem natrafisz na pola złożone z innych elementów, które nie mają kluczy „wartość”. Na przykład spójrzmy na pole adresu, w którym nie ma klucza „wartość” i są klucze zarówno w starym, jak i przygotowanym węźle, które nie mają odpowiedników.
Stary węzeł
$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,
);
Przygotowany węzeł
$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',
);
W przypadku pustych pól istnieje jeszcze jedna rozbieżność.
Stary węzeł
$old_node->field_text = array();
Przygotowany węzeł
$node->field_text = array(
'und' => array(),
);
Czy mogę ogólnie porównać starą i nową wartość dowolnego pola w celu wykrycia, czy uległo ono zmianie?
Czy to tylko niemożliwość?
_field_invoke()
czymś związanym z przygotowaniem pełnej struktury pola z „przygotowanego” węzła, renderować oba pola i po prostu porównać te ciągi HTML. Po prostu pomysł.Odpowiedzi:
To w końcu powinno działać jako ogólne rozwiązanie. Dzięki Clive i morbiD za cały wkład.
Przekaż obie wersje węzła do następującej funkcji. To będzie:
Wyciągnij z bazy danych wszystkie edytowalne pola wykrytego typu treści i ich edytowalne kolumny (tj. Elementy, które mogą pojawić się w formularzu niestandardowym).
Zignoruj pola i kolumny, które są całkowicie puste w obu wersjach.
Traktuj pole, które ma inną liczbę wartości między dwiema wersjami, jako zmianę.
Iteruj przez każde pole, wartość i kolumnę i porównaj dwie wersje.
Porównaj elementy nieidentycznie (! =), Jeśli są numeryczne i identycznie (! ==), jeśli są czymś innym.
Natychmiast zwróć wartość PRAWDA przy pierwszej wykrytej zmianie (ponieważ wystarczy jedna zmiana, aby wiedzieć, że musimy ponownie zapisać węzeł).
Zwróć FAŁSZ, jeśli po porównaniu wszystkich wartości nie zostanie wykryta żadna zmiana.
Rekurencyjnie porównuj zbiory pól, ładując je i ich schemat oraz przekazując wyniki do siebie. POWINIEN nawet pozwolić na porównywanie zagnieżdżonych zbiorów pól. Kod NIE powinien mieć żadnej zależności od modułu Field Collection.
Daj mi znać, jeśli w tym kodzie są więcej błędów lub literówek.
Czasami chcesz wiedzieć, które pola się zmieniły. Aby to wiedzieć, możesz użyć tej wersji funkcji:
Czasami możesz chcieć to zrobić, aby zmiana niektórych pól węzła nie powodowała aktualizacji znacznika czasu „zmienionego” tego węzła. Można to zaimplementować w następujący sposób:
EDYCJA (7/30/2013) Zaostrzona obsługa zbierania pól. Dodano obsługę pól z wieloma wartościami.
EDIT (31.07.2015) Dodany wersja funkcji, która powraca których pola zostały zmienione, a sprawa przykładem użycia.
źródło
Oto inne, prostsze podejście, które pozwala uniknąć skomplikowanych porównań wartości po stronie serwera i działałoby w dowolnej formie:
Możesz użyć pluginu plugin jQuery, takiego jak https://github.com/codedance/jquery.AreYouSure
Chociaż inne, które pozwalają wysłuchać formularza zmienionego / brudnego, również by działały.
Dodaj detektor, aby ustawić wartość ukrytego elementu formularza:
Ustaw ukryty element formularza na domyślną wartość „zmieniono”, aby zapisać domyślnie dla użytkowników z wyłączonym javascript (~ 2%).
na przykład:
Następnie możesz sprawdzić wartość ukrytego elementu
if ($form_state['values']['hidden_indicator'] == 'changed') { /* node_save($node) */ }
w swoim formularzu zweryfikuj / prześlij programy obsługi.
źródło
Drupal.behaviors.formUpdated
być możeval()
może być z tym powiązana, chociaż wygląda na to, że uruchomi się bez rzeczywistej zmiany wartości (np. Obejmuje zdarzenie kliknięcia), podczas gdy dedykowane wtyczki lepiej wykrywają rzeczywiste zmienione wartości formularza.Nie jestem pewien, czy to jest idealne, ale dlaczego nie odwrócić tego, porównując formy zamiast obiektów węzłów ?
Nie jestem pewien, czy jesteś ściśle w formie węzła, ale w każdym razie możesz renderować formularz ze starym i nowym węzłem:
Porównaj swoje formularze ...
Mam nadzieję, że to dobry utwór ... daj mi znać.
źródło
Oto metoda wykorzystująca hook_node_presave ($ node). To tylko makieta, jeśli uważasz, że to pomaga, przetestuj ją i popraw do swoich potrzeb!
Przypuszczam, że dla każdej wartości pola instancje zdefiniowane w $ node muszą być zdefiniowane i równe w $ node_before. Nie dbam o pola wartości, które są w $ node_before i nie są w $ node, chyba pozostaną takie same.
źródło
To tylko część kodu, który skompletowałem. Wszelkie uznanie należy przekazać @eclecto za wykonanie całej pracy nóg. Jest to tylko (podobnie niesprawdzona) odmiana, która bezpośrednio pobiera obiekty węzła, nieco zmniejsza trafienia DB i zajmuje się negocjacjami językowymi.
źródło
Podana odpowiedź jest świetna i pomogła mi, ale muszę coś poprawić.
W
foreach()
pętli musiałem zmienić z$new_field
na$old_field
. Nie wiem, czy jest to nowa wersja Drupala, czy tylko mój kod (może wynikać z innego kodu gdzie indziej), ale nie mam do niego dostępu$new_field['entity']
.źródło
Dzięki za wpis, naprawdę zaoszczędziłem dużo czasu. Naprawiłem kilka ostrzeżeń i powiadomień, które wyświetlała ta funkcja:
źródło