YIMP - Control Panel untuk Yii 2 di Bootstrap 4

Saya yakin bahwa banyak pengembang yang lebih memilih kerangka kerja daripada CMS yang sudah jadi memiliki solusi pada Bootstrap atau analognya, yang digunakan untuk membuat antarmuka admin dan antarmuka back-office lainnya. Dan saya memilikinya. Ini telah bekerja dengan sukses selama bertahun-tahun, tetapi sudah usang. Saatnya menulis ulang.


Saat mengerjakan versi baru, saya mencoba merangkum semua pengalaman saya tentang topik ini, dan sebagai hasilnya saya mendapatkan YIMP - sepeda yang tidak malu untuk saya bagikan: GitHub , LiveDemo , Dokumentasi API .


YIMP sangat sederhana. Namun di balik kesederhanaan ini adalah pemikiran yang panjang, yang juga ingin saya bagikan. Jadi artikel ini bukan instruksi. Di sini kita berbicara tentang arsitektur, manajemen ketergantungan, paradigma MVC, dan antarmuka pengguna, tentu saja.


Jadi, YIMP adalah dasbor. Ini bukan panel admin yang sudah jadi, bukan CMS atau bahkan CMF. Kode representasi perlu ditulis secara independen atau menggunakan Gii (templat terlampir). YIMP menyediakan tata letak yang menentukan di mana kontrol harus berada, serta antarmuka tempat aplikasi mentransfer data ke tata letak. Ini adalah tampilannya di desktop:



Layout adaptif. Saat layar menyusut, elemen-elemen mulai menghilang atau bergerak di atas tombol di navbar. Akibatnya , halaman yang sama di ponsel terlihat seperti ini:


Lebih baik membiarkannya di bawah spoiler

Apa yang kita lihat di tata letak? Judul aplikasi, remah roti, tiga menu (kiri, kanan dan atas), widget di bilah sisi, judul halaman. Dalam praktik saya, rangkaian elemen ini cukup untuk mengembangkan antarmuka apa pun - dari halaman admin halaman arahan ke sistem informasi perusahaan. Saya mencoba mengaturnya sehingga ruang itu digunakan seefisien mungkin. Apa yang kamu katakan


Markup ditulis dalam Bootstrap murni, tanpa ekstensi dan penyesuaian. Jika memungkinkan, kelas dari Bootstrap digunakan, jadi jika Anda memutuskan untuk menggunakan penyesuaian, maka seharusnya tidak ada masalah.


Seperti yang saya katakan, YIMP menyertakan antarmuka tempat aplikasi mentransfer data ke tata letak. Mari kita lihat bagaimana ini terjadi. Buka kap mesin!


Tata letak


Saya percaya bahwa pengembang harus memiliki kontrol penuh atas tata letak, jadi ketika menginstal YIMP, saya sarankan menyalin kodenya ke aplikasinya. Menurut pendapat saya, ini jauh lebih baik daripada meninggalkan tata letak dalam paket dan memblokir banyak pengaturan untuk itu. Mari kita lihat kode tata letak:


77 baris kode. Tidak perlu mempelajari!
<?php use dmitrybtn\yimp\widgets\Alert; use dmitrybtn\yimp\Yimp; use yii\bootstrap4\Html; $yimp = new Yimp(); $yimp->register($this); /** @var string $content Content came from view */ ?> <?php $this->beginPage() ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="<?= Yii::$app->charset ?>"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <?php echo Html::csrfMetaTags() ?> <title><?php echo Html::encode($yimp->nav->getTitle()) ?></title> <?php $this->head() ?> </head> <body> <?php $this->beginBody() ?> <?php echo $yimp->navbar() ?> <?php echo $yimp->beginSidebars() ?> <?php echo $yimp->beginLeftSidebar() ?> <?php echo $yimp->beginLeftSidebarMenu() ?> <?php echo $yimp->menuLeft([ 'options' => ['class' => 'nav-pills flex-column border rounded py-2'] ]) ?> <?php echo $yimp->endLeftSidebarMenu() ?> <?php if (isset($this->blocks[$yimp::SIDEBAR_LEFT])): ?> <?php echo $this->blocks[$yimp::SIDEBAR_LEFT] ?> <?php endif ?> <?php echo $yimp->endLeftSidebar() ?> <?php echo $yimp->beginRightSidebar() ?> <?php echo $yimp->beginRightSidebarMenu() ?> <?php echo $yimp->menuRight([ 'options' => ['class' => 'nav-pills flex-column border rounded py-2'] ]) ?> <?php echo $yimp->endRightSidebarMenu() ?> <?php if (isset($this->blocks[$yimp::SIDEBAR_RIGHT])): ?> <?php echo $this->blocks[$yimp::SIDEBAR_RIGHT] ?> <?php endif ?> <?php echo $yimp->endRightSidebar() ?> <?php echo $yimp->endSidebars() ?> <?php echo $yimp->beginContent() ?> <?php echo $yimp->headerDesktop() ?> <?php echo Alert::widget() ?> <?php echo $content ?> <?php echo $yimp->endContent() ?> <?php if (isset($this->blocks[$yimp::FOOTER])): ?> <?php echo $this->blocks[$yimp::FOOTER] ?> <?php endif ?> <?php $this->endBody() ?> </body> </html> <?php $this->endPage() ?> 

Seperti yang Anda lihat, semua markup YIMP dibungkus dengan metode. Sebagian besar metode ini hanya mencetak garis yang diambil dari array ini . Ya, prinsip KISS adalah segalanya bagi kami.


Harap perhatikan bahwa blok digunakan dalam tata letak. Mereka diperlukan untuk menampilkan widget yang ditentukan dalam tampilan (ini adalah bagaimana kontrol formulir diberikan). Nah, jika widget harus menggantung di semua halaman, lebih baik untuk menentukannya langsung di tata letak.


Jadi, area utama dan widget didefinisikan dalam tampilan. Dan di mana tajuk, menu, dan remah roti ditentukan? Menurut pendapat saya, mereka paling baik didefinisikan dalam pengontrol. Ini adalah poin penting, karena keputusan seperti itu bertentangan dengan paradigma MVC. Mari kita lihat masalah ini secara lebih rinci.


Pengontrol


Jadi, biarkan ada ProfileController yang dapat menampilkan informasi tentang profil pengguna saat ini dan mengubah kata sandi. Secara logis, tindakan profile/view akan disebut "Profil Saya." Juga logis bahwa di menu utama harus ada item "Profil saya". Akhirnya, "Profil saya" harus berada dalam remah roti: "Beranda / Profil saya / Ubah kata sandi." Saya pikir keinginan untuk mendefinisikan konstanta dengan kata-kata "Profil saya" cukup dibenarkan. Anda tidak dapat melakukan ini dalam tampilan. Memilih layer terpisah untuk judul adalah rumit. Dengan alasan seperti ini, saya datang ke controller.


Langkah selanjutnya adalah keputusan untuk menentukan tidak hanya header tindakan di controller, tetapi juga remah roti dan menu. Dan lakukan itu agar YIMP dapat membacanya. Dan di sini kita membutuhkan sebuah contoh. Mari kita lihat kemungkinan implementasi kelas ProfileController .


52 baris kode sederhana. Lebih baik perhatikan dengan teliti!
 class ProfileController extends \yii\web\Controller { public $nav; public function init() { parent::init(); $this->nav = new \dmitrybtn\yimp\Navigator; } public static function titleView() { return ' '; } public static function titlePassword() { return ' '; } public static function crumbsToView() { return [ ['label' => static::titleView(), 'url' => ['/profile/view']] ]; } public function actionView() { $this->nav->title = static::titleView(); $this->nav->menuRight = [ ['label' => ''], ['label' => static::titlePassword(), ['password']], ]; ... return $this->render('view'); } public function actionPassword() { $this->nav->title = static::titlePassword(); $this->nav->crumbs = static::crumbsToView(); ... return $this->render('password'); } } 

Header dan remah roti didefinisikan menggunakan metode statis, yang berarti mereka dapat digunakan di mana saja. Misalnya, di menu utama aplikasi Anda dapat menulis:


 ['label' => ProfileController::titleView(), 'url' => ['/profile/view']], 

Mengapa tepatnya metodenya? Karena besok Anda akan ditanya alih-alih kata "Profil saya" untuk menampilkan login pengguna saat ini.


Dengan remah roti yang sama. Misalkan pengguna kami memiliki daftar gambar yang menjadi tanggung jawab ImageController . Kemudian pada image/create tindakan Anda dapat menulis:


  $this->nav->crumbs = ProfileController::crumbsToView(), 

dan dapatkan remah roti seperti "Beranda / Profil saya / Tambahkan gambar." Ngomong-ngomong, karena tindakan image/create disebut "Tambah gambar", menu tindakan profile/view perlu diperbaiki:


  $this->nav->menuRight = [ ['label' => ''], ['label' => static::titlePassword(), ['password']], ['label' => ImageController::titleCreate(), 'url' => ['/image/create']] ]; 

Saya pikir idenya bisa dimengerti. Menurut pendapat saya, ini adalah solusi sederhana dan efektif untuk mana Anda dapat menjauh dari paradigma MVC. Ya, kode pengontrol semakin besar, tetapi ada tempat untuk itu di pengontrol - kami tidak menulis logika bisnis di sana, kan? Dan ya, saya akan sangat tertarik untuk mengetahui pendapat Anda tentang masalah ini.


Kita melangkah lebih jauh. Seperti yang mungkin sudah Anda duga, properti nav , didefinisikan sebagai \dmitrybtn\yimp\Navigator , digunakan untuk mentransfer header, menu, dan remah roti dari pengontrol ke tata letak. Dan ini adalah fitur lain dari YIMP.



Bagaimana pengaturannya masuk ke tata letak? Sangat sederhana. Selama inisialisasi, YIMP memeriksa properti nav dari kontroler saat ini. Jika properti ini dapat dibaca dan merupakan navigator ( instanceof \dmitrybtn\yimp\Navigator ), properti ini digunakan untuk menampilkan informasi yang sesuai. Navigator menyertakan properti yang berkaitan dengan elemen tata letak, daftar lengkap yang lebih mudah dilihat dalam dokumentasi API .


Dalam aplikasi, disarankan untuk membuat navigator Anda sendiri dan menentukan menu di dalamnya yang tidak akan bergantung pada tindakan saat ini (atas dan kiri). Setelah itu, di semua pengontrol, Anda perlu membuat properti nav dan mendefinisikannya sebagai navigator (Anda dapat menggunakan tangan Anda, Anda dapat mewarisi, atau Anda dapat ciri). Jika perlu, Anda dapat menetapkan beberapa navigator.


Pendekatan ini menghilangkan hubungan langsung antara YIMP dan pengontrol. Semua interaksi dilakukan melalui satu objek, yang implementasinya dikendalikan oleh pengembang. Artinya, ini adalah Prinsip Ketergantungan Inversi yang sama dari SOLID atau Low Coupling dari GRASP .


Secara ideologis akan benar untuk menggunakan antarmuka, baik untuk controller maupun navigator. Tapi di sini saya memutuskan untuk pergi dengan cara paling sederhana dan tidak mengacaukan sistem. Pada akhirnya, DIP tidak berbicara tentang antarmuka, tetapi tentang abstraksi. Dan dalam hal ini, abstraksi adalah kesepakatan tentang keberadaan jenis properti tertentu dalam kelas tertentu. Apa yang kamu pikirkan


Tidak adanya hubungan langsung antara YIMP dan controller menjadi penting ketika modul muncul di sistem yang tidak tahu apa-apa tentang YIMP. Atau sebaliknya - modul yang ditulis di bawah YIMP dipasang di sistem yang tidak digunakan YIMP.


Dalam kasus pertama, YIMP tidak akan melihat properti nav di controller. Tidak akan ada kesalahan, tetapi menu Anda akan hilang dari layar, dan ID tindakan akan digunakan sebagai judul. Bagaimana menjadi Sangat sederhana - jika YIMP tidak dapat mengambil navigator dari controller, itu akan membuatnya melalui wadah DI menggunakan alias yimp-nav . Dengan menggunakan alias ini, Anda dapat mendaftarkan navigator default Anda sendiri, misalnya dengan menentukan dalam pengaturan aplikasi:


  'container' => [ 'definitions' => [ 'yimp-nav' => [ 'class' => '\your\own\Navigator', ] ] ], 

Dalam kasus kedua, navigator di controller akan, tetapi tidak akan ada yang membacanya. Dalam hal ini, disarankan untuk menulis pembungkus dalam modul untuk tampilan, yang akan mengadaptasi navigator dari pengontrol saat ini ke format yang diterima dalam Yii. Yaitu, tampilkan <h1> di area utama, <title> dan remah roti melewati parameter tampilan Yii::$app->view , dan tampilkan menu yang tepat dalam bentuk tombol.


Kesimpulan


YIMP sekarang diterbitkan tanpa versi. Saya tidak melihat alasan untuk mempublikasikan versi pra-rilis - semuanya terlalu sederhana untuk itu. Saya pikir lebih baik untuk menguji beberapa minggu pada proyek nyata dan segera beralih ke versi 1.0.0. Jadi kritik, komentar, dan bantuan pada GitHub sangat disambut.


Dan saya menyelesaikan modul yang mengimplementasikan kontrol akses. Cara menyelesaikan - Saya akan menulis.


Seperti yang Anda lihat, tidak ada yang rumit di sini. Saya yakin banyak dari Anda memiliki stok yang serupa. Dan saya akan sangat tertarik untuk mengetahui bagaimana Anda menyelesaikan tugas antarmuka pengguna di area admin Anda.


Terima kasih semuanya!

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


All Articles