Saya menambahkan bidang tertentu dari jenis konten ke formulir kustom menggunakan field_attach_form (). Ketika formulir dikirimkan saya sedang memproses bidang-bidang tersebut dengan memanggil field_attach_form_validate () dan field_attach_submit () dari #validate dan #submit callbacks.
Pada titik itu, saya ingin membandingkan post-submit, objek node yang disiapkan dengan node asli dan hanya repot-repot untuk node_save () jika ada bidang yang berubah. Oleh karena itu, saya mulai dengan memuat simpul asli menggunakan entity_load_unchanged()
.
Sayangnya, array bidang dalam objek simpul asli tidak cocok dengan array bidang dalam objek simpul yang disiapkan yang sedang menunggu untuk disimpan, bahkan jika tidak ada perubahan yang dilakukan pada bidang, jadi sederhana "$ old_field == $ new_field "perbandingan itu tidak mungkin. Misalnya, bidang teks sederhana muncul seperti ini di aslinya:
$old_node->field_text['und'][0] = array(
'value' => 'Test',
'format' => NULL,
'safe_value' => 'Test',
);
Sedangkan pada node yang disiapkan tampak seperti ini.
$node->field_text['und'][0] = array(
'value' => 'Test',
);
Anda mungkin berpikir untuk hanya membandingkan kunci 'nilai', tetapi kemudian Anda lari ke bidang yang terdiri dari elemen lain yang tidak memiliki kunci 'nilai'. Sebagai contoh, mari kita lihat bidang alamat di mana tidak ada kunci 'nilai' dan ada kunci di kedua node lama dan siap yang tidak memiliki rekan.
Node lama
$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,
);
Node yang disiapkan
$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',
);
Untuk bidang kosong, masih ada perbedaan.
Node lama
$old_node->field_text = array();
Node yang disiapkan
$node->field_text = array(
'und' => array(),
);
Dapatkah saya secara umum membandingkan nilai lama dan baru bidang apa saja untuk mendeteksi apakah sudah berubah atau tidak?
Apakah ini hanya ketidakmungkinan?
_field_invoke()
atau sesuatu yang terkait untuk menyiapkan struktur bidang penuh dari "disiapkan" simpul, membuat kedua bidang dan hanya membandingkan string HTML ini. Hanya sebuah ide.Jawaban:
Ini, pada akhirnya, harus berfungsi sebagai solusi umum. Terima kasih kepada Clive dan morbiD untuk semua masukan.
Lewati kedua versi node ke fungsi berikut. Itu akan:
Tarik semua bidang yang dapat diedit tipe konten yang terdeteksi dan kolom yang dapat diedit (yaitu item yang mungkin muncul pada formulir khusus) dari database dalam satu permintaan.
Abaikan bidang dan kolom yang benar-benar kosong di kedua versi.
Perlakukan bidang yang memiliki jumlah nilai yang berbeda antara dua versi sebagai perubahan.
Ulangi setiap bidang, nilai, dan kolom dan bandingkan dua versi.
Bandingkan item secara non-identik (! =) Jika item berupa angka dan identik (! ==) jika item lain.
Segera kembalikan TRUE pada perubahan pertama yang dideteksinya (karena satu perubahan sudah cukup untuk mengetahui kita perlu menyimpan kembali node).
Kembalikan FALSE jika tidak ada perubahan yang terdeteksi setelah semua nilai dibandingkan.
Secara rekursif membandingkan koleksi lapangan dengan memuatnya dan skema mereka dan meneruskan hasilnya sendiri. HARUS ini bahkan memungkinkan untuk membandingkan koleksi bidang bersarang. Kode harus TIDAK memiliki ketergantungan pada modul Koleksi Lapangan.
Beri tahu saya jika ada bug atau kesalahan ketik lagi dalam kode ini.
Terkadang Anda tertarik mengetahui bidang mana yang telah berubah. Untuk mengetahuinya, Anda dapat menggunakan versi fungsi ini:
Kadang-kadang Anda mungkin ingin membuatnya sehingga mengubah bidang tertentu dari suatu simpul tidak menyebabkan timestamp "perubahan" simpul tersebut diperbarui. Ini dapat diimplementasikan seperti berikut:
EDIT (7/30/2013) Dukungan pengumpulan lapangan diperketat . Dukungan tambahan untuk bidang dengan beberapa nilai.
EDIT (7/31/2015) Menambahkan versi fungsi yang mengembalikan bidang mana yang telah berubah, dan contoh use case.
sumber
Berikut ini pendekatan lain, lebih sederhana, yang menghindari perbandingan nilai sisi server yang kompleks, dan akan bekerja dengan segala bentuk:
Anda bisa menggunakan plugin form jQuery dirty seperti https://github.com/codedance/jquery.AreYouSure
Meskipun orang lain yang membiarkan Anda mendengarkan formulir berubah / status kotor juga akan berfungsi.
Tambahkan pendengar untuk menetapkan nilai elemen formulir tersembunyi:
Setel elemen formulir tersembunyi ke nilai default 'diubah' untuk menyimpan secara default untuk para pengguna dengan javascript dinonaktifkan (~ 2%).
misalnya:
Anda kemudian dapat memeriksa nilai elemen tersembunyi
if ($form_state['values']['hidden_indicator'] == 'changed') { /* node_save($node) */ }
dalam formulir Anda validasi / kirim penangan.
sumber
Drupal.behaviors.formUpdated
mungkinval()
dapat dikaitkan dengan itu meskipun sepertinya itu akan memicu tanpa nilai yang benar-benar berubah (mis. Termasuk acara klik), sedangkan plugin khusus lebih baik dalam mendeteksi nilai formulir yang sebenarnya diubah.Saya tidak yakin itu sempurna, tetapi mengapa tidak mengambil sebaliknya, dengan membandingkan bentuk bukan objek simpul ?
Saya tidak yakin Anda jika Anda benar-benar dalam bentuk simpul, tetapi Anda bisa membuat formulir dengan simpul lama dan simpul baru Anda:
Bandingkan formulir Anda ...
Saya harap ini lagu yang bagus ... beri tahu saya.
sumber
Berikut adalah metode menggunakan hook_node_presave ($ node). Itu hanya mockup, jika menurut Anda itu membantu, ujilah dan tingkatkan sesuai kebutuhan Anda!
Saya mengira bahwa, untuk setiap nilai bidang, instance yang didefinisikan dalam $ node harus didefinisikan dan sama dengan $ node_before. Saya tidak peduli untuk bidang nilai bidang yang ada di $ node_before dan tidak dalam $ node, saya kira mereka tetap sama.
sumber
Ini hanya beberapa kode yang saya buat bersama. Semua kredit harus masuk ke @eclecto untuk melakukan semua pekerjaan kaki. Ini hanya variasi (yang belum diuji) yang mengambil objek node secara langsung, mengurangi hit DB sedikit dan menangani negosiasi bahasa.
sumber
Jawaban yang diberikan sangat bagus dan itu membantu saya, tetapi ada sesuatu yang harus saya koreksi.
Dalam
foreach()
lingkaran, saya harus mengubah dari$new_field
ke$old_field
. Saya tidak tahu apakah ini versi Drupal baru atau hanya kode saya (mungkin karena kode lain di tempat lain), tetapi saya tidak memiliki akses ke$new_field['entity']
.sumber
Terima kasih untuk postingnya, benar-benar menyelamatkan saya banyak waktu. Saya memperbaiki banyak peringatan dan pemberitahuan bahwa fungsi menghasilkan:
sumber