Mana yang lebih performan: entity_metadata_wrapper atau field_get_items?

10

Untuk mendapatkan nilai dari entitas, ada dua cara:

  • Gunakan field_get_itemsdan dapatkan nilai suatu bidang
  • Gunakan entity_metadata_wrapperdan dapatkan nilai suatu bidang

Meskipun entity_metadata_wrapperabstrak menjauhkan perbedaan bahasa, API-nya terkadang masih canggung, terutama saat menggunakan PHP 5.3. Misalnya, mendapatkan nilai bidang teks yang panjang biasanya melewati rute ini:

$field = $wrapper->field->value();
print $field['safe_value'];

Untungnya, PHP 5.4 mendukung sintaks ini: print $wrapper->field->value()['safe_value'];.

Tetapi pertanyaan saya lebih mementingkan kinerja. Bagaimana cara keduanya bekerja? Apakah mereka meminta basis data setiap kali mereka meminta nilai? Apakah entity_metadata_wrappermeminta semuanya sekaligus? (Membuat field_get_itemlebih cocok untuk pengambilan nilai-tunggal.)

Saya tidak cukup berani untuk menyelam jauh ke sumber Drupal.

Florian Margaine
sumber
1
field_view_field()adalah untuk merender bidang. Fungsi untuk mendapatkan nilai bidang adalah field_get_items () .
kiamlaluno
Dan field_get_items()mengeluarkan nol overhead database jadi saya pikir itu kasus yang cukup terbuka dan tertutup :)
Clive
@Clive kok bisa field_get_items()menimbulkan overhead database nol? Itu harus mendapatkan datanya di suatu tempat, kan?
Florian Margaine
Juga, saya benar-benar tertarik mengetahui cara entity_metadata_wrapperkerjanya, kinerja-bijaksana.
Florian Margaine
2
Anda melewatkan objek entitas yang terisi penuh ke dalam field_get_items()sehingga biaya overhead telah dikeluarkan ... ini sedikit rute yang dicekik di D7 untuk jujur
Clive

Jawaban:

12

Jawaban singkat: field_get_items () lebih berkinerja daripada entity_metadata_wrapper ().

Lihat kode untuk fungsi-fungsi ini:

Keduanya mengharuskan Anda untuk melewati entitas, yang telah dimuat dari database . Sebagai contoh:

$node = node_load(123);
$items = field_get_items('node', $node, 'field_my_field_name');
print $items[0]['value'];

atau, seperti yang telah Anda sarankan:

$wrapper = entity_metadata_wrapper('node', $node);
$field = $wrapper->field_my_field_name->value();
print $field['safe_value'];

Kedua contoh ini agak mengganggu saya karena logika konyol dalam mencoba untuk mendapatkan nilai yang sudah tersedia untuk Anda, tetapi mereka tentu berguna dalam banyak kasus.

Anda bisa melakukannya print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];tetapi itu akan menimbulkan kesalahan pemberitahuan PHP jika bidang tidak memiliki nilai, karena Anda mencoba mengakses array yang mungkin tidak ada (yaitu [LANGUAGE_NONE][0]['value']). Saya menemukan diri saya melakukan ini cukup sering belakangan ini:

if ($field = field_get_items('node', $node, 'field_my_field_name')) {
  print $field[0]['value'];
}

yang jauh lebih bersih daripada melakukan:

if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {
  print $node->field_my_field_name[LANGUAGE_NONE][0]['value'];
}

Jika Anda melihat kode untuk field_get_items())Anda akan melihat bahwa itu tidak melakukan apa-apa lagi maka memastikan bahwa array bidang memiliki data dalam bahasa saat ini dan kemudian mengembalikannya. Jadi, overhead menjalankan fungsi sekecil itu dapat diabaikan, tetapi jika Anda benar-benar peduli dengan kinerja, Anda bisa melakukan pemeriksaan sendiri apakah data ada dan kemudian mencetaknya.

Sunting: Karena field_get_items()menjalankan field_language()sebenarnya ada hit kinerja yang lebih besar daripada hanya memeriksa bahasa, jadi, jika Anda sudah tahu bahwa $ entitas-> bahasa ada, Anda bisa menulis fungsi super-kinerja Anda sendiri:

function my_super_performant_field_value_getter($entity, $field_name) {
  return isset($entity->{$field_name}[{$entity->language}]) ? $entity->{$field_name}[{$entity->language}] : FALSE;
}
Charlie Schliesser
sumber
Ok, terlepas dari cek-cek itu, entitas dimuat sekali, tidak peduli berapa kali saya menggunakannya? Bahkan jika saya menggunakan referensi entitas?
Florian Margaine
Ya, ini sebenarnya fitur yang cukup keren dari entitas API di D7. Setelah Anda memuat suatu entitas, itu di-cache selama durasi permintaan itu. Jadi, jika Anda melakukannya $node = node_load(123);dalam 1 skrip dan melakukannya lagi di tempat lain, Anda tidak dikenakan kinerja overhead dari objek penuh memuat dan membangun - Drupal hanya memberikan variabel itu salinan entitas yang ada. Jika Anda ingin memuat salinan baru , Anda harus meneruskan $reset = TRUEke fungsi memuat entitas. Juga, lihat hasil edit saya mengenai pengambil yang berkinerja super.
Charlie Schliesser
1
if (isset($node->field_my_field_name[LANGUAGE_NONE]) && isset($node->field_my_field_name[LANGUAGE_NONE][0])) {tidak perlu, isset($node->field_my_field_name[LANGUAGE_NONE][0]sudah cukup.
@ chx Saya setuju, tapi bukankah itu isset($node->field_my_field_name[LANGUAGE_NONE])karena bahasa tidak akan disetel pada bidang kosong? Saya pikir itu adalah delta / [0]yang berlebihan.
Charlie Schliesser
1
@ GilesB, lebih banyak permintaan basis data paling sering tidak lebih baik daripada bergabung. Eager loading adalah teknik optimisasi. Tetapi bahkan mengatakan itu, saya pikir asumsi Anda salah dan EntityMetadataWrapper mungkin lebih lambat, tetapi jauh lebih baik untuk digunakan. Ini juga OP optimasi mikro tidak perlu dipikirkan ketika bekerja dengan Drupal.
Nicholas Ruunu