Magento 2: Plugin sebelum / sekitar / setelah Interaksi

32

Di Magento 2, saat Anda membuat plugin "sekitar"

public function aroundRenderResult(
    \Magento\Framework\Controller\ResultInterface $subject,
    \Closure $proceed,
    ResponseHttp $response
) {
    //...
    $proceed($response);
    //...      
}    

Anda dapat melanjutkan ke plugin selanjutnya, yang berpuncak dengan memanggil metode asli yang sebenarnya, dengan memanggil / memanggil metode yang dilewatkan $proceed. Ini adalah pola desain umum, sering terlihat dalam implementasi middleware PHP Frameworks.

Namun - itu memang menghadirkan beberapa kebingungan w / r / t untuk detail implementasi. Secara khusus

Jika, di samping sebuah aroundPlugin, sebuah objek / kelas memiliki beforeatau afterplugin yang ditentukan, kapan mereka memecat sehubungan dengan rantai sekitar plugin?

yaitu apakah semua metode sebelumnya akan menyala sebelum ada metode plugin yang menyala? Atau akankah sebelum plugin hanya menembak sebelum final, metode nyata yang sebenarnya akan menyala?

Masalah khusus yang saya coba lacak adalah, sepertinya saya tidak bisa mendapatkan plugin yang terpasang pada metode pengiriman pengontrol depan Magento 2 saat Magento dalam mode caching halaman penuh . Tembolok halaman penuh beroperasi oleh plugin sekitar yang tidak memanggil $proceed($response). Saya sudah mencoba menggali ke dalam beberapa kode di sekitar plugin ini dan telah menemukan sistem sulit untuk dipikirkan tanpa mengetahui bagaimana maksudnya bahwa plugin bekerja.

yaitu - deskripsi pada halaman dev docs muncul, dalam satu contoh khusus ini, menjadi tidak akurat. Tidak jelas apakah dokumentasinya salah, atau apakah ini bug yang baru saja diperkenalkan, apakah ini kasus tepi, atau apakah konfigurasi plugin saya salah.

Adakah yang tahu, dengan pengamatan langsung, atau dengan pengetahuan budaya, bagaimana penentuan prioritas ini berfungsi?

Alan Storm
sumber
Alan, apakah Anda memiliki aturan praktis kapan harus menggunakan \closure $proceedvs. \callable $proceeddalam sebuah plugin? Dokumen resmi hanya menyebutkan \callabledan tidak pernah menyentuh \closure.
thdoan

Jawaban:

38

Plugin diurutkan berdasarkan urutan pertama, dan kemudian dengan awalan metode.

Contoh: untuk metode dengan 3 plugin (PluginA, PluginB, PluginC) dengan metode dan sortOrder berikut:

  • PluginA (sortOrder = 10)
    • sebelumDispatch ()
    • afterDispatch ()
  • PluginB (sortOrder = 20)
    • sebelumDispatch ()
    • aroundDispatch ()
    • afterDispatch ()
  • PluginC (sortOrder = 30):
    • sebelumDispatch ()
    • aroundDispatch ()
    • afterDispatch ()

Alur eksekusi harus mengikuti:

  • PluginA :: beforeDispatch ()
  • PluginB :: beforeDispatch ()
  • PluginB :: aroundDispatch ()
    • PluginC :: beforeDispatch ()
    • PluginC :: aroundDispatch ()
      • Action :: dispatch ()
    • PluginC :: afterDispatch ()
  • PluginB :: afterDispatch ()
  • PluginA :: afterDispatch ()
Anton Kril
sumber
16

Dari buku masak Magento 2:

Jika ada beberapa plugin yang memperluas fungsi asli yang sama, mereka dieksekusi dalam urutan berikut:

  • plugin sebelum dengan yang terendah sortOrder
  • plugin sekitar dengan yang terendah sortOrder
  • lainnya sebelum plugin (dari terendah ke tertinggi sortOrder)
  • plugin lain di sekitar (dari terendah ke tertinggi sortOrder)
  • plugin setelah dengan yang tertinggi sortOrder
  • lainnya setelah plugin (dari yang tertinggi ke yang terendah sortOrder)
Raphael di Digital Pianism
sumber
1

Bagi saya itu harus berfungsi sebagai:

  • jika urutan tidak didefinisikan setara dengan nol (dan ini berarti bahwa urutan nyata tidak terdefinisi)
  • plugin harus diurutkan berdasarkan pesanan

Jika Anda meninjau kode \Magento\Framework\Interception\Interceptor::___callPlugins()Anda dapat melihat bahwa plugin dipanggil agar disimpan dalam $pluginInfovariabel. Informasi ini melewati bentuk metode yang dihasilkan secara otomatis di interceptor seperti

public function {method}()
{
    $pluginInfo = $this->pluginList->getNext($this->subjectType, '{method}');
    if (!$pluginInfo) {
        return parent::{method}();
    } else {
        return $this->___callPlugins('{method}', func_get_args(), $pluginInfo);
    }
}

Seperti yang Anda lihat \Magento\Framework\Interception\PluginListInterfaceantarmuka dan \Magento\Framework\Interception\PluginList\PluginListimplementasi default bertanggung jawab untuk pengurutan plugin. Lihat _inheritPlugins: 152 metode

/**
 * Sort items
 *
 * @param array $itemA
 * @param array $itemB
 * @return int
 */
protected function _sort($itemA, $itemB)
{
    if (isset($itemA['sortOrder'])) {
        if (isset($itemB['sortOrder'])) {
            return $itemA['sortOrder'] - $itemB['sortOrder'];
        }
        return $itemA['sortOrder'];
    } elseif (isset($itemB['sortOrder'])) {
        return $itemB['sortOrder'];
    } else {
        return 1;
    }
} 

Bagi saya fungsi ini memiliki dua kesalahan logis:

  • return $itemB['sortOrder'];seharusnya return - $itemB['sortOrder'];
  • return 1; seharusnya return 0;

Semoga ini bisa membantu Anda.

KAndy
sumber
tetapi apakah $ pluginInfo terisi penuh dengan plugin? Atau adakah pemuatan malas yang terjadi yang dapat memengaruhi perilaku? Apa maksud urutan sortir untuk beberapa plugin? yaitu apakah "sebelum plugin 1, sekitar plugin 1, setelah plugin 1, sebelum plugin 2, sekitar plugin 2" atau "sebelum plugin 1", "sebelum plugin 2", "sebelum plugin 2, sekitar plugin1, sekitar plugin 2", dll. Kode terlihat seperti nanti, tetapi "getNext" mengisi informasi plugin dengan (mungkin?) Malas memuat cara, dan bagaimana Magento menghindari rekursi dengan sekitar membuat ini semua tidak jelas, dan sulit untuk menemukan apa bug, apa fitur.
Alan Storm
Kelas plugin semacam Magento bukan metode plugin.
KAndy
Dan daftar plugin bisa diubah, misalnya jika aria baru kita dimuat.
KAndy
Ada beberapa pengetahuan implisit yang Anda miliki yang tidak jelas, karena "mengurutkan kelas plugin dan bukan metode plugin" tidak memperjelas apa aturan atau interaksi dari plugin tersebut.
Alan Storm
mungkin tautan ini akan bermanfaat magehero.com/posts/472/magento-2-interception
KAndy