SitePoint Sponsor

User Tag List

Results 1 to 5 of 5
  1. #1
    SitePoint Enthusiast
    Join Date
    Aug 2005
    Location
    Germany
    Posts
    25
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Making selection lists from object collections

    Hi all,

    I am trying to find some clever way to define all kinds of HTML selection lists from my domain model collections. Because its close to christmas my example here will have a "sweets" domain model instead of the standard car/product whatever example
    PHP Code:
    class Sweets {
        public 
    $id,$description,$calories;
        public function 
    __construct($id,$desc,$calories='too many') {
            
    $this->id $id;
            
    $this->description $desc;
            
    $this->calories $calories;
        }

    A collection of sweets if fetched by a datamapper from the database
    PHP Code:
    class FakeDataMapper {
        
    // stuff here ...
        
        /**
         * @return array
         */
        
    public function fetchAll() {
            
            
    // Usually SQL ;-)
            
    $list = array(
                        new 
    Sweets('1','Chocolade'),
                        new 
    Sweets('2','Nuts'),
                        new 
    Sweets('3','Cake')
                    );
            return 
    $list;
        }
        
        
    // ... stuff there

    So far this is pretty standard and has nothing to do with selection lists or HTML
    But now I want to have a dropdown list from my collection to display in a form. What would be a pattern or solution to achieve that?

    Right now I have a ThesaurusList class which takes a collection of domain models. This collection is transformed into an assoc. array.
    PHP Code:
    class ThesaurusList implements ThesaurusListInterface {
        
    /**
         * @var array
         */
        
    protected $list = array();
        
        public function 
    setList(Array $array) {
            foreach (
    $array as $sweets) {
                
    $this->list[$sweets->id] = $sweets->description;
            }
        }
        public function 
    getList() {
            return 
    $this->list;
        }

    In the real world, sweets would implement an interface to make sure that there is a getId() and getDesc() method on the model.

    The transformed object collection is passed to a "visualizer" class which takes a ThesaurusList as an argument in the constructor
    PHP Code:
    class DropDown implements FormSelector {
        
        
    /**
         * @var ThesaurusList
         */
        
    protected $list;
        protected 
    $id,$name '';
        
        public function 
    __construct(ThesaurusListInterface $list) {
            
    $this->list $list;
        }
        
    // ... other methods
        
    public function toHTML() {
            
    $html '<select id="'.$this->id.'" name="'.$this->name.'" size="1">';
            foreach (
    $this->list->getList() as $id => $name) {
                
    $html .= '<option value="'.$id.'">'.$name.'</option>';
            }
            
    $html .= '</select>';
            return 
    $html;
        }
        
    }

    class 
    RadioButton implements FormSelector {
        
    // make radio buttons

    I could create as many "visualizer" classes as needed. They all would work with the ThesaurusList class to create the desired output. Each visualizer class would take a number of other display and html arguments which are needed to create the HTML script for the selection element. Id's, classes, styles, default values and so on would be set before ->setHtml is called.
    PHP Code:
    <?php
    $fm 
    = new FakeDataMapper();
    $list = new ThesaurusList();
    $list->setList($fm->fetchAll());

    $dropdown = new DropDown($list);
    ?>
    <form>
        <p>Select Field</p>
        <?php echo $dropdown->toHTML(); ?>
    </form>
    Is it ok to create my select lists like that? Any drawbacks that you can see? Are there some fancy patterns which might help me with my task? I had the impression that the visitor pattern might help me with that, but the examples I found in a book and on wikipedia are too much like an example - I simply can't transfer what I see there to my code This happens when geographers mess with things they never really learned

    So any tips and help are greatly appreciated - take some sweets

  2. #2
    SitePoint Zealot Amenthes's Avatar
    Join Date
    Oct 2006
    Location
    Bucharest, Romania
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm under construction | http://igstan.ro/

  3. #3
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    First, I wouldn't grab all the data if you're just gonna load up a select box. You can implement some lazy load thing with your mapper or do it the easier way and simply add a special getSelectBoxData() method to the mapper.

    Second, you may not want to create a few objects for every dropdown. You could get by with a much simpler design...

    PHP Code:
    class DropDownRenderer {
        function 
    render($data$selectedId null$idField 'id') {
            
    // TODO: return SELECT element
        
    }
    }

    $renderer = new DropDownRenderer();
    echo 
    $renderer->render($data $mapper->fetchAll(), $selectedId 1);
    echo 
    DropDownRenderer::render($data$selectedId); 
    This way you can reuse the same object or just call the render() method statically. You could also pass it into the mapper using the visitor pattern if you'd like...

    PHP Code:
    class SweetsMapper {
        function 
    accept($renderer) {
            
    $renderer->visit($this)
        }
    }
    class 
    DropDownRenderer {
        protected 
    $selectedId;
        protected 
    $html '';
        function 
    __construct($selectedId) {
            
    $this->selectedId $selectedId;
        }
        function 
    render($data$selectedId null$idField 'id') {
            
    // TODO: return SELECT element
        
    }
        function 
    visit($mapper) {
            
    $this->html .= $this->render($mapper->fetchAll(), $this->selectedId);
        }
        function 
    __toString() {
            return 
    $this->html;
        }
    }

    $mapper = new SweetsMapper();
    echo 
    $mapper->accept($renderer = new DropDownRenderer($selectedId 1));
    echo 
    $renderer

  4. #4
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You might want your collections objects to implement Traversable. Then you can let the html-widget take any kind of traversable as argument (including plain arrays)

  5. #5
    SitePoint Enthusiast
    Join Date
    Aug 2005
    Location
    Germany
    Posts
    25
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for all the answers so far
    First, I wouldn't grab all the data if you're just gonna load up a select box. ...
    I think I tend to agree, my first thoughts on the issue followed this path as well. But then take a Customer/Client dropdown for example where the descriptive part can take up more than one field in the database, like first/last name or/and company name. Other selection methods like radio buttons can even hold more descriptive data than dropdown lists. So I decided to use (the already implemented) fetchAll method from my mapper to get the data. Usually you fetch all the rows in a table for a dropdown but not all the fields. But I will give this another thought

    I also had the impression that my ThesaurusList class could be omitted, because it always had a feeling of unnessecity to me from the moment I created it. Right now I tend to implement a "getSelectBoxData" method in my mapper as you proposed ...

    The visitor pattern is still too "winded" for me, so I don't think I will use it for this. And I am not sure if there is an advantage in this case, changing my mappers and such ... but thanks for the nice example

    I came already across the traversable interface? a few times. I will investigate into this and see if I can implement this into my collections. RIght now, I just use an array for my "collections". If I want to use the traversable, I will need to create a collection class, right?


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •