Rumah > pembangunan bahagian belakang > tutorial php > Anatomi carian pintar dalam seni Joomla Mencipta pemalam I.

Anatomi carian pintar dalam seni Joomla Mencipta pemalam I.

Barbara Streisand
Lepaskan: 2024-12-04 22:29:11
asal
449 orang telah melayarinya

Dalam artikel sebelum ini, kami telah berkenalan dengan keupayaan komponen carian pintar Joomla, bercakap tentang parameter dan konfigurasi pengindeksan berjadual menggunakan CRON. Mari mula mencipta kod untuk pemalam kita sendiri.

Senarai sumber

Sebelum memulakan bahagian teknikal, saya akan menyebut beberapa artikel yang secara langsung menyentuh topik utama. Serta artikel yang secara amnya merangkumi penciptaan dan/atau pengemaskinian pemalam untuk seni bina moden Joomla 4 / Joomla 5. Seterusnya, saya akan menganggap bahawa pembaca telah membacanya dan secara amnya mempunyai idea tentang cara membuat pemalam yang berfungsi untuk Joomla:

  • Mencipta pemalam Carian Pintar - dokumentasi Joomla rasmi. Ia adalah untuk Joomla 3, tetapi kebanyakan peruntukan kekal benar untuk Joomla 4 / Joomla 5
  • Membangunkan Pemalam Carian Pintar artikel daripada Majalah Komuniti Joomla pada tahun 2012.
  • Buku Joomla Extensions Development oleh Nicholas Dionysopoulos yang merangkumi pembangunan Joomla! sambungan di bawah Joomla versi 4 dan 5.
  • Bahagian Pangkalan Data pada portal dokumentasi baharu manual.joomla.org - untuk Joomla 4 dan Joomla 5. ## Bahagian teknikal. Pembangunan pemalam carian pintar Joomla 5 Komponen carian pintar berfungsi dengan pemalam pembekal data, yang tugas utamanya tetap sama - untuk memilih data dan memberikannya kepada komponen untuk pengindeksan. Tetapi dari masa ke masa, tugas pengindeksan semula juga jatuh ke dalam bidang tanggungjawab pemalam. Dalam artikel tersebut, kami akan menganggap bahawa kami menjalankan pengindeksan kandungan secara manual daripada panel pentadbir. Karya daripada CLI berbeza secara visual, tetapi intipatinya tetap sama.

Untuk pembangun yang berpengalaman, saya akan mengatakan bahawa pemalam carian memanjangkan kelas JoomlaComponentFinderAdministratorIndexerAdapter, fail kelas terletak dalam administrator/components/com_finder/src/Indexer/Adapter.php. Nah, kemudian mereka akan memikirkannya sendiri. Selain itu, sebagai contoh, anda boleh mengkaji pemalam carian pintar teras Joomla - untuk artikel, kategori, kenalan, teg, dll. - dalam folder plugin/finder. Saya bekerja pada pemalam carian pintar untuk komponen JoomShopping (komponen e-dagang Joomla) dan SW JProjects (komponen direktori sambungan Joomla anda sendiri dengan pelayan kemas kini), jadi nama kelas dan beberapa nuansa akan dikaitkan dengannya. Saya akan menunjukkan kebanyakannya menggunakan contoh JoomShopping. Penyelesaian kepada isu multibahasa adalah berdasarkan contoh SW JProjects.

Struktur fail pemalam carian pintar

Struktur fail pemalam carian pintar untuk Joomshopping tidak berbeza daripada yang biasa:

The anatomy of smart search in Joomla art Creating a plugin I.
Struktur fail pemalam searh pintar Joomla 5

Perkhidmatan fail/penyedia.php

Fail provider.php membolehkan anda mendaftarkan pemalam dalam bekas Joomla DI dan membolehkan anda mengakses kaedah pemalam dari luar menggunakan MVCFactory.

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Fail kelas pemalam

Ini ialah fail yang mengandungi kod kerja utama pemalam anda. Ia sepatutnya terletak dalam folder src/Extension. Dalam kes saya, kelas pemalam JoomlaPluginFinderWtjoomshoppingfinderExtensionWtjoomshoppingfinder berada dalam fail plugins/finder/wtjoomshoppingfinder/src/Extension/Wtjoomshoppingfinder.php. Ruang nama pemalam ialah JoomlaPluginFinderWtjoomshoppingfinderExtension.

Terdapat set sifat kelas minimum dan kaedah yang diperlukan untuk operasi (ia diakses, termasuk oleh kelas Penyesuai induk).

Sifat minimum yang diperlukan kelas

  • $extension - ialah nama komponen anda, yang mentakrifkan jenis kandungan anda. Contohnya, com_content. Dalam kes saya, ini ialah com_jshopping.
  • $context - ialah pengecam unik untuk pemalam, ia menetapkan konteks pengindeksan, di mana pemalam itu akan diakses. Sebenarnya, ini adalah nama kelas pemalam (elemen). Dalam kes kami, Wtjoomshoppingfinder.
  • $layout - ialah nama susun atur output untuk elemen hasil carian. Reka letak ini digunakan semasa memaparkan hasil carian. Contohnya, jika parameter $layout ditetapkan kepada artikel, maka mod paparan lalai akan mencari fail reka letak bernama default_article.php apabila anda perlu memaparkan hasil carian jenis ini. Jika fail sedemikian tidak ditemui, maka fail susun atur dengan nama default_result.php akan digunakan sebaliknya. Reka letak output dengan reka letak HTML terletak dalam components/com_finder/tmpl/search. Walau bagaimanapun, kami harus meletakkan reka letak kami sebagai pengganti - dalam folder templat html - templates/YOUR_TEMPLATE/html/com_finder/search. Dalam kes saya, saya menamakan produk reka letak dan fail itu dipanggil default_product.php. The anatomy of smart search in Joomla art Creating a plugin I.
  • $table - ialah nama jadual dalam pangkalan data yang kami akses untuk mendapatkan data, contohnya, #__content. Dalam kes saya, jadual utama dengan produk JoomShopping dipanggil #__jshopping_products.
  • $state_field - ialah nama medan dalam jadual pangkalan data yang bertanggungjawab untuk sama ada elemen yang diindeks diterbitkan atau tidak. Secara lalai, medan ini dipanggil keadaan. Walau bagaimanapun, dalam kes JoomShopping, medan ini dipanggil product_publish.
<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kaedah minimum yang diperlukan kelas

  • setup() : bool - ialah kaedah untuk pra-konfigurasi pemalam, menyambungkan perpustakaan, dsb. Kaedah ini dipanggil semasa pengindeksan semula (kaedah indeks semula()), pada acara onBeforeIndex. Kaedah mesti kembali benar, jika tidak pengindeksan akan terganggu.
  • index(): void - ialah kaedah untuk memulakan pengindeksan sendiri. Ia mengumpul objek struktur yang dikehendaki daripada data pertanyaan SQL mentah, yang kemudiannya dihantar ke kelas JoomlaComponentFinderAdministratorIndexerIndexer untuk pengindeksan. Kaedah dijalankan untuk setiap elemen yang diindeks. Argumen kaedah ialah $item - hasil pertanyaan kepada pangkalan data, diformatkan dalam kelas JoomlaComponentFinderAdministratorIndexerResult.
  • getListQuery() : JoomlaDatabaseDatabaseQuery - ialah kaedah untuk mendapatkan senarai item yang diindeks…

... dan di sini kita mula menyelami butirannya, kerana kaedah getListQuery() sebenarnya tidak wajib, walaupun pada hakikatnya kedua-dua dokumentasi dan kebanyakan artikel membincangkannya.

The anatomy of smart search in Joomla art Creating a plugin I.
Sebarang gambar mengenai topik "skema kompleks" akan dilakukan di sini.

Selami butirannya. Struktur data elemen diindeks.

Sungguh menakjubkan berapa kali beberapa maklumat atau idea kadang-kadang melewati kita dalam bulatan sebelum kita perasan dan menyedarinya! Banyak perkara, berada di depan mata selama lebih daripada satu tahun, masih tidak mencapai kesedaran, dan perhatian kita tertumpu kepada mereka hanya selepas pengalaman bertahun-tahun.

Berkaitan dengan Joomla, atas sebab tertentu, penglihatan tidak serta-merta datang bahawa komponennya menganggap beberapa jenis ciri seni bina biasa Joomla (walaupun ini adalah fakta yang jelas). Termasuk pada tahap struktur jadual pangkalan data. Mari lihat beberapa medan jadual kandungan Joomla. Saya akan membuat tempahan bahawa nama lajur tertentu tidak begitu penting kepada kami (anda sentiasa boleh menanyakan SELECT nama AS tajuk), berapa banyak struktur data untuk satu elemen diindeks:

  • id - autoincrement
  • asset_id - id entri dalam jadual #__assets, tempat hak akses kumpulan dan pengguna disimpan untuk setiap elemen tapak: artikel, produk, menu, modul, pemalam dan segala-galanya. Joomla menggunakan corak Senarai Kawalan Akses (ACL).
  • tajuk - tajuk elemen
  • bahasa - bahasa unsur
  • introteks - teks pengenalan atau penerangan ringkas unsur yang boleh dilihat
  • teks penuh - teks penuh item, penerangan penuh produk, dsb.
  • nyatakan - bendera logik yang bertanggungjawab untuk status penerbitan: sama ada item itu diterbitkan atau tidak.
  • catid - ID kategori item. Joomla bukan sahaja mempunyai "halaman tapak" seperti dalam CMS lain. Terdapat entiti kandungan (artikel, kenalan, produk, dll.) yang mesti tergolong dalam beberapa kategori.
  • dicipta- tarikh item itu dicipta.
  • akses - id kumpulan hak akses (pengguna tapak yang tidak dibenarkan (tetamu), semua, berdaftar, dsb.)
  • metakey - kata kunci meta untuk elemen. Ya, sejak 2009 mereka tidak digunakan oleh Google. Tetapi dalam Joomla, ia kekal dari segi sejarah, kerana medan ini digunakan dalam modul artikel yang serupa untuk mencari artikel yang sebenarnya serupa menggunakan kata kunci tertentu.
  • metadesc - perihalan meta elemen
  • publish_up dan publish_down - tarikh permulaan penerbitan dan nyahpenerbitan elemen. Ini lebih merupakan pilihan, tetapi ia terdapat dalam banyak komponen.

Jika kita membandingkan jadual #__kandungan (artikel Joomla), #__contact_details (komponen kenalan), #__tags (tag Joomla), #__categories (komponen kategori Joomla), maka kita akan menemui hampir semua jenis data yang disenaraikan di mana-mana sahaja.

Jika komponen yang pemalam carian pintar dicipta mengikut "cara Joomla" dan mewarisi seni binanya, maka anda boleh melakukannya dengan kaedah minimum dalam kelas pemalam. Jika pembangun memutuskan untuk tidak mencari cara mudah dan mengikut cara mereka sendiri, maka anda perlu melalui cara yang sukar, mentakrifkan semula hampir semua kaedah kelas Penyesuai.

kaedah getListQuery().

Kaedah ini dipanggil dalam 3 kes:

  1. Kaedah getContentCount() kelas Penyesuai adalah untuk mendapatkan bilangan item diindeks (berapa jumlah artikel, jumlah produk, dll.). The anatomy of smart search in Joomla art Creating a plugin I. Proses pengindeksan Joomla Smart searh Anda boleh melihat bilangan item diindeks dalam mod nyahpepijat.
  2. Kaedah getItem($id) kelas Penyesuai adalah untuk mendapatkan elemen diindeks tertentu oleh idnya. Kaedah getItem() pula dipanggil dalam kaedah reindex($id) - semasa pengindeksan semula.
  3. Kaedah getItems($offset, $limit, $query = null) kelas Penyesuai ialah kaedah untuk mendapatkan senarai elemen diindeks. Offset dan had ditetapkan berdasarkan tetapan komponen - berapa banyak elemen diindeks harus disertakan dalam "himpunan". The anatomy of smart search in Joomla art Creating a plugin I. Saiz kelompok pengindeks tetapan carian pintar Joomla 5

Mari kita lihat contoh pelaksanaan dalam pemalam teras Joomla:

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Kaedah getListQuery() mengembalikan objek DatabaseQuery, objek pembina pertanyaan, di mana nama jadual dan medan untuk pemilihan sudah ditentukan. Bekerja dengannya teruskan dalam kaedah yang memanggilnya.

Jika getListQuery() dipanggil daripada getContentCount() dalam objek $query DatabaseQuery, nilai set untuk pilih digantikan dengan COUNT(*).

Jika getListQuery() dipanggil daripada getItem($id), syarat $query->where('a.id = ' . (int) $id) dan hanya elemen tertentu dipilih. Dan di sini kita melihat bahawa kelas Penyesuai induk mengandungi nama jadual dalam pertanyaan sebagai a.*. Ini bermakna kita juga harus menggunakan awalan ini dalam pelaksanaan kaedah getListQuery() kita.

Dalam kes memanggil getListQuery() daripada getItems(), $offset dan $limit ditambahkan pada pertanyaan yang telah kami bina untuk bergerak melalui senarai elemen untuk pengindeksan.
Ringkasan: getListQuery() - mesti mengandungi "bekas kerja" untuk tiga pertanyaan SQL yang berbeza. Dan tiada apa-apa yang amat sukar untuk melaksanakan Joomla di sini. Tetapi, jika perlu, anda boleh melaksanakan 3 kaedah sendiri tanpa membuat getListQuery().

Cara bukan Joomla: Dalam kes JoomShopping, saya mendapati fakta bahawa produk boleh mempunyai beberapa kategori dan mengikut sejarah komponen id kategori (catid) untuk produk itu disimpan dalam jadual berasingan. Pada masa yang sama, selama bertahun-tahun tidak mungkin untuk menentukan kategori utama produk. Setelah menerima kategori produk, pertanyaan telah dihantar ke jadual dengan kategori, di mana hanya hasil pertanyaan pertama diambil, diisih mengikut id kategori lalai - iaitu menaik. Jika kami menukar kategori semasa mengedit produk, maka kategori produk utama ialah kategori yang mempunyai nombor id yang lebih rendah. URL produk adalah berdasarkannya dan produk itu boleh melompat dari satu kategori ke kategori yang lain.

Tetapi, hampir 2 tahun yang lalu, tingkah laku JoomShopping ini telah diperbaiki. Memandangkan komponen itu mempunyai sejarah yang panjang, khalayak yang ramai dan tidak boleh mematahkan keserasian ke belakang sahaja, pembetulan itu dibuat sebagai pilihan. Keupayaan untuk menentukan kategori utama untuk produk mesti didayakan dalam tetapan komponen. Kemudian id_kategori_utama akan diisi dalam jadual dengan produk.

Tetapi fungsi ini dilumpuhkan secara lalai. Dan dalam pemalam carian pintar, kita perlu mendapatkan parameter komponen JoomShopping, lihat jika pilihan untuk menentukan kategori produk utama didayakan (dan ia mungkin didayakan baru-baru ini dan kategori utama untuk sesetengah produk tidak dinyatakan - juga nuansa...) dan menjana pertanyaan SQL untuk menerima produk berdasarkan parameter komponen: sama ada pertanyaan mudah di mana kami menambah medan utama_kategori_id, atau permintaan JOIN untuk mendapatkan id kategori dengan cara lama yang salah.

Segera, nuansa multibahasa muncul dalam permintaan ini. Mengikut cara Joomla, elemen berasingan dicipta untuk setiap bahasa tapak dan perkaitan disediakan di antara mereka. Jadi, untuk bahasa Rusia - satu artikel. Artikel yang sama dalam bahasa Inggeris sedang dibuat secara berasingan. Kemudian kami menyambungkannya antara satu sama lain menggunakan perkaitan bahasa dan apabila menukar bahasa pada bahagian hadapan Joomla, kami akan diubah hala dari satu artikel ke artikel yang lain.

Ini bukan cara ia dilakukan dalam JoomShopping: data untuk semua bahasa disimpan dalam jadual yang sama dengan produk (Ok). Menambah data untuk bahasa lain dilakukan dengan menambahkan lajur dengan akhiran bahasa ini (hmm...). Iaitu, kita tidak mempunyai hanya tajuk atau medan nama dalam pangkalan data. Tetapi terdapat medan name_ru-RU, name_en-GB, dsb.
The anatomy of smart search in Joomla art Creating a plugin I.
serpihan struktur jadual produk Joomla JoomShopping
Pada masa yang sama, kita perlu mereka bentuk pertanyaan SQL universal supaya ia boleh diindeks daripada kedua-dua panel pentadbir dan CLI. Pada masa yang sama, memilih bahasa pengindeksan semasa melancarkan CLI menggunakan CRON juga merupakan satu tugas. Saya akui, semasa menulis artikel ini, saya telah menangguhkan penyelesaian sepenuhnya untuk masalah ini buat sementara waktu. Bahasa dipilih menggunakan kaedah getLangTag() kami sendiri, di mana kami sama ada mengambil bahasa utama daripada parameter JoomShopping atau bahasa lalai tapak. Maksudnya, setakat ini penyelesaian ini hanya untuk tapak ekabahasa. Carian dalam bahasa yang berbeza tidak akan berfungsi lagi.

Walau bagaimanapun, 3 bulan kemudian saya menyelesaikan masalah ini, tetapi sudah berada dalam pemalam carian pintar untuk komponen SW JProjects. Saya akan memberitahu anda tentang penyelesaiannya dengan lebih lanjut.

Sementara itu, mari lihat apa yang berlaku untuk JoomShopping

<?php

/**
 * @package     Joomla.Plugin
 * @subpackage  Finder.Wtjoomshoppingfinder
 *
 * @copyright   (C) 2023 Open Source Matters, Inc. <https://www.joomla.org>
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

\defined('_JEXEC') or die;

use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\Database\DatabaseInterface;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Finder\Wtjoomshoppingfinder\Extension\Wtjoomshoppingfinder;

return new class () implements ServiceProviderInterface {
    /**
     * Registers the service provider with a DI container.
     *
     * @param   Container  $container  The DI container.
     *
     * @return  void
     *
     * @since   4.3.0
     */
    public function register(Container $container)
    {
        $container->set(
            PluginInterface::class,
            function (Container $container) {
                $plugin     = new Wtjoomshoppingfinder(
                    $container->get(DispatcherInterface::class),
                    (array) PluginHelper::getPlugin('finder', 'wtjoomshoppingfinder')
                );
                $plugin->setApplication(Factory::getApplication());

                // Our plugin uses DatabaseTrait, so the setDatabase() method appeared 
                // If it is not present, then we use only setApplication().
                $plugin->setDatabase($container->get(DatabaseInterface::class));

                return $plugin;
            }
        );
    }
};
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk
Salin selepas log masuk

Check point

Kami mencipta kaedah untuk menanyakan pangkalan data daripada Joomla dan belajar banyak tentang cara pemalam carian pintar berfungsi.

Dalam artikel seterusnya, kami akan mencipta kaedah untuk mengindeks kandungan dan melengkapkan penciptaan pemalam. Kami juga akan membiasakan diri dengan cara item yang diindeks disimpan dalam pangkalan data dan memahami mengapa ini penting dan menyelesaikan masalah mengindeks kandungan untuk komponen berbilang bahasa dengan pelaksanaan multibahasa yang tidak standard.

Sumber Komuniti Joomla

  • https://joomla.org/
  • Artikel ini dalam Majalah Komuniti Joomla
  • Sembang Komuniti Joomla dalam Mattermost (baca lebih lanjut)

Atas ialah kandungan terperinci Anatomi carian pintar dalam seni Joomla Mencipta pemalam I.. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

sumber:dev.to
Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn
Artikel terbaru oleh pengarang
Tutorial Popular
Lagi>
Muat turun terkini
Lagi>
kesan web
Kod sumber laman web
Bahan laman web
Templat hujung hadapan