Kısmi değişkenler arasında PHP değişkenini geçmenin en iyi yolu?


16

Header.php gibi bir değişken var:

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

Bir kez ben:

var_dump($page_extra_title);

Ben her zaman NULLheader.php dışında olsun (var_dump sadece header.php düzgün çalışır). Aynı değişkeni ihtiyacım olan her yere yapıştırıyorum (page.php, post.php, footer.php vb.), Ama delilik ve her şeyi korumak neredeyse imkansız hale getiriyor.

Temamdaki tüm dosyalardan bir değişkeni aktarmanın en iyi yolunun ne olduğunu merak ediyorum? "Get_post_meta" ile birlikte function.php kullanarak en iyi fikir olmayabilir sanırım? :)



Değişkenin aynı kapsamda olduğunu düşünüyorum, ayrıca bariz nedenlerle GLOBAL kullanmaktan kaçınmak istiyorum.
Wordpressor

Ben ialocin'in yorumunun yerinde olduğuna inanıyorum. Bir PHP betiği diğerinin var olduğunu bilmiyor ve yerel değişkenlerine veya değerlerine erişemiyor.
jdm2112

1
üstbilgi ve altbilgi bir işlev aracılığıyla dahil edilir, bu nedenle bu dosyalardaki her şeyin kapsamı bu işlevin kapsamlarıdır.
Milo

4
Elçiyi vurma :) Söylediğim tek şey, aslında bir kapsam sorunu. Bir yol var global, değil mi? Ancak iyi nedenlerden dolayı söz konusu bile değildir. Ayrıca globalanahtar kelimelerin kullanılabilir hale getirmek için tür değişkenleri de "aramak" tür var . Kullanım senaryosuna bağlı olarak oturumlar bir çözüm olabilir. Aksi takdirde - belirtildiği gibi - Sanırım bu işi sizin için yapacak bir işlev ya da sınıf gitmek için bir yol.
Nicolai

Yanıtlar:


10

Temel ayrılmış veri yapıları

Verilerin etrafından dolaşmak için normalde bir Model kullanırsınız ("MVC" de "M"). Veriler için çok basit bir arayüze bakalım. Arayüzler sadece yapı taşlarımız için "Tarifler" olarak kullanılır:

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

Yukarıda ilettiğimiz şey: Ortak bir kimlik ve bir "Etiket".

Atom parçalarını birleştirerek veri görüntüleme

Bazı ihtiyacımız Sonraki Görünüm bizim Modeli ve ... Bizim şablonu arasında görüşür.

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

Temel olarak Interface diyor ki

"Bir şey yapabiliriz ve bu görev için bir Model zorunludur"

Son olarak, yukarıdaki uygulamaları yapmalı ve gerçek Görünümü oluşturmalıyız . Gördüğünüz gibi, yapıcı bizim görüşümüz için zorunlu olan şeyin bir Şablon olduğunu ve onu oluşturabileceğimizi söyler . Kolay geliştirme uğruna, şablon dosyasının gerçekten var olup olmadığını bile kontrol ediyoruz, böylece diğer geliştiricilerin hayatlarını (ve bizimkini) çok daha kolay hale getirebiliriz ve not ederiz.

Oluşturma işlevindeki ikinci adımda, şablonun gerçek şablon sarmalayıcısını ve Modeli oluşturmak için bir Kapatma kullanırız 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
        );
    }
}

Görünümü Ayırma ve Oluşturma

Bu, aşağıdaki gibi çok basit bir şablon kullanabileceğimiz anlamına gelir

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

içerik oluşturmak için. Parçaları bir araya getirerek, aşağıdaki satırların etrafında bir şey elde ederiz (Denetleyici, Arabulucu vb.):

namespace WeCodeMore\Package;

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

Ne kazandık?

Bu şekilde yapabiliriz

  1. Veri yapısını değiştirmeden şablonları kolayca değiştirin
  2. Kolay okunan şablonlar
  3. Global kapsamdan kaçının
  4. Can Birim Testi
  5. Modeli / veriyi diğer bileşenlere zarar vermeden değiştirebilir

OOP PHP'yi WP API ile birleştirme

Tabii bu gibi temel tema işlevini kullanarak pek mümkün değildir get_header(), get_footer()doğru, vb? Yanlış. İstediğiniz şablon veya şablon parçasından sınıflarınızı arayın. İşleyin, verileri dönüştürün, istediğinizi yapın. Gerçekten güzelseniz, sadece kendi özel filtrelerinizi ekleyin ve hangi kontrol cihazının hangi rota / koşullu şablon yükü tarafından oluşturulduğuna dikkat etmek için bazı müzakerecilere sahip olursunuz.

Sonuç?

WP'de yukarıdaki gibi bir sorun olmadan çalışabilir ve yine de temel API'ye bağlı kalabilirsiniz ve tek bir global çağırmadan veya global ad alanını kirletmeden kirletmeden kod ve verileri yeniden kullanabilirsiniz.


3
Harika görünüyor! Bu konuya daha yakından bakacağım, güzel cevap
marko

@kaiser neredeyse 3 yıl sonra, yukarıdaki düşüncelerinizde herhangi bir güncelleme var mı? WP çekirdek şablonu daha gelişmiş bir yönde ilerlemedi, bu nedenle 3. taraf çözümleri hala bir şey.
lkraav

1
@Ikraav Muhtemelen bugünlerde böyle yazmazdım, ancak HTML etiketlerinin içindeki değişkenlerin içeriğini çıkarmak için ayrı bir sözdizimi kullanmamanın (ve gereksiz bir ek yükten kaçınmanın) eminim. Öte yandan, bu günlerde PHP'de nadiren ön uç şeyler yazıyorum, ancak JavaScript. Ve VueJS ve arkadaşlarının masaya getirdiklerini gerçekten seviyorum.
kaiser

11

Bu, @kaiser cevabına alternatif bir yaklaşımdır , oldukça iyi buldum (benden +1), ancak çekirdek WP işlevleriyle birlikte ek çalışma gerektiriyor ve şablon hiyerarşisi ile entegre edilmiş.

Paylaşmak istediğim yaklaşım, şablonlar için oluşturma verileriyle ilgilenen tek bir sınıfa (üzerinde çalıştığım bir şeyden soyulmuş bir sürüm) dayanıyor.

Bazı (IMO) ilginç özelliklere sahiptir:

  • şablonlar standart WordPress şablon dosyalarıdır (single.php, page.php) biraz daha fazla güç alırlar
  • mevcut şablonlar sadece çalışır, böylece var olan temalardan şablonu çaba harcamadan entegre edebilirsiniz
  • @kaiser yaklaşımından farklı olarak , şablonlarda $thisanahtar kelimeyi kullanarak değişkenlere erişirsiniz : bu, tanımlanmamış değişkenler durumunda üretimde bildirimlerden kaçınma imkanı verir

EngineSınıf

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

( Burada Gist olarak kullanılabilir .)

Nasıl kullanılır

Gereken tek şey Engine::init(), muhtemelen 'template_redirect'kancada yöntemi çağırmaktır . Bu temada functions.phpveya bir eklentiden yapılabilir.

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

Bu kadar.

Mevcut şablonlarınız beklendiği gibi çalışacaktır. Ancak artık özel şablon verilerine erişme olanağınız var.

Özel Şablon Verileri

Özel verileri şablonlara aktarmak için iki filtre vardır:

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

Birincisi tüm şablonlar için ateşlenir, ikincisi şablona özgüdür, aslında, dymamic kısmı {$type}dosya uzantısı olmayan şablon dosyasının taban adıdır .

Örneğin filtre şablona 'gm_template_data_single'veri aktarmak için kullanılabilir single.php.

Bu kancalara iliştirilen geri çağrılar , anahtarların değişken adları olduğu bir dizi döndürmek zorundadır .

Örneğin, meta verileri şablon verilerinin beğendiği şekilde iletebilirsiniz:

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

Ve sonra, şablonun içinde şunları kullanabilirsiniz:

<?= $this->extra_title ?>

Hata ayıklama modu

Hem sabit WP_DEBUGhem WP_DEBUG_DISPLAYde doğru olduğunda, sınıf hata ayıklama modunda çalışır. Bir değişken tanımlanmadıysa bir istisna atılır.

Sınıf hata ayıklama modunda olmadığında (büyük olasılıkla üretimde) tanımsız bir değişkene erişim boş bir dize çıkarır.

Veri Modelleri

Verilerinizi düzenlemenin güzel ve sürdürülebilir bir yolu model sınıflarını kullanmaktır.

Bunlar, yukarıda açıklanan filtreleri kullanarak veri döndüren çok basit sınıflar olabilir. Takip edilecek özel bir arayüz yoktur, bunlar tercihinize göre organize edilebilir.

Aşağıda, sadece bir örnek var, ancak kendi tarzınızda yapmakta özgürsünüz.

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()Yöntemi (bir sınıf bir geri gibi kullanıldığını çalışır) bir dize için kullanılacak döner <title>şablonun etiketine.

İkinci argümanın 'gm_template_data'şablon adı olması nedeniyle, yöntem ana sayfa için özel bir başlık döndürür.

Yukarıdaki koda sahip olmak gibi bir şey kullanmak mümkündür

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

içinde <head>sayfanın bölümüne.

partials

WordPress, kısmi ana şablona yüklemek için kullanılabilecek get_header()veya get_template_part()kullanılabilecek işlevlere sahiptir .

Bu işlevler, diğer tüm WordPress işlevleri gibi, Enginesınıfı kullanırken şablonlarda kullanılabilir .

Tek sorun, çekirdek WordPress işlevleri kullanılarak yüklenen kısmi bölümlerin içinde, kullanarak özel şablon verileri alma gelişmiş özelliğini kullanmak mümkün değildir $this.

Bu nedenle, Enginesınıfın partial()kısmi (tamamen alt tema ile uyumlu bir şekilde) yüklenmesine izin veren ve yine de özel şablon verilerini kısmen kullanabilen bir yöntemi vardır.

Kullanımı oldukça basit.

Theme partials/content.php(veya alt tema) klasöründe bir dosya olduğu varsayılarak , aşağıdakiler kullanılarak dahil edilebilir:

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

Bu kısmi içinde tüm ana tema verilerine erişmek aynı şekilde mümkün olacaktır.

WordPress işlevlerinin aksine, Engine::partial()yöntem belirli verileri kısmi olarak geçirmeye izin verir, sadece bir dizi veriyi ikinci argüman olarak geçirir.

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

Varsayılan olarak, kısmi öğeler ana temada bulunan verilere ve geçirilen veri açıklığına erişime sahiptir.

Kısmen iletilen bir değişkenin üst tema değişkeni ile aynı adı varsa, açıkça geçirilen değişken kazanır.

Bununla birlikte, bir kısmi izole edilmiş modda dahil etmek de mümkündür , yani kısmi üst tema verilerine erişemez. Bunu yapmak için, trueüçüncü argüman olarak şunu iletin partial():

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

Sonuç

Oldukça basit olsa bile, Enginesınıf oldukça eksiksizdir, ancak kesinlikle daha da geliştirilebilir. Örneğin, bir değişkenin tanımlanıp tanımlanmadığını kontrol etmenin bir yolu yoktur.

WordPress özellikleri ve şablon hiyerarşisi ile% 100 uyumluluğu sayesinde, mevcut ve üçüncü taraf kodlarıyla sorunsuzca entegre edebilirsiniz.

Ancak, sadece kısmen test edildiğini unutmayın, bu yüzden henüz keşfetmediğim sorunlar olabilir.

Altında beş puan "Ne kazandırdı?" içinde @kaiser cevap :

  1. Veri yapısını değiştirmeden şablonları kolayca değiştirin
  2. Kolay okunan şablonlar
  3. Global kapsamdan kaçının
  4. Can Birim Testi
  5. Modeli / veriyi diğer bileşenlere zarar vermeden değiştirebilir

hepsi benim sınıfım için de geçerli.


1
Hehe. Aferin dostum :) +1
kaiser

@gmazzap neredeyse 3 yıl sonra, yukarıdaki düşüncelerinizde herhangi bir güncelleme var mı? WP çekirdek şablonu daha gelişmiş bir yönde ilerlemedi, bu nedenle 3. taraf çözümleri hala bir şey.
lkraav

1
Bu günlerde pek çok tema yapmıyorum. Son zamanlarda benim yolum veri oluşturmak ve şablonlara geçmek için github.com/Brain-WP/Context + github.com/Brain-WP/Hierarchy'yi birleştirmekti . Şablon motorunun kendisi için farklı yaklaşımlar kullandım, Folyo (tabii ki), Bıyık, aynı zamanda Dal (sadece bağımlılık cehenneminden kaçınmak için tüm webiste kontrol sahibi olduğumda) @lkraav
gmazzap

5

Basit cevap, kötü olan global değişkenleri kullanmaya çalıştığı için değişkenleri hiçbir yere aktarmayın.

Örneğinizden, erken bir optimizasyon yapmaya çalıştığınız anlaşılıyor, ancak başka bir kötülük;)

Veritabanında depolanan verileri almak için wordpress API'sini kullanın ve API değerleri sadece değerlerden almaktan daha fazlasını yaptığında ve filtreleri ve eylemleri etkinleştirdiğinden kullanımını zekice oluşturmaya ve optimize etmeye çalışmaz. API çağrısını kaldırarak, diğer geliştiricilerin kodunuzun davranışını değiştirmeden değiştirme yeteneğini kaldırırsınız.


2

Kaiser'in cevabı teknik olarak doğru olsa da, bunun sizin için en iyi cevap olduğundan şüpheliyim.

Kendi temanızı oluşturuyorsanız, sınıfları kullanarak bir çeşit çerçeve kurmanın en iyi yolu olduğunu düşünüyorum (ve belki de ad alanları ve arayüzler de, bu bir WP teması için biraz fazla olabilir).

Öte yandan, sadece mevcut bir temayı genişletiyor / ayarlıyorsanız ve sadece bir veya birkaç değişkeni geçmeniz gerekiyorsa, buna bağlı kalmanız gerektiğini düşünüyorum global. header.phpBir işleve dahil olduğundan , bu dosyada bildirdiğiniz değişkenler yalnızca bu dosyada kullanılabilir. Sizinle globaltüm WP projesinde erişilebilir hale getirin:

İçinde header.php:

global $page_extra_title;

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

Olarak single.php(örneğin):

global $page_extra_title;

var_dump( $page_extra_title );

3
Ben kaba olmak istemiyorum, ama küresel kapsamda dalış yapmak gerçekten kötü bir uygulamadır. Küresel kapsamı tamamen eklemekten kaçınmalısınız. Buradaki kuralları adlandırmaya gerçekten dikkat etmelisiniz ve değişken adınızın, başka hiç kimsenin böyle bir adı üretemeyeceği şekilde benzersiz olacağından emin olmanız gerekir. @kaiser yaklaşımı sizin için denize dalmış gibi görünebilir, ancak en iyi ve en güvenli olanıdır. Size bununla nasıl başa çıkacağınızı söyleyemem ama gerçekten küresel kapsamın dışında kalmanızı tavsiye ediyorum :-)
Pieter Goosen

3
Tabii ki diğer değişkenlerin üzerine yazmamaya dikkat etmelisiniz. Özel değişkenlerinizle benzersiz bir önek veya bir dizi kullanarak ya $wp_theme_vars_page_extra_titleda $wp_theme_vars['page_extra_title']örneğin bu sorunu çözebilirsiniz . Burada küresel olanın neden işe yarayacağının sadece bir açıklamasıydı. OP, bir değişkenin tüm dosyalar arasında geçmesinin bir yolunu sordu, kullanarak globalbunu yapmanın bir yoludur.
redelschaap

2
Hayır, küreseller bunu yapmanın bir yolu değildir. Globalleri kullanmadan aynı şeyi başarmanın çok daha iyi yolları var. Daha önce söylediğim gibi ve @kaiser'ın cevabında belirttiği gibi, küresel kapsamdan kaçının ve bundan uzak durun. Örnek olarak, bu çok kolay alternatifi alın, kodunuzu bir işleve sarın ve gerektiğinde işlevi çağırın. Bu şekilde, bir global ayarlamanıza veya kullanmanıza gerek yoktur.
Pieter Goosen

3
Evet öyle. En iyi yol olmayabilir, ama kesinlikle bir yol.
redelschaap

2
but it is really bad practice diving into the global scopeKeşke biri bunu WP çekirdek geliştiricilerine söylemişti. Gerçekten kullanarak ad, veri soyutlama, tasarım desenleri, birim test ve noktasını anlamıyorum diğer programlama iyi uygulamalar / yazılı kod teknikleri için Wordpress çekirdek Glabal değişkenler gibi kötü kodlama uygulamaları (örneğin widget doludur Wordpress kodu).
Ejaz

1

Kolay bir çözüm ekstra başlık almak için bir işlev yazmaktır. Ben sadece bir birine veritabanı çağrıları tutmak için statik bir değişken kullanın. Bunu işlevlerinize koyun. 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 dışında, değeri almak için işlevi çağırın:

var_dump(get_extra_title($post->ID));
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.