Löse ein komplexes Problem (rational)
Um komplexe Probleme zu lösen, gibt es eine Art standardisierten / bekannten Ansatz: Teilen Sie Ihr komplexes Problem in eine Reihe kleinerer Probleme auf. Kleinere Probleme sind leichter zu lösen. Wenn Sie jedes kleinere Problem gelöst haben, haben Sie Ihr komplexes Problem meistens bereits gelöst.
Suchen und in Teile teilen
Soweit zur Theorie. Lassen Sie uns Ihre Bedürfnisse überprüfen. Ich stelle in meinen eigenen Worten auf, was Sie oben beschrieben haben:
- Behandeln Sie eine Reihe von Optionen in mehreren Instanzen (Folie 1 bis Folie N).
- Speichern Sie Werte in Post-Meta-Werten.
- Eine Metabox in mehreren Fällen (Folie-1 bis Folie-N).
- Ein Diashow-Editor (Formular) in der Metabox (wieder mehrere Instanzen)
- Ein Basisformular zum Bearbeiten der Daten (z. B. mithilfe einfacher HTML-Formularelemente)
- Das technische Problem zu lösen, mit mehreren WYSIWYG-Editoren.
- Ihre komplexere Bildeingabe.
Wie kann man das codieren? Ich denke, der wichtigste Teil ist, dass Sie sich vor dem eigentlichen Codieren überlegen, was Sie wirklich erreichen möchten und wie Sie das Problem in kleinere Teile aufteilen können. Die obige Liste ist möglicherweise nicht vollständig. Sie ist genau das, was ich aus Ihrer Frage lesen konnte. Und das Format ist ziemlich unspezifisch. Es ist nur mehr oder weniger eine Wiederholung von dem, was Sie geschrieben haben, aber ein bisschen in einzelne Punkte geordnet.
Überprüfen Sie also, ob dies Ihre Anforderungen sind, und erweitern Sie diese Liste auf alles, was Sie für richtig halten.
Wenn Sie fertig sind, besteht der nächste Schritt darin, zu prüfen, wie diese benötigten Inhalte in einfachen Worten und als Liste der Funktionen Ihres Plugins ausgedrückt werden können
- Ein Plugin dafür: SlideshowPlugin.
- Eine Diashow, die aus Folien besteht.
- Ein Editor zum Bearbeiten einer Diashow.
- Ein Ort zum Speichern und Abrufen der Diashow-Daten.
Sieht jetzt ganz einfach aus, oder? Ich habe hier wahrscheinlich etwas verpasst, also überprüfen Sie es selbst, bevor Sie fortfahren. Wie geschrieben, bevor Sie mit dem Code beginnen, entscheiden Sie sich einfach in einfachen Formulierungen und Begriffen. Denken Sie nicht einmal darüber nach, welche Teile problematisch sind und welche nicht oder wie einige Details wie die Benennung von HTML-Eingabeelementen codiert werden. Ich weiß, dass es schwierig ist, wenn Sie schon so lange versucht haben, von vorne zu beginnen, weil Sie viele Ideen im Hinterkopf haben, die wieder auftauchen.
Nimm einen Bleistift und etwas Papier. Dies hilft oft, sich für jemanden zu entscheiden.
Wie Sie sehen, habe ich hier weder die Notwendigkeit einer Metabox noch eines benutzerdefinierten Beitragstyps angegeben. Es ist zu spezifisch, um zuerst die Teile Ihres Problems kennenzulernen. Metabox oder Custom Post Type ist sehr konkret, möglicherweise etwas, wie man das Plugin bereits codiert. Also hielt ich das für den Moment aus und versuchte, die Bedürfnisse kurz, aber genau zu beschreiben. Die Metabox oder ähnliches könnte im Design eine Rolle spielen. Wir werden sehen.
Gestalte dein Plugin
Nachdem Sie wissen, was Sie erreichen müssen / möchten, können Sie sich überlegen, wie Sie das Plugin entwerfen möchten. Dies könnte durch Zeichnen eines kleinen Bildes geschehen. Identifizieren Sie einfach die Teile aus Ihrer Funktionsliste und stellen Sie sie in Beziehung zueinander. Wie Sie sehen können, müssen Sie eigentlich keine schönen Künste machen;) :)
Entschuldigen Sie mein schlechtes Schreiben, ich hoffe das kann gelesen werden. In jedem Fall können Sie Ihr eigenes Bild erstellen, dies ist nur ein Beispiel. Designs können variieren. Wenn Sie es also nicht auf die gleiche Weise zeichnen würden, ist das ganz normal.
Ich mache den Beginn des Entwurfsschritts gerne auf Papier, weil es hilft, das Problem von oben besser zu sehen, und es ist auf Papier viel schneller als auf dem Computer.
Jetzt können Sie Ihre Liste mit Ihrem Design vergleichen und prüfen, ob alle Funktionen der Liste von den Teilen abgedeckt werden, die Sie im Design haben. Sieht bisher gut aus für meine Liste und mein Bild, also fahre ich fort, aber überspringe diesen Schritt nicht. Ansonsten wissen Sie nicht, ob Sie etwas verpasst haben. Und wenn Sie bald mit dem Codieren beginnen, ist es viel schwieriger, etwas zu ändern, das bereits codiert ist, als ein Bild oder eine Liste.
Problemtrennung im Design
Jetzt wird dieses Plugin konkreter. Nach einigem Design ist es wahrscheinlich der richtige Zeitpunkt, um mit dem Codieren zu beginnen. Da wir die Liste oben haben, könnte ich jeden Punkt für sich durchgehen und mit dem Design abgleichen, damit ich weiß, wie die Teile zueinander in Beziehung stehen.
Denn wenn jeder einzelne Teil fertig ist, ist das Plugin fertig, ohne sich sofort für das Ganze zu entscheiden, was das ursprüngliche Problem war.
Ich mache die Codierung jetzt ein bisschen im Kommentarstil und etwas Beispielcode. Es ist eine erste Implementierungsidee und der Code ist ungetestet. Es ist nur, um mir die Hände schmutzig zu machen und um Ihnen wahrscheinlich ein Beispiel dafür zu geben, wie - aber nicht muss - dies geschrieben werden kann. Ich neige manchmal schon dazu, zu spezifisch zu sein, also pass auf mich auf. Wenn ich Code schreibe, schreibe ich ihn beim Erstellen ziemlich oft neu, aber ich kann ihn beim Erstellen des Beispielcodes nicht sichtbar machen. Denken Sie also daran. Wenn Sie sehen, dass etwas einfacher zu tun ist, wählen Sie Ihre Route. Sie müssen Ihren Code später ändern und erweitern, dies ist also nur ein Beispielcode.
Plugin
Nur eine Klasse, die die grundlegende Operation wie das Registrieren von Hooks und das Bereitstellen der Diashow mit den Folien und den Metaboxen für den Editor behandelt. Hier fängt alles an. Plugins werden an einem einzigen Codepunkt gestartet. Ich nenne das Bootstrap:
<?php
/** Plugin Headers ... */
return SlideshowPlugin::bootstrap();
class SlideshowPlugin {
/** @var Slideshow */
private $slideshow;
/** @var SlideshowMetaboxes */
private $metaboxes;
/** @var SlideshowPlugin */
static $instance;
static public function bootstrap() {
$pluginNeeded = (is_admin() && /* more checks based your needs */ );
if (!$pluginNeeded)
return;
}
if (NULL === self::$instance) {
// only load the plugin code while it's really needed:
require_once('library/Slideshow.php');
require_once('library/SlideshowSlide.php');
require_once('library/Store.php');
require_once('library/Metaboxes.php');
require_once('library/Metabox.php');
require_once('library/Form.php');
// ...
self::$instance = new SlideshowPlugin();
}
return self::$instance;
}
private function addAction($action, $parameters = 0, $priority = 10) {
$callback = array($this, $action);
if (!is_callable($callback)) {
throw new InvalidArgumentExeception(sprintf('Plugin %s does not supports the %s action.', __CLASS__, $action));
}
add_action($action, $callback, $parameters, $priority);
}
public function __construct() {
$this->addAction('admin_init');
}
/**
* @return bool
*/
private function isEditorRequest() {
// return if or not the request is the editor page
}
/**
* @-wp-hook
*/
public function admin_init() {
// register anything based on custom post type and location in the admin.
// I don't care about the details with CPT right now.
// It's just that editorAction is called when we're on the slideshow
// editor page:
if ($this->isEditorRequest()) {
$this->editorAction();
}
}
private function getPostID() {
// ... code goes here to get post id for request
return $postID;
}
private function getSlideshow() {
is_null($this->slideshow)
&& ($postID = $this->getPostID())
&& $slideshow = new Slideshow($postID)
;
return $slideshow;
}
private function getMetaboxes() {
is_null($this->metaboxes)
&& ($slideshow = $this->getSlideshow())
&& $this->metaboxes = new SlideshowMetaboxes($slideshow)
;
return $this->metaboxes;
}
private function editorAction() {
$metaboxes = $this->getMetaboxes();
}
}
Diese Plugin-Klasse ist also schon ziemlich vollständig. Ich habe nicht angegeben, wie die postID abgerufen werden soll, aber sie ist bereits in einer eigenen Funktion gekapselt. Daneben habe ich nicht codiert, um zu überprüfen, ob dies die richtige Seite ist, um den Editor anzuzeigen, aber dafür gibt es bereits einen Stub-Code.
Am Ende wird die editorAction () aufgerufen, wenn sich die Anforderung auf der eigentlichen benutzerdefinierten Post-Type-Editor-Seite befindet und dort die Metaboxen erfasst werden. Das ist es. Das Plugin sollte jetzt ziemlich vollständig sein. Es hat die Diashow und kümmert sich um die Metaboxen. Verglichen mit dem Design sind dies die Teile, die mit dem Plugin verknüpft sind. Vergleichen Sie mit dem Bild:
- Die Entscheidung, ob der Editor angezeigt werden soll oder nicht.
- Die Verbindung zwischen dem Plugin und der Diashow. Das Plugin hat bereits eine Diashow.
- Die Verbindung zu den Metaboxen. Das Plugin hat bereits die Metaboxen.
Sieht komplett aus. Arbeit an diesem Teil erledigt.
Diashow und Folien
Eine Diashow ist 1: 1 einem Beitrag zugeordnet. Es muss also die Post-ID haben. Die Diashow kann sich um das Speichern der Daten kümmern, es handelt sich also im Grunde genommen um einen Datentyp. Dort werden alle benötigten Werte gespeichert. Es ist ein zusammengesetzter Datentyp in dem Sinne, dass er aus 0 bis N Folien besteht. Eine Folie ist wieder ein anderer Datentyp, der die Informationen für jede Folie enthält.
Die Folie wird dann von einer Metabox und wahrscheinlich einem Formular verwendet.
Ich habe mich außerdem dafür entschieden, das Speichern und Abrufen der Diashow-Daten auch in diese Datentypen zu implementieren (das Feld mit der Bezeichnung Store in the design). Das ist irgendwie schmutzig, da es Datentypen und tatsächliche Objekte mischt.
Da der Store jedoch nur im Design mit Slideshow und Slides verbunden ist, habe ich sie miteinander verbunden. Wahrscheinlich zu nah für die Zukunft, aber für eine erste Implementierung des Designs denke ich, dass dies im Moment ein gültiger Ansatz ist. Da dies der erste Ansatz ist, wird es nicht lange dauern, bis er ohnehin überarbeitet wird. Selbst bei einigen Problemen bin ich ziemlich zuversichtlich, dass die Richtung richtig ist:
class SlideshowSlide {
private $slideshow;
private $index;
public $number, $hide, $type, $title, $image, $wysiwyg, $embed
public function __construct($slideshow, $index) {
$this->slideshow = $slideshow;
$this->index = $index;
}
public function getSlideshow() { return $this->slideshow; }
public function getIndex() { return $this->index; }
}
class Slideshow implements Countable, OuterIterator {
private $postID;
private $slides = array();
private function loadSlidesCount() {
$postID = $this->postID;
// implement the loading of the count of slides here
}
private function loadSlide($index) {
$postID = $this->postID;
// implement the loading of slide data here
$data = ... ;
$slide = new SlideshowSlide($this, $index);
$slide->setData($data); // however this is done.
return $slide;
}
private function loadSlides() {
$count = $this->loadSlidesCount();
$slides = array();
$index = 0;
while(($index < $count) && ($slide = $this->loadSlide($index++)))
FALSE === $slide || $slides[] = $slide
;
$this->slides = $slides;
}
public function __construct($postID) {
$this->postID = $postID;
$this->loadSlides();
}
public function count() {
return count($this->slides);
}
public function getInnerIterator() {
return new ArrayIterator($this->slides);
}
private function touchIndex($index) {
$index = (int) $index;
if ($index < 0 || $index >= count($this->slides) {
throw new InvalidArgumentExpression(sprintf('Invalid index %d.', $index));
}
return $index;
}
public function getSlide($index) {
$index = $this->touchIndex($index);
return $this->slides[$index];
}
}
Die Klassen Slideshow und Slide sind ebenfalls ziemlich vollständig, es fehlt jedoch auch der tatsächliche Code. Es soll nur meine Idee zeigen, die Eigenschaften / Methoden und einige Handhabungsaufgaben zu haben, wie das Abrufen von Daten implementiert werden könnte.
Metabox
Die Metabox muss wissen, welche Folie sie darstellt. Es muss also die Diashow und die konkrete Folie kennen. Die Diashow kann vom Plugin bereitgestellt werden, die Folie kann durch den Index dargestellt werden (z. B. 0 ... N, wobei N die Anzahl der Folien in der Diashow ist - 1).
class Metabox {
public function __construct(SlideshowSlide $slide) {
}
}
Die Metabox-Klasse erweitert die Plugin-Klasse tatsächlich irgendwie. Es erledigt einige der Arbeiten, die auch von der Plugin-Klasse ausgeführt werden könnten, aber da ich wollte, dass es die Folie im Kontext des Plugins darstellt, während es mehrere Instanzen haben kann, habe ich diesen Weg gewählt.
Die Metabox muss sich jetzt um die Anforderungslogik kümmern: Sie stellt eine Metabox dar, die tatsächlich irgendwie ausgegeben wird, aber auch eingegeben wird, da sie sich mit Formulareingaben befassen muss.
Das Gute ist, dass es sich eigentlich nicht um die Details handelt, da die Formularausgabe und -eingabe von den Formularobjekten erfolgt.
Wenn ich diese Klasse also bis zum Ende codiert hätte, hätte ich sie wahrscheinlich vollständig entfernt. Ich weiß es gerade nicht. Im Moment repräsentiert es die Metabox auf der Editorseite für eine bestimmte Folie.
Metaboxen
class Metaboxes
private $slideshow;
private $boxes;
public function __construct(Slideshow $slideshow) {
$this->slideshow = $slideshow;
$this->editorAction();
}
private function createMetaboxes() {
$slides = $this->slideshow;
$boxes = array();
foreach($slides as $slide) {
$boxes[] = new Metabox($slide);
}
$this->boxes = $boxes;
}
private function editorAction() {
$this->createMetaboxes();
}
Bisher habe ich hier nur einen kleinen Code geschrieben. Die Metaboxes-Klasse fungiert als Manager für alle Metaboxen. Der Vertreter der Diashow als Metabox repräsentiert eine Folie.
Dieser Stub-Code macht nicht viel, sondern instanziiert eine Metabox pro Folie. Es kann und muss am Ende mehr tun.
Möglicherweise möchten Sie hier das zusammengesetzte Muster verwenden. Wenn Sie also eine Aktion für ein Metaboxes-Objekt ausführen, wird für jede Metabox, die es enthält, dieselbe Aktion ausgeführt. Vergleichbar mit der Instanziierung, bei der neue Metaboxen erstellt werden. Sie müssen sich also später nicht mehr mit einzelnen Metaboxen befassen, sondern nur mit dem Metaboxes-Objekt. Nur eine Idee.
Bilden
Das Formular ist wahrscheinlich das komplexeste, was Sie in Bezug auf den Umgang mit Dingen und Codezeilen haben. Es muss Ihre Daten abstrahieren, um über den Browser verarbeitet zu werden. Es muss also in der Lage sein, mehrere Instanzen zu verarbeiten. Sie können dies erreichen, indem Sie Formularelementnamen (da sie eindeutig sein müssen) ein genreales Präfix (z. B. "Folie") und dann einen Bezeichner (den Folienindex, den ich hier als Index bezeichne, da Sie den Namen ändern möchten) voranstellen Nummer, z. B. um einen Sortierschlüssel zu haben) und dann die tatsächliche Wertkennung (z. B. "Nummer" oder "Verstecken").
Mal sehen: Ein Formular kennt sein Präfix, die Nummer der Folie und alle darin enthaltenen Felder. Dies entspricht weitgehend den oben erwähnten Datentypen "Diashow" und "Folie". Ein kleiner Stub-Code:
/**
* SlideForm
*
* Draw a Form based on Slide Data and a Form definition. Process it's Input.
*/
class SlideForm {
/** @var Slide */
private $slide;
private $prefix = 'slide';
public function __construct(Slide $slide) {
$this->slide = $slide;
}
private function inputNamePrefix() {
$index = $this->slide->getIndex();
$prefix = $this->prefix;
return sprintf('%s-%d-', $prefix, $index);
}
private function inputName($name) {
return $this->inputNamePrefix().$name;
}
private function printInput(array $element) {
list($type, $parameters) = $element;
$function = 'printInput'.$type;
$callback = array($this, $function)
call_user_func_array($callback, $parameters);
}
private function printInputText($value, $name, $label, $size = 4) {
$inputName = $this->inputName($name);
?>
<label for="<?php echo $inputName ; ?>">
<?php echo htmlspecialchars($label); ?>:
</label>
<input type="text"
name="<?php echo $inputName; ?>"
size="<?php echo $size; ?>"
value="<?php echo htmlspecialchars($value); ?>">
<?php
}
private function printInputCheckbox($value, $name, $label, ... ) { ... }
private function printInputRadio($value, $name, $label, ... ) { ... }
private function printInputImage($value, $name, $label, ... ) { ... }
private function printInputWysiwyg($value, $name, $label, ... ) { ... }
private function printInputTextarea($value, $name, $label, ... ) { ... }
// ...
private function mapSlideValueTo(array $element) {
$slide = $this->slide;
list($type, $parameters) = $element;
list($name) = $parameters;
$value = $slide->$name;
array_unshift($parameters, $value);
$element[1] = $parameters;
return $element;
}
/**
* @return array form definition
*/
private function getForm() {
// Form Definition
$form = array(
array(
'Text',
array(
'number', 'Number'
),
array(
'Checkbox',
array(
'hide', 'Display', 'Hide This Slide'
),
),
array(
'Radio',
array(
'type', 'Type', array('Image', 'Video')
),
),
array(
'Text',
array(
'title', 'Title', 16
),
),
// ...
);
return $form;
}
public function printFormHtml() {
$form = $this->getForm();
foreach($form as $element) {
$element = $this->mapSlideValueTo($element);
$this->printInput($element);
}
}
public function processFormElement($element) {
list($type, $parameters) = $element;
list($name) = $parameters;
$inputName = $this->inputName($name);
$map = array(
'Text' => 'String',
'Checkbox' => 'Checkbox',
'Radio' => 'Radio',
);
// I would need to continue to code there.
throw new Exception('Continue to code: Process Form Input based on Form Definition');
}
public function processForm() {
$form = $this->getForm();
foreach($form as $element) {
$this->processFormElement($element); // <- this function needs to be coded
}
}
// ...
}
Diese Klasse ist jetzt ziemlich groß, weil sie drei Dinge gleichzeitig erledigt:
- Formulardefinition
- Rendern von Formularausgaben
- Formulareingabeverarbeitung
Es ist klüger, dies in die drei Teile aufzuteilen, die es darstellt. Das überlasse ich dir. Es wird bereits gezeigt, wie Sie die Formularfunktionalität in kleinere Aufgaben kapseln können, damit Sie Ihre Anforderungen leichter erfüllen können. Wenn beispielsweise die Ausgabe des Bildeingabeelements geändert werden muss, kann sie leicht erweitert / poliert werden. Gleiches gilt für WYSIWYG. Sie können die Implementierung später ändern, da sie Ihre Diashow- und Foliendatentypen nicht stark beeinträchtigt.
Das Prinzip dahinter wird auch als Trennung von Bedenken bezeichnet , und so begann ich meine Antwort: Teilen Sie das Problem in kleinere Probleme auf. Diese getrennten Probleme sind leichter zu lösen.
Ich hoffe das hilft für den Moment. Am Ende bin ich nicht einmal zu benutzerdefinierten Beitragstypen zurückgekehrt. Ich weiß, dass sie in das Plugin gehen müssen, aber mit einem neuen Design ist es wahrscheinlich einfach, den Ort zu finden, an den der Code geschrieben werden kann.
Was ist übrig?
- Teilen Sie den Code in mehrere Dateien auf. Eine Klasse pro Datei. Sie können sie später problemlos zusammenführen, aber für die Entwicklung sollten Sie die Dinge auseinander halten, da Sie die kleineren Probleme / Teile selbst lösen möchten.
- Testen: Sie müssen die Funktionalität der Teile selbst testen. Tut die Folie das, was sie tun soll? Gerne können Sie Unittests verwenden, wenn Sie Klassen schreiben, für PHP gibt es PHPUnit . Dies hilft Ihnen, den Code zu entwickeln, während Sie wissen, dass er genau das tut, was er tut. Dies hilft, die einfachen Probleme in einer eigenen Einheit zu lösen, und Sie können Ihren Code später einfacher ändern, da Sie jederzeit Tests ausführen können.
- Test Nr. 2: Sicher müssen Sie Ihr Plugin auch als Ganzes testen, damit Sie wissen, dass es das tut, wonach Sie suchen. Können Sie sich vorstellen, wie dies automatisch durchgeführt werden kann, damit Sie den Test jederzeit mit derselben Qualität wiederholen können?