Heb je je ooit afgevraagd hoe je componenten kunt maken voor klassieke WordPress thema's? Het schrijven van modulaire code die gemakkelijk (her)gebruikt kan worden is een belangrijke vaardigheid als je overweegt WordPress thema's of plugins te ontwikkelen. In de post en de komende video leg ik één benadering uit van hoe je modulaire componenten in WordPress kunt opzetten.

Je leert over:

  • Hoe een goede bestandsstructuur op te zetten voor je componenten in je thema en plugin.
  • Hoe gebruik je Object Georiënteerd Programmeren in PHP om componenten te maken.
  • Hoe gebruik je (abstracte) klassen als sjabloon voor componenten.
  • Hoe maak je herbruikbare PHP componenten.
  • Hoe logica te scheiden van templating code.
  • Hoe moderne bundelaars gebruiken voor je assets

Om deze zelfstudie te voltooien moet je code kunnen schrijven in PHP en vertrouwd zijn met JavaScript en CSS stylesheets. En uiteraard bekend zijn met WordPress.

Waarom je componenten zou moeten gebruiken

Er zijn verschillende redenen om componenten te gebruiken:

  • Het resulteert meestal in schonere code en een logischer structuur
  • Componenten zijn gemakkelijk te hergebruiken
  • Het scheidt PHP code van HTML (zo veel mogelijk)
  • Het is gemakkelijk om componenten uit te breiden of te wijzigen

Veel voorkomende voorbeelden van componenten

Een component is alles wat ook op een WordPress website wordt weergegeven. Denk aan een raster van artikelen, knoppen, koppen, afbeeldingen, tuimelsecties, tabbladen, een lijst van categorieën, enzovoort.

Mijn benadering van het opzetten van componenten

Dus hoe kunnen we een component in onze code op een modulaire maar slimme manier opzetten? Hier zijn een paar uitwegen:

  • Ik ga één basisklasse gebruiken die enkele eigenschappen en methoden (functionaliteiten) bevat die door alle componenten gebruikt kunnen worden.
  • Componenten zullen die basisklasse uitbreiden.
  • De uitvoer van componenten wordt gescheiden in sjablonen, en automatisch geladen.
  • Alle andere bestanden die met componenten te maken hebben, zoals stijlen of scripts of in assets geplaatst zijn en dienovereenkomstig genoemd worden.

En dat is het. We hebben niet veel meer nodig om herbruikbare componenten te maken.

Een structuur voor componenten maken: stap voor stap

In de onderstaande paragraaf leg ik het proces ook stap voor stap uit. In het voorbeeld maken we een nieuwe plugin; maar dezelfde werkwijze geldt ook voor het maken van thema's.

De video geeft ook meer uitleg over coderingsbeslissingen.

Richt je ontwikkelomgeving in

Voor de ontwikkeling gebruik ik Plaatselijk om een WordPress website op te zetten die lokaal draait, en VSCode als editor. Ze zijn beide heel eenvoudig in het gebruik.

  • Ga je gang en gebruik een bestaande of nieuwe ontwikkelomgeving
  • Uiteraard kun je ook een ontwikkelomgeving gebruiken die online draait.

Maak een nieuwe mappenstructuur in /wp-content/plugins

Herbruikbare componenten mappenstructuur

De bovenstaande afbeelding toont mijn bestands- en mappenstructuur. Ik raad aan dezelfde structuur te volgen, die we in de volgende stappen zullen voltooien.

Voor deze structuur is het volgende vermeldenswaard:

  • Het kernbestand staat in de hoofdmap en is verantwoordelijk voor het opstarten van onze plugin
  • Alle klassen - bestanden die gebruikt worden bij object-georiënteerd programmeren - worden geplaatst in /src/klassen, en volgen de zogenaamde PSR-4 naamgevingsconventies. Met andere woorden, alle bestanden en mappen in /classes zijn Geactiveerd.
  • Alle niet-gebundelde scripts en stijlen worden in /src/ geplaatst. Deze moeten dus nog gebundeld worden.
  • Alles wat openbaar (gebundeld) is, zoals CSS stylesheets en JavaScript wordt in /public geplaatst

Opstarten van de plugin: /wp-php-components-example.php

De volgende code zorgt ervoor dat als we een nieuwe Klasse uitvoeren, die automatisch geladen wordt met - je raadt het al - autoloading. In dit geval gebruiken we MyPHPComponents als de naamruimte, waarin alle klassen worden geplaatst. Het start ook het hoofdplugin bestand op, dat we in de volgende stap maken.

<?php
/**
 * Plugin Name: WP Component Example
 * Description: PHP Component Tutorial
 * Version: 0.0.1
 */
spl_autoload_register( function($class_name) {

  if( strpos($class_name, 'MyPHPComponents') !== 0 ) {
    return;
  }

  $called_class = str_replace( ['\\', 'MyPHPComponents'], ['/', ''], $class_name );
  $class_file   = dirname(__FILE__) . '/src/classes' . $called_class . '.php'; 

  if( file_exists($class_file) ) {
    require_once( $class_file );
  }

} );

/**
 * Load our plugin
 */
add_action( 'plugins_loaded', function() {
  defined('PLUGIN_PATH') or define( 'PLUGIN_PATH', plugin_dir_path( __FILE__ ));
  defined('PLUGIN_URI') or define( 'PLUGIN_URI', plugin_dir_url( __FILE__ ));

  $plugin = MyPHPComponents\Plugin::instance();

} );

Ook wordt een constante toegevoegd om het pad en de URL van onze plugin op te slaan. Als je deze code uitvoert, geeft hij een foutmelding omdat het vereiste Plugin bestand niet gevonden kan worden. Laten we het maken!

De hoofdklasse: /src/classes/Plugin.php

Deze code maakt gebruik van een beroemd ontwerppatroon (ja, ook software kan ontwerppatronen hebben!) dat een singleton heet. Dit patroon zorgt ervoor dat je een klasse maar één keer kunt instantiëren - wat betekent dat alle methoden die in deze klasse worden uitgevoerd maar één keer worden uitgevoerd.

enqueue_scripts();
  }

  publieke statische functie instantie() {
    if( ! isset(self::$instance) ) {
      self::$instance = new self();
    }

    return self::$instance;
  }

  particuliere functie enqueue_scripts() {
    add_action('wp_enqueue_scripts', function() {
      wp_enqueue_style('component-styles', PLUGIN_URI . '/public/css/styles.css');
      wp_enqueue_script('component-scripts', PLUGIN_URI . '/public/js/scripts.js', [], false, true);
    });
  }

}

En dat maakt het perfect voor gebruiksgevallen als het opstarten van een plugin. Anders wordt er niet zo veel in deze code geschreven, hij registreert alleen de assets en kan naar eigen inzicht aangepast worden.

De naamruimte bovenaan geeft in principe de map aan waarin een bestand zich bevindt en zorgt ervoor dat autoload correct werkt.

De klasse van de component: /src/classes/Componenten/Component.php

Onze component klasse is interessant. Dit is een abstracte klasse, wat betekent dat ze niet zelf uitgevoerd kan worden. De abstracte klasse bepaalt de regels voor hoe alle kindklassen (klassen die deze klasse uitbreiden) er uit moeten zien.

register();

    $file = strtolower( (nieuwe \ReflectionClass($this))->getShortName() );
    $this->template = PLUGIN_PATH . '/src/templates/components/' . $file . '.php';
    $this->params = wp_parse_args( $params, $this->params );

    if( $format ) {
      $this->format();
    }

  }

  /**
   * Deze functie registreert de standaard parameters voor de component
   * Het moet gebruikt worden om $this->params in te stellen met de standaardparameters.
   */
  abstracte beschermde functie registreer();

  /**
   * Deze functie wordt gebruikt voor het opmaken of bevragen van gegevens op basis van parameters
   */
  abstracte beschermde functie format();

  /**
   * Rendert een component
   *
   * @param bool $echo Of een sjabloon moet worden weergegeven of als een string moet worden teruggegeven
   */
  publieke definitieve functie render( bool $echo = true ) {

    if( ! $this->props || ! file_exists($this->template) ) {
      terug;
    }

    foreach( $this->props als $key => $value ) {
      ${$key} = $value;
    }

    if( ! $echo ) {
      ob_start();
    }

    require( $this->template );

    if( ! $echo ) {
      geef ob_get_clean() terug;
    }

  }

}

De methode heeft de volgende kenmerken:

  • De 'props' eigenschap die alle eigenschappen bevat die in sjablonen gebruikt worden.
  • Een standaard constructor, die bestaande met standaard eigenschappen parseert en de belangrijkste functies uitvoert
  • Een abstracte registerfunctie, die elke component moet gebruiken om eigenschappen te registreren.
  • Een renderfunctie die automatisch het juiste sjabloon kan laden op basis van de naam van de klasse. Deze functie neemt alle eigenschappen en brengt ze in kaart met hun variabelen, dus $this->props['text'] wordt $text. En dat is de variabele die we in het sjabloon kunnen gebruiken.

Een voorbeeld component: /src/classes/Components/Example.php

De voorbeeldcomponentenklasse breidt de abstracte klasse uit, die zoals ik uitlegde als een klasse 'sjabloon' fungeert. Als eigenschap heb ik in 'props' de tekstsleutel toegevoegd, waarmee wat tekst kan worden toegevoegd. En dat is het! De rest is al bepaald door onze abstracte component klasse.

params = [
      'link' => '#',
      'grootte' => 'regelmatig',
      'tekst' => ''
    ];
  }

  /**
   * Formatteer onze eigenschappen
   */
  beschermde functie format() {

    foreach($this->params als $key => $value) {
      $this->props[$key] = esc_html($value);
    }
    
  }  

}

Vanzelfsprekend zouden voor meer uitgewerkte onderdelen de format en register functies veel meer inhoud bevatten!

Een voorbeeldsjabloon: /src/templates/components/voorbeeld.php

In het voorbeeld sjabloon nemen we de eigenschappen die automatisch door onze Voorbeeld component klasse worden ingevoegd. In dit geval zijn dat $ekst, $link en $size.

<?php
/**
 * The actual template for the component
 */
?>
<a class="btn btn-<?php echo $size; ?>" href="/nl/</?php echo $link; ?>">
  <?php echo $text; ?>
</a>

Assets toevoegen: /src/assets/

Als je component activa nodig heeft, zoals stijlen en scripts, is een goede aanpak om ze in /src/assets te plaatsen. Deze stap is alleen nodig als je assets gaat gebruiken.

Voor dit project gebruik ik ESBuild dat een moderne en snelle bundelaar voor assets is. Het valt buiten het bestek van het artikel om dit te bespreken, maar ESBuild is de moeite waard om te bekijken.

Na het toevoegen van je assets, voer je de volgende commando's uit (dit vereist NodeJS op je machine geïnstalleerd moet worden):

npm init

Volg de wizard, en je eindigt met een package.json. Voer vervolgens het volgende commando uit om enkele ontwikkelingsafhankelijkheden te installeren

npm i -D autoprefixer esbuild esbuild-sass-plugin postcss

Voeg in de package.json die gegenereerd wordt ook het volgende commando toe onder scripts: '"bundel": "node esbuild.config.js"". Je krijgt dan uiteindelijk iets als het volgende:

{
  "naam": "wp-php-components-example",
  "versie": "1.0.0",
  "beschrijving": "WordPress Voorbeeld voor Modulaire Componenten",
  "hoofd": "public/assets/js/scripts",
  "scripts": {
    "bundel": "node esbuild.config.js".
  },
  "devDependencies": {
    "autoprefixer": "^10.4.2",
    "esbuild": "^0.14.23",
    "esbuild-sass-plugin": "^2.2.4",
    "postcss": "^8.4.7"
  }
}

Bepaal ten slotte je configuraties voor ESBuild in esbuild.config.js. Dit is mijn aanpak:

const esbuild = require("esbuild");
const { sassPlugin } = require("esbuild-sass-plugin");
const postcss = require('postcss');
const autoprefixer = require('autoprefixer');

esbuild.build({
  entryPoints: ["src/assets/css/styles.scss", "src/assets/js/scripts.ts"]
  outdir: "public",
  bundel: waar,
  metafile: waar,
  plugins: [
      sassPlugin({
          async transform(bron) {
              const { css } = wacht op postcss([autoprefixer]).process(bron);
              geef css terug;
          },
      }),
  ],
  wacht: waar
})
.then() => console.log("⚡ Voltooid! ⚡"))
.vang(() => process.exit(1));

Het bovenstaande verklaart dat elk bestand in /src/assets/css/styles.scss en /src/assets/js/scripts.ts naar de public folder wordt uitgevoerd.

Om deze configuratie uit te voeren, moet je echter het commando `npm run bundle` in je commandoregel uitvoeren.

Vergeet niet om enqueue je openbare scripts en stijlen als je ze gebruikt.

Een component implementeren

Maar hoe implementeer je een component? Laten we eens kijken naar een codevoorbeeld (in willekeurige PHP code):

'https://example.com',
    'tekst' => __('Tekst'),
    'grootte' => 'groot'
  ]);

  $component_string = $component->render(false);

  geef $content terug . $component_string;

});

In dit eenvoudige voorbeeld implementeer ik de component na alle losse berichten en pagina's - elke plaats waar 'the_content' wordt aangeroepen. In dit geval rendert het 'Some Text'. Het mooie is dat we de 'render' methode uit de geërfde klasse kunnen gebruiken om ook een string uit te voeren.

Herbruikbare componenten bouwen

Ik hoop dat je het bouwen van je eerste herbruikbare component leuk vond - en dat het je hongerig maakte om meer componenten te maken. Je kunt de volledige code voor deze post vinden op Github.

In deze post richtten we ons op het bouwen van herbruikbare componenten met PHP, dat is de klassieke manier waarop WordPress thema's gebouwd werden. In een volgende post zullen we ons richten op het bouwen van een eigen component in Gutenberg (met behulp van JavaScript en React).

Ik maakte ook een complete bibliotheek voor veel voorkomende WordPress componenten.