Generator widget CRUD untuk Yii

Apa komentar di artikel tentang Habré dan opsi tambahan yang sama saat membeli mobil?



Dari sudut pandang pemodelan data, keduanya merupakan entitas "bersarang" yang tidak memiliki signifikansi independen dalam isolasi dari objek induk.

Di Yii ( kerangka php ) ada Gii - generator kode bawaan yang memungkinkan Anda membuat antarmuka CRUD dasar menggunakan model data dengan beberapa klik mouse, yang secara signifikan mempercepat pengembangan, tetapi hanya berlaku untuk entitas independen, seperti artikel atau mesin dalam contoh di atas.

Akan sangat bagus untuk dapat menghasilkan sesuatu yang serupa untuk objek data "bersarang", kan? Sekarang - Anda bisa, selamat datang di kat untuk detailnya.

Untuk yang paling tidak sabar di akhir artikel, instruksi diberikan untuk memulai dengan cepat.

Dan bagi mereka yang tertarik dengan artikel tersebut, aspek dari aplikasi bisnis ke perangkat internal dipertimbangkan:

  • Kasus Bisnis: Posting berdasarkan Topik
    • Daftar topik di utama
    • Daftar posting terkait
  • Di bawah tenda: generator gii berdasarkan CRUD
    • Template Generator Gii
    • Kelas dasar widget
    • Pengontrol fasad terintegrasi
  • Mulai cepat
    • Tentang dukungan dan pengembangan

Kasus Bisnis: Posting berdasarkan Topik


Mungkin komentar tentang habr dan contoh buruk sejak itu seringkali lebih bermanfaat daripada artikel itu sendiri, tetapi, dalam kasus apa pun, ketika mengembangkan suatu aplikasi, sering kali ada situasi di mana objek tertentu dari model data kurang menarik bagi pengguna sebagai entitas independen.

Pertimbangkan tugas bisnis yang disederhanakan: untuk membuat situs web untuk menerbitkan pesan yang dikelompokkan berdasarkan berbagai topik.

Situs harus memiliki antarmuka berikut:

  1. Halaman utama - harus mendukung berbagai widget di masa depan, tetapi pada tahap implementasi saat ini hanya ada satu: daftar topik yang disaring oleh beberapa kriteria.
  2. Daftar lengkap topik - daftar lengkap topik dalam bentuk tabel;
  3. Halaman topik - informasi tentang topik dan daftar tulisan yang diterbitkan di dalamnya.

Cukup standar, bukan?

Mari kita lihat model data:



Juga tidak ada kejutan. Dua kelas model akan berisi logika bisnis kami:

  • Kelas Topik - data tentang topik, validasi, daftar posting di dalamnya, serta metode terpisah yang mengembalikan daftar topik yang difilter menurut kriteria widget di halaman utama.
  • Kelas Post hanya data dan validasi.

Aplikasi akan dilayani oleh dua pengontrol:

  • SiteController - halaman standar (tentang kami, kontak, dll.), Otorisasi (tidak diperlukan oleh ketentuan referensi, tetapi kami tahu sesuatu) dan indeks - halaman utama. Karena kami berharap banyak widget yang berbeda di masa mendatang, masuk akal untuk meninggalkan halaman utama di controller ini, dan tidak mentransfernya ke model yang spesifik untuk satu.
  • TopicController adalah serangkaian tindakan standar: daftar, buat, edit, lihat, dan hapus topik.

Secara potensial, Anda juga dapat membuat PostController - untuk keperluan administrasi dan / atau menyalin-tempel kode ke widget khusus, tetapi biarkan ini di luar cakupan artikel ini.
Hingga saat ini, sebagian besar kode dapat dihasilkan menggunakan gii, yang mempercepat pengembangan dan mengurangi risiko (kode manual lebih sedikit = lebih sedikit peluang untuk melakukan kesalahan).

Masih ada dua pertanyaan:

  1. Bagaimana cara menampilkan daftar topik yang difilter pada halaman utama?
  2. Bagaimana cara menampilkan daftar posting berdasarkan topik?

Jika Anda dapat menyelesaikannya menggunakan generator otomatis - ini akan menjadi pencapaian yang solid.

Daftar topik di utama


Halaman utama yang dilayani oleh situs / alamat indeks harus berisi daftar topik yang difilter oleh kriteria yang telah ditentukan. Kriteria penyaringan, sebagai bagian dari logika bisnis, kami telah dimasukkan dalam model.

Untuk tampilan, ada beberapa opsi implementasi.

Yang pertama, kotor dan cepat, adalah melakukan semuanya langsung di file tampilan ( views / site / index.php ):

  1. Buat ActiveDataProvider ;
  2. Isi dengan data dari model Topik ;
  3. Tampilan menggunakan widget ListView / GridView standar, menentukan bidang yang diperlukan secara manual.

Anda dapat melangkah lebih jauh dan mengemas semuanya ke dalam file tampilan yang terpisah, sesuatu seperti views / site / _topic-list-widget.php , menggunakan rendernya dari file utama. Ini akan memberikan sedikit lebih mudah dikelola dan diperpanjang, tetapi masih terlihat sangat kotor.

Sebagian besar dari kita cenderung membuat widget terpisah sesuai dengan semua aturan, dalam ruang nama yang terpisah ( komponen app \ widgets atau app \ untuk templat dasar - tergantung pada versi yang Anda gunakan), di mana mereka merangkum pembuatan ActiveDataProvider berdasarkan model dan ditampilkan secara terpisah. pengajuan. Yang tersisa hanyalah memanggil widget ini dari halaman utama. Solusi ini adalah yang paling benar dari sudut pandang dekomposisi kelas, pengelolaan dan ekstensibilitas kode.

Tetapi apakah rasanya kode widget ini akan sangat mengulang kode TopicController dalam hal menangani actionIndex () ? Dan sangat menjengkelkan menulis kode ini secara manual.

Akan jauh lebih baik untuk menghasilkan kode ini secara otomatis dan kemudian panggil widget yang sudah selesai:

<?= \app\widgets\TopicControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => app\models\Topic::findBySomeSpecificCriteria() ], ]) ?> 

Daftar posting terkait

Halaman untuk melihat topik yang dilayani oleh topik / alamat tampilan harus berisi informasi tentang topik itu sendiri dan daftar pesan yang diterbitkan di dalamnya. Kami mendapatkan daftar pesan untuk topik dalam model secara otomatis jika kami telah mengkonfigurasi hubungan antara tabel dengan benar, sehingga hanya pertanyaan tampilan yang tersisa.

Dengan analogi dengan daftar topik yang difilter, kami memiliki opsi yang hampir sama.

Yang pertama adalah melakukan segala sesuatu dalam kode file tampilan untuk melihat topik ( views / topic / view.php ):

  1. Buat ActiveDataProvider ;
  2. Isi dengan data dari model $ model-> getPosts () ;
  3. Tampilan menggunakan widget ListView / GridView standar, menentukan bidang yang diperlukan secara manual.

Yang kedua adalah mengisolasi kode ini ke dalam file presentasi yang terpisah: views / topic / _posts-list-widget.php , supaya tidak merusak pemandangan - menggunakannya kembali di suatu tempat masih akan gagal.

Yang ketiga adalah widget lengkap yang sebagian besar akan menduplikasi kode dari PostController bersyarat di bagian actionIndex () , tetapi ditulis secara manual.

Atau buat kode secara otomatis dan panggil widget yang sudah selesai:

 <?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $model->getPosts(), ], ]) ?> 

Di bawah tenda: generator gii berdasarkan CRUD


Tugas bisnis ditentukan, persyaratan untuk widget yang dihasilkan diuraikan, kami akan mencari tahu bagaimana tepatnya kami akan menghasilkannya. Gii sudah memiliki generator untuk pengontrol CRUD. Untuk widget CRUD, kita perlu membuat generator baru berdasarkan yang sudah ada.

Beberapa tautan ke dokumentasi sebelum memulai - itu juga akan berguna jika Anda memutuskan untuk menulis ekstensi Anda sendiri:


Jelas, semua fungsi dikemas dalam ekstensi Yii, yang diinstal melalui komposer dan masuk ke folder vendor proyek Anda.

Perpanjangan terdiri dari tiga bagian:

  1. Direktori templat / crud yang berisi templat generator gii;
  2. File controller.php - pengontrol fasad bawaan untuk panggilan widget;
  3. File Widget.php adalah kelas dasar untuk semua widget yang dihasilkan.



Template Generator Gii


Ekstensi harus menghasilkan kode, jadi bagian utamanya adalah generator Gii.

Awalnya, diasumsikan bahwa untuk mengimplementasikan ekstensi itu akan cukup untuk menulis template Anda sendiri untuk generator CRUD-Controller bawaan. Ngomong-ngomong, inilah sebabnya direktori tersebut disebut template, bukan generator. Tetapi ternyata generator CRUD-Controller melakukan validasi data input yang sangat intensif, yang tidak memungkinkan untuk mengimplementasikan banyak persyaratan, misalnya, mengubah kelas untuk warisan. Oleh karena itu, ekstensi berisi generator lengkap, dan bukan hanya templat.

Generator gii terdiri dari bagian-bagian berikut (semua ada di dalam direktori templat / crud):

  • Direktori default adalah template di mana semua keajaiban terjadi: setiap file dalam direktori ini akan sesuai dengan satu file yang dihasilkan dalam proyek Anda;
  • File form.php - seperti yang Anda tebak dari namanya, ini adalah formulir untuk memasukkan parameter pembuatan (nama kelas, dll.);
  • File Generator.php - orkestra generasi yang menerima data dari formulir, memvalidasinya, dan kemudian secara berurutan memanggil file templat untuk membuat hasilnya.

File Generator.php dan form.php sebagian besar berisi perubahan kosmetik relatif terhadap yang asli dari generator CRUD: nama file, validasi, deskripsi, dan prompt teks, dll.

File template bertanggung jawab atas tampilan yang dihasilkan dan kode widget itu sendiri. Pertama-tama, templat file / crud / default / controller.php adalah penting, yang bertanggung jawab untuk secara langsung menghasilkan kelas widget yang sesuai dengan kelas controller dari generator asli.

Widget harus memiliki tindakan yang sama dengan kontroler CRUD, tetapi dihasilkan sedikit berbeda. Contoh di bawah ini menunjukkan hasil generasi dengan komentar:

  • actionIndex - alih-alih keluaran tanpa syarat semua model, metode menerima parameter $ query;

     public function actionIndex($query) { $dataProvider = new ActiveDataProvider([ 'query' => $query, ]); return $this->render('index', [ 'dataProvider' => $dataProvider, ]); } 
  • actionCreate dan actionUpdate - jika sukses, alih-alih redirect, mereka hanya mengembalikan kode sukses, pemrosesan lebih lanjut disediakan oleh pengontrol fasad bawaan;

     public function actionCreate() { $model = new Post(); if ($model->load(Yii::$app->request->post()) && $model->save()) { return 'success'; } return $this->render('create', [ 'model' => $model, ]); } 

  • actionDelete - mendukung metode GET untuk menampilkan widget hapus (secara default - satu tombol) dan POST untuk melakukan tindakan; jika berhasil, itu juga tidak melakukan redirect, tetapi mengembalikan kode.

     public function actionDelete($id) { $model = $this->findModel($id); if (Yii::$app->request->method == 'GET') { return $this->render('delete', [ 'model' => $model, ]); } else { $model->delete(); return 'success'; } } 

Akhirnya, lihat file berisi suntingan dasar berikut:

  • Semua header diterjemahkan ke h2 bukan h1;
  • Menghapus kode yang bertanggung jawab untuk menampilkan judul halaman dan untuk remah roti - widget tidak boleh memengaruhi hal-hal ini;
  • Membuat dan mengedit model dilakukan menggunakan jendela modal (widget Modal bawaan);
  • Menambahkan template hapus widget - dengan satu tombol merah besar.

Kelas dasar widget


Ketika generator menyelesaikan tugasnya, itu akan membuat kelas widget di namespace aplikasi. Rantai pewarisan terlihat seperti ini: widget yang dihasilkan untuk aplikasi diwarisi dari widget ekstensi dasar, kelas \ ianikanov \ wce \ Widget , yang, pada gilirannya, diwarisi dari widget Yii dasar, kelas \ yii \ base \ Widget .

Kelas dasar widget ekstensi menyelesaikan tugas-tugas berikut:

  1. Menentukan dua bidang utama: $ action dan $ params, yang dengannya kontrol ditransfer ke widget dari tampilan panggilan;
  2. Menentukan sejumlah parameter standar yang dapat ditimpa di kelas yang dibuat, seperti jalur ke file tampilan widget, nama dan jalur ke pengontrol fasad (tentangnya di bawah) dan pesan kesalahan;
  3. Menentukan parameter standar saat menampilkan tampilan: render dan renderFile;
  4. Menyediakan infrastruktur acara yang mirip dengan infrastruktur pengontrol sehingga filter standar seperti AccessControl dan VerbFilter berfungsi ;
  5. Menentukan metode run yang mengumpulkan semua ini bersama-sama.

Pengontrol fasad terintegrasi

Tidak ada masalah dengan tampilan data ini - widget dimaksudkan untuk tujuan ini. Tetapi untuk mengedit, Anda memerlukan pengontrol. Hasilkan pengontrol unik untuk setiap widget - seluruh esensinya hilang. Menggunakan CRUD standar tidak selalu relevan, dan saya tidak ingin bergantung pada peluncuran tambahan gii. Oleh karena itu, opsi ini digunakan dengan pengontrol-fasad universal yang terintegrasi.

Pengontrol ini terdaftar di peta aplikasi melalui file konfigurasi dan hanya berisi satu metode - actionIndex, yang melakukan tindakan berikut:

  1. Menerima permintaan dari klien;
  2. Kontrol transfer ke kelas widget yang sesuai;
  3. Menangani kesalahan bisnis sebagai akibat dari widget;
  4. Pengalihan kembali ke aplikasi utama.

Mungkin lebih penting untuk menunjukkan apa yang BUKAN pengendali ini:

  1. Itu tidak memeriksa tingkat akses - logika ini milik widget tertentu;
  2. Itu tidak melakukan manipulasi input - parameter dilewatkan ke widget apa adanya;
  3. Itu tidak memanipulasi output kecuali untuk memeriksa kode sukses yang telah ditentukan.

Pendekatan ini memungkinkan Anda untuk mempertahankan fleksibilitas fasad, meninggalkan penerapan persyaratan bisnis, termasuk persyaratan keamanan, kode aplikasi aplikasi.

Mulai cepat

Tantangan bisnisnya jelas, siap untuk memulai? Menggunakan ekstensi memiliki empat langkah:

  1. Instalasi;
  2. Konfigurasi;
  3. Generasi;
  4. Aplikasi.

Memasang ekstensi dilakukan menggunakan komposer:

 php composer.phar require --prefer-dist ianikanov/yii2-wce "dev-master" 

Selanjutnya, Anda perlu membuat beberapa perubahan pada file konfigurasi aplikasi.

Pertama, tambahkan referensi ke generator gii:

 if (YII_ENV_DEV) { $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', 'allowedIPs' => ['127.0.0.1', '::1', '192.168.0.*', '192.168.178.20'], 'generators' => [ //here 'widgetCrud' => [ 'class' => '\ianikanov\wce\templates\crud\Generator', 'templates' => [ 'WCE' => '@vendor/ianikanov/yii2-wce/templates/crud/default', // template name ], ], ], ]; } 

Kedua, tambahkan pengontrol fasad terintegrasi ke peta:

 $config = [ ... 'controllerMap' => [ 'wce-embed' => '\ianikanov\wce\Controller', ], ... ]; 

Ini menyelesaikan instalasi dan konfigurasi.

Untuk menghasilkan widget:

  1. Buka gii;
  2. Pilih "Widget Pengontrol CRUD";
  3. Isi kolom formulir;
  4. Lihat dan hasilkan kode.

Selanjutnya, untuk menggunakan widget, itu harus dipanggil dengan menentukan tindakan dan params - hampir sama dengan controller yang disebut.

Widget untuk melihat daftar model:

 <?= app\widgets\PostControllerWidget::widget([ 'action' => 'index', 'params' => [ 'query' => $otherModel->getPosts(), ], ]) ?> 

Widget untuk melihat satu model:

 <?= app\widgets\PostControllerWidget::widget(['action' => 'view', 'params' => ['id' => $post_id]]) ?> 

Widget pembuatan model (tombol + formulir yang dibungkus dengan Modal):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'create']) ?> 

Widget perubahan model (tombol + formulir yang dibungkus dengan Modal):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'update', 'params'=>['id' => $post_id]]) ?> 

Widget penghapusan model (tombol):

 <?= app\widgets\PostControllerWidget::widget(['action' => 'delete', 'params'=>['id' => $post_id]]) ?> 

Kode widget dan semua tampilan milik aplikasi dan dapat dengan mudah diubah - semuanya persis sama seperti saat membuat controller.

Tentang dukungan dan pengembangan


Beberapa kata tentang bagaimana ekspansi akan didukung dan dikembangkan. Saya memiliki pekerjaan utama dan beberapa proyek "sampingan" saya (proyek kesayangan). Jadi, ekstensi ini adalah proyek sampingan dari proyek sampingan saya, jadi saya akan mengembangkan peningkatan hanya untuk kebutuhan proyek saya.

Dalam tradisi open source terbaik, kode tersedia di github , dan saya akan mendukungnya dalam hal memperbaiki bug, dan saya juga akan mencoba melakukan tinjauan tepat waktu jika ada yang ingin mengirim permintaan tarik, jadi siapa yang tertarik, bergabunglah.

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


All Articles