Utilizarea register_activation_hook în clase

10 aug. 2017, 16:36:08
Vizualizări: 22.2K
Voturi: 10

Încerc să dezvolt un plugin pentru scopuri SEO de bază, deoarece mulți dintre cunoscuții mei nu agreează Yoast. Tocmai am început plugin-ul și construiesc mesajul de activare afișat utilizatorului când activează plugin-ul. Am probleme cu îmbinarea între OOP și funcțiile integrate WordPress și nu sunt sigur unde greșesc.

Singura modalitate prin care pot face acest lucru să funcționeze este prin crearea unei noi instanțe a clasei SEO_Plugin_Activation în constructorul fișierelor mele principale și apoi apelarea metodei activatePlugin din acea clasă. Simt că acest lucru este inutil. De asemenea, cred că modul în care execut funcțiile mele în fișierul clasei de activare nu are prea mult sens. Dar este singura modalitate prin care pot face să funcționeze momentan.

Nu sunt sigur dacă ceea ce fac este pentru că nu înțeleg tehnicile OOP 100% sau dacă nu utilizez corect API-ul WordPress. Am inclus trei exemple de cod în următoarea ordine:

  1. Fișierul principal al plugin-ului
  2. Fișierul clasei care gestionează cerințele mele de activare
  3. Ce speram eu de fapt să pot face.

seo.php (fișierul principal al plugin-ului)

<?php
/*
... informații generice despre plugin
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    $activate = new SEO_Plugin_Activation();
    $activate->activatePlugin();
  }

}

new SEO();
?>

class-plugin-activation.php

<?php
class SEO_Plugin_Activation {

  function __construct() {
    register_activation_hook(__FILE__ . '../seo.php', array($this, 'activatePlugin'));
    add_action('admin_notices', array($this, 'showSitemapInfo'));
  }

  function activatePlugin() {
    set_transient('show_sitemap_info', true, 5);
  }

  function showSitemapInfo() {
    if(get_transient('show_sitemap_info')) {
      echo '<div class="updated notice is-dismissible">' .
              'Fișierele sitemap pot fi găsite la următoarele link-uri: ' .
            '</div>';
      delete_transient('show_sitemap_info');
    }
  }

}
?>

seo.php (Ce speram să pot face)

<?php
/*
 ... bla bla bla
*/

require_once(dirname(__FILE__) . '/admin/class-plugin-activation.php');

class SEO {
  function __construct() {
    register_activation_hook(__FILE__, array($this, 'wp_install'));
  }

  function wp_install() {
    $activate = new SEO_Plugin_Activation();
    // Execută aici niște metode care s-ar ocupa 
    // de tot bootstrapping-ul activării plugin-ului meu
  }

}

new SEO();
?>

Am încercat să o fac în modul în care prezint în al treilea script, dar nu am avut succes. În momentul de față, mesajul se afișează corect, fără mesaje de eroare.

4
Comentarii

Cauți reasigurare sau modul corect de a face OO în WordPress? Din păcate, nu există o cale canonică de urmat aici, singurele lucruri pe care le pot împărtăși sunt lucruri generice despre OO. De exemplu, nu crea SEO_Plugin_Activation în interiorul clasei tale SEO, folosește injectarea dependențelor și trimite-o ca argument în schimb, și nu defini și folosi o clasă în același fișier, încărcarea unui fișier care descrie o clasă nu ar trebui să o și execute altfel este imposibil să scrii teste unitare

Tom J Nowell Tom J Nowell
10 aug. 2017 16:47:12

Mulțumesc pentru răspuns. Sincer, nu prea mă interesează să respect "100% Wordpress Way". Judecând după comentariul tău, se pare că clar îmi lipsește o înțelegere a principiilor OOP. Am avut impresia că trebuie să creez o nouă clasă în interiorul unei clase pentru a-i apela metodele.

Dan Zuzevich Dan Zuzevich
10 aug. 2017 16:50:59

Poți face asta, dar nu este la fel de flexibil, de exemplu dacă vrei să testezi clasa SEO, cum ai înlocui clasa SEO_Plugin_Activation cu un obiect mock fără să o modifici? Oricum, se pare că această întrebare este despre o neînțelegere a modului în care funcționează register_activation_hook

Tom J Nowell Tom J Nowell
10 aug. 2017 17:04:04

Am înțeles, mulțumesc mult. Sincer, nu cred că voi efectua teste pe orice, deoarece asta depășește nivelul meu actual de cunoștințe.

Dan Zuzevich Dan Zuzevich
10 aug. 2017 17:21:13
Toate răspunsurile la întrebare 4
6
14

După ce am recitit întrebarea ta, cred că am identificat problema, care provine dintr-o neînțelegere a modului în care funcționează register_activation_hook, combinată cu o oarecare confuzie privind modul în care îți structurezi codul și ce înseamnă de fapt acest proces.

Partea 1: register_activation_hook

Această funcție acceptă 2 parametri:

register_activation_hook( string $file, callable $function )

Primul parametru, $file este fișierul principal al plugin-ului, nu fișierul care conține codul pe care vrei să-l execuți. Este folosit în același mod ca plugins_url, deci ai nevoie de valoarea __FILE__, mai exact valoarea sa din fișierul rădăcină al plugin-ului care conține antetul.

Al doilea parametru este un callable și funcționează cum te-ai aștepta, dar de fapt utilizează intern add_action

Când un plugin este activat, se apelează hook-ul 'activate_NUMEPLUGIN'. În numele acestui hook, NUMEPLUGIN este înlocuit cu numele plugin-ului, inclusiv subdirectorul opțional. De exemplu, dacă plugin-ul este în wp-content/plugins/plugin-exemplu/plugin.php, atunci numele acestui hook va deveni 'activate_plugin-exemplu/plugin.php'.

Partea 2: Inițializarea și __FILE__

O problemă fundamentală aici este că __FILE__ va avea valori diferite în locații diferite, iar tu ai nevoie de o valoare specifică.

Mai ai și problema că procesul de inițializare ar trebui să asambleze graful de obiecte, dar tu nu faci asta. Toate obiectele tale sunt create imediat ce sunt definite sau create unele în interiorul altora, făcând dificilă sau imposibilă transmiterea de valori către ele.

De exemplu, aș putea scrie un plugin astfel:

plugin.php:

<?php
/**
 * Plugin Name: Pluginul Meu
 * Version: 0.1
 */

// pasul de încărcare
require_once( 'php/app.php' );

// pasul de inițializare
$app = new App( __FILE__ );

// pasul de execuție
$app->run();

Am definit toate clasele în subdirectorul php și le-am încărcat într-un singur loc. PHP acum cunoaște ce clase am, numele lor etc, dar încă nu s-a întâmplat nimic.

Apoi creez toate obiectele, transmitându-le ce au nevoie - în acest caz, App are nevoie de valoarea __FILE__ așa că i-o transmit. Observă că asta creează obiectele în memorie, dar nu execută nicio acțiune. Aplicația plugin-ului este pregătită să intre în acțiune, dar este încă în faza de pregătire.

Următorul pas ar putea fi să trimit aceste obiecte către teste unitare, dar acum voi rula aplicația. Am terminat procesul de inițializare, iar aplicația este gata de rulare, așa că declanșez metoda run. Nu ar trebui să fie nevoie să transmit ceva către run deoarece tot ce era necesar a fost deja transmis prin constructori. În metoda run, adaug toate filtrele și hook-urile. În timpul acestor hook-uri vor rula toate celelalte părți ale plugin-ului.

Important este că am o structură definită și că transmit tot ce este necesar în timpul fazei de construcție/inițializare, cu un ciclu de viață clar definit.

10 aug. 2017 17:02:24
Comentarii

Mulțumesc pentru răspunsul detaliat. Din explicația ta, cel puțin ceea ce înțeleg eu, este că trebuie să găsesc o metodă prin care plugin-ul meu principal să fie conștient de toate clasele pe care le-am scris? De exemplu, probabil voi avea fișiere de clasă într-un folder "inc" și mai multe fișiere de clasă într-un folder "admin". Am avut impresia că ori de câte ori voiam să folosesc o clasă undeva, trebuia să fac "$var = new ClassName();" pentru a putea accesa acele metode. De aceea mă vezi creând clasa în interiorul clasei principale.

Dan Zuzevich Dan Zuzevich
10 aug. 2017 17:17:21

nu trebuie să creezi obiectul în interiorul clasei, doar să-i transmiți $var, constructorii pot primi și parametri, iar obiectele pot fi transmise ca orice altă variabilă. Poți să o faci așa cum ai făcut, dar există dezavantaje. Problema cu accesarea lui __FILE__ este una dintre ele, acea valoare trebuie să vină din fișierul principal al plugin-ului, altfel nu va fi corectă, așa că trebuie transmisă către alte părți ale plugin-ului

Tom J Nowell Tom J Nowell
10 aug. 2017 17:25:59

În regulă. Mulțumesc. Acest răspuns este cu siguranță suficient. Știu acum la ce aspecte trebuie să mai lucrez.

Dan Zuzevich Dan Zuzevich
10 aug. 2017 17:28:15

Partea orientată pe obiecte a acestei întrebări este oarecum off-topic pentru acest site, am păstrat întrebarea deschisă sub pretextul utilizării corecte a funcției register_activation_hook. Pentru partea OO, consultați https://tomjn.com/2015/06/24/root-composition-in-wordpress-plugins/ dar există și alte modalități de a o face. Nu există o modalitate corectă de a o face, toate au avantajele și dezavantajele lor, ați putea scrie plugin-ul folosind doar construcții de programare funcțională în stil Haskell

Tom J Nowell Tom J Nowell
10 aug. 2017 17:28:41

Ar trebui să reformulez întrebarea în vreun fel, sau este în regulă?

Dan Zuzevich Dan Zuzevich
10 aug. 2017 17:29:19

editările pentru clarificare sunt întotdeauna binevenite, doar nu schimbați sensul întrebării

Tom J Nowell Tom J Nowell
10 aug. 2017 17:30:00
Arată celelalte 1 comentarii
0

Puteți seta o constantă care va conține valoarea __FILE__ și o puteți utiliza oriunde doriți în clasele voastre.

Exemplu:

main-plugin-file.php

<?php
/**
* Plugin Name: Plugin cu Clase
*/

define( 'PLUGIN_WITH_CLASSES__FILE__', __FILE__ );

include( 'my-class.php' );

my-class.php

<?php
class myClass {
  function __construct() {
    // Rulează această acțiune la activarea pluginului
    register_activation_hook( PLUGIN_WITH_CLASSES__FILE__,  [ $this, 'create_plugin_database_table' ] );
  }

  function create_plugin_database_table(){
    //Creează tabelă în baza de date ...
  }
}

new myClass();
12 sept. 2019 04:33:38
2

Cu siguranță ar trebui să înregistrezi hook-urile de activare/dezactivare/dezinstalare în afara clasei pluginului tău, conform răspunsului lui kaiser aici, care oferă o explicație mult mai detaliată pe această temă decât aș putea scrie eu.

Aceasta ar trebui să fie suficient dacă vrei să scrii acest plugin ca exercițiu de învățare. Dacă tot ce cauți este un plugin SEO care funcționează bine și nu este acoperit cu reclame ieftine ca Yoast, îți pot recomanda cu încredere The SEO Framework.

10 aug. 2017 16:52:31
Comentarii

Acest lucru a fost util și apreciez răspunsul.

Dan Zuzevich Dan Zuzevich
10 aug. 2017 17:19:23

Am votat pozitiv răspunsul, deoarece este cu siguranță o resursă utilă.

Dan Zuzevich Dan Zuzevich
11 aug. 2017 16:22:44
1

Tom McFarlin a creat un șablon foarte extins pentru plugin-uri (acum întreținut de Devin Vinson), scris complet în OOP, pe care îl poți folosi fie pentru a-ți crea un nou plugin, fie doar pentru a studia fluxul aplicației și a răspunde la întrebarea ta. L-am folosit pentru mai multe plugin-uri personalizate și trebuie să spun că mi-a deschis ochii la unele dintre misterele OOP.

10 aug. 2017 17:38:29
Comentarii

Aceasta este o idee foarte bună. Am uitat complet despre șablonul de plugin. L-am verificat acum ceva timp și am fost derutat de faptul că era destul de confuz. Dar cu siguranță o voi descărca din nou și o voi folosi ca referință. Mulțumesc mult pentru sugestie.

Dan Zuzevich Dan Zuzevich
11 aug. 2017 16:20:47