1

I'm trying to learn PHP OOP, but when I've followed tutorials, all the examples seem to involve things like:

class Human {
    $_sex;

    public function setSex($sex) {
        $this->_sex = $sex;
    }

    public function getSex() {
        return $this->sex;
    }
}

(Pointless example, but hopefully you get what I mean -- An object has properties that are stored in the object.)

I've never come across a web application where I've needed to create objects about humans, dogs, cars, or any of the other weird examples you find when reading tutorials about OOP...

So I'm trying to find a similarly simple, but more realistic, project with which to learn OOP. I've chosen a website for an internet comic (e.g. XKCD.com). (This would allow people to view the comic, but also the author to edit and update it.)

So how would this translate to real-world PHP OOP?

My initial instinct would be to break the objects down thusly:

  • Catalogue
  • Episode

Where the Catalogue class would contain methods pertaining to all the episodes of the comic, e.g. getMostRecentEpisodeID(), deleteEpisode($episodeID), addNewEpisode($newEpisode), etc. etc.

And Episode would contain methods pertaining to the individual episode, e.g. getEpisodeComments($episodeID) (if the website allowed people to leave comments on individual episodes), editEpisode($episodeID), getEpisode($episodeID), etc.

Is this right, or have I made an absolute hash of what OOP is for?

Edit: Rather than just giving me a list of examples, it would really be helpful if your answer made specific reference to the problem I'm trying to solve.

Thanks.

4
  • 3
    You'll find a lot of real world examples on our sister site Code Review. This search would be a good place to start. If you build your project, you should post the full code there for review. – yannis Feb 20 '13 at 17:58
  • @YannisRizos Thanks! Would Code Review be able to tell me if my understanding is correct? – Django Reinhardt Feb 20 '13 at 18:41
  • 1
    Code Review is strictly for reviews of working code, so don't post there unless your code is actually working. Questions on design choices are more suitable for Programmers in general. This one has a few problems, we generally frown upon questions asking for lists of things, so we might need to rephrase the request for real world examples a bit. I'll take a closer look when I'm off work. – yannis Feb 20 '13 at 18:45
  • I don't know PHP but I could give you that in Java. – Tulains Córdova Feb 20 '13 at 20:29
4

The problem with Object Oriented Programming is exactly that, the shape and vehicle examples. What I'm going to type below is not the actual truth, it is an opinion. An opinion shared by more people than just me though :)

Object oriented vs "Class oriented"

Your example where there would be a catalog and an episode is perfectly well. The problem is the functions you propose these Objects should get. That is where you go from Object Oriented Programming to Class Oriented Programming. Just fitting a lot of methods concerning the same concept in an Object doesn't make programming object oriented. All you do is make some sort of container for methods.

The Episode Object should be just an Object that "models" the Episode. It has a title, content, author, that kind of stuff. In no way should this Episode have access to the database. It is not supposed to save itself or be able to load more instances of other episodes. The same goes for Catalog, it should not be able to retrieve its newest Episode itself. A catalog is a model, it has a title and some other stuff.

Splitting up responsibilities

Getting stuff from and saving stuff to a DataBase is not a models responsibility. So someone else has to do the job. A good start might be to create a CatalogRepository which could look like so:

CatalogRepository
{
    /**
     * @param int $id
     * @return Catalog
     */
    public function getCatalogById($id)
    {
        //Do a query, populate a new Catalog and return it. 
        //The constructor of Catalog should not be the one quering! 
        //It should receive data, nothing more
    }

    /**
     * @param Catalog $Catalog
     */    
    public function saveCatalog(Catalog $Catalog)
    {
        //stuff....
    }

    /**
     * @param string $name
     * @return Catalog[]
     */    
    public function searchCatalogsByName($name)
    {
        //do a full text search or something?
    }
}

The Episode would have something along the same lines where you could get all Episodes for a specific Catalog but also the five most popular Episodes:

class EpisodeRepository
{
    /**
     * @param Catalog $Catalog
     * @return Episode[]
     */
    public function getEpisodesByCatalog(Catalog $Catalog)
    {

    }

    /**
     * @param int $amount
     * @return Episode[]
     */
    public function getMostPopularEpisodes($amount)
    {

    }
}

There are some really good guidelines for writing good object oriented code, take a look at SOLID for instance

2
  • 1
    While the SOLID principles are excellent, I still advise people new to OOP to try to understand cohesion and coupling (which underly SOLID, or rather, SOLID answers the question "how do we achieve the desired levels of cohesion and coupling"). It took me a longer time than I like to admit to really understand those two, but it was a "lights on" moment when it finally came. Although I've linked the Wikipedia article here, I'd recommend reading several different viewpoints to try to grasp it intuitively. – Daniel B Feb 21 '13 at 11:39
  • @DanielB Years later I've since learned all about the four pillars of OOP and know the SOLID principles by heart, and looking back, this is actually excellent advice. – Django Reinhardt Jan 14 at 22:21
3

Is this right, or have I made an absolute hash of what OOP is for?

It's not right, but also not completely wrong.

The main point of OOP is that classes aren't just collections of methods, but also contain the data those methods operate on - you wouldn't call editEpisode($episodeID), but instead $episode->edit(). This has two advantages:

  • Less passing around of data, which leads to less cluttered function signatures and less potential for errors.
  • By making the data (fields) private, it's possible to control access to it and allow only those operations on it that make sense.

For a real-world useful example, look at mysqli and compare its procedural and its OOP API. The procedural one requires you to pass a link, statement or result handle to almost every method, while the OOP API doesn't need that because you call the methods on an object. Additionally, the OOP method names don't need the mysqli prefix.

It's not a big difference, but still leads to nicer code, and of course that's just the beginning.

1
  • ... not that mysqli was a good piece of OO design (me being a maintainer) a "true" OO interface might be much more advanced, but mysqli was created in the early days of PHP 5 development by C developers taking their first steps – johannes Feb 21 '13 at 0:30
0

You're right: 99% of the tutorials that you'll find online are too basic and don't make sense. My suggestion to anybody trying to learn object-oriented programming is: find some code that you've written in procedural programming and try to convert it into OOP.

Take all your variables and make them a properties of a class, find a suitable class name. Then take all your functions, if you have functions, and group them within a class.

If you wrote your code without using functions, first try to convert your code into a collection of functions. Find suitable function names: a function should define what a piece of code does. Once you have your functions, move them into a class.

I'll give you a simple example of a pagination code that I converted into a reusable pagination class. Find the full tutorial on Youtube, link to the source code is in the description box:

   class Paginator{
    
        /*the class need to know the current page to calculate the offset
           the offset tell my sql how many rows to skip */
       private $current_page;
       //the limit is the number of results to display per page
       private $limit;
       /*pagination links to display below the results with next 
       and previous button*/
       private $pagination_links;
       //the last page of your pagination links 
       private $last_page;

    
        public function __contruct($number_of_rows_found,$results_to_display_per_page){

          //the paginator uses the number_of_rows_found to determine the last page 
           $this->last_page = $number_of_rows_found/$results_to_display_per_page;
              //get the current page, set it to 1 by default
             $this->current_page = isset($_GET['page']) ? ($_GET['page'] : 1;
    
        }

       public function generate_pagination_links(){ 
              this method uses the last_page property to generate pagination links
               if the last page is 3, this will give you 3 pagination links
             for ($page = 1; $page <= $this->last_page; $page++) {
                //use
             }
       }
    public function get_offset_and_limit(){
         //this method calculates the offset based on the current page
          return "LIMIT $this->per_page OFFSET ".($this->page - 1) * $this->per_page;
     }

    }
}

Then to use the pagination class, you create a new instance of it, and pass the number of results you want to display per page, and then the number of results returned by your query as parameters.

The pagination class will generate pagination links for you and calculate the offset and limit. That's a good example of a php reusable class, you can use it in multiple projects without having to rewrite or change it.

     $paginator = New Paginator($rows_found,8);
        $pagination_links = $paginator->get_pagination_links();
    
        $offset_and_limit =  $paginator->get_offset_and_limit();
    
        //apend the limit and offset to your sql query
        $query = $query. ' '.$offset_and_limit;
    
        $connection = getdbconnection();
        $stmt = $connection->prepare($query);
        $stmt->execute();
        $movies = $stmt->fetchAll();

Not the answer you're looking for? Browse other questions tagged or ask your own question.