Bölüm 6 – WordPress ve Nesne Yönelimli Programlama: Bir WordPress Örneği – Uygulama: Bölümlerin Kaydedilmesi

Yayınlanan: 2022-02-04

Nesne Yönelimli Programlama serimize tekrar hoş geldiniz.

Dizinin Tasarım kısmında da anlattığımız gibi bir admin sayfası bölümlerden oluşmaktadır . Her bölüm bir veya daha fazla alan içerir ve bu alanların her biri bir veya daha fazla öğe içerir.

Bu kodda nasıl görünürdü?

 public function register_sections() { $my_section = $this->register_section( /* ... */ ); $my_field = $my_section->add_field( /* ... */ ); $my_element = $my_field->add_element( /* ... */ ); }

Pekala, kullanımı kolay görünüyor ve muhtemelen üç yeni sınıf oluşturmamız gerekeceğini şimdiden söyleyebiliriz: Section , Field ve Element .

 class Section {}
 class Field {}
 class Element {}

Bir dakikanızı ayıralım ve bu sınıflar hakkında şu ana kadar ne bildiğimizi kendimize soralım.

  • $my_section->add_field()Section sınıfı yeni bir Field nesnesi ekleyebilmeli (ve depolayabilmelidir)
  • $my_field->add_element()Field sınıfı, yeni bir Element nesnesi ekleyebilmeli (ve depolayabilmelidir).

Normalde yaptığımız gibi Field nesnelerimizi bir dizide saklayarak başlıyoruz:

 class Section { /** * @var Field[] Section field objects. */ protected $fields = array();

Bu $fields değişkeni bir sınıf üyesidir ve buna özellik diyoruz. Özellikler, bir sınıfta yaşayan PHP değişkenleridir ve herhangi bir veri türünde ( string , integer , object , vb.) olabilirler.

Ayrıca yeni bir alan oluşturmak ve eklemek için add_field() yöntemini yazacağız.

 public function add_field() { $field = new Field( /* ... */ ); $this->fields[] = $field; return $field; }

Bu yöntem, yeni bir Field nesnesi oluşturur, onu field özelliğine ekler ve yeni oluşturulan nesneyi döndürür. Oldukça basit.

Aynı işlemi Field sınıfı için de tekrarlayalım.

 class Field { /** * @var Element[] Field elements. */ private $elements = array(); /** * Create a new element object. * * @return Element */ private function create_element() { return new Element( /* ... */ ); } /** * Add a new element object to this field. */ public function add_element() { $element = $this->create_element(); $this->elements[] = $element; } }

Bu bir başlangıç! Sıradaki ne?

Bölüm Sınıfı

Yeni bir bölüm oluşturulduğunda add_settings_section()'ı çağırmamız gerekiyor. Bir kez daha, yapıcı yöntemi, başlatmamızı gerçekleştirmenin harika bir yoludur. Bunu sınıfa ekleyelim:

 class Section { // ... public function __construct() { add_settings_section( $this->id, $this->title, array( $this, 'print_description' ), $this->page ); } }

Bir Bölümün onu tanımlamak için bir bilgi ismine ihtiyacı var gibi görünüyor (etiketlerin id özelliğinde kullanılır). Ayrıca bir başlığı, açıklaması olabilir ve belirli bir sayfaya ait olabilir.

 class Section { /** * @var Field[] Section field objects. */ protected $fields = array(); /** * @var string Section title. */ public $title; /** * @var string Section id. */ public $id; /** * @var string Slug-name of the settings page this section belongs to. */ public $page; /** * @var string Section description. */ public $description;

Bunun gibi bir şey yaparak bölümün başlığını belirleyebiliriz:

 $section = new Section(); $section->title = __( 'Hello world', 'prsdm-limit-login-attempts' );

Bu pek doğru değil. Yukarıdaki kod tamamen geçerli olsa da, aslında yapmasını beklediğimiz şeyi yapmıyor.

Yapıcı yöntemi, yeni bir Bölüm nesnesi oluşturulduğunda yürütülür. Bu nedenle, başlığı belirleme şansımız bile olmadan add_settings_section() çağrılacak. Sonuç olarak, bölümün bir başlığı olmayacaktır.

Pressidium ile web sitenizi barındırın

60 GÜN PARA GERİ GARANTİSİ

PLANLARIMIZI GÖRÜN

Nesnemizin başlatılması sırasında başlığın mevcut olması gerekiyor, bu yüzden bunu yapıcıda yapmamız gerekiyor.

 class Section { /** * @var string Section title. */ private $title; public function __construct( $title ) { $this->title = $title; // ... } // ..

$this->title başlık sınıfı özelliğine atıfta bulunduğuna dikkat edin, burada $title yapıcının argümanına atıfta bulunur.

Burada da görünürlükten yararlanıyoruz. $title özelliğimize yalnızca onu tanımlayan sınıf tarafından erişileceğinden, onu private olarak ilan edebiliriz. Bu nedenle, sınıf dışından erişilmesini engelliyoruz.

Ayrıca, bölümün açıklamasını yazdıracak bir print_description() yöntemi de eklemeliyiz.

 /** * Print the section description. */ public function print_description() { echo esc_html( $this->description ); }

Hepsini bir araya getirdiğimizde, Bölüm sınıfımız şöyle görünür.

 class Section { /** * @var Field[] Section field objects. */ protected $fields = array(); /** * @var string Section title. */ private $title; /** * @var string Section id. */ private $id; /** * @var string Slug-name of the settings page this section belongs to. */ private $page; /** * @var string Section description. */ private $description; /** * Section constructor. * * @param string $id Section id. * @param string $title Section title. * @param string $page Slug-name of the settings page. * @param string $description Section description. */ public function __construct( $id, $title, $page, $description ) { $this->id = $id; $this->title = $title; $this->page = $page; $this->description = $description; add_settings_section( $this->id, $this->title, array( $this, 'print_description' ), $this->page ); } /** * Print the section description. */ public function print_description() { echo esc_html( $this->description ); } /** * Create and add a new field object to this section. */ public function add_field() { $field = new Field( /* ... */ ); $this->fields[] = $field; return $field; } }

Alan Sınıfı

Section benzer bir şekilde, şimdi devam edebilir ve add_settings_field() WordPress işlevini kullanacak olan Field sınıfını oluşturabiliriz.

 class Field { /** * @var Element[] Field elements. */ private $elements = array(); /** * @var string ID of the section this field belongs to. */ private $section_id; /** * @var string Field description. */ private $description; /** * Field constructor. * * @param string $id Field ID. * @param string $label Field label. * @param string $page Slug-name of the settings page. * @param string $section_id ID of the section this field belongs to. * @param string $description Field description. */ public function __construct( $id, $label, $page, $section_id, $description ) { $this->section_id = $section_id; $this->description = $description; add_settings_field( $id, $label, array( $this, 'render' ), $page, $this->section_id ); } }

Burada ayrıca alanın kimliği, etiketi ve açıklaması için varsayılan değerler sağlamak istiyoruz. Bunu, yapıcıya bir seçenekler dizisi ileterek ve bu seçenekleri ayrıştırmak için wp_parse_args() WordPress işlevini kullanarak yapabiliriz.

 class Field { /** * @var int Number of fields instantiated. */ private static $number_of_fields = 0; // ... /** * Field constructor. * * @param string $section_id ID of the section this field belongs to. * @param string $page Slug-name of the settings page. * @param array $options Options. */ public function __construct( $section_id, $page, $options = array() ) { self::$number_of_fields++; $options = wp_parse_args( $options, array( 'label' => sprintf( __( 'Field #%s', 'prsdm-limit-login-attempts' ), self::$number_of_fields 'id' => 'field_' . self::$number_of_fields, 'description' => '' ) ); $this->section_id = $section_id; $this->description = $options['description']; add_settings_field( $options['id'], $options['label'], array( $this, 'render' ), $page, $this->section_id ); } }

wp_parse_args() işlevi, kullanıcı tanımlı değerleri ( $options dizisi) varsayılan değerlerle birleştirmemizi sağlar.

 array( 'label' => sprintf( __( 'Field #%s', 'prsdm-limit-login-attempts' ), self::$number_of_fields 'id' => 'field_' . self::$number_of_fields, 'description' => '' )

Ayrıca her alan için benzersiz etiketler belirlememiz gerekiyor. Bunu, etiketi bir önek ( 'field_' ) ve ardından yeni bir Field nesnesi oluşturulduğunda artırılacak bir sayıya ayarlayarak halledebiliriz. Bu numarayı $number_of_fields statik özelliğinde saklayacağız.

 /** * @var int Number of fields instantiated. */ private static $number_of_fields = 0;

Statik bir özelliğe, önce bir sınıf örneği oluşturmaya gerek kalmadan doğrudan erişilebilir.

 'id' => 'field_' . self::$number_of_fields

self anahtar kelimesi mevcut sınıfa atıfta bulunmak için kullanılır ve kapsam çözümleme operatörü :: (genellikle "çift kolon" olarak adlandırılır) yardımıyla statik özelliğimize erişebiliriz.

Bu şekilde, yapıcıda her zaman aynı $number_of_fields özelliğine erişiriz, her nesne oluşturulduğunda değerini artırırız, bu da her alana benzersiz bir etiket eklenmesiyle sonuçlanır.

İleriye dönük olarak, render() yöntemi, açıklamayı yazdırdıktan sonra (varsa), tüm öğeleri yineler ve her birini işler.

 public function render() { if ( ! empty( $this->description ) ) { printf( '<p class="description">%s</p>', esc_html( $this->description ) ); } foreach ( $this->elements as $key => $element ) { $element->render(); } }

Hepsini bir araya koy…

 class Field { /** * @var int Number of fields instantiated. */ private static $number_of_fields = 0; /** * @var Element[] Field elements. */ private $elements = array(); /** * @var string ID of the section this field belongs to. */ private $section_id; /** * @var string Field description. */ private $description; /** * Field constructor. * * @param string $section_id ID of the section this field belongs to. * @param string $page Slug-name of the settings page. * @param array $options Options. */ public function __construct( $section_id, $page, $options = array() ) { self::$number_of_fields++; $options = wp_parse_args( $options, array( 'label' => sprintf( /* translators: %s is the unique s/n of the field. */ __( 'Field #%s', 'prsdm-limit-login-attempts' ), self::$number_of_fields 'id' => 'field_' . self::$number_of_fields, 'description' => '' ) ); $this->section_id = $section_id; $this->description = $options['description']; add_settings_field( $options['id'], $options['label'], array( $this, 'render' ), $page, $this->section_id ); } /** * Create a new element object. * * @return Element */ private function create_element() { return new Element( /* ... */ ); } /** * Add a new element object to this field. */ public function add_element() { $element = $this->create_element(); $this->elements[] = $element; } /** * Render the field. */ public function render() { if ( ! empty( $this->description ) ) { printf( '<p class="description">%s</p>', esc_html( $this->description ) ); } foreach ( $this->elements as $key => $element ) { $element->render(); } } }

Eleman Sınıfı

İleride, Element sınıfını benzer bir şekilde oluşturacağız!

Sınıfı şöyle yazmaya başlayacağız:

 class Element { /** * @var int Number of elements instantiated. */ private static $number_of_elements = 0; /** * @var string Element label. */ private $label; /** * @var string Element name. */ private $name; /** * @var mixed Element value. */ private $value; /** * Element constructor. * * @param string $section_id Section ID. * @param array $options Options. */ public function __construct( $section_id, $options = array() ) { self::$number_of_elements++; $options = wp_parse_args( $options, array( 'label' => sprintf( /* translators: %s is the unique s/n of the element. */ __( 'Element #%s', 'prsdm-limit-login-attempts' ), self::$number_of_elements ), 'name' => 'element_' . self::$number_of_elements ) ); $this->label = $options['label']; $this->name = $options['name']; $this->value = ''; } /** * Render the element. */ public function render() { ?> <fieldset> <label> <input type="number" name="<?php echo esc_attr( $this->name ); ?>" value="<?php echo esc_attr( $this->value ); ?>" /> <?php echo esc_html(); ?> </label> </fieldset> <?php } }

Siteler arası komut dosyası çalıştırma saldırılarını önlemek için, burada yaptığımız gibi, esc_attr() ve esc_html() WordPress işlevlerini kullanarak çıktınızdan kaçtığınızdan emin olun. Öğelerimizi yalnızca yönetici sayfalarında oluşturuyor olsak da, çıktı verilerinden her zaman kaçmak yine de iyi bir fikirdir.

NOT: Siteler arası komut dosyası çalıştırma (veya XSS), genellikle web uygulamalarında bulunan bir tür güvenlik açığıdır. XSS, saldırganların diğer kullanıcılar tarafından görüntülenen web sayfalarına istemci tarafı kodu eklemesine olanak tanır. Aynı kaynak ilkesi gibi erişim denetimlerini atlamak için saldırganlar tarafından bir siteler arası komut dosyası çalıştırma güvenlik açığı kullanılabilir.

Eklentinin gereksinimlerini toplarken, birden fazla öğe türü olduğunu fark ettik - onay kutuları, radyo düğmeleri, sayı alanları vb. Tasarımımızı ortaya koyduğumuzda, genişletilmesi gereken bir Element sınıfı oluşturmaya karar verdik. Böylece, her eleman türü için bir alt sınıf elde edeceğimizi biliyoruz.

Çıktı, öğe türüne bağlı olarak farklılık göstermelidir, bu nedenle render() 'ı soyut bir yönteme dönüştüreceğiz. Bu, elbette, sınıfın kendisinin de soyut olması gerektiği anlamına gelir.

 abstract class Element { /** * @var int Number of elements instantiated. */ private static $number_of_elements = 0; /** * @var string Element label. */ protected $label; /** * @var string Element name. */ protected $name; /** * @var mixed Element value. */ protected $value; /** * Element constructor. * * @param string $section_id Section ID. * @param array $options Options. */ public function __construct( $section_id, $options = array() ) { self::$number_of_elements++; $options = wp_parse_args( $options, array( 'label' => sprintf( /* translators: %s is the unique s/n of the element. */ __( 'Element #%s', 'prsdm-limit-login-attempts' ), self::$number_of_elements ), 'name' => 'element_' . self::$number_of_elements ) ); $this->label = $options['label']; $this->name = $options['name']; $this->value = ''; } /** * Render the element. */ abstract public function render(); }

Örneğin, Number_Element sınıfı şöyle görünür:

 class Number_Element extends Element { /** * Render the element. */ public function render() { ?> <fieldset> <label> <input type="number" name="<?php echo esc_attr( $this->name ); ?>" value="<?php echo esc_attr( $this->value ); ?>" /> <?php echo esc_html(); ?> </label> </fieldset> <?php } }

Benzer şekilde, öğelerimizin geri kalanı için bir Checkbox_Element , bir Radio_Element ve hatta bir Custom_Element sınıfı oluşturabiliriz.

Sınıflarımızı , hepsinin aynı şekilde kullanılabilmesi için oluşturduğumuza dikkat edin. Element öğesinin herhangi bir alt render() yönteminin çağrılması, bir miktar HTML çıktısı verir.

Bu, nesne yönelimli programlamanın temel kavramlarından biri olan polimorfizmin bir örneğidir.

polimorfizm

"Polimorfizm", kelimenin tam anlamıyla "birçok biçim" anlamına gelir (Yunanca "çok" anlamına gelen "poli" ve "biçim" anlamına gelen "morfe" sözcüklerinden). Bir Element alt sınıfının birçok biçimi olabilir, çünkü üst hiyerarşisinde herhangi bir sınıf biçimini alabilir.

Bir Element nesnesinin beklendiği herhangi bir yerde Number_Element , Checkbox_Element veya başka herhangi bir alt türü kullanabiliriz, çünkü tüm alt nesneler aynı şekilde kullanılabilir (yani render() yöntemini çağırarak), yine de davranabilir . farklı (çıktı, her öğe türü için farklı olacaktır).

Muhtemelen söyleyebileceğiniz gibi, polimorfizm ve kalıtım yakından ilişkili kavramlardır.

ikame edilebilirlik

SOLID'deki “L” olan Liskov İkame İlkesi (veya LSP) , şunları belirtir:

“Bir bilgisayar programında, eğer S, T'nin bir alt tipiyse, o zaman T tipindeki nesneler, S tipindeki nesnelerle değiştirilebilir (yani, T tipindeki bir nesne, S alt tipindeki herhangi bir nesne ile ikame edilebilir), herhangi bir değişiklik yapılmadan. programın arzu edilen özellikleri.”

Layman'ın terimleriyle, herhangi bir beklenmedik davranış olmadan herhangi bir alt sınıfı ana sınıfının yerine kullanabilmelisiniz.

fabrikalar

Şimdi yeni bir Element oluşturan create_element() yöntemine sahip olduğumuz Field sınıfımıza geri dönelim.

 /** * Create a new element object. * * @return Element */ private function create_element() { return new Element( /* ... */ ); } /** * Add a new element object to this field. */ public function add_element() { $element = $this->create_element(); $this->elements[] = $element; }

Yeni bir nesne döndüren bir yönteme genellikle basit fabrika denir (bir tasarım deseni olan "fabrika yöntemi" ile karıştırılmamalıdır).

Element üst sınıfının yerine herhangi bir alt türün kullanılabilir olduğunu bilerek, devam edip bu fabrikayı değiştireceğiz, böylece herhangi bir alt sınıftan nesneler yaratabilecek.

 /** * Create a new element object. * * @throws Exception If there are no classes for the given element type. * @throws Exception If the given element type is not an `Element`. * * @param string $element_type * @param array $options * * @return Element */ private function create_element( $element_type, $options ) { $element_type = __NAMESPACE__ . '\\Elements\\' . $element_type; if ( ! class_exists( $element_type ) ) { throw new Exception( 'No class exists for the specified type' ); } $element = new $element_type( $this->section_id, $options ); if ( ! ( $element instanceof Element ) ) { throw new Exception( 'The specified type is invalid' ); } return $element; } /** * Add a new element object to this field. * * @param string $element_type * @param array $options */ public function add_element( $element_type, $options ) { try { $element = $this->create_element( $element_type, $options ); $this->elements[] = $element; } catch ( Exception $e ) { // Handle the exception } }

Öğe türünü geçerli adla önekleyerek başlıyoruz:

 $element_type = __NAMESPACE__ . '\\Elements\\' . $element_type;

__NAMESPACE__ sihirli sabiti, geçerli ad alanı adını içerir.

Ardından, belirtilen öğe türü için bir sınıf olduğundan emin oluruz:

 if ( ! class_exists( $element_type ) ) { throw new Exception( 'No class exists for the specified type' ); }

Ardından, yeni bir nesne oluşturuyoruz:

 $element = new $element_type( $this->section_id, $options );

Ve son olarak, yeni oluşturulan nesnenin gerçekten bir Element örneği olduğundan emin oluruz:

 if ( ! ( $element instanceof Element ) ) { return; }

Genişleyen

Eklentimizi genişletilebilir olacak şekilde oluşturduğumuzu belirtmekte fayda var. Farklı türde sayfalar, bölümler, öğeler eklemek, Admin_Page , Section , Element vb. öğelerini genişleten yeni bir sınıf oluşturmak kadar kolaydır. Bu temel sınıflar, yeni bir sayfa, bölüm veya öğe eklemek için değiştirilmesi gereken herhangi bir kod içermez.

Açık/Kapalı İlke (veya OCP), SOLID'deki "O" şunları belirtir:

“Yazılım varlıkları (sınıflar, modüller, işlevler vb.) genişlemeye açık, ancak değişikliğe kapalı olmalıdır.”

Bu, Admin_Page gibi bir sınıfı genişletip yeniden kullanabilmemiz gerektiği anlamına gelir, ancak bunu yapmak için onu değiştirmemiz gerekmez.

Çözüm

Bu yazımızda bölümlerimizi, alanlarımızı ve elementlerimizi kaydettik. Bunları uygularken polimorfizmin ne olduğuna ve neden faydalı olduğuna daha yakından baktık. Ayrıca birkaç SOLID ilkesine, “Liskov Değiştirme İlkesi” ve “Açık/Kapalı İlke”ye göz attık.

WordPress kancalarımızı yönetme şeklimizi nasıl iyileştirebileceğimize daha yakından bakacağımız bu yolculuğun bir sonraki bölümünde bizimle kalın.

Nesneye Yönelik Programlama Dizimizin 7. Bölümü'nü okumak için buraya tıklayın

Ayrıca bakınız

  • WordPress ve Nesne Yönelimli Programlama – Genel Bakış
  • Bölüm 2 – WordPress ve Nesne Yönelimli Programlama: Gerçek Bir Dünya Örneği
  • Bölüm 3 – WordPress ve Nesne Yönelimli Programlama: Α WordPress Örneği – Kapsamın Tanımlanması
  • Bölüm 4 – WordPress ve Nesne Yönelimli Programlama: Bir WordPress Örneği – Tasarım
  • Bölüm 5 – WordPress ve Nesne Yönelimli Programlama: Bir WordPress Örneği – Uygulama: Yönetim Menüsü