Membangun paket transportasi tanpa menginstal MODX



Menulis paket Anda untuk MODX tidak mudah bagi pemula, dan pengembang yang berpengalaman terkadang memiliki waktu yang menyenangkan. Tetapi pemula takut, dan yang berpengalaman mengerti :).

Posting ini berbicara tentang bagaimana Anda dapat menulis dan membangun paket komponen untuk MODX tanpa menginstal dan mengkonfigurasi MODX itu sendiri. Levelnya di atas rata-rata, jadi Anda mungkin harus memeras otak Anda dalam beberapa kasus, tetapi itu sepadan.

Saya meminta detail di bawah kucing.

Suatu ketika, ketika MODX Revolution baru saja muncul, itu dalam versi beta awal, para pengembang belum tahu bagaimana bekerja dengannya dan bagaimana menulis plugin untuk itu. Ya, kecuali tim yang meneliti CMS. Dan tim, saya harus mengatakan, sebagian berhasil dan menyediakan sistem dengan kemampuan untuk dengan mudah mengumpulkan paket yang kemudian dapat diinstal melalui repositori, yang tampaknya logis. Namun sejak itu, bertahun-tahun telah berlalu dan persyaratan untuk paket dan perakitannya telah sedikit berubah.

Copy-paste itu jahat, meski tidak selalu


Selama beberapa bulan terakhir, saya dihantui oleh pemikiran mengapa, untuk membangun paket untuk MODX, Anda harus menginstalnya, membuat database, membuat admin, dll. Begitu banyak aksi ekstra. Tidak, tidak ada yang salah dengan ini jika Anda mengaturnya sekali dan kemudian menggunakannya. Banyak yang melakukannya. Tetapi bagaimana ketika Anda ingin mempercayakan naskah kepada skrip, dan pergi dan minum kopi sendiri?

Kebetulan bahwa pencipta MODX digunakan untuk bekerja dengan MODX itu sendiri dan menambahkan kelas ke paket langsung ke kernel. Mereka juga menulis komponen pertama, skrip build pertama, yang kemudian digunakan sebagai contoh oleh pengembang lain yang hanya menyalin solusi, tidak selalu menggali esensi dari apa yang terjadi. Dan saya berhasil.

Tetapi tugasnya adalah untuk mengotomatiskan perakitan paket, lebih disukai di server, selalu dengan perangkat lunak minimum yang diperlukan, dengan sumber daya minimal dan karenanya dengan kecepatan yang lebih besar. Tugasnya sudah diatur dan setelah mempelajari sumbernya, Jason yang mengerem dalam obrolan menemukan solusinya.

Dan yang mana?


Hal pertama yang saya temukan adalah bahwa kode yang bertanggung jawab untuk membangun paket secara langsung terletak di pustaka xPDO, dan di MODX hanya ada kelas pembungkus yang menyediakan API yang lebih nyaman dan agak lebih mudah untuk dikerjakan, tetapi hanya jika MODX diinstal. Oleh karena itu, mungkin hanya xPDO yang dapat digunakan, tetapi dalam kode, konstruktor objek xPDO mengharuskan Anda menentukan data untuk koneksi database.

public function __construct( $dsn, $username = '', $password = '', $options = [], $driverOptions= null ); 

Setelah menginterogasi Jason, menjadi jelas bahwa meskipun parameter perlu diatur, koneksi fisik nyata ke database terjadi tepat pada saat diperlukan. Malas memuat semua kemuliaan. Masalah kedua telah diatasi.

Masalah ketiga adalah masalah menghubungkan xPDO ke proyek. Composer segera muncul di pikiran, tetapi versi 2.x yang dijalankan oleh MODX saat ini tidak mendukung Composer, dan cabang 3.x menggunakan ruang nama dan nama kelas ditulis berbeda dari 2.x, yang mengarah pada konflik dan kesalahan. Secara umum, tidak kompatibel. Kemudian saya harus menggunakan alat git dan menghubungkan xPDO sebagai submodule.

Cara menggunakan submodula



Pertama, baca dokumentasinya .

Kemudian, jika ini adalah proyek baru, Anda perlu menambahkan submodule:

 $ git submodule add https://github.com/username/reponame 

Perintah ini akan mengkloning dan menginstal submodule di proyek Anda. Maka Anda perlu menambahkan folder submodule ke repositori Anda dengan perintah git add. Itu tidak akan menambahkan seluruh folder dengan submodule, tetapi akan menambah hanya git tautan ke komit terakhir dari submodule.

Agar pengembang lain dapat mengkloning proyek dengan semua dependensi, Anda perlu membuat konfigurasi .gitmodules untuk submodula. Dalam proyek Slackify, seperti ini:

 [submodule "_build/xpdo"] path = _build/xpdo url = https://github.com/modxcms/xpdo.git branch = 2.x 

Setelah itu, saat kloning, cukup tentukan flag rekursif dan git akan mengunduh semua repositori dependen.
Akibatnya, kita memiliki xPDO, xPDO dapat digunakan tanpa terhubung ke database, jika tidak diperlukan, xPDO dapat dihubungkan ke kode komponen sebagai ketergantungan eksternal (git submodule). Sekarang implementasi skrip build.

Mari kita mengerti


Saya akan menjelaskan skrip build dari add Slackify -pada baru-baru ini diposting oleh saya. Komponen ini gratis dan tersedia untuk umum di GitHub, yang akan memfasilitasi belajar mandiri.

Hubungkan xPDO


Kami menghilangkan tugas konstanta dengan nama paket dan panggilan lain yang diperlukan dan menghubungkan xPDO.

 require_once 'xpdo/xpdo/xpdo.class.php'; require_once 'xpdo/xpdo/transport/xpdotransport.class.php'; $xpdo = xPDO::getInstance('db', [ xPDO::OPT_CACHE_PATH => __DIR__ . '/../cache/', xPDO::OPT_HYDRATE_FIELDS => true, xPDO::OPT_HYDRATE_RELATED_OBJECTS => true, xPDO::OPT_HYDRATE_ADHOC_FIELDS => true, xPDO::OPT_CONNECTIONS => [ [ 'dsn' => 'mysql:host=localhost;dbname=xpdotest;charset=utf8', 'username' => 'test', 'password' => 'test', 'options' => [xPDO::OPT_CONN_MUTABLE => true], 'driverOptions' => [], ] ] ]); 

Saya menambahkan submodule xPDO ke folder _build, yang kita perlukan hanya pada tahap pengembangan dan perakitan paket dan yang tidak akan masuk ke arsip utama komponen. Salinan kedua xPDO di situs dengan MODX langsung yang tidak kita butuhkan.

Dalam pengaturan koneksi xPDO, saya mengatur nama database di dsn , tetapi tidak memainkan peran apa pun. Adalah penting bahwa folder cache di dalam xPDO dapat ditulis. Itu saja, xPDO diinisialisasi.

Membuat hack yang rumit dengan kelas-kelas


Saat menggunakan MODX yang terinstal saat membuat paket, semuanya sederhana, kami mengambil dan membuat objek dari kelas yang kami butuhkan. MODX benar-benar menemukan kelas yang diperlukan, menemukan implementasi yang diperlukan untuk kelas ini (kelas dengan postfix _mysql), yang tergantung pada database dan kemudian membuat objek yang diinginkan (karena fitur ini, Anda mungkin mendapatkan kesalahan ketika membangun paket yang kelasnya * _mysql tidak ditemukan, ini tidak menakutkan). Namun, kami tidak memiliki basis atau implementasi. Kita perlu entah bagaimana mengganti kelas yang diinginkan, yang sedang kita lakukan.

 class modNamespace extends xPDOObject {} class modSystemSetting extends xPDOObject {} 

Kami membuat kelas dummy (rintisan), yang diperlukan untuk membuat objek yang diinginkan. Ini tidak harus dilakukan jika xPDO tidak secara khusus memeriksa kelas objek yang dimiliki. Tapi dia memeriksa.

Tetapi ada beberapa kasus khusus ketika Anda perlu melakukan sedikit lebih dari sekedar mendefinisikan kelas. Ini adalah kasus ketergantungan antar kelas. Misalnya, kita perlu menambahkan plugin ke kategori. Dalam kode, hanya $category->addOne($plugin); tetapi dalam kasus kami ini tidak akan berhasil.

Jika Anda pernah melihat skema database MODX , Anda mungkin melihat elemen seperti agregat dan komposit. Itu ditulis tentang mereka dalam dokumentasi , tetapi jika dengan cara yang sederhana, mereka menggambarkan hubungan antar kelas.

Dalam kasus kami, mungkin ada beberapa plugin dalam suatu kategori, di mana elemen agregat bertanggung jawab atas kelas modCategory . Oleh karena itu, karena kita memiliki kelas tanpa implementasi konkret, kita perlu menunjukkan koneksi ini dengan tangan. Lebih mudah untuk melakukan ini dengan getFKDefinition metode getFKDefinition :

 class modCategory extends xPDOObject { public function getFKDefinition($alias) { $aggregates = [ 'Plugins' => [ 'class' => 'modPlugin', 'local' => 'id', 'foreign' => 'category', 'cardinality' => 'many', 'owner' => 'local', ] ]; return isset($aggregates[$alias]) ? $aggregates[$alias] : []; } } 

Di komponen kami, hanya plugin yang digunakan, jadi kami menambahkan tautan hanya untuk mereka. Setelah itu, metode addMany dari kelas modCategory dapat dengan mudah menambahkan plugin yang diperlukan ke kategori, dan kemudian ke paket.

Buat paket


 $package = new xPDOTransport($xpdo, $signature, $directory); 

Seperti yang Anda lihat, semuanya sangat, sangat sederhana. Di sini kami perlu melewati parameter $xpdo , yang kami inisialisasi di awal. Jika tidak untuk saat ini, tidak akan ada masalah 2. $signature - nama paket, termasuk versi, $directory - tempat paket akan ditempatkan dengan hati-hati. Dari mana variabel-variabel ini berasal, lihat sendiri di sumbernya.

Buat namespace dan tambahkan ke paket


Kami membutuhkan namespace untuk mengikat lexicons dan pengaturan sistem untuk itu. Dalam kasus kami, hanya untuk ini, yang lain belum dipertimbangkan.

 $namespace = new modNamespace($xpdo); $namespace->fromArray([ 'id' => PKG_NAME_LOWER, 'name' => PKG_NAME_LOWER, 'path' => '{core_path}components/' . PKG_NAME_LOWER . '/', ]); $package->put($namespace, [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::RESOLVE_FILES => true, xPDOTransport::RESOLVE_PHP => true, xPDOTransport::NATIVE_KEY => PKG_NAME_LOWER, 'namespace' => PKG_NAME_LOWER, 'package' => 'modx', 'resolve' => null, 'validate' => null ]); 

Bagian pertama jelas bagi siapa saja yang pernah menulis kode untuk MODX. Yang kedua, dengan tambahan paket, sedikit lebih rumit. Metode put mengambil 2 parameter: objek itu sendiri dan berbagai parameter yang menggambarkan objek ini dan perilaku yang mungkin terjadi pada saat menginstal paket. Sebagai contoh, xPDOTransport::UNIQUE_KEY => 'name' berarti bahwa untuk namespace, bidang name dengan nama namespace itu sendiri sebagai suatu nilai akan digunakan sebagai kunci unik dalam database. Anda dapat membaca lebih lanjut tentang parameter dalam dokumentasi xPDO , dan lebih baik dengan mempelajari kode sumber.

Dengan cara yang sama, Anda dapat menambahkan objek lain, seperti pengaturan sistem.

 $package->put($setting, [ xPDOTransport::UNIQUE_KEY => 'key', xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => true, 'class' => 'modSystemSetting', 'resolve' => null, 'validate' => null, 'package' => 'modx', ]); 

Buat kategori


Dengan tambahan kategori, saya memiliki lelucon terbesar ketika saya menemukan jawabannya. Elemen-elemen yang dimasukkan ke dalam kategori dalam model xPDO harus keduanya termasuk dalam kategori ini, mis. bersarang di dalamnya, dan hanya kemudian kategori itu sendiri harus disarangkan dalam paket. Dan pada saat yang sama, Anda perlu memperhitungkan hubungan antar kelas, yang telah saya jelaskan di atas. Butuh waktu yang cukup lama untuk memahami, menyadari, dan menerapkannya dengan benar.

 $package->put($category, [ xPDOTransport::UNIQUE_KEY => 'category', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => true, xPDOTransport::ABORT_INSTALL_ON_VEHICLE_FAIL => true, xPDOTransport::RELATED_OBJECTS => true, xPDOTransport::RELATED_OBJECT_ATTRIBUTES => [ 'Plugins' => [ xPDOTransport::UNIQUE_KEY => 'name', xPDOTransport::PRESERVE_KEYS => false, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ], 'PluginEvents' => [ xPDOTransport::UNIQUE_KEY => ['pluginid', 'event'], xPDOTransport::PRESERVE_KEYS => true, xPDOTransport::UPDATE_OBJECT => false, xPDOTransport::RELATED_OBJECTS => true ] ], xPDOTransport::NATIVE_KEY => true, 'package' => 'modx', 'validate' => $validators, 'resolve' => $resolvers ]); 

Itu terlihat mengerikan, tetapi tidak begitu terlihat. Parameter penting adalah xPDOTransport::RELATED_OBJECTS => true , yang menunjukkan bahwa kategori tersebut memiliki elemen bersarang yang juga perlu dikemas dan kemudian diinstal.

Karena sebagian besar modul berisi berbagai elemen (bongkahan, cuplikan, plugins), kategori dengan elemen adalah bagian terpenting dari paket transportasi. Oleh karena itu, di sini validator dan resolver ditentukan, yang dilakukan selama instalasi paket.
Validator dilakukan sebelum instalasi, resolvers - after.

Saya hampir lupa, sebelum mengemas kategori, kita perlu menambahkan elemen kita ke dalamnya. Seperti ini:

 $plugins = include $sources['data'] . 'transport.plugins.php'; if (is_array($plugins)) { $category->addMany($plugins, 'Plugins'); } 

Tambahkan data lain ke paket.


Dalam paket Anda perlu menambahkan file lain dengan lisensi, file dengan log perubahan dan file dengan deskripsi komponen. Jika perlu, Anda dapat menambahkan skrip khusus lain melalui atribut setup-options , yang akan menampilkan jendela sebelum menginstal paket. Ini adalah saat alih-alih "Instal" tombol "Opsi Instalasi". Dan dari versi MODX 2.4 menjadi mungkin untuk menentukan dependensi antara paket menggunakan atribut yang requires , dan di dalamnya Anda juga dapat menentukan versi PHP dan MODX.

 $package->setAttribute('changelog', file_get_contents($sources['docs'] . 'changelog.txt')); $package->setAttribute('license', file_get_contents($sources['docs'] . 'license.txt')); $package->setAttribute('readme', file_get_contents($sources['docs'] . 'readme.txt')); $package->setAttribute('requires', ['php' => '>=5.4']); $package->setAttribute('setup-options', ['source' => $sources['build'] . 'setup.options.php']); 

Kami berkemas


 if ($package->pack()) { $xpdo->log(xPDO::LOG_LEVEL_INFO, "Package built"); } 

Itu saja, ambil paket yang sudah jadi di _packages , well, atau dari mana Anda mengkonfigurasi assembly.

Apa hasilnya?


Hasilnya melebihi harapan saya, karena pendekatan ini, meskipun memberlakukan beberapa batasan dan di beberapa tempat menambah beberapa ketidaknyamanan, tetapi menang dalam hal kemungkinan aplikasi.

Untuk membangun paket, cukup jalankan 2 perintah:

 git clone --recursive git@github.com:Alroniks/modx-slackify.git cd modx-slackify/_build && php build.transport.php 

Yang pertama adalah kloning dari repositori dan submodulanya. Parameter penting adalah --recursive , berkat itu git akan mengunduh dan menginstal, selain kode komponen itu sendiri, semua dependensi digambarkan sebagai submodul.

Yang kedua adalah membangun paket secara langsung. Setelah itu, Anda dapat mengambil package-1.0.0-pl.transport.zip yang sudah _packages folder _packages dan memuatnya, misalnya, ke dalam repositori.

Prospeknya luas. Misalnya, Anda dapat mengonfigurasi kail di GitHub, yang, setelah melakukan ke cabang, akan menjalankan skrip di server Anda yang akan mengumpulkan paket dan meletakkannya di semua situs yang Anda miliki. Atau unggah versi baru ke beberapa repositori, dan pada saat itu Anda akan membuat kopi untuk diri sendiri, seperti yang saya katakan di awal. Atau Anda dapat membuat dan menulis tes untuk modul dan menjalankan uji coba dan membangun melalui Jenkins atau Travis. Ya, banyak skenario yang bisa Anda buat. Dengan pendekatan ini, melakukan ini sekarang jauh lebih mudah.

Ajukan pertanyaan, coba jawab.

PS Jangan lewat, letakkan bintang Slackify di GitHub .

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


All Articles