Implementasi mesin negara pada Zend Framework3 + Doctine2

Pendahuluan: mengapa kita membutuhkan mesin negara


Dalam aplikasi, seringkali perlu untuk membatasi akses ke tindakan tertentu pada suatu objek. Untuk ini, modul RBAC digunakan yang memecahkan masalah membatasi akses tergantung pada hak pengguna. Tugas mengelola tindakan, tergantung pada keadaan objek, tetap tidak terpecahkan. Masalah ini diselesaikan dengan baik menggunakan mesin negara atau mesin negara. Mesin keadaan mudah memungkinkan Anda untuk tidak hanya mengumpulkan di satu tempat semua aturan transisi antara keadaan objek, tetapi juga menempatkan beberapa urutan dalam kode dengan memisahkan aturan transisi, memeriksa kondisi dan penanganan dan menjadikannya aturan umum.


Saya ingin berbagi implementasi mesin negara untuk Zend Framework 3 menggunakan Doctrine 2
untuk bekerja dengan database. Proyek itu sendiri dapat ditemukan di sini .


Dan di sini saya ingin berbagi prinsip-prinsip dasar yang ditetapkan.


Mari kita mulai




Kami akan menyimpan deskripsi grafik transisi dalam tabel database karena:


  1. Ini jelas.
  2. Memungkinkan Anda menggunakan kamus status yang sama dengan yang digunakan dalam kamus yang menarik
    kami objek yang memiliki keadaan.
  3. Memungkinkan Anda untuk menjamin integritas basis data menggunakan kunci asing.

Menggunakan mesin keadaan terbatas non-deterministik akan meningkatkan fleksibilitas solusi kami.


Grafik transisi akan dijelaskan menggunakan sepasang tabel A dan B, dihubungkan oleh hubungan satu ke banyak.


Tabel a:


CREATE TABLE `tr_a` ( `id` int(11) NOT NULL AUTO_INCREMENT, `src_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, `action_id` varchar(64) COLLATE utf8_unicode_ci NOT NULL COMMENT ' ', `condition` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '   ', PRIMARY KEY (`id`), KEY `IDX_96B84B3BFF529AC` (`src_id`), KEY `IDX_96B84B3B9D32F035` (`action_id`), CONSTRAINT `FK_96B84B3B9D32F035` FOREIGN KEY (`action_id`) REFERENCES `action` (`id`), CONSTRAINT `FK_96B84B3BFF529AC` FOREIGN KEY (`src_id`) REFERENCES `state` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Tabel B:


 CREATE TABLE `tr_b` ( `id` int(11) NOT NULL AUTO_INCREMENT, `transition_a_id` int(11) NOT NULL, `dst_id` varchar(32) COLLATE utf8_unicode_ci NOT NULL, `weight` int(11) DEFAULT NULL COMMENT '  ,- , null-  ', `condition` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '   ', `pre_functor` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT ' ,  ,   ', `post_functor` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT ' ,  ,   ', PRIMARY KEY (`id`), KEY `IDX_E12699CB85F4C374` (`transition_a_id`), KEY `IDX_E12699CBE1885D19` (`dst_id`), CONSTRAINT `FK_E12699CB85F4C374` FOREIGN KEY (`transition_a_id`) REFERENCES `tr_a` (`id`), CONSTRAINT `FK_E12699CBE1885D19` FOREIGN KEY (`dst_id`) REFERENCES `state` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=16 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci 

Saya akan menghilangkan definisi kelas entitas; Anda dapat melihat contoh di sini:


  1. meja a
  2. tabel B

Semuanya standar di sini, termasuk deskripsi hubungan antar entitas.


Untuk menggunakan mesin negara, kita hanya perlu beberapa metode umum


 /** *            * @param object $objE * @param string $action * @param array $data extra data * @return array * @throws ExceptionNS\StateMachineException */ public function doAction($objE, $action, array $data = []) /** *          * @param object $objE * @param string $action * @param array $data * @return bool */ public function hasAction($objE, $action, $data=[]) 

Ada beberapa metode yang lebih umum untuk penggunaan yang nyaman, tetapi di sini saya ingin memperhatikan algoritma dari metode doAction() utama.


Dari objek kita memperoleh statusnya.


Mengetahui itu dan pengidentifikasi tindakan, entitas-A mudah ditemukan di tabel transisi A.
Kondisi yang diperoleh oleh pengidentifikasi kondisi, yang terletak pada condition dari entitas-A, memungkinkan Anda untuk memeriksa apakah tindakan dapat dilakukan. Secara khusus, klausa RBAC yang disebutkan di awal dapat digunakan dalam validator kondisi.


Validator akan ditemukan oleh pengidentifikasi dari bidang condition melalui ValidatorPluginManager dan
harus mengimplementasikan \Zend\Validator\ValidatorInterface . Saya lebih suka menggunakan ahli waris dari ValidatorChain . Ini membuatnya mudah untuk mengubah komposisi kondisi terkontrol dan menggunakan kembali validator sederhana sebagai bagian dari rantai tes.


Kami menentukan transisi, memeriksa kondisinya. Karena kita memiliki non-deterministik
mesin negara , hasil tindakan mungkin salah satu dari beberapa negara.


Kasus-kasus seperti itu tidak terlalu umum, tetapi proyek yang diusulkan mudah diimplementasikan.
Dengan koneksi A - <B, kami memperoleh kumpulan kemungkinan keadaan baru dari objek (entitas-B).
Untuk memilih satu status, kami memeriksa kondisi Entity-B dari koleksi yang dihasilkan, mengurutkannya berdasarkan weight dari yang lebih besar ke yang lebih kecil di bidang weight . Pemeriksaan kondisi pertama yang berhasil memberi kita Entity-B, di mana ada keadaan objek yang baru (lihat bidang dst_id ).


Negara baru ditentukan. Sekarang, sebelum mengubah status, mesin status akan menjalankan
tindakan yang didefinisikan dalam pendahuluan, maka itu akan mengubah status dan melakukan tindakan,
didefinisikan dalam postfungsi. Mesin negara akan mendapatkan functors berdasarkan nama dari bidang pre_functor untuk fungsi dan post_functor untuk fungsi posting menggunakan manajer plug-in dan akan memanggil metode __invoke () untuk objek yang diterima.


Tidak perlu mengubah status menggunakan functors. Ini adalah tugas mesin negara. Jika tidak perlu melakukan tindakan tambahan saat mengubah status, maka setel nol ke bidang di atas.


Keripik lainnya:


  1. Di bidang condition tabel transisi, pre_funtor , post_functor saya menggunakan alias, menurut pendapat saya itu nyaman.
  2. Untuk kenyamanan visual, buat tampilan dari tabel A dan B.
  3. Saya menggunakan pengidentifikasi string sebagai kunci utama dalam kamus keadaan dan tindakan. Ini tidak perlu, tetapi nyaman. Kamus dengan pengidentifikasi numerik juga dapat digunakan.
  4. Karena mesin keadaan terbatas nondeterministic digunakan, tindakan tidak harus mengarah pada perubahan status. Ini memungkinkan Anda untuk menggambarkan tindakan seperti melihat, misalnya.
  5. Selain metode untuk memverifikasi suatu tindakan dan melakukan tindakan, ada sejumlah metode publik yang memungkinkan, misalnya, memperoleh daftar tindakan untuk keadaan objek tertentu atau daftar tindakan yang tersedia untuk keadaan objek tertentu, dengan mempertimbangkan pemeriksaan akun. Seringkali di antarmuka dalam grid untuk setiap catatan yang Anda butuhkan untuk menunjukkan serangkaian tindakan. Metode mesin negara ini akan membantu untuk mendapatkan daftar yang diperlukan.
  6. Tentu saja, mesin negara lain dapat disebut sebagai functors di dalam, apalagi, Anda dapat memanggil diri Anda sendiri, tetapi dengan objek yang berbeda atau dengan objek yang sama, tetapi setelah perubahan status (mis. Dalam post-functor). Ini kadang-kadang berguna untuk mengatur transisi cascading di bawah perubahan kondisi "tiba-tiba" dari pelanggan;)

Kesimpulan


Meskipun banyak tugas yang cocok untuk penggunaan mesin negara, programmer web jarang menggunakannya. Keputusan yang sama yang saya lihat tampak mengerikan bagi saya.


Saya berharap solusi yang diusulkan akan membantu seseorang menghemat waktu dalam implementasi.

Source: https://habr.com/ru/post/id413701/


All Articles