it-swarm.com.de

Beste Art zu überholen PHP variabel zwischen Teiltönen?

Ich habe eine Variable in header.php, wie zum Beispiel:

$page_extra_title = get_post_meta($this_page->ID, "_theme_extra_title", true);

Sobald ich mache:

var_dump($page_extra_title);

Ich erhalte NULL immer außerhalb von header.php (var_dump funktioniert nur in header.php richtig). Ich habe dieselbe Variable überall dort eingefügt, wo ich sie benötige (page.php, post.php, footer.php usw.), aber das ist Wahnsinn und macht es fast unmöglich, alles zu pflegen.

Ich frage mich, wie ich eine Variable am besten durch alle Dateien in meinem Design leiten kann. Ich vermute, dass die Verwendung von functions.php zusammen mit "get_post_meta" nicht die beste Idee ist. :)

15
Wordpressor

Grundlegende getrennte Datenstrukturen

Um Daten weiterzugeben, verwenden Sie normalerweise ein Modell (das ist das "M" in "MVC"). Schauen wir uns eine sehr einfache Schnittstelle für Daten an. Schnittstellen werden nur als "Rezepte" für unsere Bausteine ​​verwendet:

namespace WeCodeMore\Package\Models;
interface ArgsInterface
{
    public function getID();
    public function getLabel();
}

Oben ist, was wir herumreichen: Eine gemeinsame ID und ein "Label".

Anzeigen von Daten durch Kombinieren von Atomteilen

Als nächstes brauchen wir ein paar View , die zwischen unserem Model und ... unserer Vorlage verhandeln.

namespace WeCodeMore\Package;
interface PackageViewInterface
{
    /**
     * @param Models\ArgsInterface $args
     * @return int|void
     */
    public function render( Models\ArgsInterface $args );
}

Grundsätzlich ist das Interface sagt

"Wir können etwas rendern und ein Modell ist für diese Aufgabe obligatorisch"

Schließlich müssen wir oben implementieren und das aktuelle View erstellen. Wie Sie sehen können, sagt der Konstruktor, dass die obligatorische Sache für unsere Ansicht ein Template ist und dass wir es rendern können. Um die Entwicklung zu vereinfachen, prüfen wir sogar, ob die Vorlagendatei tatsächlich vorhanden ist, damit wir anderen Entwicklern das Leben (und auch uns) erheblich erleichtern und dies beachten können.

In einem zweiten Schritt der Render-Funktion verwenden wir ein Closure , um den eigentlichen Template-Wrapper und bindTo() das Model für das Template zu erstellen.

namespace WeCodeMore\Package;

use WeCodeMore\Package\Models\ArgsInterface;

/** @noinspection PhpInconsistentReturnPointsInspection */
class PackageView implements PackageViewInterface
{
    /** @var string|\WP_Error */
    private $template;
    /**
     * @param string $template
     */
    public function __construct( $template )
    {
        $this->template = ! file_exists( $template )
            ? new \WP_Error( 'wcm-package', 'A package view needs a template' )
            : $template;
    }
    /**
     * @param Models\ArgsInterface $args
     * @return int|void
     */
    public function render( Models\ArgsInterface $args )
    {
        if ( is_wp_error( $this->template ) )
            return print $this->template->get_error_message();

        /** @var $callback \Closure */
        $callback = function( $template )
        {
            extract( get_object_vars( $this ) );
            require $template;
        };
        call_user_func(
            $callback->bindTo( $args ),
            $this->template
        );
    }
}

Ansicht und Rendering trennen

Dies bedeutet, dass wir eine sehr einfache Vorlage wie die folgende verwenden können

<!--suppress HtmlFormInputWithoutLabel -->
<p><?= $label ?></p>

um unsere Inhalte zu rendern. Wenn wir die Teile zusammenfügen, erhalten wir etwas in den folgenden Zeilen (in unserem Controller, Mediator usw.):

namespace WeCodeMore\Package;

$view = new PackageView( plugin_dir_path( __FILE__ ).'tmpl/label.tmpl.php' );
$view->render( new Models\Args );

Was haben wir gewonnen?

Auf diese Weise können wir

  1. Tauschen Sie Vorlagen einfach aus, ohne die Datenstruktur zu ändern
  2. Habe leicht zu lesende Tempaltes
  3. Vermeiden Sie globalen Geltungsbereich
  4. Kann Unit-Test
  5. Kann das Modell/die Daten austauschen, ohne andere Komponenten zu beschädigen

Kombination von OOP PHP mit der WP-API

Natürlich ist dies kaum möglich, wenn grundlegende Theming-Funktionen wie get_header(), get_footer() usw. verwendet werden, oder? Falsch. Rufen Sie einfach Ihre Klassen in der gewünschten Vorlage oder im gewünschten Vorlagenteil auf. Rendern Sie es, transformieren Sie die Daten und machen Sie, was Sie wollen. Wenn Sie wirklich nett sind, fügen Sie einfach Ihre eigenen benutzerdefinierten Filter hinzu und lassen Sie sich von einem Unterhändler darüber informieren, was von welchem ​​Controller auf welcher Route bzw. unter bestimmten Bedingungen geladen wird.

Fazit?

Sie können problemlos mit Dingen wie dem oben genannten in WP arbeiten und sich dennoch an die grundlegende API halten und Code und Daten wiederverwenden, ohne ein einzelnes Global aufzurufen oder den globalen Namensraum durcheinander zu bringen und zu verschmutzen.

9
kaiser

Dies ist ein alternativer Ansatz zu @kaiser answer, den ich für ziemlich gut befunden habe (+1 von mir), der jedoch zusätzliche Arbeit erfordert, um mit den WP - Kernfunktionen verwendet werden zu können, und der per se niedrig mit integriert ist Vorlagenhierarchie.

Der Ansatz, den ich teilen möchte, basiert auf einer einzelnen Klasse (es ist eine abgespeckte Version von etwas, an dem ich arbeite), die sich um das Rendern von Daten für Vorlagen kümmert.

Es hat einige (IMO) interessante Funktionen:

  • templates sind Standard-WordPress-Template-Dateien (single.php, page.php), die ein bisschen mehr power enthalten.
  • bestehende Vorlagen funktionieren einfach, sodass Sie ohne großen Aufwand Vorlagen aus vorhandenen Themen integrieren können
  • im Gegensatz zum @kaiser - Ansatz greifen Sie in Vorlagen mit dem Schlüsselwort $this auf Variablen zu: Dies gibt Ihnen die Möglichkeit, bei undefinierten Variablen Benachrichtigungen in der Produktion zu vermeiden

Die Klasse Engine

namespace GM\Template;

class Engine
{
    private $data;
    private $template;
    private $debug = false;

  /**
   * Bootstrap rendering process. Should be called on 'template_redirect'.
   */
  public static function init()
  {
      add_filter('template_include', new static(), 99, 1);
  }

  /**
   * Constructor. Sets debug properties.
   */
  public function __construct()
  {
      $this->debug =
          (! defined('WP_DEBUG') || WP_DEBUG)
          && (! defined('WP_DEBUG_DISPLAY') || WP_DEBUG_DISPLAY);
  }

  /**
   * Render a template.
   * Data is set via filters (for main template) or passed to method for partials.
   * @param string $template template file path
   * @param array  $data     template data
   * @param bool   $partial  is the template a partial?
   * @return mixed|void
   */
  public function __invoke($template, array $data = array(), $partial = false)
  {
      if ($partial || $template) {
          $this->data = $partial
              ? $data
              : $this->provide(substr(basename($template), 0, -4));
          require $template;
          $partial or exit;
      }

      return $template;
  }

  /**
   * Render a partial.
   * Partial-specific data can be passed to method.
   * @param string $template template file path
   * @param array  $data     template data
   * @param bool   $isolated when true partial has no access on parent template context
   */
  public function partial($partial, array $data = array(), $isolated = false)
  {
      do_action("get_template_part_{$partial}", $partial, null);
      $file = locate_template("{$partial}.php");
      if ($file) {
          $class = __CLASS__;
          $template = new $class();
          $template_data =  $isolated ? $data : array_merge($this->data, $data);
          $template($file, $template_data, true);
      } elseif ($this->debug) {
          throw new \RuntimeException("{$partial} is not a valid partial.");
      }
  }

  /**
   * Used in templates to access data.
   * @param string $name
   * @return string
   */
  public function __get($name)
  {
      if (array_key_exists($name, $this->data)) {
          return $this->data[$name];
      }
      if ($this->debug) {
          throw new \RuntimeException("{$name} is undefined.");
      }

      return '';
  }

  /**
   * Provide data to templates using two filters hooks:
   * one generic and another query type specific.
   * @param string $type Template file name (without extension, e.g. "single")
   * @return array
   */
  private function provide($type)
  {
      $generic = apply_filters('gm_template_data', array(), $type);
      $specific = apply_filters("gm_template_data_{$type}", array());

      return array_merge(
        is_array($generic) ? $generic : array(),
        is_array($specific) ? $specific : array()
     );
  }
}

(Verfügbar als Kern hier.)

Wie benutzt man

Sie müssen nur die Methode Engine::init() aufrufen, wahrscheinlich auf 'template_redirect' hook. Dies kann im Theme functions.php oder über ein Plugin erfolgen.

require_once '/path/to/the/file/Engine.php';
add_action('template_redirect', array('GM\Template\Engine', 'init'), 99);

Das ist alles.

Ihre vorhandenen Vorlagen funktionieren wie erwartet. Jetzt haben Sie jedoch die Möglichkeit, auf benutzerdefinierte Vorlagendaten zuzugreifen.

Benutzerdefinierte Vorlagendaten

Um benutzerdefinierte Daten an Vorlagen zu übergeben, gibt es zwei Filter:

  • 'gm_template_data'
  • 'gm_template_data_{$type}'

Der erste wird für alle Vorlagen ausgelöst, der zweite ist vorlagenspezifisch. Der dynamische Teil {$type} ist der Basisname der Vorlagendatei ohne Dateierweiterung.

Z.B. Mit dem Filter 'gm_template_data_single' können Daten an die Vorlage single.php übergeben werden.

Die an diese Hooks angehängten Callbacks müssen ein Array zurückgeben, wobei die Schlüssel die Variablennamen sind.

Beispielsweise können Sie Metadaten wie folgt als Vorlagendaten übergeben:

add_filter('gm_template_data', function($data) {
    if (is_singular()) {
        $id = get_queried_object_id();
        $data['extra_title'] = get_post_meta($id, "_theme_extra_title", true);
    }

    return $data;
};

Und dann können Sie innerhalb der Vorlage einfach Folgendes verwenden:

<?= $this->extra_title ?>

Debug-Modus

Wenn sowohl die Konstanten WP_DEBUG als auch WP_DEBUG_DISPLAY wahr sind, arbeitet die Klasse im Debug-Modus. Dies bedeutet, dass eine Ausnahme ausgelöst wird, wenn eine Variable nicht definiert ist.

Wenn sich die Klasse nicht im Debug-Modus befindet (wahrscheinlich in der Produktion), wird beim Zugriff auf eine undefinierte Variable eine leere Zeichenfolge ausgegeben.

Datenmodelle

Eine gute und leicht zu pflegende Möglichkeit, Ihre Daten zu organisieren, ist die Verwendung von Modellklassen.

Dies können sehr einfache Klassen sein, die Daten mit den oben beschriebenen Filtern zurückgeben. Es gibt keine bestimmte Benutzeroberfläche, sie kann nach Ihren Wünschen organisiert werden.

Im Folgenden finden Sie nur ein Beispiel, aber Sie können es auf Ihre eigene Weise tun.

class SeoModel
{
  public function __invoke(array $data, $type = '')
  {
      switch ($type) {
          case 'front-page':
          case 'home':
            $data['seo_title'] = 'Welcome to my site';
            break;
          default:
            $data['seo_title'] = wp_title(' - ', false, 'right');
            break;
      }

      return $data;
  }
}

add_filter('gm_template_data', new SeoModel(), 10, 2);

Die Methode __invoke() (die ausgeführt wird, wenn eine Klasse wie ein Rückruf verwendet wird) gibt eine Zeichenfolge zurück, die für das Tag <title> der Vorlage verwendet wird.

Dank der Tatsache, dass das zweite von 'gm_template_data' übergebene Argument der Vorlagenname ist, gibt die Methode einen benutzerdefinierten Titel für die Homepage zurück.

Mit dem obigen Code ist es dann möglich, so etwas zu verwenden

 <title><?= $this->seo_title ?></title>

im Abschnitt <head> der Seite.

Teilstücke

WordPress verfügt über Funktionen wie get_header() oder get_template_part(), mit denen Partials in die Hauptvorlage geladen werden können.

Diese Funktionen können, genau wie alle anderen WordPress-Funktionen, in Vorlagen verwendet werden, wenn die Klasse Engine verwendet wird.

Das einzige Problem ist, dass in den Partials, die mit den wichtigsten WordPress-Funktionen geladen wurden, die Funktion erweitert zum Abrufen benutzerdefinierter Vorlagendaten mit $this nicht verwendet werden kann.

Aus diesem Grund verfügt die Klasse Engine über eine Methode partial(), mit der ein Teil geladen werden kann (vollständig untergeordnetes Design) und die benutzerdefinierten Vorlagendaten weiterhin in Teilbereichen verwendet werden können.

Die Verwendung ist ziemlich einfach.

Angenommen, es gibt eine Datei mit dem Namen partials/content.php im Ordner theme (oder child theme), kann sie mit folgendem Befehl eingefügt werden:

<?php $this->partial('partials/content') ?>

Innerhalb dieses Teils ist der Zugriff auf alle übergeordneten Themendaten auf dieselbe Weise möglich.

Im Gegensatz zu WordPress-Funktionen können Sie mit der Engine::partial()-Methode bestimmte Daten an Partials übergeben, indem Sie einfach ein Array von Daten als zweites Argument übergeben.

<?php $this->partial('partials/content', array('greeting' => 'Welcome!')) ?>

Standardmäßig haben Partials Zugriff auf Daten, die im übergeordneten Design verfügbar sind, und auf Daten, die explizit übergeben wurden.

Wenn eine explizit an partial übergebene Variable denselben Namen wie eine übergeordnete Designvariable hat, gewinnt die explizit übergebene Variable.

Es ist jedoch auch möglich, einen Teil in den Modus isoliert aufzunehmen, d. H. Der Teil hat keinen Zugriff auf übergeordnete Themendaten. Übergeben Sie dazu einfach true als drittes Argument an partial():

<?php $this->partial('partials/content', array('greeting' => 'Welcome!'), true) ?>

Fazit

Auch wenn es ziemlich einfach ist, ist die Klasse Engine ziemlich vollständig, kann aber sicherlich weiter verbessert werden. Z.B. Es gibt keine Möglichkeit zu überprüfen, ob eine Variable definiert ist oder nicht.

Dank seiner 100% igen Kompatibilität mit WordPress-Funktionen und Vorlagenhierarchie können Sie es problemlos in vorhandenen Code und Code von Drittanbietern integrieren.

Beachten Sie jedoch, dass dies nur teilweise getestet wurde. Möglicherweise gibt es Probleme, die ich noch nicht entdeckt habe.

Die fünf Punkte unter "Was haben wir gewonnen?" in @kaiserAntwort :

  1. Tauschen Sie Vorlagen einfach aus, ohne die Datenstruktur zu ändern
  2. Habe leicht zu lesende Tempaltes
  3. Vermeiden Sie globalen Geltungsbereich
  4. Kann Unit-Test
  5. Kann das Modell/die Daten austauschen, ohne andere Komponenten zu beschädigen

sind auch für meine Klasse gültig.

11
gmazzap

Einfache Antwort: Übergeben Sie keine Variablen, da es stinkt, als würden Sie globale Variablen verwenden, was böse ist.

Deinem Beispiel nach scheint es so, als ob du versuchst, eine frühe Optimierung vorzunehmen, aber noch ein Übel;)

Verwenden Sie die WordPress-API, um Daten abzurufen, die in der Datenbank gespeichert sind, und versuchen Sie nicht, deren Verwendung zu überlisten und zu optimieren, da die API nicht nur Werte abruft und Filter und Aktionen aktiviert. Durch Entfernen des API-Aufrufs entfernen Sie die Fähigkeit anderer Entwickler, das Verhalten Ihres Codes zu ändern, ohne ihn zu ändern.

5
Mark Kaplun

Obwohl Kaisers Antwort technisch richtig ist, bezweifle ich, dass es die beste Antwort für Sie ist.

Wenn Sie ein eigenes Thema erstellen, ist dies meiner Meinung nach der beste Weg, ein Framework mithilfe von Klassen (und möglicherweise auch Namespaces und Interfaces) einzurichten, obwohl dies für ein WP ein wenig zu viel sein könnte. Thema).

Wenn Sie jedoch nur ein vorhandenes Thema erweitern/anpassen und nur eine oder mehrere Variablen übergeben müssen, sollten Sie sich an global halten. Da header.php in einer Funktion enthalten ist, können die Variablen, die Sie in dieser Datei deklarieren, nur in dieser Datei verwendet werden. Mit global machen Sie sie im gesamten WP Projekt zugänglich:

In header.php:

global $page_extra_title;

$page_extra_title = get_post_meta($this_page->ID, "_theme_extra_title", true);

In single.php (zum Beispiel):

global $page_extra_title;

var_dump( $page_extra_title );
2
redelschaap

Eine einfache Lösung besteht darin, eine Funktion zu schreiben, um den zusätzlichen Titel zu erhalten. Ich verwende eine statische Variable, um nur einen Datenbankaufruf zu speichern. Gib das in deine functions.php ein.

function get_extra_title($post_id) {
    static $title = null;
    if ($title === null) {
        $title = get_post_meta($post_id, "_theme_extra_title", true)
    }
    return $title;
}

Rufen Sie außerhalb von header.php die Funktion auf, um den Wert zu erhalten:

var_dump(get_extra_title($post->ID));
1
pbd