Лучший способ передать переменную PHP между частичными?

У меня есть переменная в header.php, например:

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

Как только я это сделаю:

var_dump($page_extra_title);

Я всегда получаю NULL вне header.php (var_dump работает правильно только в header.php). Я вставлял одну и ту же переменную везде, где мне это нужно (page.php, post.php, footer.php и т. Д.), Но это безумие и делает почти невозможным поддерживать.

Мне интересно, какой лучший способ передать переменную через все файлы в моей теме? Я предполагаю, что использование функций. Php вместе с «get_post_meta» может быть не лучшей идеей? :)

14 голосов | спросил Wordpressor 15 AMpWed, 15 Apr 2015 00:55:22 +030055Wednesday 2015, 00:55:22

5 ответов


9

Основные разделенные структуры данных

Для передачи данных обычно используется Model (это «M» в «MVC»). Давайте рассмотрим очень простой интерфейс для данных. Интерфейсы используются только как «Рецепты» для наших строительных блоков:

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

Выше мы просматриваем: общий идентификатор и «Ярлык».

Отображение данных путем объединения атомных фрагментов

Далее нам понадобится View , который ведет переговоры между нашей моделью и ... нашим шаблоном.

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

В основном, что Интерфейс говорит

  

«Мы можем сделать что-то, и модель обязательна для этой задачи»

Наконец, нам нужно реализовать выше и построить фактический Вид . Как вы можете видеть, конструктор говорит, что обязательным для нашего представления является Template , и мы можем его отобразить. Ради удобства мы даже проверяем, действительно ли файл шаблона присутствует, чтобы мы могли сделать другие жизни разработчиков (и наших) намного проще и отметить, что.

На втором этапе функции рендеринга мы используем Closure для создания фактической оболочки шаблона и bindTo() Модели к шаблону.

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
        );
    }
}

Разделение представления и рендеринга

Это означает, что мы можем использовать очень простой шаблон, например, следующий

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

, чтобы отобразить наш контент. Объединив части, мы получим что-то вокруг следующих строк (в нашем контроллере, посреднике и т. Д.):

namespace WeCodeMore\Package;

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

Что мы получили?

Таким образом, мы можем

  1. Легко обмениваться шаблонами без изменения структуры данных
  2. Легко читаемые tempaltes
  3. Избегать глобальной области
  4. Может ли Unit-Test
  5. Можно обменять Модель /данные без ущерба для других компонентов.

Объединение OOP PHP с WP API

Конечно, это вряд ли возможно, используя базовые функциональные возможности, такие как get_header(), get_footer() и т. д., правильно? Неправильно. Просто позвоните в свои классы в любом шаблоне или части шаблона, который вам нужен. Извлеките его, преобразуйте данные, делайте все, что хотите. Если вы действительно милый, вы просто добавляете свой собственный набор настраиваемых фильтров и имеете некоторую переговорщику, чтобы позаботиться о том, что получает от какого контроллера, на каком маршруте /условномзагрузка шаблона.

Заключение

Вы можете работать с такими вещами, как указано выше, в WP без проблем и по-прежнему придерживаться базового API и повторно использовать код и данные, не вызывая одного глобального или испортить глобальное пространство имен.

ответил kaiser 15 AMpWed, 15 Apr 2015 01:36:14 +030036Wednesday 2015, 01:36:14
11

Это альтернативный подход к @kaiser , который я нашел довольно хорошо (+1 от меня) но требует дополнительной работы, которая будет использоваться с основными функциями WP, и ее минимальная интеграция с иерархией шаблонов.

Подход, который я хочу использовать, основан на одном классе (это урезанная версия от того, над чем я работаю), который занимается обработкой данных для шаблонов.

В нем есть некоторые (IMO) интересные функции:

  • шаблоны являются стандартными файлами шаблонов WordPress (single.php, page.php), они получают немного больше power
  • существующие шаблоны просто работают, поэтому вы можете без проблем интегрировать шаблон из существующих тем.
  • в отличие от @kaiser , в шаблонах вы получаете доступ к переменным, используя $this : это дает вам возможность избежать уведомлений в производстве в случае неопределенных переменных

Класс 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()
     );
  }
}

(Доступно как Gist здесь.)

Как использовать

Только нужно вызвать метод Engine::init(), возможно, на крюке 'template_redirect'. Это можно сделать в теме functions.php или из плагина.

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

Вот и все.

Существующие шаблоны будут работать как завещание. Но теперь у вас есть возможность доступа к пользовательским данным шаблона.

Пользовательские данные шаблона

Для передачи пользовательских данных в шаблоны есть два фильтра:

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

Первый из них запускается для всех шаблонов, второй - для конкретного шаблона, на самом деле, динамическая часть {$type} - это базовое имя файла шаблона без расширения файла.

например. фильтр 'gm_template_data_single' может использоваться для передачи данных в шаблон single.php.

Обратные вызовы, привязанные к этим крючкам , должны возвращать массив , где ключи являются именами переменных.

Например, вы можете передавать метаданные в соответствии с данными шаблонов:

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;
};

И затем внутри шаблона вы можете просто использовать:

<?= $this->extra_title ?>

Режим отладки

Когда оба константы WP_DEBUG и WP_DEBUG_DISPLAY являются true, класс работает в режиме отладки. Это означает, что если переменная не определена, генерируется исключение.

Когда класс не находится в режиме отладки (возможно, в процессе производства), доступ к неопределенной переменной выведет пустую строку.

Модели данных

Хорошим и надежным способом организации ваших данных является использование классов моделей.

Они могут быть очень простыми классами, которые возвращают данные, используя те же фильтры, которые описаны выше. Нет конкретного интерфейса, чтобы следовать, они могут быть организованы в соответствии с вашими предпочтениями.

Ниже приведен пример, но вы можете делать это по-своему.

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);

Метод __invoke() (который запускается, когда класс используется как обратный вызов) возвращает строку, которая будет использоваться для тега <title> шаблона .

Благодаря тому, что второй аргумент, передаваемый 'gm_template_data', является именем шаблона, метод возвращает пользовательский заголовок для домашней страницы.

Имея код выше, можно использовать что-то вроде

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

в разделе <head> страницы.

Partials

WordPress имеет такие функции, как get_header() или get_template_part(), которые можно использовать для загрузки частичных файлов в основной шаблон.

Эти функции, как и все другие функции WordPress, могут использоваться в шаблонах при использовании класса Engine.

Единственная проблема заключается в том, что внутри частичных файлов, загруженных с использованием основных функций WordPress, невозможно использовать функцию advanced для получения пользовательских данных шаблона с помощью $this.

По этой причине класс Engine имеет методpartial(), который позволяет загружать частичный (полностью совместимый с дочерней темой способ) и по-прежнему иметь возможность использовать в частичных пользовательских данных шаблона.

Использование довольно просто.

Предполагая, что есть файл с именем partials/content.php внутри темы (или дочерней темы), его можно включить, используя:

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

Внутри этого частичного доступа можно будет получить доступ ко всем родительским данным тем же способом.

В отличие от функций WordPress, метод Engine::partial() позволяет передавать определенные данные в частичные, просто передавая массив данных в качестве второго аргумента.

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

По умолчанию частичные имеют доступ к данным, доступным в родительской теме, и к передаваемой информации.

Если какая-то переменная, явно переданная в partial, имеет одно и то же имя родительской переменной темы, то переменная явно пропускает выигрыши.

Однако также возможно включить частичный в изолированный режим, то есть частичный не имеет доступа к данным родительской темы. Для этого просто передайте true в качестве третьего аргумента в partial():

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

Заключение

Даже если довольно простой, класс Engine довольно полный, но, безусловно, его можно улучшить. Например. нет способа проверить, определена ли переменная или нет.

Благодаря 100% -ной совместимости с функциями WordPress и иерархией шаблонов вы можете интегрировать его с существующим и сторонним кодом без проблем.

Однако обратите внимание, что это только частично проверено, так что возможны проблемы, которые я еще не обнаружил.

Пять пунктов под «Что мы получили?» в @kaiser ответ :

  1. Легко обмениваться шаблонами без изменения структуры данных
  2. Легко читаемые tempaltes
  3. Избегать глобальной области
  4. Может ли Unit-Test
  5. Можно обменять Модель /данные без ущерба для других компонентов.

все допустимы для моего класса.

ответил gmazzap 15 PMpWed, 15 Apr 2015 18:09:24 +030009Wednesday 2015, 18:09:24
5

Простой ответ, не пропускайте переменные нигде, поскольку он воняет с использованием глобальных переменных, которые являются злыми.

Из вашего примера кажется, что вы пытаетесь сделать раннюю оптимизацию, еще одно зло;)

Используйте API Wordpress для получения данных, которые хранятся в БД, и не пытайтесь перехитрить и оптимизировать его использование, поскольку API делает больше, а просто извлекает значения и активирует фильтры и действия. Удалив вызов API, вы удаляете способность других разработчиков изменять поведение вашего кода, не изменяя его.

ответил Mark Kaplun 15 AMpWed, 15 Apr 2015 10:41:06 +030041Wednesday 2015, 10:41:06
2

Хотя ответ кайзера технически прав, я сомневаюсь, что это лучший ответ для вас.

Если вы создаете свою собственную тему, то я думаю, что это действительно лучший способ создать какую-то структуру с использованием классов (и, возможно, пространств имен и интерфейсов), хотя это может быть слишком много для темы WP ).

С другой стороны, если вы просто расширяете /корректируете существующую тему и должны передавать только одну или несколько переменных, я думаю, вы должны придерживаться global. Поскольку header.php включен в функцию, переменные, которые вы объявляете в этом файле, могут использоваться только в этом файле. С помощью global вы сделаете их доступными во всем проекте WP:

В header.php:

global $page_extra_title;

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

В single.php (например):

global $page_extra_title;

var_dump( $page_extra_title );
ответил redelschaap 15 AMpWed, 15 Apr 2015 09:13:43 +030013Wednesday 2015, 09:13:43
1

Простое решение - написать функцию, чтобы получить дополнительный заголовок. Я использую статическую переменную, чтобы поддерживать только вызовы базы данных. Поместите это в свои функции. Php.

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

За пределами header.php вызовите функцию, чтобы получить значение:

var_dump(get_extra_title($post->ID));
ответил pbd 15 PMpWed, 15 Apr 2015 17:04:03 +030004Wednesday 2015, 17:04:03

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132