views:

391

answers:

5

Hi Guys,

Had a bit of spare time to learn some OOP in PHP this evening and hit a brick wall. I am creating a little book library app to keep track of my book collection and thought I would use OOP for practice.

I have a main php file called library.php that contains 2 main classes so far.

  1. class database (connects to mysql, database etc)
  2. class books (contains, title, author, genre info etc and methods to insert data into the db)

So, my questions:

Is it OK to use one file for all of my classes?

And the below code is the process page that will add the books to the db, Is this ok or am I going in the wrong direction?

Also, how do I go about knowing when to create new methods in a class or create an entire new class?

  <?php 
    include 'library.php';

    // Connect to Host & DB
    $database = new database();
    $database->mysqlConnect('localhost', 'root', '');
    $database->selectDB('books');

    // Retrieve Values
    $book = new books();
    $book->retrieveValues();
?>

thanks, Keith

+1  A: 

I think part of the idea of OO programming is that classes and logic is separated so it would probably be better to split each class up into a separate file. As for when to create a new method in a class or a new class, I think if the method you use has to do with the main operations of that specific class it should be added. Only make a new class when It performs routine or important functions, and keep the right logic in the right class.

teh_noob
Classes and logic are separated? What do you mean?
Ionuț G. Stan
+3  A: 

Sorry, I don't have time to address all your questions at the moment (I may come back to this tomorrow morning), but I can quickly answer the first one:

Is it OK to use one file for all of my classes?

It's technically "OK", as in the language will allow you to do it, but you shouldn't. Beyond the benefits of improving the organization of your code, it also lets you use a nice feature of PHP known as Autoloading Objects.

If you follow the example that page shows, you can define the __autoload function as follows:

function __autoload($class_name)
{
    require_once $class_name . '.php';
}

This makes it so that you don't have to explicity include all the files with your class definitions. Instead, as long as you put them in files named the same as the class name, autoloading will automatically include files as they are needed. That is, in your code you show that you have a class named "books". If you save this class's definition to a file named "books.php", when you have a line of code like:

$book = new books();

PHP will automatically include books.php at that point. This is very handy, and is definitely good motivation to keep each class in its own file.

Chad Birch
I've never found __autoload to be readable. In fact it has been cause for confusion when I thought I had something autoloaded but I ended up not. Better to be explicit.
Nick Stinemates
thank you very much
Keith Donegan
+2  A: 

A class is a collection of functions and properties. If you need one specific action to be done, then you create a new function. If you need one type of data that can have a group of different actions performed on it, you create a new class.

And since I see you're using MySQL, you should check out prepared statements (http://us2.php.net/pdo.prepared-statements) to avoid insecure code.

PS: I would name the class "book" and not "books." Think about the way it is declared -- new books()? You're creating one new book, so new book() seems more appropriate.

Josh Leitzel
cheers,k appreciate your feedback
Keith Donegan
+7  A: 

in my opinion it's better to create a file for every class, where the file is named after the class. i use a slightly different notation than chad birch, namely a Classname.class.php. class autoloading is a nice feature, but i don't use it very often (only under certain circumstances, like MVC, where classes of the same type are added or removed often).

regarding object modelling: start creating classes for nouns. identify the actors in your application. a method of doing this is to write down what your application is supposed to do, then scan it for nouns and modelling your classes after those. Because a class represents a single object, class namens are singular most of the time. If there are multiple objects, you should name it ObjectCollection or something like this (e.g. "BookCollection").

Lets try it:

I want to create a little book library app to keep track of my book collection. It should track the books i own, the genre, the author(s) and the friends i lent them to. Additionally, for every friend the phone number should be stored, so i can call him. All the information is stored in a database.

  1. Book: represents a book ("The godfather", "Cryptonomicon")
  2. Library: represents a collection of Books
  3. Collection: already covered by Library
  4. Genre ("Sci-fi", "Thriller", ...)
  5. Author ("Neal Stephenson", "Mario Puzo")
  6. Friend ("Joe", "Jill", "Jack")
  7. PhoneNumber (an property of a friend)
  8. Database (duh)

then you build the hierarchy. which objects contain other objects, how does the relation between them look like?

  • Library contains 0, 1 or more Books
  • A Book belongs to exactly one Genre
  • A Book is written by 1 or more Authors
  • I can lend a book to 0 or 1 Friend and only to one Friend at a time
  • A Friend has a phone number

(this will also tell you a thing or two about how your database should look like)

not everything is obvious. do you really need a phone number class? it's only a string. in this case, you should make it a property. Additionally phone numbers are not really shared between Friend objects (it would make sense if several friends living together having the same number and it often changes, but for now that would be a bit overkill). if the noun can be represent by a scalar value (a string, integer, float) and there are no other values connected to them, it may be overkill to make classes.

so, over time you extend your application. you decided to store not only the phone number, but also the address. address and phone number are quite similar types of information, so you add other variables for street, city, post code and so on. at a certain point you'll get frustrated because the Friend class grew huge, with lots of properties, getters- and setters-methods, some of whom are prefixed to group them together (Friend->addressStreet, Friend>addressPostCode and so on). so you might decide to relocate those to an own class, called Address (if you leave the phone number to Friend) or Contact, just to keep the code clean (nothing wrong with that, but be careful).

what about class hierarchy and object hierarchy? don't confuse them, they are entirely different things! a class hierarchy arises if similar classes share some properties but are different in others. so you create one class with the properties and methods they share and child classes which inherit the properties and methods of the parent and also add their own to handle the differences.

in our library example, what could that be? to be honest, i can't think of anything besides one thing thats a bit nonsensical, but let's go for it ... Friends and Authors are both Persons! both have names, phone numbers and an address. but they also differ, because authors write books and friends don't (and authors normally don't borrow your books). so you might define the name and Address-object in the Person class, but "getUnreturnedBooks()" clearly belongs to Friend while "getBooksWritten()" belongs to Author.

it doesn't really make sense because you're never going to process Authors and Friends in the same run. but lets say you needed a list of all the adresses stored in your library, you could loop over your collection of Persons and call getAddress(). To be honest, even PHP is a bad example for this, because you can store whatever types you want together in an array and access them in every way you want, but in stronly typed languages that's not possible. in java you have to define a datatype for an array, and it's possible to store different types if they have a common inheritance (but you're restricted to the methods the original class had). enough of that class inheritance voodoo.

an object hierarchy simply states which objects should contain other objects. a library contains book objects. a book contains an author object.

some additional pointers:

  • try to be descriptive. don't use names like "Library->retrieveValues()", name it "Library->getBooks()" instead, because Book objects is what you get.

  • sometimes it's a good idea to name methods after the data type they're going to return. it's common if they return booleans. good naming conventions may be "has" or "is" as in "Friend->hasBook($book)" or "Book->isInLibraryCurrently()"

  • values that are always the same for all objects of a certain class may be class constants. lets say, if a Person can be male or female and you're storing that as "m" or "f" in the database, it makes sense define the class constants Person::GENDER_MALE = 'm' and Person::GENDER_FEMALE = 'f', so you don't have "m"s and "f"s scattered in your sql-queries all over the place.

  • Classes that only generate a single instance could be modelled as Singletons. a good example for this may be the Database class - it's highly unlikley you ever need to open more than one transaction at once. read more about singletons at wikipedia (http://en.wikipedia.org/wiki/Singleton_pattern), but it may be a bit early for that.

update: some people think Singletons are a bad idea, because they're more or less "carved in stone" once you implement them this way. so if you model your Database class as a singleton, but later decide you need more than one database connection (unlikley, but possible), you have a problem. if you take the risk, singletons may be convenient.

Schnalle
I'd argue against Singletons. "highly unlikely" means that is likely, so what do you do then? Singleton is an anti-pattern because it's a global symbol wandering in your source code that cannot easily be replaced when need comes.
Ionuț G. Stan
+1 for good detailed answer and takeing the time to step into the problem domain.
I've only used singletons in PHP for static variable registries - as a way of getting around using globals (and passing references as parameters).
Ross
Wow! - Schnalle, thank so much mate
Keith Donegan
+1  A: 

One other thing you may think about is using a database library that is already written. One of the best things about programming and particularly OOP is the ability to reuse pieces to make a different whole. Personally, I have used ADODB with excellent results. I agree with putting the classes in separate files, both for autoloading and for you to keep them separate in your mind. Would you mind posting a copy of your classes on here so we may get a better look at the inner-workings of them and not just the method names?

Daniel