2011-09-25 25 views
10

Tôi mới này, zend trang trí malarchy, nhưng tôi có hai câu hỏi quan trọng mà tôi không thể có được đầu của tôi xung quanh. Câu hỏi ai được theo sau bởi một số ví dụZend_Framework Decorators Wrap Label và ViewHelper bên trong một div

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

... 

$name = new Zend_Form_Element_Text('title'); 
$name->setLabel('Title') 
    ->setDescription("No --- way"); 

$name->setDecorator($decorate); 

Những kết quả đầu ra

<li class="element"> 
    <label for="title" class="required">Title</label> 
    <input type="text" name="title" id="title" value=""> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and can't be empty</li> 
    </ul> 
</li> 

Các câu hỏi # 1

Làm thế nào để tôi quấn labelinput xung quanh một thẻ div? Vì vậy, đầu ra là như sau:

<li class="element"> 
    <div> 
     <label for="title" class="required">Title</label> 
     <input type="text" name="title" id="title" value=""> 
    </div> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and can't be empty</li> 
    </ul> 
</li> 

Các Câu hỏi # 2

là gì với thứ tự của các elements trong mảng $decorate? Họ KHÔNG CÓ BẤT K S SENSE!

Trả lời

21

decorator pattern là mẫu thiết kế để thêm chức năng vào các lớp hiện có mà không thay đổi các lớp hiện có. Thay vào đó, một lớp trang trí kết thúc tốt đẹp quanh một lớp khác và thường hiển thị cùng một giao diện với lớp được trang trí.

dụ cơ bản:

interface Renderable 
{ 
    public function render(); 
} 

class HelloWorld 
    implements Renderable 
{ 
    public function render() 
    { 
     return 'Hello world!'; 
    } 
} 

class BoldDecorator 
    implements Renderable 
{ 
    protected $_decoratee; 

    public function __construct(Renderable $decoratee) 
    { 
     $this->_decoratee = $decoratee; 
    } 

    public function render() 
    { 
     return '<b>' . $this->_decoratee->render() . '</b>'; 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator 
$decorator = new BoldDecorator(new HelloWorld()); 
echo $decorator->render(); 

// will output 
<b>Hello world!</b> 

Bây giờ, bạn có thể bị cám dỗ để nghĩ rằng bởi vì Zend_Form_Decorator_* lớp học trang trí, và có một phương pháp render, điều này tự động có nghĩa là đầu ra của lớp trang trí render phương pháp sẽ luôn được trang trí với nội dung bổ sung bởi người trang trí. Nhưng về thanh tra của các ví dụ cơ bản của chúng tôi ở trên, chúng ta có thể dễ dàng nhận thấy điều này không nhất thiết phải là trường hợp ở tất cả tất nhiên, được minh họa bằng bổ sung (mặc dù khá vô dụng) ví dụ này:

class DivDecorator 
    implements Renderable 
{ 
    const PREPEND = 'prepend'; 
    const APPEND = 'append'; 
    const WRAP = 'wrap'; 

    protected $_placement; 

    protected $_decoratee; 

    public function __construct(Renderable $decoratee, $placement = self::WRAP) 
    { 
     $this->_decoratee = $decoratee; 
     $this->_placement = $placement; 
    } 

    public function render() 
    { 
     $content = $this->_decoratee->render(); 
     switch($this->_placement) 
     { 
      case self::PREPEND: 
       $content = '<div></div>' . $content; 
       break; 
      case self::APPEND: 
       $content = $content . '<div></div>'; 
       break; 
      case self::WRAP: 
      default: 
       $content = '<div>' . $content . '</div>'; 
     } 

     return $content; 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator and a DivDecorator (with DivDecorator::APPEND) 
$decorator = new DivDecorator(new BoldDecorator(new HelloWorld()), DivDecorator::APPEND); 
echo $decorator->render(); 

// will output 
<b>Hello world!</b><div></div> 

Đây là trong thực tế về cơ bản, có bao nhiêu công cụ trang trí Zend_Form_Decorator_* hoạt động, nếu nó có ý nghĩa đối với họ để có chức năng này.

Đối với những người trang trí hợp lý, bạn có thể kiểm soát vị trí với ví dụ setOption('placement', 'append') hoặc bằng cách chuyển tùy chọn 'placement' => 'append' vào mảng tùy chọn.

Đối Zend_Form_Decorator_PrepareElements, ví dụ, tùy chọn vị trí này là vô ích và do bỏ qua, vì nó chuẩn bị các yếu tố hình thức được sử dụng bởi một decorator ViewScript, làm cho nó một trong những trang trí mà không chạm vào nội dung trả lại của các yếu tố trang trí .

Tùy thuộc vào chức năng mặc định của các trang trí riêng lẻ, nội dung của lớp được trang trí, được thêm vào, được thêm vào, loại bỏ hoặc điều gì đó hoàn toàn khác với lớp được trang trí mà không thêm nội dung nào trực tiếp vào nội dung, trước khi chuyển nội dung sang trang trí tiếp theo. Hãy xem xét ví dụ đơn giản này:

class ErrorClassDecorator 
    implements Renderable 
{ 
    protected $_decoratee; 

    public function __construct(Renderable $decoratee) 
    { 
     $this->_decoratee = $decoratee; 
    } 

    public function render() 
    { 
     // imagine the following two fictional methods 
     if($this->_decoratee->hasErrors()) 
     { 
      $this->_decoratee->setAttribute('class', 'errors'); 
     } 

     // we didn't touch the rendered content, we just set the css class to 'errors' above 
     return $this->_decoratee->render(); 
    } 
} 

// wrapping (decorating) HelloWorld in a BoldDecorator and an ErrorClassDecorator 
$decorator = new ErrorClassDecorator(new BoldDecorator(new HelloWorld())); 
echo $decorator->render(); 

// might output something like 
<b class="errors">Hello world!</b> 

Bây giờ, khi bạn thiết lập các trang trí cho một yếu tố Zend_Form_Element_*, họ sẽ được bao bọc, và do đó thực hiện, theo thứ tự mà chúng được thêm vào.Vì vậy, đi bằng ví dụ của bạn:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

... cơ bản những gì xảy ra là như sau (tên lớp thực tế cắt ngắn cho ngắn gọn):

$decorator = new HtmlTag(new Label(new Errors(new Description(new ViewHelper())))); 
echo $decorator->render(); 

Vì vậy, về kiểm tra đầu ra ví dụ của bạn, chúng ta nên có thể chưng cất hành vi vị trí mặc định của các trang trí riêng lẻ:

// ViewHelper->render() 
<input type="text" name="title" id="title" value=""> 

// Description->render() 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> // placement: append 

// Errors->render() 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> 
<ul class="error"> // placement: append 
    <li>Value is required and cant be empty</li> 
</ul> 

// Label->render() 
<label for="title" class="required">Title</label> // placement: prepend 
<input type="text" name="title" id="title" value=""> 
<p class="hint">No --- way</p> 
<ul class="error"> 
    <li>Value is required and cant be empty</li> 
</ul> 

// HtmlTag->render() 
<li class="element"> // placement: wrap 
    <label for="title" class="required">Title</label> 
    <input type="text" name="title" id="title" value=""> 
    <p class="hint">No --- way</p> 
    <ul class="error"> 
     <li>Value is required and cant be empty</li> 
    </ul> 
</li> 

Và bạn biết gì; điều này thực sự là vị trí mặc định của tất cả các trang trí tương ứng.

Nhưng bây giờ đến phần khó khăn, chúng ta cần làm gì để có được kết quả mà bạn đang tìm kiếm? Để quấn labelinput chúng ta không thể chỉ đơn giản là làm điều này:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'div')), // default placement: wrap 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

... vì điều này sẽ quấn tất cả nội dung trước (ViewHelper, Description, ErrorsLabel) với một div, phải không? Thậm chí không ... trang trí thêm sẽ được thay thế bằng trang trí tiếp theo, vì trang trí được thay thế bởi một trang trí sau nếu nó thuộc cùng một lớp. Thay vì bạn sẽ phải cung cấp cho nó một khóa duy nhất:

$decorate = array(
    array('ViewHelper'), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // we'll call it divWrapper 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), 
); 

Bây giờ, chúng tôi vẫn đang phải đối mặt với các vấn đề mà divWrapper sẽ quấn tất cả nội dung trước (ViewHelper, Description, ErrorsLabel). Vì vậy, chúng ta cần phải sáng tạo ở đây. Có rất nhiều cách để đạt được những gì chúng ta muốn. Tôi sẽ đưa cho một ví dụ, đó có lẽ là dễ nhất:

$decorate = array(
    array('ViewHelper'), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), // default placement: prepend 
    array(array('divWrapper' => 'HtmlTag'), array('tag' => 'div')), // default placement: wrap 
    array('Description'), // default placement: append 
    array('Errors', array('class'=>'error')), // default placement: append 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')), // default placement: wrap 
); 

Đối với lời giải thích thêm về Zend_Form trang trí tôi khuyên bạn nên đọc article about Zend Form Decorators

+0

Chà, đó là một số câu trả lời hoàn chỉnh! Đẹp nhất! –

+2

Oh wow, tôi đã có một khoảnh khắc ahaa trong câu trả lời đó, Cảm ơn và đã chấp nhận: D –

+0

P.S đó là câu trả lời tuyệt vời nhất mà tôi từng có –

2

Câu hỏi # 1

Thay đổi trang trí trật tự và thêm một HtmlTag helper theo cách này:

$decorate = array(
    array('ViewHelper'), 
    array('Label', array('tag'=>'div', 'separator'=>' ')), 
    array('HtmlTag', array('tag' => 'div')), 
    array('Description'), 
    array('Errors', array('class'=>'error')), 
    array('HtmlTag', array('tag' => 'li', 'class'=>'element')) 
); 

Câu hỏi # 2

trang trí là một chuỗi, đầu ra của mỗi cái được truyền cho đầu vào của cái tiếp theo, để được 'trang trí' bởi nó.

Theo mặc định, chúng nối thêm nội dung (mô tả, lỗi), nội dung thêm (nhãn ..) và hoặc quấn quanh (HtmlTag). Nhưng đây là những hành vi mặc định, và bạn có thể thay đổi nó cho hầu hết trong số họ:

array('HtmlTag', array('tag' => 'span', placement=>'APPEND')); 
//this would append <span></span> to the output of the previous decorator instead of wrapping it inside the <span> 

Hãy có một cái nhìn gần gũi hơn với những gì xảy ra trong chuỗi của bạn:

  1. ViewHelper làm cho yếu tố hình thức của bạn sử dụng nó default viewHelper, được khai báo trong lớp của phần tử biểu mẫu.

  2. Label prepends nhãn với sản lượng trước

  3. HtmlTag kết thúc tốt đẹp một <div> xung quanh

  4. Description gắn thêm mô tả các yếu tố

  5. Errors gắn thêm các thông báo lỗi, nếu có

  6. HtmlTag kết thúc tốt đẹp tất cả điều này trong một <li>

EDIT

tôi đã viết câu trả lời này mà không kiểm tra bất cứ điều gì, vì vậy có thể có một số không chính xác nhỏ ở đây và ở đó. Người đọc thân mến, nếu bạn thấy một số chỉ cần thả một bình luận và tôi sẽ cập nhật.

+0

Tốt câu trả lời nhà phát triển dẫn Zend Framework của Matthew Weier O'Phinney của cũng tất nhiên ! Chỉ báo trước là một hoặc thậm chí cả hai (hoặc không chắc chắn thực sự) của trang trí 'HtmlTag' phải được thêm vào với một khóa duy nhất' mảng ('someUniqueKey' => 'HtmlTag') ', nếu không giá trị cuối cùng sẽ thay thế . –

+0

wow cảm ơn. ....: D –

Các vấn đề liên quan