SitePoint Sponsor

User Tag List

Page 2 of 2 FirstFirst 12
Results 26 to 49 of 49
  1. #26
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Here is a interface I was working on to allow transformations and a separation between the transformation, column and grid.

    PHP Code:
    interface IActiveRecordDataGridTransformation {

        public function 
    transform($data);

    }

    interface 
    IActiveRecordDataGridColumn {

        public function 
    setName($name);
        public function 
    setPath($path);
        public function 
    setTransform(IActiveRecordDataGridTransformation $transform);
        
        public function 
    getName();
        public function 
    getPath();
        public function 
    getTransform();

    }

    interface 
    IActiveRecordDataGrid {

        public function 
    paint();


    Then something like the below which seems a little long winded which is why I'm hesitating to follow its path.

    PHP Code:
    $grid = new ActiveRecordDataGrid($records);

    $grid->addColumn(
        new 
    ActiveRecordDataGridColumn(
            
    'title'
            
    ,array('music_artist','arist')
            ,new 
    ActiveRecordGridTransformation('strtowlower')
        )
    );

    $grid->paint(); 

  2. #27
    SitePoint Guru
    Join Date
    Jun 2006
    Posts
    638
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    arborint, passed you a personal message with my contact information.

    I'm always interested to find a "better" way to do something.
    I already have the benchmark data, it was done 1-2 years ago, but should still hold true.

  3. #28
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Interesting. I see where you are going. I was thinking there there are a set of things that you need to know about a column:

    - What is the name of the column field (this is the database table column which is the most common use case)

    - What is the Label for the column heading (e.g. the column is 'cat_name' and the label is 'Category'). If it is empty then no heading for that column.

    - If the column heading should allow sorting by that column.

    - How to display a cell in that column. This could be mean just displaying the value as is or displaying the value as a link that might use other fields as parameters.

    - Apply transform/filters to the value after is retrieved from the datasource, but before displayed. I suppose if you did edit in place you might need filters before saving.

    Take for example a CRUD example like I showed above:
    ID Title Category
    11. Foo One edit
    12. Bar Two edit
    13. Baz Two edit
    14. Faz One edit
    15. Boo Two edit
    First Prev 5 6 7 8 9 Next Last


    So for example:
    - you cannot sort by the ID column
    - the 'edit' column might be a link to mysite.com/something/edit/{id}

    So it seem like the system needs to be aware of all columns and have some way to use them anywhere.

    I think the direction you are headed with adding Column objects to the DataGrid (plus transformation/filters objects) is the right direction.
    Christopher

  4. #29
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Quote Originally Posted by arborint
    Apply transform/filters to the value after is retrieved from the datasource, but before displayed. I suppose if you did edit in place you might need filters before saving.
    One use of a transformation would to to transform a MySQL timestamp to a nice readable format.

    Another use could be to transform a tinyint status column to the string 'Y' or 'N'.

    Its not uncommon to display data differently then its stored in the model. So a interface that allows that seems essential.

    Those are two instances that seem common right off hand.

  5. #30
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    One use of a transformation would to to transform a MySQL timestamp to a nice readable format.

    Another use could be to transform a tinyint status column to the string 'Y' or 'N'.

    Its not uncommon to display data differently then its stored in the model. So a interface that allows that seems essential.

    Those are two instances that seem common right off hand.
    Yeah, those are really common. They are really little View Helpers. With dates you also might want to have a filter to convert it back to 'yyyy-mm-dd' to save in the database. Or how about splitting it into three year/month/day <select>s or how about a date picker?

    There are others too. For example, I find that I often want to generate a <select> for a column using data in another table as the options (e.g. the column is category_id and there is another table category_names that has id,name for all categoies). Or translate 0/1 or Y/N into a select or radio buttons with Yes/No as options.

    It almost seems like there is not much difference between standard View Helpers and what is needed here.
    Christopher

  6. #31
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    You could also possibly integrate a row level template mechanism. Where a template is loaded and is rendered for each row while passing the data pertaining to that row.

  7. #32
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Here is a idea using a template. Although I'm not quite certain how to get columns into the equation just yet.

    PHP Code:
    <?php
    interface IViewable {
        public function 
    render();
    }

    interface 
    IRowView {
        public function 
    before($data,$i);
        public function 
    now($data,$i);
        public function 
    after($data,$i);
        public function 
    start($rows);
        public function 
    finish($rows);
    }

    class 
    RowTemplate implements IRowView {

        public function 
    start($rows) {
            echo 
    '<ul>';
        }
        
        public function 
    before($data,$i) {
            echo 
    '<li>';
        }
        
        public function 
    now($data,$i) {
            echo 
    $data['name'];
        }
        
        public function 
    after($data,$i) {
            echo 
    '</li>';
        }
        
        public function 
    finish($rows) {
            echo 
    '</ul>';
        }
        
    }

    class 
    Grid implements IViewable  {

        protected 
    $rows;
        protected 
    $view;
        
        public function 
    __construct(IRowView $view,$rows) {
            
    $this->view $view;
            
    $this->rows $rows;
        }
        
        public function 
    setView(IRowView $view) {
            
    $this->view $view;
        }
        
        public function 
    setRows($rows) {
            
    $this->rows $rows;
        }

        public function 
    render() {
        
            
    $this->view->start($this->rows);
        
            foreach(
    $this->rows as $key=>$row) {            
                
    $this->view->before($row,$key);
                
    $this->view->now($row,$key);
                
    $this->view->after($row,$key);        
            }    
            
            
    $this->view->finish($this->rows);
        }

    }


    $data = array(
        array(
    'name'=>'a')
        ,array(
    'name'=>'b')
        ,array(
    'name'=>'c')
    );

    $grid = new Grid(new RowTemplate(),$data);
    $grid->render();
    The obvious advantage of this would be the ability to manipulate the display of the grid.

  8. #33
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yeah. I have also thought about allowing you to register little templates like:

    Row:
    "<ul>{columns}</ul>"

    Columns:
    "<li>{id}</li>"

    "<li>{name}</li>"

    "<li><select name="{status}">
    <option>On</option>
    <option>Off</option>
    </select></li>"

    "<li><a href="{url}&edit=yes&id={id}</li>"


    It might be best if we just allow any Helper that had a render() method. We could pass in the registered field names, labels, etc. and let you use whatever Helper you wanted to write.
    Christopher

  9. #34
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hmm, this is when a 5.3 closures come in handy I think....

    PHP Code:
    <?php

    function table($fns)
    {
        return function(
    $rows) use ($fns)
        {
            echo 
    '<table>'"\n"'<tbody>'"\n"'<thead>'"\n"'<tr>'"\n";
            foreach(
    $fns as $label => $fn)
                echo 
    '<th>'$label'</th>';
            echo 
    '</tr>'"\n"'</thead>'"\n";
            foreach(
    $rows as $row)
            {
                echo 
    '<tr>';
                foreach(
    $fns as $fn)
                {
                    echo 
    '<td>';
                    
    $fn($row);
                    echo 
    '</td>';
                }
                echo 
    '</tr>'"\n";
            }
            echo 
    '</tbody>'"\n"'</table>'"\n";
        };
    }

    $render table(array(
    // Label => Function
    'Id' => function ($row) { echo $row[0], '. '; },
    'Title' => function ($row) { echo $row[1]; },
    'Category' => function ($row) { echo $row[2]; },
    '' => function ($row) { echo '<a href="edit.php?id='$row[0], '">Edit</a>'; },
    ));

    $rows = array(
    array(
    11'Foo''One'),
    array(
    12'Bar''Two'),
    array(
    13'Baz''Two'),
    array(
    14'Faz''One'),
    array(
    15'Boo''Two'),
    );

    $render($rows);

  10. #35
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Here is some more sketch work based of of the ideas here.

    PHP Code:
    <?php
    define
    ('PUBLIC_ROOT',$_SERVER['DOCUMENT_ROOT']);
    define('PRIVATE_ROOT',str_replace('/httpdocs','',PUBLIC_ROOT));
    require_once(
    PRIVATE_ROOT.'/include/php/config.php');

    interface 
    IManipulator {
       
        public function 
    prepare();
           public function 
    finish($index);
           public function 
    begin($index);
        public function 
    end($index);
        public function 
    current($row,$index);
       
    }

    interface 
    ITransformable {
        public function 
    apply($value);
    }

    class 
    DateTransform implements ITransformable {
        public function 
    apply($value) {
            return 
    date('M d, Y',strtotime($value));
        }
    }

    class 
    TableColumn {

        protected 
    $title;
        protected 
    $path;
        protected 
    $transformations;
        
        public function 
    __construct($title='',$path=null,$transformations=null) {
            
            
    $this->title $title;
            
    $this->path $path;
            
    $this->transformations $transformations;
            
        }

        public function 
    getPath() {
        
            return 
    $this->path;
        
        }
        
        public function 
    getTitle() {
        
            return 
    $this->title;
        
        }
        
        public function 
    getTransformations() {
        
            return 
    $this->transformations;
            
        }
        
        public function 
    setPath($path) {
        
            
    $this->path $path;
        
        }
        
        public function 
    setTitle($title) {
        
            
    $this->title $title;
        
        }
        
        public function 
    setTransformations($transformations) {
        
            
    $this->transformations $transformations;
            
        }
        
        public function 
    hasPath() {
            
            return !
    is_null($this->path) && is_array($this->path)?true:false;
            
        }
        
        public function 
    hasTransformations() {
            
            return !
    is_null($this->transformations) && is_array($this->transformations)?true:false;
            
        }

    }

    class 
    TableManipulator implements IManipulator {

        protected 
    $columns;
        
        public function 
    __construct() {
        
            
    $this->columns func_get_args();
        
        }
        
        public function 
    prepare() {
        
            echo 
    '<table>',"\n";
            
            echo 
    "\t",'<thead>',"\n";
            echo 
    "\t\t",'<tr>',"\n";
            foreach(
    $this->columns as $column) {
                echo 
    "\t\t\t",'<th>',$column->getTitle(),'</th>',"\n";
            }
            echo 
    "\t\t",'<tr>',"\n";
            echo 
    "\t",'</thead>',"\n";
            echo 
    "\t",'<tbody>',"\n";
        
        }
        
        public function 
    finish($index) {
            
            echo 
    "\t",'</tbody>',"\n";
            echo 
    '</table>',"\n";
        
        }
        
        public function 
    begin($index) {
            echo 
    "\t\t",'<tr>',"\n";
        }
        
        public function 
    end($index) {
            echo 
    "\t\t",'</tr>',"\n";
        }
        
        public function 
    current($row,$index) {
        
            foreach(
    $this->columns as $column) {
            
                echo 
    "\t\t\t",'<td>',$this->_getColumnValue($row,$column),'</td>',"\n";
            
            }
        
        }
        
        protected function 
    _getColumnValue($row,$column) {
            
            
    $value $row;
            
            if(
    $column->hasPath()===true) {
            
                foreach(
    $column->getPath() as $path) {
                
                    
    $value $value[$path];
                    
                }
                
            }
            
            if(
    $column->hasTransformations()===true) {
            
                foreach(
    $column->getTransformations() as $transform) {
                    
    $value $transform->apply($value);
                }
                
            }
            
            return 
    $value;
        
        }
        

    }

    class 
    Loop {
       
        public static function 
    run(Iterator $pIteratorIManipulator $pManipulator) {
        
            
    $index=1;
            
            
    $pIterator->rewind();
            if(
    $pIterator->valid()) {
                
    $pManipulator->prepare();
            }
            
            while(
    $pIterator->valid()) {
                
    $current $pIterator->current();
                if(
    $index) {
                    
    $pManipulator->begin($index);
                }
                
    $pManipulator->current($current,$index++);
                if(
    $index) {
                    
    $pManipulator->end($index);
                }
                
    $pIterator->next();
            }
            
            if(
    $index) {
                
    $pManipulator->finish($index);
            }
            
            return 
    $index;
        }
       
    }
    ?>
    Practical use:
    PHP Code:

    Loop
    ::run(
        
    MusicTrack::find(array('include'=>'music_album'),array('include'=>'music_genres'))->getIterator()
        ,new 
    TableManipulator(
            new 
    TableColumn('ID',array('id'))
            ,new 
    TableColumn('Artist',array('music_album','artist'))
            ,new 
    TableColumn('Album',array('music_album','title'))
            ,new 
    TableColumn('Track',array('title'))
            ,new 
    TableColumn('Release',array('music_album','release_date'),array(new DateTransform()))
            ,new 
    TableColumn('genre',array('music_album','music_genres',0,'name'))
        )
    ); 
    example of HTML:
    HTML Code:
    <table>
    	<thead>
    		<tr>
    			<th>ID</th>
    			<th>Artist</th>
    			<th>Album</th>
    			<th>Track</th>
    
    			<th>Release</th>
    			<th>genre</th>
    		</tr>
    	</thead>
    	<tbody>
    		<tr>
    			<td>1</td>
    
    			<td>Angels & Airwaves</td>
    			<td>I-Empire</td>
    			<td>Everything's Magic</td>
    			<td>Nov 06, 2007</td>
    			<td>Alternative</td>
    		</tr>
    
    		<tr>
    			<td>2</td>
    			<td>Angels & Airwaves</td>
    			<td>I-Empire</td>
    			<td>Breath</td>
    			<td>Nov 06, 2007</td>
    
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>3</td>
    			<td>Angels & Airwaves</td>
    			<td>I-Empire</td>
    
    			<td>Sirens</td>
    			<td>Nov 06, 2007</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>4</td>
    			<td>Angels & Airwaves</td>
    
    			<td>I-Empire</td>
    			<td>Call To Arms</td>
    			<td>Nov 06, 2007</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>5</td>
    
    			<td>Angels & Airwaves</td>
    			<td>I-Empire</td>
    			<td>Love Like Rockets</td>
    			<td>Nov 06, 2007</td>
    			<td>Alternative</td>
    		</tr>
    
    		<tr>
    			<td>10</td>
    			<td>Flaw</td>
    			<td>Endangered Species</td>
    			<td>Endangered Species</td>
    			<td>May 04, 2004</td>
    
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>9</td>
    			<td>Angels & Airwaves</td>
    			<td>I-Empire</td>
    
    			<td>True Love</td>
    			<td>Nov 06, 2007</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>11</td>
    			<td>Flaw</td>
    
    			<td>Endangered Species</td>
    			<td>Recognize</td>
    			<td>May 04, 2004</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>12</td>
    
    			<td>Flaw</td>
    			<td>Endangered Species</td>
    			<td>Wait for Me</td>
    			<td>May 04, 2004</td>
    			<td>Alternative</td>
    		</tr>
    
    		<tr>
    			<td>13</td>
    			<td>Flaw</td>
    			<td>Endangered Species</td>
    			<td>Medicate</td>
    			<td>May 04, 2004</td>
    
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>14</td>
    			<td>Smile Empty Soul</td>
    			<td>Vultures</td>
    			<td>The Hit</td>
    
    			<td>Oct 24, 2004</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>15</td>
    			<td>Smile Empty Soul</td>
    			<td>Vultures</td>
    
    			<td>Loser</td>
    			<td>Oct 24, 2004</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>16</td>
    			<td>Smile Empty Soul</td>
    
    			<td>Vultures</td>
    			<td>Jesus is the Manager of Wal-Mart</td>
    			<td>Oct 24, 2004</td>
    			<td>Alternative</td>
    		</tr>
    		<tr>
    			<td>17</td>
    
    			<td>Smile Empty Soul</td>
    			<td>Vultures</td>
    			<td>Out To Sea</td>
    			<td>Oct 24, 2004</td>
    			<td>Alternative</td>
    		</tr>
    
    	</tbody>
    </table>
    Then a separate raw manipulator could be set up for lists. A Lists wouldn't allow columns to be added. A user could also create their own custom manipulator or extend the functionality of a the Table or List one.
    Last edited by oddz; May 31, 2009 at 09:41.

  11. #36
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Honestly I think I would rather see:
    PHP Code:
    $table = new Table(
        
    MusicTrack::find(array('include'=>'music_album'),array('include'=>'music_genres'))->getIterator()
        , array(
            new 
    TableColumn('ID',array('id'))
            ,new 
    TableColumn('Artist',array('music_album','artist'))
            ,new 
    TableColumn('Album',array('music_album','title'))
            ,new 
    TableColumn('Track',array('title'))
            ,new 
    TableColumn('Release',array('music_album','release_date'),array(new DateTransform()))
            ,new 
    TableColumn('genre',array('music_album','music_genres',0,'name'))
        )
    );
    echo 
    $table->render(); 
    Or with setters:
    PHP Code:
    $table = new Table();
    $table->setRowData(MusicTrack::find(array('include'=>'music_album'),array('include'=>'music_genres'))->getIterator())
    $table->addColumn('ID',array('id'));
    $table->addColumn('Artist',array('music_album','artist'));
    $table->addColumn('Album',array('music_album','title'));
    $table->addColumn('Track',array('title'));
    $table->addColumn('Release',array('music_album','release_date'),array(new DateTransform());
    $table->addColumn('genre',array('music_album','music_genres',0,'name'));
    echo 
    $table->render(); 
    I am not sure there is one best way, because sometimes you have array data, for example from some config source. Other times you want to create objects because they can be extended and passed easily. And in the second case above it may create objects internally for you. I do thing having everything be an object with a render() methods is the way to do with HTML rendering because of it hierarchical nature.
    Christopher

  12. #37
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Perhaps something like this then:

    PHP Code:
    <?php
    abstract class DataGrid {
        
        protected 
    $cursor;
        protected 
    $rows;
        protected 
    $row;
        
        
        public function 
    __construct($rows=null) {
        
            
    $this->rows is_null($rows)?array():$rows;
            
    $this->cursor 0;
        
        }

        public function 
    setRowData($rows) {
        
            
    $this->rows $rows;
            
        }
        
        public function 
    render() {
            
            
    $this->rewind();
            
            if(
    $this->valid()) $this->prepare();
            
            while(
    $this->valid()) {
            
                
    $this->current();
                
                if(
    $this->cursor$this->begin();
                       
                
    $this->now();
                if(
    $this->cursor$this->end();
                
    $this->next();
                
            }
            
            if(
    $this->cursor$this->finish();    
        
        }
        
        protected function 
    rewind() {
        
            
    $this->cursor=0;
            
        }
        
        protected function 
    current() {
        
            
    $this->row $this->rows[$this->cursor];
        
        }
        
        protected function 
    key() {
            return 
    $this->cursor;
        }
        
        protected function 
    next() {
            ++
    $this->cursor;
        }

        protected function 
    valid() {
            return isset(
    $this->rows[$this->cursor]);
        }

        abstract protected function 
    prepare();
           abstract protected function 
    finish();
           abstract protected function 
    begin();
        abstract protected function 
    end();
        abstract protected function 
    now();

    }

    class 
    DataTable extends DataGrid {

        protected 
    $titles;
        protected 
    $paths;
        protected 
    $transformations;
        
        public function 
    __construct($rows=null,$columns=null) {
            
            
    parent::__construct($rows);
            
    $this->_init();
            
            if(!
    is_null($columns) && is_array($columns)) {
                
    $this->_fillColumns($columns);
            }
            
        }
        
        protected function 
    _init() {
            
            
    $this->titles = array();
            
    $this->paths = array();
            
    $this->transformations = array();
        
        }
        
        protected function 
    _fillColumns($columns) {
        
            foreach(
    $columns as $column) {
            
                
    $title array_key_exists(0,$column)?$column[0]:'';
                
    $path array_key_exists(1,$column)?$column[1]:array();
                
    $transform array_key_exists(2,$column)?$column[2]:array();
            
                
    $this->addColumn($title,$path,$transform);
            
            }
        
        }
        
        public function 
    addColumn($title,$path,$transform=null) {
        
            
    $this->titles[] = $title;
            
    $this->paths[] = $path;
            
    $this->transformations[] = is_null($transform)?array():$transform;
        
        }

        protected function 
    prepare() {
        
            echo 
    '<table>',"\n";
            
            echo 
    "\t",'<thead>',"\n";
            echo 
    "\t\t",'<tr>',"\n";
            
            
    $totalColumns count($this->titles);
            for(
    $i=0;$i<$totalColumns;$i++) {
                echo 
    "\t\t\t",'<th>',$this->titles[$i],'</th>',"\n";
            }
            
            echo 
    "\t\t",'</tr>',"\n";
            echo 
    "\t",'</thead>',"\n";
            echo 
    "\t",'<tbody>',"\n";
        
        }
        
        protected function 
    finish() {
            
            echo 
    "\t",'</tbody>',"\n";
            echo 
    '</table>',"\n";
        
        }
        
        protected function 
    begin() {
        }
        
        protected function 
    end() {
        }
        
        protected function 
    now() {
        
            
    $totalColumns count($this->titles);
            
            echo 
    "\t\t",'<tr>',"\n";
            for(
    $i=0;$i<$totalColumns;$i++) {
            
                echo 
    "\t\t\t",'<td>',$this->_getColumnValue($i),'</td>',"\n";
            
            }
            echo 
    "\t\t",'<tr>',"\n";
        
        }
        
        protected function 
    _getColumnValue($column) {
            
            
    $value $this->row;
            
            
            foreach(
    $this->paths[$column] as $path) {            
                
    $value $value[$path];                
            }
            
            foreach(
    $this->transformations[$column] as $transform) {
                
    $value $transform->apply($value,$row);
            }
            
            return 
    $value;
        
        }

    }

    class 
    DataList extends DataGrid {

        protected function 
    prepare() {
        }
        
        protected function 
    finish() {
        }
        
        protected function 
    begin() {
        }
        
        protected function 
    end() {
        }
        
        protected function 
    now() {
        }

    }

    $table = new DataTable();
    $table->setRowData(MusicTrack::find(array('include'=>'music_album'),array('include'=>'music_genres')));
    $table->addColumn('ID',array('id'));
    $table->addColumn('Artist',array('music_album','artist'));
    $table->addColumn('Album',array('music_album','title'));
    $table->addColumn('Track',array('title'));
    $table->addColumn('Release',array('music_album','release_date'));
    $table->addColumn('genre',array('music_album','music_genres',0,'name'));
    $table->render(); 
    ?>

  13. #38
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Using generic data:

    PHP Code:
    <?php
    interface IGridTransformable {
        public function 
    apply($value,$row);
    }

    abstract class 
    DataGrid {
        
        protected 
    $cursor;
        protected 
    $rows;
        protected 
    $row;
        
        
        public function 
    __construct($rows=null) {
        
            
    $this->rows is_null($rows)?array():$rows;
            
    $this->cursor 0;
        
        }

        public function 
    setRows($rows) {
        
            
    $this->rows $rows;
            
        }
        
        public function 
    render() {
            
            
    $this->_rewind();
            
            if(
    $this->_valid()) $this->_prepare(); else return $this->_empty();
            
            while(
    $this->_valid()) {
            
                
    $this->_current();
                
                if(
    $this->cursor$this->_begin();
                       
                
    $this->_now();
                if(
    $this->cursor$this->_end();
                
    $this->_next();
                
            }
            
            if(
    $this->cursor$this->_finish();    
        
        }
        
        protected function 
    _rewind() {
        
            
    $this->cursor=0;
            
        }
        
        protected function 
    _current() {
        
            
    $this->row $this->rows[$this->cursor];
        
        }
        
        protected function 
    _key() {
            return 
    $this->cursor;
        }
        
        protected function 
    _next() {
            ++
    $this->cursor;
        }

        protected function 
    _valid() {
            return isset(
    $this->rows[$this->cursor]);
        }

        abstract protected function 
    _prepare();
           abstract protected function 
    _finish();
           abstract protected function 
    _begin();
        abstract protected function 
    _end();
        abstract protected function 
    _now();
        abstract protected function 
    _empty();

    }

    class 
    DataTable extends DataGrid {

        protected 
    $titles;
        protected 
    $paths;
        protected 
    $transformations;
        
        public function 
    __construct($rows=null,$columns=null) {
            
            
    parent::__construct($rows);
            
    $this->_init();
            
            if(!
    is_null($columns) && is_array($columns)) {
                
    $this->_fillColumns($columns);
            }
            
        }
        
        protected function 
    _init() {
            
            
    $this->titles = array();
            
    $this->paths = array();
            
    $this->transformations = array();
        
        }
        
        protected function 
    _fillColumns($columns) {
        
            foreach(
    $columns as $column) {
            
                
    $title array_key_exists(0,$column)?$column[0]:'';
                
    $path array_key_exists(1,$column)?$column[1]:array();
                
    $transform array_key_exists(2,$column)?$column[2]:array();
            
                
    $this->addColumn($title,$path,$transform);
            
            }
        
        }
        
        public function 
    addColumn($title,$path,$transform=null) {
        
            
    $this->titles[] = $title;
            
    $this->paths[] = $path;
            
    $this->transformations[] = is_null($transform)?array():$transform;
        
        }

        protected function 
    _prepare() {
        
            echo 
    '<table>',"\n";
            
            echo 
    "\t",'<thead>',"\n";
            echo 
    "\t\t",'<tr>',"\n";
            
            
    $totalColumns count($this->titles);
            for(
    $i=0;$i<$totalColumns;$i++) {
                echo 
    "\t\t\t",'<th>',$this->titles[$i],'</th>',"\n";
            }
            
            echo 
    "\t\t",'</tr>',"\n";
            echo 
    "\t",'</thead>',"\n";
            echo 
    "\t",'<tbody>',"\n";
        
        }
        
        protected function 
    _finish() {
            
            echo 
    "\t",'</tbody>',"\n";
            echo 
    '</table>',"\n";
        
        }
        
        protected function 
    _begin() {
        }
        
        protected function 
    _end() {
        }
        
        protected function 
    _now() {
        
            
    $totalColumns count($this->titles);
            
            echo 
    "\t\t",'<tr>',"\n";
            for(
    $i=0;$i<$totalColumns;$i++) {
            
                echo 
    "\t\t\t",'<td>',$this->_getColumnValue($i),'</td>',"\n";
            
            }
            echo 
    "\t\t",'<tr>',"\n";
        
        }
        
        protected function 
    _getColumnValue($column) {
            
            
    $value $this->row;
            
            
            foreach(
    $this->paths[$column] as $path) {            
                
    $value $value[$path];                
            }
            
            foreach(
    $this->transformations[$column] as $transform) {
                
    $value $transform->apply($value,$this->row);
            }
            
            return 
    $value;
        
        }
        
        protected function 
    _empty() {
        
            
    $totalColumns count($this->titles);
        
            
    $this->_prepare();
            echo 
    "\t\t",'<tr>',"\n";    
            echo 
    "\t\t\t",'<td class="empty-result" cellspan="'.$totalColumns.'">No Data Available</td>',"\n";
            echo 
    "\t\t",'<tr>',"\n";
            
    $this->_finish();
        
        }

    }

    class 
    DataList extends DataGrid {

        public function 
    __construct($rows=null) {
        
            
    parent::__construct($rows);
            
        }

        protected function 
    _prepare() {
        }
        
        protected function 
    _finish() {
        }
        
        protected function 
    _begin() {
        }
        
        protected function 
    _end() {
        }
        
        protected function 
    _now() {
        }
        
        protected function 
    _empty() {
        
        }

    }

    class 
    DateTransform implements IGridTransformable {

        public function 
    apply($value,$row) {
        
            return 
    date('M D, Y g:ma',$value);
        
        }

    }

    $data = array(
        array(
    'id'=>2,'time'=>time(),'entry'=>'d qw dq','title'=>'asd')
        ,array(
    'id'=>45,'time'=>time(),'entry'=>' dq','title'=>'fgtr')
        ,array(
    'id'=>23,'time'=>time(),'entry'=>'cwedfw','title'=>'fgtr')
        ,array(
    'id'=>34,'time'=>time(),'entry'=>'xwqd dqwd','title'=>'uiyt')
    );

    $table = new DataTable(
        
    $data
        
    ,array(
            array(
    'ID',array('id'))
            ,array(
    'TITLE',array('title'))
            ,array(
    'ENTRY',array('entry'))
            ,array(
    'TIME',array('time'),array(new DateTransform()))
        )
    );
    $table->render();
    ?>

  14. #39
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yikes ... you are a maniac! That is a lot of code! Not sure what to say? I think this is the general direction. I think I might create some more general purpose classes (e.g. Table, Table_Row, Table_Column) to do HTML Tables -- which you sort of have. Then have those used by whatever extra functionality is needed for the actual DataGrid. Or maybe your DataTable is the middle layer? What you posted certainly works. It looks like more code than is needed. Are you modifying some existing code or just coding like a madman?
    Christopher

  15. #40
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    The one thing that I think would be nice to have is a way to add classes and ids. For the table this is not all that much of a problem, but for the rows and cells it becomes a little more difficult. Especially, if the classes or ids are based on the data in the row like the primary key. Not really certain how to best allow that.

    Quote Originally Posted by arborint
    Are you modifying some existing code or just coding like a madman?
    All from scratch. You really think that's a lot of code?,lol

  16. #41
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    The one thing that I think would be nice to have is a way to add classes and ids. For the table this is not all that much of a problem, but for the rows and cells it becomes a little more difficult. Especially, if the classes or ids are based on the data in the row like the primary key. Not really certain how to best allow that.
    Yeah, that's why I think the Table problem is really just a generic HTML generation problem -- separate from any DataGrid functionality. Does anyone have a HTML table classes that they think are exceptionally well done?

    Quote Originally Posted by oddz View Post
    All from scratch. You really think that's a lot of code?,lol
    Not a lot of code. A lot for a forum post. I am impressed.
    Christopher

  17. #42
    SitePoint Enthusiast
    Join Date
    Dec 2003
    Location
    norway
    Posts
    61
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    As of RESTfully save the total count in the URL pagination list..(if I understand correctly)
    Would'nt that cause trouble if you for example want to bookmark a certain page and the total has changed?

  18. #43
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    I think at the least a Column object needs to be factored out. Using a array() to represent a column goes against the object-oriented methodology.

    $table->addColumn(new Column('title',array('title')));

    The first parameter of addColumn() could be type hinted to a "column" interface of some sort. If the column were represented as a object it would be easy to add a class or something to all instances of it.

    $col = new Column();
    $col->addClass('highlight');

    However, adding classes or ids to specific rows in an automated fashion seems as if it would require a "event" of the sorts. Something like onBeforeRow() where another object could be passed which could alter something. Not exactly sure how that would both work and be easy to use though.

  19. #44
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by danman View Post
    As of RESTfully save the total count in the URL pagination list..(if I understand correctly)
    Would'nt that cause trouble if you for example want to bookmark a certain page and the total has changed?
    I think that the idea of bookmarking a page in a volatile data table is problematic in itself. What exactly are you bookmarking? And as discussed above with Vali, if the table is volatile then you may be forced to a scheme where there is some recalculation every page. With databases there are always trade-offs.

    I do think that this system needs the ability to either cache or recalculate the total number of rows of data each page. One of the ideas with this component is that you can wrap your Model classes with this Adapter interface and use your optimized queries.

    But also remember, there is a lot of data that is not volatile. On the web there are a lot of read only tables that are only edited through some CRUD admin interface.
    Christopher

  20. #45
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    However, adding classes or ids to specific rows in an automated fashion seems as if it would require a "event" of the sorts. Something like onBeforeRow() where another object could be passed which could alter something. Not exactly sure how that would both work and be easy to use though.
    I think if you have Columns, Rows and a Table then you should be able to have the control that you need. I agree that IDs would be tricky, but I am not sure they are actually needed. I think classes would be enough.
    Christopher

  21. #46
    SitePoint Enthusiast
    Join Date
    Dec 2003
    Location
    norway
    Posts
    61
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by arborint View Post
    I think that the idea of bookmarking a page in a volatile data table is problematic in itself. What exactly are you bookmarking? And as discussed above with Vali, if the table is volatile then you may be forced to a scheme where there is some recalculation every page. With databases there are always trade-offs.

    I do think that this system needs the ability to either cache or recalculate the total number of rows of data each page. One of the ideas with this component is that you can wrap your Model classes with this Adapter interface and use your optimized queries.

    But also remember, there is a lot of data that is not volatile. On the web there are a lot of read only tables that are only edited through some CRUD admin interface.
    Agree with what you say, that the bookmarking of volatile data table is problematic itself. However it happens that one bookmark search results or browsing through recent URL's in the adress bar even though the data is not more then subset of given database rows defined by "offset" and "limit".
    So my preference would be that the "default" setting should be to not persist this value RESTfully ,( if not so already).
    Anyway, really nice work you have done!

  22. #47
    SitePoint Enthusiast
    Join Date
    Feb 2009
    Location
    Athens, Greece
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    sorry for beeing offtopic - but it would be less time-consuming to use a class like PEAR pagination

    My 0.02 €

  23. #48
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 01globalnet View Post
    sorry for beeing offtopic - but it would be less time-consuming to use a class like PEAR pagination

    My 0.02
    Why is that? Is PEAR::Pager more intuitive than our classes? I would be interested to hear why you think PEAR::Pager would take less time to learn.

  24. #49
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 01globalnet View Post
    sorry for beeing offtopic - but it would be less time-consuming to use a class like PEAR pagination

    My 0.02
    You might be right. How would be know without an example? Here is a quick one I found:
    Code PHP:
    <?php
    //copy the Pager_Wrapper file where you can include it
    require_once 'Pager_Wrapper.php';
    require_once 'MDB2.php';
     
    $article_id = 1; //if you fetch this parameter via GET/POST, remember to validate it!
     
    //skipped the db connection code...
    //let's just suppose we have a valid db connection in $db.
     
    $pager_options = array(
        'mode'       => 'Sliding',
        'perPage'    => 1, //we want only ONE paragraph per page
        'delta'      => 3,
    );
     
    $query = 'SELECT articles.title AS article_title,
                     articles.submission_date,
                     articles.abstract,
                     paragraphs.title AS paragraph_title,
                     paragraphs.content
                FROM paragraphs
           LEFT JOIN articles ON articles.id = paragraphs.article_id
               WHERE articles.id = '. (int)$article_id .'
            ORDER BY paragraphs.paragraph_id;
     
    $paged_data = Pager_Wrapper_MDB2($db, $query, $pager_options);
     
    //show the results
    echo '<h1>'.$paged_data['data'][0]['article_title'].'</h1>';
    echo '<p><i>'.$paged_data['data'][0]['submission_date'].'</i></p>';
    if ($paged_data['page_numbers']['current'] == 1) {
        // also show the abstract on the first page
        echo '<p>'.$paged_data['data'][0]['abstract'].'</p>';
    }
    echo '<h2>'.$paged_data['data'][0]['paragraph_title'].'</h2>';
    echo '<p>'.$paged_data['data'][0]['content'].'</p>';
     
    //show the links
    echo $paged_data['links'];
    ?>
    I don't know if that is the latest version or representative -- let me know.

    What is it about the PEAR style that you find less time-consuming? What parts of the design and functionality do you like?
    Christopher


Tags for this Thread

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
  •