SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 30
  1. #1
    SitePoint Enthusiast jitao's Avatar
    Join Date
    Feb 2003
    Location
    Shanghai
    Posts
    42
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    How to list object?

    Hello,
    A very basic question about OOP, but make me headache for weeks.
    Let me show you by this example:

    A small web app managing personal books. I stored information of all my book in a database. I can add, modify and delete the book's information and search and browse the books by key words or category.

    Now I wan to code this program in OOP. Then I find here are serveral objects:
    • book
    • reader
    • books category
    and I think I understand what a class is so I just write the first class "book" like below:
    PHP Code:
    class book
    {
    var 
    title;
    var 
    author;
    var 
    ISBN;
    var 
    date;

    function 
    getTitle ()
    function 
    setISBN()
    ...

    looks fine? But a big problem comes, where should I put the function like listAllBooks() searchBookByKeywords();

    First, I want to put those functions into class "book" but that seems very illogical, each instance of class "book" is a single book. How can it have the capability to list all other books?

    Then, I think I should put those functions into "reader" class, since they really do the list and search action. But if I think about it this way, I find that I can move all the methods from "book" to "reader"
    PHP Code:
    class reader
    {
    //member variables;
    ...

    function 
    setBookTitle ($id,$title)
    {
    // update the database
    }

    function 
    listAllBooks()
    {
    // select all books from database;
    }

    because that is the "reader" who actually do those actions, and then the "book" class becomes a data container just like an array.
    Is this normal in OOP ?
    I know this question sounds stupid, but it is a really headache for me.

    Need your comments and help.
    Ji Tao

  2. #2
    SitePoint Addict Avido's Avatar
    Join Date
    Jul 2003
    Location
    Kortrijk, Belgium, Europe, the world
    Posts
    203
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Maybe a stupid answer(It has been a long time since i read something about oop) but why not create an extra class "books".

  3. #3
    SitePoint Enthusiast jitao's Avatar
    Join Date
    Feb 2003
    Location
    Shanghai
    Posts
    42
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Question

    Quote Originally Posted by Avido
    Maybe a stupid answer(It has been a long time since i read something about oop) but why not create an extra class "books".
    After viewing many lines of java code, I find there are 2 ways to solve this problem.
    1. Add the listAllBook function in the "books" class and marked it "static".
    2. As Avido said using another class like "books" or "myLibrary" which will has the list and search functions.
    Which one do you think is better? and Why? This is a very basic problem for many OOP web applications, how do you solve this?


    Thank you,
    Ji Tao

  4. #4
    Non-Member coo_t2's Avatar
    Join Date
    Feb 2003
    Location
    Dog Street
    Posts
    1,819
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    I had a similar question a few months back.

    Here's some threads that deal with similar situations.

    http://www.sitepointforums.com/showt...&highlight=dao

    http://www.sitepointforums.com/showt...&highlight=dao

    You may find the "keeping queries out of classes" thread
    especially helpful.

    One solution, as pointed out in those threads, is a DAO.
    Codezilla uses a DAO and returns an array of bidder objects.
    You could probably take a similar approach with your book objects.

    If you need an intro to DAOs, go here:
    http://phppatterns.com/index.php/art...leview/25/1/1/

    --ed

  5. #5
    Non-Member coo_t2's Avatar
    Join Date
    Feb 2003
    Location
    Dog Street
    Posts
    1,819
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    By the way, it looks like what you want to do with your "reader" class is more or less a DAO.

    Here's a _very_ basic example of what listAllBooks() could look like.
    I'm gonna change "list" to "get", and you can figure out the best way to print the results.


    PHP Code:
    <?php

    class BookDao {

        function 
    getAllBooks()
        {
            
    $result mysql_query('SELECT * FROM books');
            while (
    $row mysql_fetch_assoc($result))
            {   
    #use whatever fields you need in the constructor
                
    $bookObjsArr[] =& new Book($row['field1'], $row['field2'], etc);
            }

            return 
    $bookObjsArr;
        }
    }


    $bookDao =& new BookDao(constructor args);
    $bookObjArr $bookDao->getAllBooks();
    foreach (
    $bookObjArr as $bookObj)
    {   
    # do something with $bookObj;
    }

    ?>
    --ed

  6. #6
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by jitao
    But a big problem comes, where should I put the function like listAllBooks() searchBookByKeywords();
    How about a Librarian class?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  7. #7
    SitePoint Enthusiast Remy's Avatar
    Join Date
    Oct 2002
    Location
    Amsterdam
    Posts
    47
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I am also struggeling with the same question for quite some time.

    coo_t2, if you create a array of objects of all books and there are about 4000 of them, you would have to query the database for 4001 times to get a list of books. How you solve(d) this?

    Lascraft,
    A librarianClass looks like usefull solution, but should the addBook() and deleteBook() be in here (I think it should) or should this stay in the bookClass?

    - Rémy

  8. #8
    SitePoint Enthusiast jitao's Avatar
    Join Date
    Feb 2003
    Location
    Shanghai
    Posts
    42
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks for all your comments and the constructive ideas. i think they help me get better understanding of OOP.

    I think whether an object should include a method depends on what kind of data or variable this method process. A method of an object should only work on their member variables (right?).

    Therefore, "book" can change the title, the ISBN, or the author name or price of its own. but it has no ideas about other books in the library, so it should never have function like listAllBook().

    Librarian "class" is a good idea. Since he knows every book in the library, he can have the listAllBook() function, also addBook() deleteBook() functions.

    Also I think the addBook() and deleteBook() will take a "book" object as their parameter (right?). So ideally if I want to read a book I will give "librarian" a book's ISBN (if I know). And he will return me a "book" object, but knowing nothing about the content or price of that book.

    "book" can make changes on its own attributes. But maintaining an index of the library (list, add, delete) should be the work of "librarian".

    Just my understanding want to hear more from you.

  9. #9
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    That about looks right enough though what has been puzzling me is doesn't a Library have different categories for their books ?

    You are asking/referring to a listAllBooks() though should this be categorised as well no ? Ie You've demonstrated a few methods of by which books would be listed though are you also missing out catogorisation I wonder ?

    Maybe it's just me being picky huh ?

  10. #10
    SitePoint Zealot Sork's Avatar
    Join Date
    Jul 2002
    Location
    Portugal
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Remy
    coo_t2, if you create a array of objects of all books and there are about 4000 of them, you would have to query the database for 4001 times to get a list of books. How you solve(d) this?
    This might be a bit off topic.

    I never liked to load daos results, even if only 50 or 100.
    So what i actually do is:
    PHP Code:
    ...
    function &
    loadAll()
    {
       
    //statement here
       
    return new ObjectLoaderIterator(new Book(), new ResultSetIterator($rs));
    }
    ... 
    The ObjectLoaderIterator simply fills the models data with setters and getters from an object or array, if the iterator::next() an object or an array respectively.

    Off course it depends on what behave our models have, i think.

  11. #11
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Not a bad idea actually

    What I have at the moment takes 2 seperate peices of script, whereas as I see it, you do it with just the one ?

    But as you say, depends on how you implement the above I suppose as to how someone else can use your suggestion aye ?


  12. #12
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by jitao
    I think whether an object should include a method depends on what kind of data or variable this method process. A method of an object should only work on their member variables (right?).
    There is something called the "Law of Demeter". It is difficult to always follow it to the letter, but it goes something like...
    1) Only access your own data.
    2) Only access public methods of objects you take as a parameter.
    3) Only access public methods of objects you create.
    4) That's it.

    Where it gets tricky is with objects inside objects. e.g...
    PHP Code:
    function &findBestPhpBook() {
        
    $librarian = &new Librarian();
        
    $iterator = &$librarian->fetchAllBooksBySubject('PHP');
        if (!(
    $top = &$iterator->next())) {
            return 
    false;
        }
        while (
    $book = &$iterator->next()) {
            if (
    $top->getRating() < $book->getRating()) {
                
    $top = &$book;
            }
        }
        return 
    $top;

    By the law of Demeter we should not be able to access the methods of the object returned by $iterator->next() as that is one access too far. We can refactor the function into two methods to get around this, but it is probable that the extra flexibility is not worth the loss of clarity caused by lots of small methods in this case.

    Quote Originally Posted by jitao
    Therefore, "book" can change the title, the ISBN, or the author name or price of its own. but it has no ideas about other books in the library, so it should never have function like listAllBook().

    Librarian "class" is a good idea. Since he knows every book in the library, he can have the listAllBook() function, also addBook() deleteBook() functions.
    Spot on.

    Quote Originally Posted by jitao
    And he will return me a "book" object, but knowing nothing about the content or price of that book.
    Spot on again.

    Quote Originally Posted by jitao
    Just my understanding want to hear more from you.
    You have got it exactly right. You try everything you can to avoid knowing things about other objects. They are fickle, apt to change without warning and cannot be trusted.

    In your example the Book class should know how to update itself. In order to accomplish this it may need to ask the librarian for help, setting up a conversation between the two. I find it very hard to get that conversation right just by design and usually have to feel my way. To me this part is a knotty issue that I still have not fully resolved.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  13. #13
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I find it very hard to get that conversation right just by design and usually have to feel my way. To me this part is a knotty issue that I still have not fully resolved.
    I have to agree with you on this; Time this is very difficult to design for and you've basically got to jump in with both feet;

    Just to get a feel for what works and what doesn't I suppose ? Takes a few re-writes to get things as they should be as I see it myself.

  14. #14
    SitePoint Zealot Sork's Avatar
    Join Date
    Jul 2002
    Location
    Portugal
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Dr Livingston
    What I have at the moment takes 2 seperate peices of script, whereas as I see it, you do it with just the one ?
    ??

    But as you say, depends on how you implement the above I suppose as to how someone else can use your suggestion aye ?
    When I say depends its just because this Loader is very basic.
    Suppose we have a list of authors and each of them a list of books
    PHP Code:
    $authors =& $dao->loadAll();

    while (
    $author =& $authors->next())
    {
        echo 
    '<p>'$author->getName(), '</p>';
        
    $books =& $author->getBooks();
        while (
    $book =& $books->next())
        {
             echo 
    $book->getTitle(), '<br />';
        }

    well this loader only will load the Author class.

    edit
    why not pickup the Eclipse's RowLoopManipulator or doing something based on that so we can put some custom behavior like $author->setBooks(new ObjectLoaderIterator(...)).

  15. #15
    Non-Member coo_t2's Avatar
    Join Date
    Feb 2003
    Location
    Dog Street
    Posts
    1,819
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Remy
    coo_t2, if you create a array of objects of all books and there are about 4000 of them, you would have to query the database for 4001 times to get a list of books. How you solve(d) this?
    You're right. That example was sort of off the top of my head, and I'm
    still pretty new at using DAOs and patterns in general. I think Codezilla's code probably takes this problem into consideration.

    It's also good that Sork posted a response to it. I'll have to study that code as well.

    --ed

  16. #16
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What I have at the moment takes 2 seperate peices of script, whereas as I see it, you do it with just the one ?
    Whereas you have BOOK and the Iterator from one class instance I do not ?

    I have the MODEL create an array of objects which then I've got to go and iterate through the array... ?

    I'll have a look at Vincents' class library again although I don't have the time just now

    I'll keep an eye on this thread though since folks have some ideas...

  17. #17
    Non-Member coo_t2's Avatar
    Join Date
    Feb 2003
    Location
    Dog Street
    Posts
    1,819
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Dr Livingston

    I'll have a look at Vincents' class library again although I don't have the time just now
    ...
    I'll have another look too. Last time I looked at RowLoopManipulator(which was several months ago) I didn't quite get it. Maybe I've learned enough OOP
    by now that I'll understand what the hell's going on.

    --ed

  18. #18
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ...what the hell's going on.
    Me Too

  19. #19
    SitePoint Enthusiast Remy's Avatar
    Join Date
    Oct 2002
    Location
    Amsterdam
    Posts
    47
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Sork
    The ObjectLoaderIterator simply fills the models data with setters and getters from an object or array, if the iterator::next() an object or an array respectively.
    Thanks for this insight.

    I use a QueryIterator(resultsetIterator) to show a list of Books but I am not happy with it. I have to use $book['title'] but I want to use the BookObject to show the title ($book->getTitle()), but never could find a good solution for it. You give me a new way to look at it .

    Could you show how you link the BookClass with the Resultsetiterator? The only way I can think of is to use a second param for the BookConstructor. The second param would be the current row of a QueryIterator(ResultsetIterator). If the second param is blank the bookObject should query the database. Something like this:
    PHP Code:
    Class Book {
      function 
    Book($id$row=NULL) {
        if (!
    is_null($row)) {
          
    $this->$title $row['title'];
        } else {
          
    $rs $db->query("SELECT * FROM books WHERE id=$id");
          
    $row $rs->getCurrentRow();
          
    $this->title $row['title'];
        }
      }
      
    //....rest of the class

    Is this a good way or am I not getting it?

    - Rémy

  20. #20
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    The constructor for our current persistence library looks like this...
    PHP Code:
    class Persistent extends Record {
        function 
    Persistent($query$row) { ... }
        ...

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  21. #21
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ...

    PHP Code:
    Class Book {
      function 
    Book($id$row=NULL) {
        if (!
    is_null($row)) {
          
    $this->$title $row['title'];
        } else {
          
    $rs $db->query("SELECT * FROM books WHERE id=$id);
          
    $row $rs->getCurrentRow();
          
    $this->title $row['title'];
        }
      }
      
    //....rest of the class

    Not sure that is the best way; I'm going to see what I can think off to clear it up myself ?

    Looking at this just now...

    http://www.sitepointforums.com/showt...hreadid=111744

    Post Number 30 By Codezilla...

  22. #22
    SitePoint Enthusiast jitao's Avatar
    Join Date
    Feb 2003
    Location
    Shanghai
    Posts
    42
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This thread become really interesting. I learn much more than I expected, but here comes a new question about DAO.

    If using DAO, I find myself easily falling back to procedural coding. I pack all kinds of database access function into several DAO classes and just call them in my page and I don't need the Book or Libarian class anymore. That doesn't look like an OOP way.

    Do you feel it?

  23. #23
    SitePoint Enthusiast jitao's Avatar
    Join Date
    Feb 2003
    Location
    Shanghai
    Posts
    42
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Sork
    This might be a bit off topic.

    I never liked to load daos results, even if only 50 or 100.
    So what i actually do is:
    PHP Code:
    ...
    function &
    loadAll()
    {
    //statement here
    return new ObjectLoaderIterator(new Book(), new ResultSetIterator($rs));
    }
    ... 
    The ObjectLoaderIterator simply fills the models data with setters and getters from an object or array, if the iterator::next() an object or an array respectively.

    Off course it depends on what behave our models have, i think.
    I think that is not the "ObjectLoaderIterator" but a "Loop" and a "LoopManipulator" see my code below and want to hear more from you.
    PHP Code:

    class bookLoader extends LoopManipulator 
    {

    //array of book object $books
    var $books;

    function 
    booksLoader(&$booksObjArray
    {
    $this->books = &$booksObjArray;
    }

    function 
    current(&$row$index)
    {
    $aBook = new Book($row['id']);
    $aBook->setTitle($row['title']);
    $aBook->setContent($row['content']);
    $aBook->setAuthor($row['author']);
    array_push($this->books,$aBook);
    }

    }
    $bookObjArr=array();
    $result $database->query('SELECT * FROM book');
    Loop::run( new QueryIterator($result), new booksLoader($bookObjArr); 

  24. #24
    SitePoint Zealot Sork's Avatar
    Join Date
    Jul 2002
    Location
    Portugal
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by jitao
    I think that is not the "ObjectLoaderIterator" but a "Loop" and a "LoopManipulator" see my code below and want to hear more from you.
    Well, this code is what i was trying to avoid
    PHP Code:
    $bookObjArr=array();
    $result $database->query('SELECT * FROM book');
    Loop::run( new QueryIterator($result), new booksLoader($bookObjArr); 
    it's the same of this
    PHP Code:
    $bookObjArr = array();
    $book =& new Book()
    ...
    $bookObjArr[] =& $book
    The point of the "ObjectLoaderIterator" that i showed above is that, like the use of mysql_fetch_assoc, we dont load all the objects into an array and then loop them.

    Here's the ObjectLoaderIterator class
    PHP Code:
    /** */
    class ObjectLoaderIterator /*implements Iterator*/
    {
        
    // DATA MEMERS

        /** */
        
    var $object;

        
    /** */
        
    var $iterator;

        
    // CREATORS

        /** */
        
    function ObjectLoaderIterator(/*Object*/&$object/*Iterator*/&$iterator)
        {
            
    $this->object =& $object;
            
    $this->iterator =& $iterator;
        }

        
    // ACCESSORS

        /** */
        
    function &next()
        {
            
    $next =& $this->iterator->next();
            
    $object $this->object->clone();
            if (
    is_object($next))
            {
                
    $methods get_class_methods($next);
                foreach (
    $methods as $method)
                {
                    if (
    stristr($method'get'))
                    {
                        
    $method2 str_replace('get''set'str_replace('_'''$method));
                        if (
    method_exists($object$method2))
                            
    $object->$method2($next->$method());
                    }
                }
            }
            else if (
    is_array($next))
            {
                foreach (
    $next as $key => $val)
                {
                    
    $method 'set' str_replace('_'''$key);
                    if (
    method_exists($object$method))
                    {
                        
    $object->$method($next[$key]);
                    }
                }
            }
            return (
    $next) ? $object false;
        }

        
    /** */
        
    function &previous()
        {
            return 
    $this->iterator->previous();
        }

        
    // MANIPULATORS

        /** */
        
    function reset()
        {
            
    $this->iterator->reset();
        }

        
    /** */
        
    function end()
        {
            
    $this->iterator->end();
        }

        
    /** */
        
    function seek($pos)
        {
            
    $this->iterator->seek($pos);
        }
    }

    //===============

    /** */
    class BookDAO
    {
        
    /** */
        
    function &loadAll()
        {
            
    $stmt =& $this->conn->createStatement();
            
    $rs =& $stmt->executeQuery('SELECT * FROM books');
            return new 
    ObjectLoaderIterator(new Book(), new ResultSetIterator($rs));
        }
    }

    //===============

    $dao =& new BookDAO($dsn);
    $books =& $dao->loadAll();

    while (
    $book =& $books->next())
    {
        echo 
    '<p>'$book->getTitle(), '<br />';
        echo 
    $book->getAuthorName(), '</p>';

    Like i said before it's very basic in the way we might want some behavior concerning queries that return something like this
    Code:
    +--------------------+
    |  authors  | book   |
    +-----------+--------+
    +  author_1 | book_1 |
    +-----------+--------+
    +  author_1 | book_2 |
    +-----------+--------+
    +  author_2 | book_3 |
    +-----------+--------+
    +  author_2 | book_4 |
    +-----------+--------+
    +  author_2 | book_5 |
    +-----------+--------+
    +  author_3 | book_6 |
    +-----------+--------+
    +  author_3 | book_7 |
    +-----------+--------+
    But perhaps using the RowLoopManipulator width watchers we can acomplish that, must think about it and try some code

    Remy, guess i answered yor question here

  25. #25
    Non-Member coo_t2's Avatar
    Join Date
    Feb 2003
    Location
    Dog Street
    Posts
    1,819
    Mentioned
    1 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Sork
    Here's the ObjectLoaderIterator class
    I was hoping you'd post that.

    --ed


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
  •