SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 38
  1. #1
    SitePoint Enthusiast snajt's Avatar
    Join Date
    May 2008
    Location
    Kalmar, Sweden
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    PDO pagination with mapper pattern

    Hi!

    I'm currently building an application where I use PDO and the mapper pattern, basicly the structure is the following:

    Data access layer:

    PHP Code:
    Class User
    {
      public 
    $username;
    }

    Class 
    UserMapper
    {
      public function 
    fetchAll()
      {
         
    $db DB::getInstance();
         
    /* fetch the columns and return a new user object */
      
    }

    And the view layer:
    PHP Code:
    <ul>
    <?php foreach(UserMapper::fetchAll() as $user): ?>
      <li><?php echo $user->username?></li>
    <?php endforeach; ?>
    </ul>
    No to my question, how would you implement a paging function or class to this? I want as loose coupling to the mapper pattern as possible..

    Hope you'll get the idea. Thanks!

  2. #2
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe instead of fetchAll() you could have a fetchPage($pageNumber) function? Of course that means you would have to set the number of records per page, etc. but it may be an idea.

  3. #3
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Or your fetchAll could take parameters, such as:fetchAll ($limit, $offset)

    You could then make a generic pagination class that takes a mapper in its constructor, and calls fetchAll with the appropriate limit/offset for the page.

  4. #4
    SitePoint Enthusiast snajt's Avatar
    Join Date
    May 2008
    Location
    Kalmar, Sweden
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    Or your fetchAll could take parameters, such as:fetchAll ($limit, $offset)

    You could then make a generic pagination class that takes a mapper in its constructor, and calls fetchAll with the appropriate limit/offset for the page.
    Yepp, I think that sounds like a good solution. But just to be sure, do you mean something like this?

    PHP Code:
    <?php
    error_reporting
    (E_ALL E_STRICT);

    require_once 
    "includes/db.class.php";

    Interface 
    Mapper
    {
        public function 
    fetchAll($limit$offset);
    }

    Class 
    User
    {
        private 
    $id$name$password$email$status;
        
        public function 
    __get($key)
        {
            return 
    $this->$key;
        }
        
        public function 
    __set($key$value)
        {
            
    $this->$key $value;
        }
    }

    Class 
    UserMapper Implements Mapper
    {
        public function 
    fetchAll($limit$offset)
        {
            
    $db DB::getInstance();
            
            
    $stmt $db->prepare('SELECT * FROM users LIMIT ?,?');
            
    $stmt->execute(array($limit$offset));
            
            
    $users = array();
            
            foreach(
    $stmt->fetchAll() as $row)
            {
                
    $user = new User;
                
    $user->id $row["user_id"];
                
    $user->name $row["user_name"];
                
    $user->password $row["user_password"];
                
    $user->email $row["user_email"];
                
    $user->status $row["user_status"];
                
                
    $users[] = $user;
            }
            
            return 
    $users;
        }
    }

    Class 
    Pager
    {
        private 
    $mapper;
        
        public function 
    __construct(Mapper $mapper)
        {
            
    $this->mapper $mapper;
        }
        
        public function 
    fetchAll($limit$offset)
        {
            return 
    $this->mapper->fetchAll($limit$offset);
        }
    }

    $pager = new Pager(new UserMapper);
    var_dump($pager->fetchAll(010));
    ?>
    Thanks again!

  5. #5
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Close, I was thinking something more like this for the pager class:

    PHP Code:
    class Pager    {

    function 
    __construct (Mapper $mapper$limit 20)    {
        
    $this->mapper $mapper;
        
    $this->limit $limit;
    }

    function 
    fetch ($page)    {
        return 
    $this->mapper->fetchAll ($this->limit$this->getOffset ($page));
        }

    function 
    getOffset ($page)    {
        
    // calculate offset
        
    }



  6. #6
    SitePoint Enthusiast snajt's Avatar
    Join Date
    May 2008
    Location
    Kalmar, Sweden
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for you help,

    i'm not sure about the getOffset-method though, would it be somethin like?

    PHP Code:
    return ENTRIES_PER_PAGE $page

  7. #7
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think this would work:

    return (($page - 1) * $this->limit);

  8. #8
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    Or your fetchAll could take parameters, such as:fetchAll ($limit, $offset)

    You could then make a generic pagination class that takes a mapper in its constructor, and calls fetchAll with the appropriate limit/offset for the page.
    I like your idea much better than my hasty solution . The only problem I see is a way to do it consistently for any query. What if you wanted to find() rows with conditions? Do you add $limit and $offset parameters to each finder function, or come up with a more generic way to do it?

  9. #9
    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 Czaries View Post
    I like your idea much better than my hasty solution . The only problem I see is a way to do it consistently for any query. What if you wanted to find() rows with conditions? Do you add $limit and $offset parameters to each finder function, or come up with a more generic way to do it?
    Haven't thought that far ahead... maybe a find() method could take conditions, and a findAll() could take limit, offset?

  10. #10
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think the paging should be broken away from the database specific stuff --

    PHP Code:
    class Pager {
        protected 
    $total;
        protected 
    $page;
        protected 
    $limit;
        function 
    __construct($total$page 1$limit 10) {
            
    $this->total $total;
            
    $this->limit $limit;
            if (
    == $page || $page $this->lastPage()) {
                throw new 
    OutOfBoundsException("Page $page is out of range");
            }
            
    $this->page $page;
        }
        function 
    currentPage() {
            return 
    $this->page;
        }
        function 
    offset() {
           return (
    $this->page 1) * $this->limit;
        }
        function 
    limit() {
            return 
    $this->limit;
        }
        function 
    lastPage() {
            return 
    ceil($this->total $this->limit);
        }
        function 
    pageRange($maxPages) {
            
    $first max(1$this->currentPage() - $maxPages);
            
    $last min($first $maxPages 1$this->lastPage());
            return 
    range($first$last);
        }

    Then you can do stuff like this, in addition to paging database results --

    PHP Code:
    class User {
        protected 
    $username;
        function 
    __construct($username) {
            
    $this->username $username;
        }
    }

    $users = new ArrayObject(array(
        new 
    User('bob'),
        new 
    User('joe'),
        new 
    User('sam')
    ));

    $it $users->getIterator();
    $pager = new Pager(count($it));
    foreach (new 
    LimitIterator($it$pager->offset(), $pager->limit()) as $item) {
        
    var_dump($item);
    }

    foreach (
    $pager->pageRange(3) as $page) {
        echo (
    $page).($page == $pager->currentPage() ? '(current)|' '|');


  11. #11
    SitePoint Enthusiast snajt's Avatar
    Join Date
    May 2008
    Location
    Kalmar, Sweden
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cuberoot View Post
    I think the paging should be broken away from the database specific stuff --

    PHP Code:
    class Pager {
        protected 
    $total;
        protected 
    $page;
        protected 
    $limit;
        function 
    __construct($total$page 1$limit 10) {
            
    $this->total $total;
            
    $this->limit $limit;
            if (
    == $page || $page $this->lastPage()) {
                throw new 
    OutOfBoundsException("Page $page is out of range");
            }
            
    $this->page $page;
        }
        function 
    currentPage() {
            return 
    $this->page;
        }
        function 
    offset() {
           return (
    $this->page 1) * $this->limit;
        }
        function 
    limit() {
            return 
    $this->limit;
        }
        function 
    lastPage() {
            return 
    ceil($this->total $this->limit);
        }
        function 
    pageRange($maxPages) {
            
    $first max(1$this->currentPage() - $maxPages);
            
    $last min($first $maxPages 1$this->lastPage());
            return 
    range($first$last);
        }

    Then you can do stuff like this, in addition to paging database results --

    PHP Code:
    class User {
        protected 
    $username;
        function 
    __construct($username) {
            
    $this->username $username;
        }
    }

    $users = new ArrayObject(array(
        new 
    User('bob'),
        new 
    User('joe'),
        new 
    User('sam')
    ));

    $it $users->getIterator();
    $pager = new Pager(count($it));
    foreach (new 
    LimitIterator($it$pager->offset(), $pager->limit()) as $item) {
        
    var_dump($item);
    }

    foreach (
    $pager->pageRange(3) as $page) {
        echo (
    $page).($page == $pager->currentPage() ? '(current)|' '|');

    I really like this approach, but if I implement this, doesn't it mean that I have to fetch the whole table every time, instead of using LIMIT ?,? in SQL?

  12. #12
    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 cuberoot View Post
    I think the paging should be broken away from the database specific stuff --
    I think the Pager I outlined above is pretty separated from the DB- as long as the mapper implements a limit + offset interface, anything that implemented that interface could be passed, including an iterator.

    In your example, the client code still needs to know about limits and offsets-- even if its using the pager class to calculate them. I'd rather see the iterator passed into the pager class, and the pager class retrieve the requested results from the iterator.

  13. #13
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think it's a bad idea, mostly because your code becomes more involved when it comes to testing. With the pager class above you simply test numbers, with yours you need additional classes (mocked up or whatever).

    Something to keep in mind when designing classes is the Single Responsibility Principle. Classes should do one thing and do it well. Putting a mapper into a pager sort of muddies the water a bit.

    I would have a collection class that proxies a query, executing on first access only. The collection would also have the mappers necessary to convert rows into objects during iteration.

  14. #14
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by snajt View Post
    I really like this approach, but if I implement this, doesn't it mean that I have to fetch the whole table every time, instead of using LIMIT ?,? in SQL?
    You're gonna need the total anyways, assuming you want to display a navigation widget and validate incoming page numbers. One way to get this number is to execute the query with COUNT(*) initially and then a second time with field names and the LIMIT clause.

  15. #15
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't like that solution either. That's fine for databases that don't support returning a limited result set, but for those that do I think it really should be used to save memory. There has to be some elegant way to do this. I'm thinking something along the lines of using the database adapter. The problem is you still have to pass in a $limit and $offset, before the query is executed.

    Of course the easiest way would be to abstract the SQL syntax into some query builder like Zend_Db and CodeIgniter:
    $db->select()->from($table)->limit($rows, $offset)->execute();

    Using finders, the only ways I can think of right now would be quite messy. Any other thoughts?

  16. #16
    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 cuberoot View Post
    I think it's a bad idea, mostly because your code becomes more involved when it comes to testing. With the pager class above you simply test numbers, with yours you need additional classes (mocked up or whatever).
    What's wrong with mocking classes? I think that's essential to testing, and I don't see any reason to avoid it.

    Quote Originally Posted by cuberoot View Post
    Something to keep in mind when designing classes is the Single Responsibility Principle. Classes should do one thing and do it well. Putting a mapper into a pager sort of muddies the water a bit.
    I see the responsibility of a pager class as retrieving a subset of data from a data source. If the pager class doesn't retrieve the subset of data, then the client code has to do it. I'd rather encapsulate that logic, so all I have to do is ask for the data for page 3, and it takes care of everything else.

  17. #17
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Czaries View Post
    I don't like that solution either. That's fine for databases that don't support returning a limited result set, but for those that do I think it really should be used to save memory.
    Not following what you're saying. What's fine for databases that don't support limited result sets?

    Key point is you need to know the total size of the set regardless of whether or not you elect to page through it. Otherwise you'll be sacrificing display and validation capabilities.

    Another thing to consider is that paging results is specifically related to list views. There is no other application. The average full service mapper is a poor fit for this type of view, since they would either load a bunch of useless dependent objects or require a fancy lazy loading setup. And even with the lazy load, you'd pay the price of additional database queries for every row, rather than one query upfront that includes all relevant information. Compounding this problem is read consistency. Are you going to deal with all of those hairy concurrency issues for a simple list view? That would be a tragedy of epic proportions.

    In my opinion, it would be a good idea to hide one big query in a UserInfo class or some such thing. It would use a single query to gather all of the information you need, which gets rid of all of your problems.

    Consider the fact that you are not grabbing this list for update purposes. You're simply spitting them out in the view along with some ids. The user may elect to do various things with that info in the view and then submit it, but at that point you're fetching entities by id, so that's when your mapper and the potentially large load graph gets involved. This is a lot more justifiable, since you need the full entity there for validation purposes.

    In short, if you're not intending to update the model, consider losing the mapper.
    Last edited by cuberoot; Oct 5, 2008 at 18:35.

  18. #18
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    What's wrong with mocking classes? I think that's essential to testing, and I don't see any reason to avoid it.
    No problem with mocking classes, but like anything they can be abused. You shouldn't add a dependency if you can do without it. It's good for isolation and it saves you the maintenance chore of dealing with/setting up dependencies.

    I see the responsibility of a pager class as retrieving a subset of data from a data source.
    A pager really just takes a number and distributes it across pages.

    Let's say I have a gallery app that allows users to set up display cycles for their homepages. It'll basically show n images a day until the end of the cycle. Something like...

    PHP Code:
    $pager = new Pager(
        
    $gallery->getTotalImages(),
        
    $displayCycle->todaysSequenceNumber(),
        
    $displayCycle->getImagesPerDay());
    $links $gallery->imageLinks($pager->offset(), $pager->limit()); 
    If the pager class doesn't retrieve the subset of data, then the client code has to do it. I'd rather encapsulate that logic, so all I have to do is ask for the data for page 3, and it takes care of everything else.
    Client is an ambiguous term though. You can have a lot clients. You can still encapsulate the idea of using a paged set of numbers to step through database results, you just wouldn't do it in the Pager class. You simply create another class that uses Pager internally.

  19. #19
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I would rather see:

    PHP Code:
    $pager = new Pager(
        
    $galleryMapper,
        
    $displayCycle->getImagesPerDay());
    $links $pager->fetch ($displayCycle->todaysSequenceNumber()); 

  20. #20
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    For what reason? In what way is that an improvement? Why would you build a dependency in there if you didn't absolutely need it?

  21. #21
    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 cuberoot View Post
    For what reason? In what way is that an improvement? Why would you build a dependency in there if you didn't absolutely need it?
    Simpler, more encapsulated, more logical to me. I'm not afraid of dependencies... I prefer an explicit dependency on a data source over muddy client code that has to wire everything together.

  22. #22
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cuberoot View Post
    Not following what you're saying. What's fine for databases that don't support limited result sets?
    Sorry for not clarifying further - I'm saying that for databases that support LIMIT clauses, it should be used instead of fetching the entire result set. So there does need to be a way of modifying the query to save resources, especially with large datasets.

    Quote Originally Posted by cuberoot View Post
    Key point is you need to know the total size of the set regardless of whether or not you elect to page through it. Otherwise you'll be sacrificing display and validation capabilities.
    Uuhh... Yeah I think we all know that. I've done paged result sets many many times, as I'm sure everyone else here has as well.

    Quote Originally Posted by cuberoot View Post
    Another thing to consider is that paging results is specifically related to list views. There is no other application. The average full service mapper is a poor fit for this type of view, since they would either load a bunch of useless dependent objects or require a fancy lazy loading setup. And even with the lazy load, you'd pay the price of additional database queries for every row, rather than one query upfront that includes all relevant information. Compounding this problem is read consistency. Are you going to deal with all of those hairy concurrency issues for a simple list view? That would be a tragedy of epic proportions.
    I don't see how there would be all the overhead you claim or any extra queries with an intelligently designed data mapper? The way I look at it, there would pretty much always have to be 2 queries:
    1 - Query to fetch rows with the LIMIT clause
    2 - Query to COUNT() all records with the same conditions

    The mapper could return a result set of row objects (essentially dumb value objects) - something that doesn't take any extra queries to do. Of course the tricky part here would be modifying the SQL or setting it up in such a way that you can add limits to any query and get all the relevant information you need.

    Injecting the mapper into a separate Pager object seems like the way to go to me (for simplification), but it also seems like there would need to be some built-in methods for adding the modifications to the query in the mapper itself, so they can be passed on to the adapter to put the query together. That integration point is what I would like to figure out.

  23. #23
    SitePoint Enthusiast snajt's Avatar
    Join Date
    May 2008
    Location
    Kalmar, Sweden
    Posts
    45
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi guys, sorry for not replying!

    I've actually played with all of your methods, but i found cuberoot's solution the best and it was the easiest to implement to my current project and this was the implementation:

    PHP Code:
    /* mapper.php */
    Interface IMapper
    {
        public function __construct($table, $primary_key);
        public function fetchAll($offset, $limit);
    }

    Abstract Class Mapper Implements IMapper
    {
        public $table;
        public $primary_key;
        
        public function __construct($table, $primary_key)
        {
            $this->table        = $table;
            $this->primary_key = $primary_key;
        }
    }

    /* pager.php */
    /* Simple pager interface */
    Interface IPager
    {
        public function fetchAll($page);
        public function countRows();
        public function printLinks($page);
    }

    /* The pager base-class */
    Abstract Class Pager Implements IPager
    {
        public $mapper;
        public $limit;
        
        public function __construct(IMapper $mapper, $limit)
        {
            $this->mapper = $mapper;
            $this->limit = $limit;
        }
        
        public function countRows()
        {
            $db = DB::getInstance();
            
            $stmt = $db->prepare('SELECT COUNT('.$this->mapper->primary_key.') FROM ' . $this->mapper->table . ' LIMIT 1');
            $stmt->execute();
            
            $result = $stmt->fetch(PDO::FETCH_ASSOC);
            return $result['COUNT('.$this->mapper->primary_key.')'];
        }
        
        public function fetchAll($page)
        {
            return $this->mapper->fetchAll(($page - 1) * $this->limit, $this->limit);
        }
    }

    /* handler.php */
    require_once "pager.php";
    require_once "mapper.php";

    Class User
    {
        public $id;
        public $username;
    }

    Class UserMapper Extends Mapper
    {
        public function __construct($table, $primary_key)
        {
            parent::__construct($table, $primary_key);
        }
        
        public function fetchAll($offset, $limit)
        {
            $db = DB::getInstance();
            $users = array();
            
            $stmt = $db->prepare('SELECT * FROM ' . $this->table . ' ORDER BY ' . $this->primary_key . ' DESC' . ' LIMIT ' . $offset . ',' . $limit);
            $stmt->execute();

            foreach($stmt->fetchAll() as $row)
            {
                $user = new User;
                $user->id = $row['user_id'];
                $user->username = $row['user_name'];
                
                $users[] = $user;
            }
            
            return $users;
        }
    }

    Class UserPager Extends Pager
    {
        public function countRows()
        {
            return parent::countRows();
        }
        
        public function printLinks($page)
        {
            $links = $this->countRows();
            $retval = '';
            
            for($i = 0; $i < $links / $this->limit; $i++)
            {
                $retval .= '<a href="?page='.($i+1).'"><li>' . ($i + 1) . '</li></a>';
            }
            
            return $retval;
        }
    }

    $page = $_GET['page'];

    $uMapper = new UserMapper('users', 'user_id');
    $uPager = new UserPager($uMapper, 4);

    /* template */
    <html>
        <head>
        
        </head>
        <body>
            <ul>
                <?php echo $uPager->printLinks($page); ?>
            </ul>

            <table border=1>
                <tr><th>Username</th></tr>
            <?php foreach($uPager->fetchAll($page) as $user): ?>
                <tr>
                    <td>
                        <?php echo $user->username?>
                    </td>
                </tr>
            <?php endforeach; ?>
            </table>
        </body>
    </html>
    I have no idea about how good this is from an OO point of view, so please, feel free to comment my solution. And if this post got to long, please tell me and i put it on rafb of something.

    The only thing i don't like about my implementation at the moment is that i output html directly from the printLinks()-function, but this is easy to fix.

  24. #24
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by snajt View Post
    I have no idea about how good this is from an OO point of view, so please, feel free to comment my solution. And if this post got to long, please tell me and i put it on rafb of something.

    The only thing i don't like about my implementation at the moment is that i output html directly from the printLinks()-function, but this is easy to fix.
    The only problem I really see with your solution, as I pointed out before, is that $offest and $limit would then have to be included and integrated with every finder function on both the Mapper and the Pager class. That's a lot of extra work. I'm looking for a more automated solution that will work universally with any finder function I choose to use. Something like this:

    PHP Code:
    <?php
    /* The pager base-class */
    class Pager implements IPager
    {
        public 
    $mapper;
        public 
    $page;
        public 
    $limit;
        
        public function 
    __construct(IMapper $mapper$page 1$limit 20)
        {
            
    $this->mapper $mapper;
            
    $this->page $page;
            
    $this->limit$limit;
        }
        

        
    // Passthrough to mapper
        
    public function __call($func$args)
        {
            
    // Set limit to maper
            
    $this->mapper->setLimit(($this->page 1) * $this->limit$this->limit);

            
    // Let mapper handle the specifics
            
    return call_user_func_array(array($this->mapper$func), $args);
        }
    }

    // Get current page
    $page = isset($_GET['page']) ? $_GET['page'] : 1;

    // Syntax to use it
    $uPager = new Pager(new UserMapper(), $page);
    ?>
    <html>
        <head>
        
        </head>
        <body>
            <ul>
                <?php echo $uPager->printLinks($page); ?>
            </ul>

            <table border=1>
                <tr><th>Username</th></tr>
            <?php foreach($uPager->fetchAll() as $user): ?>
                <tr>
                    <td>
                        <?php echo $user->username?>
                    </td>
                </tr>
            <?php endforeach; ?>
            </table>
        </body>
    </html>
    Of course the only downside then, is that the limit/page links functions are tied to the mapper. That's not necessarily a bad thing because LIMIT is a standard SQL syntax that the database adapters should handle anyways, but it does create additional functions in your mapper that perhaps may not necessarily fit.

  25. #25
    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 snajt View Post
    i found cuberoot's solution the best
    cuberoot's solution, huh?

    Quote Originally Posted by snajt View Post
    I have no idea about how good this is from an OO point of view, so please, feel free to comment my solution. And if this post got to long, please tell me and i put it on rafb of something.
    I think it looks good. I'm not a big fan of abstract classes, but to each his own.

    Quote Originally Posted by czaries
    The only problem I really see with your solution, as I pointed out before, is that $offest and $limit would then have to be included and integrated with every finder function on both the Mapper and the Pager class. That's a lot of extra work. I'm looking for a more automated solution that will work universally with any finder function I choose to use.
    I realize the offset/limit stuff kind of clutters up what may have been a clean method on the mapper, but the capability has to go somewhere. I don't know yet what I like the best (or dislike the least, rather), but you can either pass them in every finder method, make a new method on the mapper like you did, or just grab the whole table and limit in php as cuberoot proposed. They all have their pros and cons.


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
  •