2

I am trying to make a class in php to build a house in my webbapplication. I don't know if I am using classes and objects in the most effective way, am I? I am new to oop...

This is the request from jquery, that the user wants to build a house:

// Add house
$.get('stats.php?house=cottage', function(data){
    // if(data == 1) // Build a house
});

This is the stats.php file,

require_once('House.php');
// Requests to see if the requirements to build a new building is met, if so, return 1, else return 0.
if(isset($_GET['house'])) {
    // Check with database to se if there is enough resources.
    $house = new House;
    $house->type = $_GET['house'];
    if($house->isResources) {
        $house->buildHouse;
        echo 1; // This is the answer to the ajax request.
    } else {
        echo 0;
    }
}

And here is my class file:

<?php
class Build {

    public $type;

    function isResources() {
        // Check resourses in database, compare that to the number of houses already built, and level.
        // return true; // If requirements are met, otherwise return false.
    }

    function buildHouse() {
        // Insert coordinates and $type into databse.
    }
}
?>

I have not done any code inside the class besides the one above, I just wonder if this seems to be the best way to create the class. Before I go any further with the coding... Thanks!

rablentain
  • 6,641
  • 13
  • 50
  • 91
  • Well, other than the fact that you're not really creating an instance of the `House` class. It looks ok to me. You'll want to instantiate the House class with parentheses like this: `$h = new House()`. – Lix Sep 21 '13 at 11:01
  • 2
    it really depends on your needs but using an existing framework example Yii or CodeIgniter will teach you the good practices in PHP OOP – Netorica Sep 21 '13 at 11:01
  • 4
    Neither Yii nor CodeIgniter teaches good OOP practices. Whether it is the "statics everywhere" approach or very high cyclomatic complexity it's just not very good. – PeeHaa Sep 21 '13 at 12:19
  • You need to call your `buildHouse` function like `$house->buildHouse()`. Also, rather than returning `1` or `0` I suggest returning a real JSON response. Maybe something like `json_encode($house)`, or if you really just want true/false, you could return `true` or `false`, or simply `json_encode(true)` (which is the same thing). – Brad Dec 09 '13 at 18:01

4 Answers4

7

There are a couple of issues I see with the limited code you have provided.

Your House class has a public member type which means that at any point in the lifetime of the objects (instances of Houses) you can change the type. This makes your code not only hard to test, but also hard to maintain. Because the value of type cannot really be trusted (because it can be changed at any point). So the first thing I would do is make that property private. And set the property using the constructor of the class.

The second thing I notice is the isResources method which apparently does something with the database. But I don't see any database connection getting passed in. Neither in the constructor nor in the method. This is very suspicious, because that means the database connection is accessed by either:

  • creating a new connection inside the method
  • using some form of a global inside your method

Both have issue:

creating a new connection inside the method

This would mean you are tightly coupling the database connection to the House class with no easy (and sane) way to unit test your House. Because there is no way to swap the database connection with some other connection. Or even some totally other form of storage. Or perhaps some mocked storage.

Also this method would mean that you are going to have a lot of database connections throughout your application, because you are going to create a new connection in every class / method that needs it.

Also there is no way you can see by looking at the method signature you are actually using a database connection in there. This is called a hidden dependency and should be avoided whenever possible.

using some global inside your method

This has for most points raised the exact same problems as the above approach. Globals and global state should be avoided at all cost. Whether you are using the global keyword directly, access the $_GLOBALS array or whether you are using the singleton pattern. All have the same issues in regards to maintenance and testability.

I have already written down the reasons, drawbacks and solution for this in another post some while ago: Use global variables in a class

Another thing I notice in the isResources method is that based on the comment it checks for available resources. Now lets take that example into the real life. When you are going to build a house in real life do you really ask (or check) the house itself to see whether there are enough resources to build the house? No you don't. This is a violation of the Single Responsibility Principle and just doesn't make much sense (asking the house whether there are resources to build the house).

I see your class also has a buildHouse method, which is also something strange. Objects are constructed (build) using the constructor. There is no reason for that method to be there. You should pass all the information (elements of the house) into the constructor.

With the information I provided above (there is probably lots more I could tell you) you end up with something like the following:

<?php
class Factory
{
    private $resources;

    public function __construct(Resources $resources)
    {
        $this->resources = $resources;
    }

    public function build($type, array $coordinates)
    {
        if (!$this->resources->areAvailable()) {
            throw new \UnavailableResourcesException('Not enough resources to build the house');
        }

        return new House($type, array $coordinates);
    }
}

class Resources
{
    private $dbConnection;

    public function __construct(\PDO $dbConnection)
    {
        $this->dbConnection = $dbConnection;
    }

    public function areAvailable()
    {
        // check database for resources
        return true;
    }
}

class House
{
    private $type;

    private $coordinates;

    public function __construct($type, array $coordinates)
    {
        $this->type        = $type;
        $this->coordinates = $coordinates;
    }
}

$dbConnection = new PDO('mysql:dbname=yourdatabase;host=127.0.0.1;charset=utf8', 'user', 'pass');
$dbConnection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
$dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$resources = new Resources($dbConnection);
$factory = new Factory($resources);

$myHouse = $factory->build('tipi', array(22, 13));

Note there still can be enough improved in my example code above, but this is just to give you an idea to get started.

Also note that the advice given to you by fellow Stack Overflowers about looking into Yii, Cake or CI is terrible imho. Because those frameworks really do not teach good OOP practices at all. Yii for example is full of static method which basically means your application is full of global state. Cake isn't OOP by any definition of it. Also note that (again imho) Yii, CI and Cake are the three worst popular framework out there.

Community
  • 1
  • 1
PeeHaa
  • 71,436
  • 58
  • 190
  • 262
  • I like this answer! There is a lot of information to understand, there is many improvements of my code here. Thanks! What does the \PDO do? – rablentain Sep 21 '13 at 22:35
  • `\PDO` is just an example of initializing a database connection: http://www.php.net/manual/en/pdo.construct.php – PeeHaa Sep 21 '13 at 22:44
  • Should I write \PDO or is that just like a comment? I know PDO but i do not recognize the \PDO thing. And one question more, should I have separate files for each class? – rablentain Sep 21 '13 at 22:52
  • `\PDO` is to tell PHP to get the class from the global [namespace](http://php.net/manual/en/language.namespaces.php). It is considered best practice to put all classes in a separate file. – PeeHaa Sep 21 '13 at 22:57
  • How do I keep track of all those class files? Should I just create a bunch of request-calls in one php file or is there a better way? – rablentain Sep 21 '13 at 23:30
  • Normally you would use autoloading for this. – PeeHaa Sep 22 '13 at 11:32
  • Ok, I will look into that! What about this line: $this->resources->areAvailable(), $this refers to the created object and resources to the private $resourses variable. I do not understand the last arrow, is there supposed to be a areAvaliable() method inside $resources? – rablentain Sep 22 '13 at 14:44
  • There is an `areAvailable()` method in the `Resources` class – PeeHaa Sep 22 '13 at 14:47
5

[EDIT] I just found this answer on design patterns for web applications. It's not on PHP, but it still applies, and has instantly become one of my all time SO favorites.

[EDIT 2] This article is also quite good describing exemplifying different common OO patterns you may want to use in PHP: http://www.phptherightway.com/pages/Design-Patterns.html

[EDIT 3] Added number 1, which I believe now is the most important thing about OOP.

IMHO the best way to learn OO design is by using a pure OOP language, like Java. Otherwise you end up with spaghetti and meatballs.

That being said, I also suggest you take a look at other OO projects in PHP to get an idea. I would tell you if you start working on popular projects like Wordpress or Drupal not to get caught in their patterns because they are a mess in terms of OO design, but there are many projects out there using PHP with classes AND proper OO design patterns; just take one you are interested in, examine its code and work with it before you start writing your own.

I did those two things and ended up with this (my first OO project in PHP), which may not be the perfect example (I've learned a lot from its many design flaws so I know it can be improved), but I believe it turned out not so bad.

Getting on specifics, as noted on the comments your class should be named House, not Build, so you instantiate (create an object or build) a new House(), and there are also some other things you are doing wrong, not necessary related to OO design, but important:

  1. Use interfaces: That's what OOP is all about: House implements Building, maybe also Residence. Think twice before writing abstract classes (will you keep overriding the implemented methods? Write an interface instead) and try to only extend classes you cannot instantiate (You can go in as deep as you want but you don't want like 5 different generations of objects being instantiated in your app with who knows which methods overridden. I've seen overriden method on children calling method on parent which at some point calls method on grandparent which in turn at some point calls overriden method on children. That's a mess and even you won't be able to understand it fully with a quick overview after a while. Even 2 or 3 generations is a mess if it happens with too many classes).

  2. Don't assign $_GET parameters to object properties without filtering them first. That's messy and unsafe! Check out the following resources for more info:

  3. Use constructors: When you create a new house, don't you want to specify directly what type it is? So: $house = new House($type), and inside House:

    function __construct($type){ $this->type = $type; }

  4. use namespaces and class autoloaders. This simple piece of code and some simple PSR naming conventions can save you from having any require or include or require_once in your code:

    namespace myspace; set_include_path('..'.PATH_SEPARATOR.get_include_path()); spl_autoload_extensions(".php"); spl_autoload_register();

    Check this for more info: https://wiki.php.net/rfc/splclassloader

  5. Your server side code should have only one entry point: a call to a main (static) method of a main class. The rest should all be calls to static methods or class instances (objects), all coming from code inside a class definition. If you are using Apache as web server, having rewrite module enabled and being able to manipulate .htaccess can help you have clean urls such as http://myserver/mypage instead of http://myserver/main.php?function=mypage. This is not written in stone, and you need to be careful to black box (encapsulate) all components and avoid behavioral mangling between them. The more interdependent the different parts of your project are, the more you have to struggle to let them grow their own proper way (avoid global state). DRY says you have to share code, but you must also be able to know the difference between a small piece of shared code which is fixed there because it fits a proper design where the two flows meet and an entangled knot tying two different flows that should be developed separately: >< vs ||, to put it graphically.

  6. Related to 4 and also not written in stone, but really useful: Use dependency injection, so you can test each class independently with fake input. Red-green-refactor is one of the best programming mantras to apply to a web app. If you can't at least think of a quick stupid minimal test for a typical case of use (not a full complete battery; no need to go full NASA until you want to deliver a stable release..) for whatever you are about to do, you are probably doing it wrong. Since it's quick, go on and implement it before you start. (Believe me on this: It will save you loads of time later on, almost every time.)

  7. Mind the documentation. Your code describes itself, no need to comment every line. Documentation shows other people how to use your code, so you must focus on a clear and brief explanation on what each public method does, its input parameters and its output. Be specially clear describing how you handle special cases, like errors and limits.

That's all I can think of right now. I would also recomend reading php.net tutorial on classes for more help: http://php.net/manual/en/language.oop5.php, and the collection of resources found in this other article: http://www.ibm.com/developerworks/library/os-php-7oohabits/#resources

Community
  • 1
  • 1
NotGaeL
  • 8,344
  • 5
  • 40
  • 70
  • 1
    Are you sure you want to suggest looking at Wordpress code for OO idea? *_*. I would not do that. Drupal is fine. I would suggest Symfony or something along the line though it may be too complicated for beginners. – mr1031011 Sep 21 '13 at 11:59
  • I'm not suggesting it, in fact I'm advising against it after my personal experiences :-) You're right about Symfony being a very good example, but perhaps there is something simpler to recommend. I was thinking of Ratchet: I'm working with it right now and I like very much its design, but it's a websockets framework, not a web app or web app framework. I haven't work with any simple and clean fully OOP web app code (I don't have that much experience with OOP in PHP), so I honestly don't know what to recommend. – NotGaeL Sep 21 '13 at 12:07
  • 1
    Ratchet is not had. Symfony also has a much simpler framework named http://silex.sensiolabs.org/ which may help the op. – mr1031011 Sep 21 '13 at 13:27
  • So you suggest that the whole php-code-structure should be called like a tree, how should I send my AJAX requests through the main-class? – rablentain Sep 22 '13 at 08:53
  • Yes, I suggest you think of your web application as ONE program instead of a collection of scripts. But try to keep your modules independent encapsulating their behaviour or you will end up changing the whole codebase just because you changed a few lines in a class. In this terms, the main method of the main class is just a class loader and the modules it loads don't really depend on it, they could be called by a totally different program and still work fine. – NotGaeL Sep 22 '13 at 10:02
  • So, if I first follow PeeHaas example above and then yours, do I have to create a main class which has methods for each Factory, Resources and House? – rablentain Sep 22 '13 at 14:15
  • No. What's the case of use? Do you want to build a house? Alright, then from main you instance or call a static method of a class that handles this case of use. It's about being tidy and structured, that's all. Do you also want to handle another case of use to build a neighborhood with different kinds of houses and community centers and all of that? Then you do the same, and add a call in main for this new case of use. That's how I do it, but before blind trusting me, check some references on OO design (e.g. this article is good, the resources it mentions even better: http://goo.gl/0Xl5V5) – NotGaeL Sep 22 '13 at 15:04
1

In the code of the class file you have given, the class name is Build. However in your stats.php file, you are trying to create a new class called House. Perhaps you should try to specify the correct class name, or change your class name to House.

class Build {
   ...
}
 require_once('House.php');
    // Requests to see if the requirements to build a new building is met, if so, return 1, else return 0.
    if(isset($_GET['house'])) {
        // Check with database to se if there is enough resources.
        $house = new Build;    // you have to make object of class  
        $house->type = $_GET['house'];
        if($house->isResources) {
            $house->buildHouse;
            echo 1; // This is the answer to the ajax request.
        } else {
            echo 0;
        }
    }
Lix
  • 47,311
  • 12
  • 103
  • 131
Janak Prajapati
  • 896
  • 1
  • 9
  • 36
  • How exactly does this answer the question? – Lix Sep 21 '13 at 11:04
  • here you have make class named build not house .. to code is not correct i think – Janak Prajapati Sep 21 '13 at 11:05
  • Oh. I see now. You'll want to add some more explanation in your answer. It is not clear what you are suggesting. – Lix Sep 21 '13 at 11:06
  • okey !! i am new to stackoverflow that's main problem i dnt know how to answer :( – Janak Prajapati Sep 21 '13 at 11:07
  • i think you are goin to make new application try yii or cakephp frameworks. specially yii it is mvc but it will help you make your application better so instead of wasting time to develop your own oops or mvc framework use existing. – Janak Prajapati Sep 21 '13 at 11:09
  • You should leave that comment on the actual question, @jan. That way the original poster (OP) will be able to see it. – Lix Sep 21 '13 at 11:10
0

Okie, since everyone already suggested so many good ways to learn OO on PHP, I will add just a short answer.

So far I haven't seen anyone suggested Symfony 2, in my opinion it's truly a great framework making use of latest PHP 5 features to build an OO framework (as much as PHP allows it to be). You can learn lots of useful practices, standards, and design patterns when you work with Symfony 2. However, I must warn that SF2 is really complicated for beginner, if you do have time opt for it, otherwise go for its mini framework which is called Silex.

Anyhow, your question was if your code looks right or not. Personally I think it may use a bit clean up. You should sanitize your input data, and you may want to send your ajax response with a correct header instead of just echoing like that.

mr1031011
  • 3,574
  • 5
  • 42
  • 59