Giải quyết một vấn đề phức tạp (hợp lý)
Để giải quyết các vấn đề phức tạp, có một cách tiếp cận được tiêu chuẩn hóa / nổi tiếng: Chia vấn đề phức tạp của bạn thành một tập hợp các vấn đề nhỏ hơn. Những vấn đề nhỏ hơn sẽ dễ giải quyết hơn. Nếu bạn đã giải quyết từng vấn đề nhỏ hơn, bạn thường đã giải quyết vấn đề phức tạp của mình rồi.
Xác định vị trí và chia thành các phần
Cho đến nay cho lý thuyết. Hãy kiểm tra nhu cầu của bạn. Tôi xếp hàng theo cách riêng của tôi những gì bạn mô tả ở trên:
- Xử lý một tập hợp các tùy chọn trong nhiều trường hợp (slide-1 đến slide-N).
- Lưu trữ giá trị trong giá trị bài meta.
- Một Metabox trong nhiều trường hợp (slide-1 đến slide-N).
- Trình chỉnh sửa trình chiếu (Biểu mẫu) trong Metabox (lại nhiều trường hợp)
- Biểu mẫu cơ bản để chỉnh sửa dữ liệu (ví dụ: sử dụng các thành phần biểu mẫu HTML đơn giản)
- Vấn đề kỹ thuật cần giải quyết, có nhiều biên tập viên WYSIWYG.
- Đầu vào hình ảnh phức tạp hơn của bạn.
Vậy làm thế nào để mã đó? Tôi nghĩ phần quan trọng nhất là trước khi bạn bắt đầu viết mã thực tế, bạn quyết định những gì bạn thực sự muốn đạt được và làm thế nào để phân chia vấn đề thành các phần nhỏ hơn. Danh sách trên có thể không đầy đủ, đó chỉ là những gì tôi có thể đọc được từ câu hỏi của bạn. Và định dạng khá đặc biệt. Nó ít nhiều chỉ là sự lặp lại từ những gì bạn đã viết nhưng một chút được sắp xếp thành các điểm duy nhất.
Vì vậy, hãy kiểm tra xem đó là những nhu cầu của bạn và mở rộng danh sách đó cho tất cả những gì bạn thấy phù hợp.
Khi hoàn tất, bước tiếp theo là xem làm thế nào những thứ cần thiết có thể được diễn tả bằng những từ đơn giản và như một danh sách các tính năng của plugin của bạn
- Một plugin cho việc này: slideshowPlugin.
- Một trình chiếu bao gồm các slide.
- Trình chỉnh sửa để chỉnh sửa Trình chiếu.
- Một nơi để lưu trữ và lấy dữ liệu Trình chiếu từ đó.
Trông bây giờ khá đơn giản phải không? Tôi có thể đã bỏ lỡ điều gì đó ở đây, vì vậy hãy tự kiểm tra lại trước khi bạn tiếp tục. Như đã viết, trước khi bắt đầu viết mã, hãy tạo ra suy nghĩ của bạn bằng những từ ngữ và thuật ngữ đơn giản. Thậm chí đừng nghĩ về phần nào có vấn đề và phần nào không hoặc cách mã hóa một số chi tiết như cách đặt tên của các phần tử Nhập HTML. Tôi biết rằng thật khó nếu bạn đã cố gắng trong một thời gian dài như vậy để khởi động lại từ đầu bởi vì có rất nhiều ý tưởng trong đầu bạn xuất hiện trở lại.
Lấy một cây bút chì và một số giấy. Điều này thường giúp tạo nên tâm trí của một ai đó.
Như bạn có thể thấy tôi đã không chỉ định nhu cầu của Metabox hoặc Loại bài đăng tùy chỉnh ở đây. Trước tiên, quá cụ thể để tìm hiểu về các phần của vấn đề của bạn. Metabox hoặc Custom Post Type rất cụ thể, có thể là cách mã hóa plugin đã có. Vì vậy, tôi giữ điều này trong thời điểm này và cố gắng mô tả ngắn gọn nhưng chính xác các nhu cầu. Metabox hoặc tương tự là thứ có thể đóng vai trò trong Thiết kế. Hãy xem nào.
Thiết kế Plugin của bạn
Sau khi bạn biết những gì bạn cần / muốn đạt được, bạn có thể quyết định về cách thiết kế plugin. Điều này có thể được thực hiện bằng cách vẽ một bức tranh nhỏ. Chỉ cần xác định các phần trong Danh sách tính năng của bạn và đặt chúng trong mối quan hệ với nhau. Như bạn có thể thấy, bạn thực sự không cần phải làm mỹ thuật;):
Xin lỗi vì bài viết xấu của tôi, tôi hy vọng điều này có thể được đọc. Trong mọi trường hợp, bạn có thể tạo hình ảnh của riêng bạn, đây chỉ là một ví dụ. Các thiết kế có thể khác nhau, vì vậy nếu bạn không vẽ nó theo cùng một cách, điều đó chỉ bình thường.
Tôi thích bắt đầu bước thiết kế trên giấy vì nó giúp có cái nhìn rõ hơn về vấn đề từ trên cao và nó nhanh hơn nhiều trên giấy sau đó trên máy tính.
Vì vậy, bây giờ bạn có thể so sánh danh sách của bạn với thiết kế của bạn và kiểm tra xem tất cả các tính năng trong danh sách có được bao phủ bởi các phần bạn có trong thiết kế hay không. Có vẻ tốt cho danh sách của tôi và hình ảnh của tôi, vì vậy tôi tiếp tục, nhưng đừng bỏ qua bước này. Nếu không, bạn không biết nếu bạn đã bỏ lỡ một cái gì đó. Và khi bạn bắt đầu viết mã sớm, việc thay đổi thứ gì đó đã được mã hóa sẽ khó hơn nhiều so với hình ảnh hoặc danh sách.
Tách các vấn đề trong thiết kế
Bây giờ plugin này trở nên cụ thể hơn trong tâm trí. Sau một vài thiết kế, có lẽ đây là thời điểm thích hợp để bắt đầu viết mã. Khi chúng tôi có danh sách trên cùng, tôi có thể đi qua và suy nghĩ về từng điểm trên đó và kiểm tra chéo với Thiết kế để tôi biết các phần có mối quan hệ với nhau như thế nào.
Bởi vì nếu mỗi phần được thực hiện, plugin đã sẵn sàng mà không cần phải khắc phục tất cả mọi thứ cùng một lúc, đó là vấn đề ban đầu.
Bây giờ tôi viết mã một chút theo kiểu nhận xét và một số mã mẫu. Đó là một ý tưởng thực hiện đầu tiên và mã chưa được kiểm tra. Nó chỉ để làm bẩn tay tôi và bạn có thể có một ví dụ về cách - nhưng không phải - điều này có thể được viết. Tôi có xu hướng quá cụ thể đôi khi đã có, vì vậy hãy nhớ tôi. Khi tôi viết mã, tôi viết lại nó khá thường xuyên trong khi tạo mã, nhưng tôi không thể hiển thị mã này trong khi tạo mã mẫu. Vì vậy, hãy ghi nhớ điều này. Nếu bạn thấy một cái gì đó được thực hiện đơn giản hơn, chọn tuyến đường của bạn. Bạn cần thay đổi và mở rộng mã của mình sau này, vì vậy đây thực sự chỉ là một số mã ví dụ.
Cắm vào
Chỉ là một lớp xử lý các hoạt động cơ bản như đăng ký các hook và cung cấp Trình chiếu với Slides và Metaboxes cho Trình chỉnh sửa. Đây là nơi mọi thứ bắt đầu. Plugin được bắt đầu tại một điểm mã duy nhất. Tôi gọi 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();
}
}
Vì vậy, lớp plugin này đã khá hoàn chỉnh. Tôi không chỉ định cách truy xuất postID nhưng nó đã được gói gọn trong một chức năng. Bên cạnh đó tôi không mã để kiểm tra xem đây có phải là trang phù hợp để hiển thị trình chỉnh sửa hay không, nhưng đã có một số mã còn sơ khai cho điều đó.
Cuối cùng, EditorAction () được gọi nếu yêu cầu nằm trên trang chỉnh sửa loại bài đăng tùy chỉnh thực tế và trong đó, Metaboxes được cung cấp. Đó là nó. Plugin nên khá đầy đủ ngay bây giờ. Nó có trình chiếu và chăm sóc Metaboxes. So với thiết kế, đó là những phần được liên kết với plugin. So sánh với hình ảnh:
- Quyết định có hay không Trình chỉnh sửa sẽ được hiển thị.
- Sự kết nối giữa plugin và slideshow. Các plugin đã có một slideshow.
- Sự kết nối với Metaboxes. Các plugin đã có Metaboxes rồi.
Trông xong. Công việc được thực hiện trên phần đó.
Trình chiếu và slide
Trình chiếu là 1: 1 ánh xạ tới bài viết. Vì vậy, nó cần phải có ID bài. Trình chiếu có thể đảm nhiệm việc giữ dữ liệu, vì vậy về cơ bản nó là một kiểu dữ liệu. Nó lưu trữ tất cả các giá trị bạn cần ở đó. Đó là một kiểu dữ liệu hỗn hợp theo nghĩa là nó bao gồm có 0 đến N slide. Một lần nữa Slide là một kiểu dữ liệu khác chứa thông tin cho mỗi Slide.
Các slide sau đó được sử dụng bởi một metabox và có thể là một hình thức.
Tôi cũng được chọn để thực hiện việc lưu trữ và truy xuất dữ liệu trình chiếu vào các kiểu dữ liệu đó (hộp có nhãn Store trong thiết kế). Điều đó bằng cách nào đó bẩn khi nó trộn các kiểu dữ liệu và các đối tượng thực tế.
Nhưng khi Cửa hàng được kết nối với Trình chiếu và Bản chiếu chỉ trong Thiết kế, tôi đã kết nối chúng với nhau. Có lẽ quá gần với tương lai, nhưng đối với lần đầu tiên thực hiện thiết kế, tôi nghĩ đó là một cách tiếp cận hợp lệ vào lúc này. Vì đây là cách tiếp cận đầu tiên nên sẽ không mất nhiều thời gian sau khi nó sẽ được tái cấu trúc dù thế nào đi nữa, ngay cả với một số vấn đề tôi khá tự tin rằng hướng đi là đúng:
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];
}
}
Các lớp Trình chiếu và Slide cũng khá hoàn chỉnh nhưng cũng thiếu mã thực tế. Nó chỉ để thể hiện ý tưởng của tôi về việc có các thuộc tính / phương thức và một số công cụ xử lý cũng như cách truy xuất dữ liệu có thể được thực hiện.
Metabox
Metabox cần biết Slide mà nó đại diện. Vì vậy, nó cần phải biết Trình chiếu và Slide cụ thể. Trình chiếu có thể được cung cấp bởi Plugin, Slide có thể được biểu thị bằng chỉ mục (ví dụ 0 ... N trong đó N là số lượng slide trong trình chiếu - 1).
class Metabox {
public function __construct(SlideshowSlide $slide) {
}
}
Lớp Metabox thực sự đang mở rộng lớp plugin bằng cách nào đó. Nó cũng thực hiện một số công việc có thể được thực hiện bởi lớp plugin, nhưng vì tôi muốn nó đại diện cho slide trong ngữ cảnh của plugin trong khi có thể có nhiều phiên bản, tôi đã chọn theo cách này.
Bây giờ Metabox cần quan tâm đến logic yêu cầu: Nó đại diện cho một Metabox thực sự là đầu ra nào đó nhưng nó cũng là đầu vào vì nó cần xử lý đầu vào dạng.
Điều tốt là, nó thực sự không xử lý các chi tiết vì đầu ra và đầu vào của biểu mẫu được thực hiện bởi các đối tượng biểu mẫu.
Vì vậy, có lẽ nếu tôi đã mã hóa lớp này đến hết, tôi sẽ loại bỏ nó hoàn toàn. Tôi không biết ngay bây giờ. Hiện tại, nó đại diện cho Metabox trên trang biên tập cho một slide cụ thể.
Metaboxes
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();
}
Tôi chỉ viết một số mã nhỏ ở đây cho đến nay. Lớp Metaboxes hoạt động như một người quản lý cho tất cả các metaboxes. Đại diện của Trình chiếu là Metabox đại diện cho một slide.
Mã sơ khai đó không nhiều nhưng bắt đầu một Metabox trên mỗi Slide. Nó có thể và phải làm nhiều hơn cuối cùng.
Bạn có thể muốn sử dụng Mẫu tổng hợp ở đây, do đó, để thực hiện một hành động trên đối tượng Metaboxes sẽ thực hiện hành động tương tự trên mọi Metabox mà nó mang theo. So sánh với khởi tạo, nơi nó tạo ra Metaboxes mới. Vì vậy, bạn không cần phải đối phó với các Metaboxes riêng lẻ sau này, chỉ với đối tượng Metaboxes. Chỉ là một ý tưởng.
Hình thức
Biểu mẫu có lẽ là điều phức tạp nhất mà bạn có về mặt xử lý các công cụ và dòng mã. Nó cần phải trừu tượng hóa dữ liệu của bạn để được xử lý thông qua Trình duyệt. Vì vậy, nó phải có khả năng xử lý nhiều trường hợp. Bạn có thể đạt được điều này bằng cách thêm tiền tố tên các thành phần của biểu mẫu (vì chúng cần phải là duy nhất) với tiền tố genreal (ví dụ: "slide"), sau đó là một định danh (chỉ mục slide, tôi đặt tên cho chỉ mục ở đây vì bạn muốn có thể thay đổi số ví dụ: để có khóa sắp xếp) và sau đó là định danh giá trị thực (ví dụ: "số" hoặc "ẩn").
Chúng ta hãy xem: Một biểu mẫu biết về tiền tố của nó, số của slide và tất cả các trường mà nó chứa. Điều này khá nhiều bản đồ cho các kiểu dữ liệu Trình chiếu và Slide được nói ở trên. Một số sơ khai nhỏ:
/**
* 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
}
}
// ...
}
Lớp này bây giờ khá lớn vì nó chăm sóc ba thứ cùng một lúc:
- Định nghĩa mẫu
- Kết xuất đầu ra mẫu
- Xử lý đầu vào mẫu
Sẽ khôn ngoan hơn khi chia phần này thành ba phần mà nó đại diện. Tôi để điều đó cho bạn. Nó đã chỉ ra cách bạn có thể gói chức năng biểu mẫu vào các tác vụ nhỏ hơn để dễ dàng tuân thủ nhu cầu của bạn hơn. Ví dụ, trong trường hợp đầu ra dạng phần tử Image Input cần chỉnh sửa, nó có thể dễ dàng được mở rộng / đánh bóng. Tương tự cho WYSIWYG. Bạn có thể thay đổi triển khai sau này vì nó sẽ không can thiệp cho bạn trình chiếu và kiểu dữ liệu trượt nhiều.
Nguyên tắc đằng sau điều này cũng được gọi là Tách biệt các mối quan tâm , và đó chỉ là cách tôi bắt đầu câu trả lời của mình: Chia vấn đề thành các vấn đề nhỏ hơn. Những vấn đề riêng biệt dễ giải quyết hơn.
Tôi hy vọng điều này sẽ giúp cho thời điểm này. Cuối cùng, tôi thậm chí không quay lại Loại bài tùy chỉnh. Tôi biết họ phải vào bên trong plugin, nhưng với một thiết kế mới, có thể dễ dàng tìm thấy nơi để viết mã.
Những gì còn lại?
- Chia mã thành nhiều tệp. Một lớp cho mỗi tệp. Bạn có thể hợp nhất chúng lại với nhau sau này một cách dễ dàng, nhưng để phát triển, hãy tách biệt mọi thứ khi bạn muốn tự mình giải quyết các vấn đề / bộ phận nhỏ hơn.
- Kiểm tra: Bạn cần tự kiểm tra chức năng của các bộ phận. Là slide làm những gì nó nên làm? Rất vui khi bạn có thể sử dụng Unittests nếu bạn viết các lớp, đối với PHP có PHPUnit . Điều này giúp bạn phát triển mã trong khi biết rằng nó thực hiện chính xác những gì nó làm. Điều này giúp giải quyết các vấn đề đơn giản trong một đơn vị của mỗi đơn vị và bạn có thể dễ dàng thay đổi mã của mình sau này vì bạn có thể chạy thử nghiệm mọi lúc.
- Kiểm tra số 2: Chắc chắn bạn cũng cần kiểm tra toàn bộ plugin của mình để bạn biết rằng nó đang làm những gì bạn đang tìm kiếm. Bạn có thể tưởng tượng làm thế nào điều đó có thể được thực hiện tự động để bạn có thể lặp lại thử nghiệm với cùng chất lượng mọi lúc không?