Cum să structurezi un plugin
Aceasta nu este o întrebare despre cum să construiești un plugin WordPress. Mai degrabă, ce ghiduri, dacă există, ar putea fi aplicate pentru organizarea arhitecturii de fișiere a oricărui plugin.
Unele alte limbaje de programare sau biblioteci au modalități foarte stricte de organizare a directoarelor și fișierelor. Uneori acest lucru este deranjant și evidențiază libertatea pe care o oferă PHP, dar pe de altă parte plugin-urile WordPress sunt construite în orice mod determinat de autorul lor.
Nu există un răspuns corect, dar speranța mea este să rafinăm modul în care eu și alții construim plugin-uri pentru a le face mai prietenoase pentru alți dezvoltatori să le analizeze, mai ușor de depanat, mai ușor de navigat și posibil mai eficiente.
Întrebarea finală: care crezi că este cea mai bună modalitate de a organiza un plugin?
Mai jos sunt câteva structuri exemplu, dar în niciun caz nu este o listă exhaustivă. Simțiți-vă liberi să adăugați propriile recomandări.
Structura Implicită Presupusă
/wp-content
/plugins
/my-plugin
my-plugin.php
Metoda Model View Controller (MVC)
/wp-content
/plugins
/my-plugin
/controller
Controller.php
/model
Model.php
/view
view.php
my-plugin.php
Cele trei părți ale MVC:
- Modelul interacționează cu baza de date, interogând și salvând date, și conține logica.
- Controlerul ar conține tag-uri de șablon și funcții pe care le-ar utiliza view-ul.
- View-ul este responsabil pentru afișarea datelor furnizate de model așa cum sunt construite de controller.
Metoda organizată pe tipuri
/wp-content
/plugins
/my-plugin
/admin
admin.php
/assets
css/
images/
/classes
my-class.php
/lang
my-es_ES.mo
/templates
my-template.php
/widgets
my-widget.php
my-plugin.php
WordPress Plugin Boilerplate
Disponibil pe Github
Bazat pe API-ul pentru Plugin-uri, Standarde de Codare, și Standarde de Documentație.
/wp-content
/plugins
/my-plugin
/admin
/css
/js
/partials
my-plugin-admin.php
/includes
my_plugin_activator.php
my_plugin_deactivator.php
my_plugin_i18n.php
my_plugin_loader.php
my_plugin.php
/languages
my_plugin.pot
/public
/css
/js
/partials
my-plugin-public.php
LICENSE.txt
README.txt
index.php
my-plugin.php
uninstall.php
Metoda organizată liber
/wp-content
/plugins
/my-plugin
css/
images/
js/
my-admin.php
my-class.php
my-template.php
my-widget.php
my-plugin.php

Rețineți că toate plugin-urile sunt "controlere" conform standardelor WordPress.
Depinde de ce ar trebui să facă plugin-ul, dar în toate cazurile aș încerca să separ afișarea pe ecran de codul PHP cât mai mult posibil.
Iată o modalitate ușoară de a face acest lucru - mai întâi, definiți o funcție care încarcă template-ul:
function my_plugin_load_template(array $_vars){
// nu puteți lăsa locate_template să încarce template-ul vostru
// deoarece dezvoltatorii WP au făcut să nu puteți transmite
// variabile în template :(
$_template = locate_template('my_plugin', false, false);
// folosiți cel implicit dacă tema nu îl are
if(!_$template)
$_template = 'views/template.php';
// încărcați-l
extract($_vars);
require $template;
}
Acum, dacă plugin-ul folosește un widget pentru a afișa date:
class Your_Widget extends WP_Widget{
...
public function widget($args, $instance){
$title = apply_filters('widget_title', $instance['title'], $instance, $this->id_base);
// acest widget afișează ultimele 5 "filme"
$posts = new WP_Query(array('posts_per_page' => 5, 'post_type' => 'movie'));
if($title)
print $before_title . $title . $after_title;
// aici ne bazăm pe template pentru a afișa datele pe ecran
my_plugin_load_template(array(
// variabile pe care doriți să le expuneți în template
'posts' => $posts,
));
print $before_widget;
}
...
}
Template-ul:
<?php while($posts->have_posts()): $posts->the_post(); ?>
<p><?php the_title(); ?></p>
<?php endwhile; ?>
Fișiere:
/plugins/my_plugin/plugin.php <-- doar hook-uri
/plugins/my_plugin/widget.php <-- clasa widget, dacă aveți un widget
/themes/twentyten/my_plugin.php <-- template
/plugins/my_plugin/views/template.php <-- template de rezervă
Unde puneți CSS-ul, JS-ul, imaginile sau cum proiectați containerul pentru hook-uri este mai puțin important. Cred că este o chestiune de preferință personală.

Depinde de plugin. Acesta este structura mea de bază pentru aproape fiecare plugin:
my-plugin/
inc/
Orice fișiere PHP suplimentare specifice plugin-ului se află aici
lib/
Clase de bibliotecă, fișiere css, js și alte fișiere pe care le folosesc cu multe
plugin-uri se află aici
css/
js/
images/
lang/
Fișiere de traducere
my-plugin.php
readme.txt
Acesta ar fi ceva care ar merge în folderul lib
.
Dacă este un plugin deosebit de complex, cu multă funcționalitate în zona de administrare, aș adăuga un folder admin
pentru a conține toate acele fișiere PHP. Dacă plugin-ul face ceva precum înlocuirea fișierelor de temă incluse, poate exista și un folder template
sau theme
.
Deci, o structură de directoare ar putea arăta astfel:
my-plugin/
inc/
lib/
admin/
templates/
css/
js/
images/
lang/
my-plugin.php
readme.txt

Ai include și fișierele CSS și JS ale administratorului în folderul /admin? Astfel, având un alt /css și /js în /admin?

După părerea mea, cea mai ușoară, puternică și ușor de întreținut cale este utilizarea unei structuri MVC, iar WP MVC este conceput să facă scrierea plugin-urilor MVC foarte simplă (deși sunt puțin părtinitor...). Cu WP MVC, pur și simplu creezi modelele, view-urile și controllerele, iar restul este gestionat în spatele scenei pentru tine.
Se pot crea controllere și view-uri separate pentru secțiunile publice și cele de administrare, iar întregul cadru beneficiază de multe dintre caracteristicile native ale WordPress. Structura fișierelor și mare parte din funcționalitate sunt exact la fel ca în cele mai populare framework-uri MVC (Rails, CakePHP, etc).
Mai multe informații și un tutorial pot fi găsite aici:

Folosim un mix al tuturor metodelor. În primul rând, utilizăm Zend Framework 1.11 în pluginurile noastre și, din această cauză, a trebuit să folosim o structură similară pentru fișierele de clasă datorită mecanismului de autoload.
Structura pluginului nostru principal (care este folosit ca bază de toate pluginurile noastre) arată astfel:
webeo-core/
css/
images/
js/
languages/
lib/
Webeo/
Core.php
Zend/
/** fișiere ZF **/
Loader.php
views/
readme.txt
uninstall.php
webeo-core.php
- WordPress apelează fișierul
webeo-core.php
din directorul rădăcină al pluginului. - În acest fișier setăm calea de includere PHP și înregistrăm hook-urile de activare și dezactivare pentru plugin.
- De asemenea, avem o clasă
Webeo_CoreLoader
în acest fișier, care setează unele constante ale pluginului, inițializează autoloaderul de clasă și face un apel către metoda de setup a claseiCore.php
din folderullib/Webeo
. Acest lucru rulează pe hook-ul de acțiuneplugins_loaded
cu o prioritate de9
. - Clasa
Core.php
este fișierul nostru de bootstrap pentru plugin. Numele este bazat pe numele pluginului.
După cum puteți vedea, avem un subdirector în folderul lib
pentru toate pachetele noastre de furnizori (Webeo
, Zend
). Toate subpachetele din cadrul unui furnizor sunt structurate după modulul în sine. Pentru un nou formular de administrare Mail Settings
, am avea următoarea structură:
webeo-core/
...
lib/
Webeo/
Form/
Admin/
MailSettings.php
Admin.php
Core.php
Form.php
Subpluginurile noastre au aceeași structură cu o singură excepție. Mergem cu un nivel mai adânc în folderul furnizorului pentru a rezolva conflictele de nume în timpul evenimentului de autoload. De asemenea, apelăm clasa de bootstrap a pluginului De ex. Faq.php
cu prioritatea 10
în cadrul hook-ului plugins_loaded
.
webeo-faq/ (folosește/extinde webeo-core)
css/
images/
js/
languages/
lib/
Webeo/
Faq/
Faq.php
/** toate fișierele de clasă relevante pentru plugin **/
views/
readme.txt
uninstall.php
webeo-faq.php
Probabil voi redenumi folderul lib
în vendors
și voi muta toate folderele publice (css, images, js, languages) într-un folder numit public
în următoarea versiune.

Ca mulți au răspuns deja, chiar depinde de ceea ce ar trebui să facă plugin-ul, dar iată structura mea de bază:
my-plugin/
admin/
conține toate fișierele pentru administrare din back-end
js/
conține toate fișierele JavaScript pentru back-end
css/
conține toate fișierele CSS pentru back-end
images/
conține toate imaginile pentru back-end
admin_file_1.php fișier de funcționalități pentru back-end
admin_file_2.php alt fișier de funcționalități pentru back-end
js/
conține toate fișierele JavaScript pentru front-end
css/
conține toate fișierele CSS pentru front-end
inc/
conține toate clasele helper
lang/
conține toate fișierele de traducere
images/
conține toate imaginile pentru front-end
my-plugin.php fișierul principal al plugin-ului cu meta informații, include mostly, action și filter hooks
readme.txt
changelog.txt
license.txt

Prefer următoarea structură pentru plugin-uri, deși de obicei aceasta se schimbă în funcție de cerințele specifice ale plugin-ului.
wp-content/
plugins/
my-plugin/
inc/
Fișiere specifice doar pentru acest plugin
admin/
Fișiere pentru gestionarea sarcinilor administrative
lib/
Clase de bibliotecă/ajutătoare se află aici
css/
Fișiere CSS pentru plugin
js/
Fișiere JavaScript
images/
Imagini pentru pluginul meu
lang/
Fișiere de traducere
plugin.php
Acesta este fișierul principal care include/apelează alte fișiere
README
De obicei pun aici detalii despre licență, plus informații utile
Încă nu am creat un plugin WordPress care să necesite o arhitectură în stil MVC, dar dacă ar fi să o fac, aș organiza-o cu un director MVC separat, care să conțină view-uri/controlere/modele.

Toate plugin-urile mele urmează această structură, care pare să fie foarte asemănătoare cu ce fac majoritatea altor dezvoltatori:
plugin-folder/
admin/
css/
images/
js/
core/
css/
images/
js/
languages/
library/
templates/
plugin-folder.php
readme.txt
changelog.txt
license.txt
plugin-folder.php este de obicei o clasă care încarcă toate fișierele necesare din folderul core/. Cel mai adesea pe hook-ul init sau plugins_loaded.
Obișnuiam să prefixez toate fișierele mele, dar așa cum a menționat @kaiser mai sus, este cu adevărat redundant și am decis recent să renunț la această practică pentru viitoarele plugin-uri.
Folderul library/ conține toate bibliotecile externe de ajutor de care ar putea depinde plugin-ul.
În funcție de plugin, poate exista și un fișier uninstall.php în rădăcina plugin-ului. Totuși, de cele mai multe ori acest lucru este gestionat prin register_uninstall_hook().
Evident, unele plugin-uri s-ar putea să nu necesite fișiere de administrare sau template-uri etc., dar structura de mai sus funcționează pentru mine. În final, trebuie doar să găsești o structură care funcționează pentru tine și apoi să te ții de ea.
De asemenea, am un plugin de pornire, bazat pe structura de mai sus, pe care îl folosesc ca punct de plecare pentru toate plugin-urile mele. Tot ce trebuie să fac apoi este să caut și să înlocuiesc prefixele funcțiilor/claselor și să încep. Când mai prefixam fișierele, acesta era un pas în plus pe care trebuia să-l fac (și destul de enervant), dar acum trebuie doar să redenumesc folderul plugin-ului și fișierul principal al plugin-ului.

Logica mea este că, cu cât este mai mare plugin-ul, cu atât mai multă structură folosesc.
Pentru plugin-uri mari tind să folosesc MVC.
Folosesc acest punct de plecare și omit ceea ce nu este necesar.
controller/
frontend.php
wp-admin.php
widget1.php
widget2.php
model/
standard-wp-tables.php // dacă este necesar, împărțit în fișiere separate
custom-tabel1.php
custom-tabel2.php
view/
helper.php
frontend/
fisiere...php
wp-admin/
fisiere...php
widget1/
fisier...php
widget2/
fisier...php
css/
js/
imagini/
library/ //doar php, în principal pentru Zend Framework, din nou dacă este necesar
constants.php //tind să o folosesc des
plugin.php //fișier de inițializare
install-unistall.php //doar pentru plugin-uri mari

De asemenea, consultă acest boilerplate excelent pentru widget-uri WP. Oferă indicații valoroase privind structurile (chiar dacă nu există o clasă sau un folder pentru modele separate).

O abordare mai puțin comună pentru structurarea fișierelor și directorelor unui plugin este cea bazată pe tipul de fișier. Merită menționată aici pentru completitudine:
plugin-name/
js/
sparkle.js
shake.js
css/
style.css
scss/
header.scss
footer.scss
php/
class.php
functions.php
plugin-name.php
uninstall.php
readme.txt
Fiecare director conține doar fișiere de acel tip. Este important de menționat că această abordare are limitări atunci când aveți mai multe tipuri de fișiere .png .gif .jpg
care ar putea fi mai bine organizate într-un singur director, de exemplu images/
.

Am dezvoltat un șablon de repository GitHub pentru plugin-uri WordPress, încapsulând peste 10 ani de experiență într-o structură bine definită!
https://github.com/EdwardBock/wordpress-plugin-starterkit
Șablonul respectă standardele PSR-4 pentru a minimiza includerea de șiruri de caractere și utilizează namespace-uri pentru o denumire concisă a claselor și o clasă componentă de templating. De asemenea, include un fișier docker compose pentru a permite dezvoltarea plugin-ului în izolare. Dar poate fi plasat și într-un proiect existent, funcționând la fel de bine.
Bucurați-vă să explorați și nu ezitați să contribuiți!
