Utilizzo di register_activation_hook nelle classi

10 ago 2017, 16:36:08
Visualizzazioni: 22.2K
Voti: 10

Sto cercando di sviluppare un plugin per scopi SEO di base, poiché molte persone che conosco non amano utilizzare Yoast. Ho appena iniziato il plugin e sto costruendo il messaggio di attivazione mostrato all'utente quando attivano il plugin. Sto avendo difficoltà con un mix tra OOP e funzioni integrate di WordPress e non sono sicuro dove sto sbagliando.

L'unico modo in cui riesco a farlo funzionare è creando una nuova istanza della classe SEO_Plugin_Activation all'interno del costruttore dei miei file principali e quindi chiamando il metodo activatePlugin in quella classe. Sento che questo è superfluo. Penso anche che il modo in cui sto eseguendo le mie funzioni nel file della classe di attivazione non abbia molto senso. Ma è l'unico modo in cui riesco a farlo funzionare al momento.

Non sono sicuro se quello che sto facendo sia perché non padroneggio le tecniche OOP al 100% o se non sto utilizzando correttamente l'API di WordPress. Ho incluso tre esempi di codice nel seguente ordine:

  1. File principale del plugin
  2. File di classe che gestisce i miei requisiti di attivazione
  3. Quello che speravo si potesse fare.

seo.php (file principale del plugin)

<?php
/*
... informazioni generiche del 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">' .
              'I tuoi file sitemap possono essere trovati ai seguenti link: ' .
            '</div>';
      delete_transient('show_sitemap_info');
    }
  }

}
?>

seo.php (Quello che speravo)

<?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();
    // Esegui alcuni metodi qui che si occuperebbero 
    // di tutto il bootstrap di attivazione del mio plugin
  }

}

new SEO();
?>

Ho provato a farlo nel modo che delineo nel terzo script, ma non ho successo. Al momento, il messaggio viene visualizzato correttamente, senza messaggi di errore.

4
Commenti

Stai cercando rassicurazioni o il modo corretto di fare OO in WordPress? Purtroppo non c'è un percorso canonico da seguire qui, le uniche cose che posso condividere sono principi OO generici. Ad esempio non creare SEO_Plugin_Activation all'interno della tua classe SEO, usa invece l'iniezione delle dipendenze e passala come argomento, e non definire e usare una classe nello stesso file - caricare un file che descrive una classe non dovrebbe anche eseguirla altrimenti è impossibile scrivere test unitari

Tom J Nowell Tom J Nowell
10 ago 2017 16:47:12

Grazie per la risposta. Onestamente non mi preoccupo troppo di attenermi al "100% Wordpress Way". A giudicare dal tuo commento, sembra che mi manchi chiaramente una comprensione di alcuni principi OOP. Ero convinto di dover creare una nuova classe all'interno di una classe per chiamare i suoi metodi.

Dan Zuzevich Dan Zuzevich
10 ago 2017 16:50:59

Puoi farlo, ma non è altrettanto flessibile, ad esempio se vuoi testare la classe SEO, come potresti sostituire la classe SEO_Plugin_Activation con un mock object senza modificarla? Comunque sembra che questa domanda derivi da un fraintendimento su come funziona register_activation_hook

Tom J Nowell Tom J Nowell
10 ago 2017 17:04:04

Ok, capito, grazie mille. Onestamente non credo che farò test su nulla perché al momento è un po' troppo avanzato per me.

Dan Zuzevich Dan Zuzevich
10 ago 2017 17:21:13
Tutte le risposte alla domanda 4
6
14

Avendo riletto la tua domanda, penso di aver capito il problema, che deriva da un fraintendimento su come funziona register_activation_hook, combinato con qualche confusione su come stai facendo il bootstrapping del tuo codice e su cosa significhi fare bootstrapping

Parte 1: register_activation_hook

Questa funzione accetta 2 parametri:

register_activation_hook( string $file, callable $function )

Il primo parametro, $file è il file principale del plugin, non il file che contiene ciò che vuoi eseguire. Viene usato nello stesso modo di plugins_url, quindi hai bisogno del valore di __FILE__, specificamente il suo valore nel file root del plugin con l'header del tuo plugin.

Il secondo parametro è un callable e funziona come ti aspetti, ma internamente utilizza semplicemente add_action

Quando un plugin viene attivato, viene chiamato l'hook 'activate_PLUGINNAME'. Nel nome di questo hook, PLUGINNAME viene sostituito con il nome del plugin, inclusa l'eventuale sottodirectory. Ad esempio, se il plugin si trova in wp-content/plugins/sampleplugin/sample.php, allora il nome di questo hook diventerà 'activate_sampleplugin/sample.php'.

Parte 2: Bootstrapping e __FILE__

Un problema fondamentale qui è che __FILE__ avrà valori diversi in posizioni diverse, e tu hai bisogno di un valore specifico.

Hai anche un problema, che il bootstrapping dovrebbe assemblare il grafo degli oggetti, ma tu non lo fai. Tutti i tuoi oggetti vengono creati non appena sono definiti, o creati l'uno dentro l'altro, rendendo difficile o impossibile passare valori tra loro.

Ad esempio, potrei scrivere un plugin così:

plugin.php:

<?php
/**
 * Plugin Name: My Plugin
 * Version: 0.1
 */

// fase di caricamento
require_once( 'php/app.php' );

// fase di bootstrapping
$app = new App( __FILE__ );

// fase di esecuzione
$app->run();

Ho definito tutte le mie classi nella sottocartella php e le ho caricate tutte nello stesso posto. PHP ora conosce quali sono le mie classi, i loro nomi ecc., ma non è ancora successo nulla.

Poi creo tutti i miei oggetti, passando loro ciò di cui hanno bisogno, in questo caso App ha bisogno del valore di __FILE__ quindi glielo passo. Nota che questo crea gli oggetti in memoria, ma non esegue alcun lavoro. L'applicazione del plugin è pronta per partire, per entrare in azione, ma è ancora nella fase di preparazione.

Il passo successivo potrebbe essere di inserire questi oggetti in una serie di test unitari, ma ora li eseguirò. Ho completato il mio processo di bootstrapping e l'applicazione è pronta per essere eseguita, quindi attivo il metodo run. Non dovrei aver bisogno di passare nulla a run perché tutto il necessario è stato passato ai costruttori. Durante il metodo run, aggiungo tutti i miei filtri e hook. È durante questi hook che vengono eseguite tutte le altre parti del plugin.

La parte importante però è che ho una struttura definita e che passo ciò che è necessario durante la fase di costruzione/bootstrapping, con un ciclo di vita definito

10 ago 2017 17:02:24
Commenti

Grazie per la risposta dettagliata. Da quello che ho capito dalla tua spiegazione, è che devo trovare un modo per far sì che il mio plugin principale sia a conoscenza di tutte le classi che ho scritto? Ad esempio, probabilmente avrò file di classi in una cartella "inc" e altri file di classi in una cartella "admin". Avevo l'impressione che ogni volta che volessi usare una classe da qualche parte, dovessi fare "$var = new ClassName();" per poter accedere a quei metodi. Ecco perché mi vedi creare la classe all'interno della classe principale.

Dan Zuzevich Dan Zuzevich
10 ago 2017 17:17:21

non devi creare l'oggetto all'interno della classe, basta passare $var ad essa, i costruttori possono accettare parametri e gli oggetti possono essere passati in giro come qualsiasi altra variabile. Puoi farlo nel modo in cui l'hai fatto, ma ci sono degli svantaggi. Il problema con l'accesso a __FILE__ è uno di questi, quel valore deve provenire dal file principale del plugin, altrimenti non sarà corretto, quindi deve essere passato ad altre parti del plugin

Tom J Nowell Tom J Nowell
10 ago 2017 17:25:59

Perfetto. Grazie. Questa risposta sarà sicuramente sufficiente. Ora so su cosa devo rinfrescare le mie conoscenze.

Dan Zuzevich Dan Zuzevich
10 ago 2017 17:28:15

L'aspetto OO di questa domanda è un po' fuori tema per questo sito, ho tenuto la domanda aperta con il pretesto di utilizzare correttamente register_activation_hook. Per la parte OO guarda https://tomjn.com/2015/06/24/root-composition-in-wordpress-plugins/ ma ci sono altri modi per farlo. Non esiste un modo corretto per farlo, tutti hanno i loro vantaggi e svantaggi, potresti scrivere il tuo plugin utilizzando costrutti di programmazione puramente funzionali in stile haskell

Tom J Nowell Tom J Nowell
10 ago 2017 17:28:41

Dovrei riformulare la domanda in qualche modo, oppure va bene così?

Dan Zuzevich Dan Zuzevich
10 ago 2017 17:29:19

modifiche per chiarimenti sono sempre benvenute, basta non cambiare il significato della domanda

Tom J Nowell Tom J Nowell
10 ago 2017 17:30:00
Mostra i restanti 1 commenti
0

Puoi impostare una costante che conterrà il valore di __FILE__ e utilizzarla ovunque desideri nelle tue classi.

Esempio:

main-plugin-file.php

<?php
/**
* Plugin Name: Plugin con Classi
*/

define( 'PLUGIN_WITH_CLASSES__FILE__', __FILE__ );

include( 'my-class.php' );

my-class.php

<?php
class myClass {
  function __construct() {
    // Esegui questo all'attivazione del plugin
    register_activation_hook( PLUGIN_WITH_CLASSES__FILE__,  [ $this, 'create_plugin_database_table' ] );
  }

  function create_plugin_database_table(){
    //Crea la tabella del database ...
  }
}

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

Dovresti davvero registrare i tuoi hook di attivazione/disattivazione/disinstallazione al di fuori della classe del tuo plugin, come spiegato nella risposta di kaiser qui, che fornisce una spiegazione molto più dettagliata sull'argomento di quanto potrei scrivere io.

Questo dovrebbe bastare se stai cercando di scrivere questo plugin come esercizio di apprendimento. Se invece stai cercando semplicemente un plugin SEO che funzioni bene e non sia ricoperto di fastidiosi annunci come Yoast, posso consigliarti vivamente The SEO Framework.

10 ago 2017 16:52:31
Commenti

È stato utile e apprezzo la risposta.

Dan Zuzevich Dan Zuzevich
10 ago 2017 17:19:23

Ho votato positivamente la risposta perché è una risorsa decisamente utile.

Dan Zuzevich Dan Zuzevich
11 ago 2017 16:22:44
1

Tom McFarlin ha creato un plugin boilerplate molto completo (ora mantenuto da Devin Vinson), tutto scritto in OOP, che puoi utilizzare sia per creare il tuo nuovo plugin, sia semplicemente per studiare il flusso dell'applicazione e rispondere alla tua domanda. L'ho utilizzato per diversi plugin personalizzati e devo dire che mi ha davvero aperto gli occhi su alcuni dei misteri della OOP.

10 ago 2017 17:38:29
Commenti

Questa è un'ottima idea. Mi ero completamente dimenticato del plugin boilerplate. L'avevo controllato un po' di tempo fa e mi aveva confuso perché era piuttosto complicato. Ma lo scaricherò sicuramente di nuovo e lo userò come riferimento. Grazie mille per il suggerimento.

Dan Zuzevich Dan Zuzevich
11 ago 2017 16:20:47