I am new to PHP OOP (coded PHP in procedural style for some time now) and I think that I understood the SOLID principles and I understood that an object not necessarely is equal to an entity in a relational database. Anyway I have slight problems to apply this knowledge to this practical situation:
I want to write a class serving/handling product data which is attributes of products (like product name, weight, etc.) but als quantity on stock, open orders, next incoming shipment (with date and quantity being delivered) and so on.
This alone raises questions:
Does the plan to create one class to serve all this data interfere with the single responsibility principle (so is serving the quantity in stock another responsibility than serving the weight of the product)? In other words: Should I rather write an extra class for serving product attributes and another class to serve quantity in stock and another class to serve incoming shipments on this product? To explain: It can be, that external code accessing the object data of this class does not need all these information but just a part of it (e.g. either an attribute of the product or the next incoming shipments, but maybe the external code also needs both).
If I should write two different classes would they be totally seperate or should one extend the other? I personally do not see why one should extend the other as it can always be that only one of these two is needed, so why should I link them together then.
What I know from past experience is that the bottle neck of PHP applications very often is the number of queries you send to the database interface. If I divide everything in different classes, I have several database queries while I would only need two if I have it in one class.
Anyway if I write it in one class, I still have two queries while maybe the outside code needs only data that can be retrieved from one of these two queries. Should I put the two queries in two different methods and let the code outside of the class decide which methods should be executed and therefore decide which parts of the product data are available in the object? (This would be to save on resources and execution time.)
If I want to prevent that the outside code has to decide which methods to call to have the right data available should I better declare the properties to be private and have the magic __get function execute the necessary queries? I myself believe this is a dirty work around and I read somewhere that the magic __get function is rather a type of error handling if an outside code wants to access private properties.
Should I also add data manipulation methods to this class? I could add one for changing the static attributes of a product, but does it make sense to have a method in this class to change the quantity in orders? I think this would rather belong to an "orders" class than to a class of product data, right?
Here is some code snippet to illustriate, what I intend to do (in this case I wrote to serve all data by one class and let the outside code decide which method to call to have the right data available:
<?php
class productdata {
public $productID;
public $productname;
public $weight;
public $deliverytime;
public $deprecated;
public $openorderquantity;
public $stock;
public $opensupplierorders;
public function readproductdatafromDB() {
$db = new Db();
$safe_productID = $db->quote($this->productID);
$rows_get_productdata = $db->select("SELECT ...");
$error = $db -> error();
foreach ($rows_get_productdata as $row_get_productdata) {
$this -> productname = $row_get_productdata["productname"];
$this -> weight = $row_get_productdata["weight"];
$this -> deliverytime = $row_get_productdata["deliverytime"];
$this -> deprecated = $row_get_productdata["deprecated"];
$this -> openorderquantity = $row_get_productdata["openorderquantity"];
$this -> stock = $row_get_productdata["stock"];
}
}
public function readopensupplierordersfromDB () {
$db = new Db();
$safe_productID = $db->quote($this->productID);
$this->opensupplierorders = $db->select("SELECT ...");
$error = $db -> error();
}
} ?>
Please do not care about the DB calls I have a good DB wrapping class, I think.
When calling the class I would do:
<?php
$productdata = new productdata();
$productdata->productID = '123';
$productdata->readproductdatafromDB();
echo "The weight of the product is: " . $productdata->weight;
?>
I hope I am asking the right questions. Thank you very much in advance to help me with this.
Best Regards,
Benedikt.
First principles: design it right and worry about performance/optimisation if and when you encounter problems there.
So, product attributes are seperate from stock attributes, which are seperate but related to order atributes, which are seperate but related to supplier atributes, etc. Thus each type of information should be encapsulated into its own class: and yes this will mirror the noramalised database schema to a great extent.
In your code, you're mainly breaking the Single Responsibility principe. Your class is doing two things: managing the product information and managing your database connection/queries. That's the wrong way to do it.
You'll have a lot of duplicate code this way. Now you only have a Product entity. What if you have 50 of them? You'll have the same code in all 50 classes, not something you'll want to do. Have a look at the Repository pattern, which can help you for database interaction.
The correct way is to seperate these. Create a class for managing the connection and retrieving the data and a class Product, not ProductData, of which objects can be created with the data you receive from the database.
I'd suggest you have a look at some OOP frameworks first. Work with them, try and understand them and you'll understand these concepts a lot better so you can use them in your own projects.
Other than that, have a look at design patterns. The book Head First Design Patterns is an excellent introduction to understand design principles and patterns.
Related
As a means to try and learn object oriented PHP scripting, I'm currently attempting to rewrite a database web application that I previously wrote in procedural PHP. The application is used to store car parts and information about car parts.
In my application there are car parts. They are identified by various reference numbers, which are assigned by different organisations (the part's manufacturer, re-manufacturers, vehicle manufacturers, etc.), and any particular car part could have zero, one or many reference numbers as assigned by these organisations (and, confusingly, each reference number may refer to more than one unique car part as defined in the database I'm working on).
As far as I understand things, I am dealing with three different classes of entities. There is that of the car part, the reference number, and that of the reference-assigner (in my internal nomenclature I call these 'referrers'). As I am just getting started with learning OOP, I have begun by creating some very basic classes for each:
class Part {
public $part_id;
public $part_type;
public $weight;
public $notes;
public $references;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
class Reference {
public $reference_id;
public $reference;
public $referrer;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
class Referrer {
public $referrer_id;
public $referrer_name;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
}
?>
What I've been struggling with is how to populate these and subsequently glue them together. The most basic function of my web application is to view a car part, including its metrics and its assigned reference numbers.
A part_id is included in a page request. Initially, I wrote a constructor method in my Part class which would look up that part in the database, and then another method which would look up reference numbers (which were JOINed with the referrer table) assigned to that Part ID. It would iterate through the results and create a new object for each reference, and hold the complete set in an array indexed by the reference_id datum.
After further reading, I began to understand that I should use a factory class to carry this out, however, as this kind of conjunction between my Part class and my Reference class is not the responsibility of any one of those discrete classes. This made conceptual sense, and so I have since devised a class that I've called PartReferenceFactory, which I understand should be responsible for assembling any kind of collation of part reference numbers:
class PartReferenceFactory {
public static function getReferences(Database $db, $part_id) {
$db_result = $db->query(
'SELECT *
FROM `' . REFERRER_TABLE . '`
LEFT JOIN `' . REFERENCE_TABLE . '` USING (`referrer_id`)
INNER JOIN `' . REFERENCE_REL . '` USING (`reference_id`)
WHERE `part_id` = :part_id
ORDER BY `order` ASC, `referrer_name` ASC, `reference` ASC',
array(':part_id' => $part_id);
);
if(empty($db_result)) {
return FALSE;
} else {
$references = array();
foreach($db_result as $reference_id => $reference_properties) {
$references[$reference_id] = new Reference($db);
$references[$reference_id]->reference = $reference_properties['reference'];
$references[$reference_id]->referrer = new Referrer($db);
$references[$reference_id]->referrer->referrer_id = $reference_properties['referrer_id'];
$references[$reference_id]->referrer->referrer_name = $reference_properties['referrer_id'];
}
return $references;
}
}
}
?>
The getReferences method of my factory class is then called inside my Part class, which I revised thusly:
class Part {
public $part_id;
public $part_type;
public $weight;
public $notes;
public $references;
private $db;
function __construct(Database $db) {
$this->db = $db;
}
function getReferences() {
$this->references = PartReferenceFactory::getReferences($this->db, $this->part_id);
}
}
Really I'm looking for advice on whether my general approach is a good one or if, as I suspect, I've misunderstood something, am overlooking other things, and am tying myself in knots. I will try to distill this into some underlying, directly-answerable questions:
Does my understanding of the purpose of classes and of factory classes seem erroneous?
Is it a bad idea for me to store arrays of objects? I ask from a viewpoint more of design than performance, insofar as they are not intertwined.
Is this even an appropriate way to structure relationships/inter-dependencies between classes in PHP?
Is it correct to call my PartReferenceFactory inside the getReferences method of the (instantiated) Part class? And is storing the returned reference objects within the part objects appropriate?
As a part of the application's GUI, I'll need to provide lists of referrers, requiring me to create another array of ALL referrers independently of any part object. Yet some of these referrers will exist within the $references array inside the part object. It has occurred to me that in order to avoid duplicate referrer objects, I could SELECT a list of all referrers at the beginning of each page request, formulate these into a global array of referrers, and simply reference these from within my Part and Reference classes as needed. However, I have read that it is not good to rely on the global scope within classes. Is there an elegant/best-practice solution to this?
Thank you very much for the time of whomever happens to read this, and I apologise for the extremely long question. I often worry that I mis-explain matters, and so wanted to be as precise as possible. Please let me know if it would be beneficial for any parts of my post to be deleted.
Hmmm, I think Jon is right, this question has taken real commitment to get my head round and I'm still not sure I know everything you're asking, but I'll give it a go.
1) Is your understanding of classes/factory classes right?
Well yes and no. Yes you do understand it, but you're taking a very classroom type approach to the problem, rather than the sort of pragmatism that comes with experience. OOP is a way of modelling real things, but it's still fundamentally a programming tool, so don't over complicate your structure, just to keep it real. The main think OOP gives you over structured programming is inheritance, which means you can code things which are kind of like other things, so reuse code better by only coding the stuff that's actually different. If 2 things would share code, make them a shared parent and put the code in there. Envisaging how the hierarchy might work efficiently is 80% of the task of designing an OOP application, and you're bound to get it wrong first time and have to restructure.
For example. I've coded a number of classes which represent entities in a database: ie Users, Realthings, Collectibles. Each has fundamentally different set of attributes and relations, but also has a core set of things that are similar: the way they're interrogated, the way the data is presented, the fact that they have attributes, fields and relations. So I coded a parent class which everything inherits, and it contains most of the code, then the specific children just define the stuff which is different for each class, and the hard work was deciding how much could go in Indexed to avoid repeating code in the child classes. Initially I had a lot of code in the children, but as it evolved I moved more and more code into the parent class, and thinned out the children. Like I say, don't assume you'll get it right first time.
2) Is it a bad idea to store objects in arrays? Well no, but why would you want to? In your example you have an array of references in the database, where the relationship is a many to many relation (ie you have 3 tables with a joining table in the middle). Then when you get the data you create each object and store it in an array. Sure you can do that, but remember each object has an overhead. Loading everything into memory is fine, but don't do it unless you need to. Also be sure you need a many<=>many relationship. I may have mis-read your explanation, but I thought you'd only need a one<=>many relationship, which could be done by storing a reference to the 'one' in the record of each of the 'many' (thus loosing the middle table and simplifying the join).
5) I'm going to jump to the last one at this point because it feeds into #2. You need to think of your application from different perspectives (a) when you have data and are trying to present it (read) (b) when you have some data and are trying to add new data, with links to existing data (adding a relation) and (c) when you have no data, and are adding a fresh record. How might the user get to each of those perspectives and what would happen. It's often easy to get (a) and (c) to work, but making (b) intuitive is often the hard part, and if you over complicate it, then users simply won't get it. When building up the inner data structures in #2 only do what you need, for the perspective a user is in. Unless you're writing an app, you don't need to load everything, only the stuff for the task at hand.
3) Not sure what you mean by 'is this appropriate'. OOP allows you to tie yourself up in knots, or create wonderful works of art. In theory you should just try to keep it simple. There are great books which give examples of why you might want to make things more complicated, but most of the reasons aren't obvious, so you'll need experience to decide if what you're doing is needlessly over complicating things or if it actually avoids a pitfall. From personal experience if you think it's overcomplicating, it probably is.
4) Not sure if this is what you meant, but I've taken this question to be asking if you've used Factory classes correctly. My understanding of the theory is that what you should end up with, is next to no references to doing things statically, beyond the initial creation of a FactoryClass object, stored statically in the child class being used, then you have to code an abstract method in parent class, implemented in each child class, which gets you that object, so you can call on the object of that FactoryClass, using a method call. I normally only call FactoryClass stuff directly when a non-factory class is initialised (ie you need to populate the static in a new object, incase the class hasn't been init'd yet). I don't see any problem calling it directly as you have, but I'd avoid that as the Factory is IMHO an implementation detail of the class, and so shouldn't be exposed outside that.
In my experience you're always learning new things and discovering pitfalls in OOP, so even thought I've been coding professionally for nearly 20 years, I'd not claim to be an expert on this, so I'm sure there will be totally opposing views to what I've said. I think you learn most by doing what your gut says is right, then not being too stubborn to start over again, if you gut changes it's mind :-)
Please note I'm not looking for 'use a framework' answers. I'm trying to structurally improve the way I code websites and approach databases from PHP.
I'm building a web service from scratch, without any frameworks. I'm using a LAMP stack and am trying to learn a bit of PHP's OO functionality while I'm at it. I've previously only used OO to make mobile apps.
I've been at it for months now (as planned, no worries). Along the way I've bumped into a couple of structural problems, making me wonder what the best way would be to make the code object oriented.
Pretty much all of the problems involve the database in some way. Say we have a class DB and a class User. In most cases I only need to fetch a single user's information from the database. I thought a good way to handle it was to have a global $_db variable and have the User object query the database like so (oversimplified):
class User {
function __construct($id) {
global $_db;
$q = $_db->query("SELECT name, mail FROM user WHERE id = ?", $id);
$this->loadProperties($q);
}
}
Now say we have a page that shows a list of users. I still want to make User objects for each of them, but I don't want to query the database for each separate user.
So, I extend the User class to take an object as an argument:
class User {
function __construct($id) {
if(is_object($id))
$q = $id;
else {
global $_db;
$q = $_db->query("SELECT name, mail FROM user WHERE id = ?", $id);
}
$this->loadProperties($q);
}
}
Now I can create a list of, for example, the 100 most recently created and active accounts:
$user_list = [];
$q = $_db->query("SELECT name, mail FROM user WHERE banned = 0 ORDER BY date_created DESC LIMIT 100");
while($a = $_db->fetch($q))
$user_list[] = new User($a);
This all works great, except for one big downside: the database queries for table user are no longer in one place, which is kind of making spaghetti code. This is where I'm starting to wonder whether this can be done more efficiently.
So maybe I need to extend my DB object instead of my User object, for example:
class DB {
public function getUsers($where) {
$q = $this->query("SELECT name, mail FROM user WHERE ".$where);
$users = [];
while($a = $this->fetch($q))
$users[] = new User($a);
}
}
Now I would create the user list as follows:
$user_list = $_db->getUsers("banned = 0 ORDER BY date_created DESC LIMIT 100");
But now I'm calling the getUsers() method in various places using various SQL queries, solving nothing. I also don't want to load the same properties each time, so my getUsers() method will have to take entire SQL queries as an argument. Anyway, you get the point.
Speaking of loading different properties, there's another thing that has been bugging me writing OO in PHP. Let's assume our PHP object has at least every property our database row has. Say I have a method User::getName():
class User {
public function getName() {
return $this->name;
}
}
This function will assume the appropriate field has been loaded from the database. However it would be inefficient to preload all of the user's properties each time I make an object. Sometimes I'll only need the user's name. On the other hand it would also be inefficient to go into the database at this point to load this one property.
I have to make sure that for each method I use, the appropriate properties have already been loaded. This makes complete sense from a performance perspective, but from an OO perspective, it means you have to know beforehand which methods you're gonna use which makes it a lot less dynamic and, again, allows for spaghetti code.
The last thing I bumped into (for now at least), is how to separate actual new users from new User. I figured I'd use a separate class called Registration (again, oversimplified):
class Registration {
function createUser() {
$form = $this->getSubmittedForm();
global $_db;
$_db->query("INSERT INTO user (name, mail) VALUES (?, ?)", $form->name, $form->mail);
if($_db->hasError)
return FALSE;
return $_db->insertedID;
}
}
But this means I have to create two separate classes for each database table and again I have different classes accessing the same table. Not to mention there's a third class handling login sessions that's also accessing the user table.
In summary, I feel like all of the above can be done way more efficiently. Most importantly I want pretty code. I feel like I'm missing a way to approach the database from an OO perspective. But how can I do so without losing the dynamics and power of SQL queries?
I'm looking forward to reading your experiences and ideas in this field.
Update
Seems most of you condemn my use of global $_db. Though you've convinced me this isn't the best approach, for the scope of this question it's irrelevant whether I'm supplying the database through an argument, a global or a singleton. It's still a separate class DB that handles any interaction with the database.
It's a common thing to have a separate class to handle SQL queries and to keep the fetched data. In fact, it is the real application of the Single Responsibility Principle.
What I usually do is keep a class with all the information concerning the data, in your case the User class, with all the user information as fields.
Then comes the business layer, for instance UserDataManager (though the use of "Manager" as a suffix is not recommended and you'd better find a more suitable name in each scenario) which takes the pdo object in its constructor to avoid use of global variables and has all the SQL methods. You'd thus have methods registerNewUser, findUserById, unsuscribeUser and so on (the use of "User" in the method can be implied by the class name and be omitted).
Hope it helps.
I've liked to use the data mapper pattern (or at least I think that's how I'm doing it). I've done this for some sites built on Silex, though it's applicable to going without a framework since Silex is very lightweight and doesn't impose much on how you architect your code. In fact, I recommend you check out Symfony2/Silex just to get some ideas for ways to design your code.
Anyway, I've used classes like UserMapper. Since I was using the Doctrine DBAL library, I used Dependency injection to give each mapper a $db. But the DBAL is pretty much a wrapper on the PDO class as far as I understand, so you could inject that instead.
Now you have a UserMapper who is responsible for the CRUD operations. So I solve your first problem with methods like LoadUser($id) and LoadAllUsers(). Then I would set all the properties on the new User based on the data from the database. You can similarly have CreateUser(User $user). Note that in "create", I'm really passing a User object and mapping it to the database. You could call it PersistUser(User $user) to make this distinction more clear. Now all of the SQL queries are in one place and your User class is just a collection of data. The User doesn't need to come from the database, you could create test users or whatever else without any modification. All of the persistence of `User is encapsulated in another class.
I'm not sure that it's always bad to load all of the properties of a user, but if you want to fix that, it's not hard to make LoadUsername($id) or other methods. Or you could do LoadUser($id, array $properties) with a set of properties taht you want to load. If your naming is consistent, then it's easy to have set the properties like:
// in a foreach, $data is the associative array returned by your SQL
$setter = 'set'.$property;
$user->$setter($data[$property]);
Or (and?) you could solve this with Proxy objects. I haven't done this, but the idea is to return a bunch of UserProxy objects, which extend User. But they have the Mapper injected and they override the getters to call into the Mapper to select more. Perhaps when you access one property on a proxy, it will select everything via the mapper (a method called populateUser($id)?) and then subsequent getters can just access the properties in memory. This might make some sense if you, for example, select all users then need to access data on a subset. But I think in general it may be easier to select everything.
public function getX()
{
if (!isset($this->x)) {
$this->mapper->populateUser($this);
}
return $this->x;
}
For new users, I say just do $user = new User... and set everything up, then call into $mapper->persist($user). You can wrap that up in another class, like UserFactory->Create($data) and it can return the (persisted) User. Or that class can be called Registration if you'd like.
Did I mention you should use Dependency Injection to handle all of these services (like the Mappers and others like Factories maybe)? Maybe just grab the DIC from Silex, called Pimple. Or implement a lightweight one yourself (it's not hard).
I hope this helps. It's a high-level overview of some things I've picked up from writing a lot of PHP and using Syfmony2/Silex. Good luck and glad to see PHP programmers like yourself actually trying to "do things right"! Please comment if I can elaborate anywhere. Hope this answer helps you out.
You should first begin by writing a class as a wrapper to your Database object, which would be more clean that a global variable (read about the Singleton Pattern if you don't know it, and there is a lot of examples of Singleton Database Wrapper on the web). You'll then have a better view of the architecture you should implement.
Best is to separate datas from transactions with the database, meaning that you can for example have two classes for your User ; one that will only send queries and fetch responses, and the other that will manage datas thanks to object's attributes and methods. Sometimes, there can be also some action that doesn't require to interact with the database, and that would be implemented in these classes too.
Last but not least, it can be a good idea to look a MVC frameworks and how they work (even if you don't want to use it) ; that would give you a good idea of how can be structured a web application, and how to implement these pattern for you in some way.
I need some advice\suggestions on how to create\handle database connections for a project I'm working on. I'm creating a simple work order system for my company using PHP 5.4.3, right now there isn't any type of authentication as I'm not ready for that complexity. So far I'm planning on the following structure
<? php
class Db {
private static $dbh = null;
static function open_connection() {
self::$dbh = new PDO(... }
static function query($sql) {
$result = self::$dbh->... }
static function fetch_array($result){
...}
...
}
class Workorder extends Db {
protected static $table_name = ...
...
public $wonum;
...
function instantiate {
//return workorder objects
...}
function findallwos {
//find all work orders...
...}
...}
I think this will work fine for pages that I need to display all of the work orders or put in new work orders. But I have a few pages that require very simple queries, for example one of the reporting pages will just have a drop down list of the 3 technicians we have working for us, if I was using a global $dbh variable I could just do
function create_dropdown () {
GLOBAL $dbh;
$sql = "select...";
$sth = $dbh->prepare($sql);
$sth->execute();
$arry = $sth->fetchAll(PDO::FETCH_ASSOC);
.....
But I'd rather stay away from global variables as eventually I would like to add future complexity to this site which might not work with globals. (Including multiple users with authentication etc). So my questions are:
Should I not use the Db class as I have it designed, and instead use some sort of connection_factory class instead that passes out connections? (and not have my workorder class be an extension of anything)
Should I just use static calls to the Db class to pull in simple queries? db::fetch_array...
Or do you guys have any other suggestions? I've come across a lot of different ways to go about this, but ideally I'd like to design this so that I don't have to completely recode everything when I get to the point where I'm adding multiple users with different permissions etc.
What I personally would criticise on the code design you provided is that you don't separate concerns and that there is no abstraction. Don't make the huge mistake so many did before to mix SQL statements, the type of presentation and your business logic all over your code. At some point you might want to switch the database product, create a mobile version, add functionality ... and then you need to change dozens of files.
To prevent these things in general, I would suggest an approach like this:
Create a clean and lightweight model of the entities and their relationships and don't care about persistence (i.e. database), access control, etc. at this point
(e.g. a simple class Workorder that contains classes of Employee, ...)
Write interfaces that define the necessary persistence operations on your model (i.e. saveWorkorder(Workorder $workorder), getAllWorkorders(), loadWorkorder(), ...). In your code, refer only to these interfaces and the methods they provide and never to a concrete implementation
Now, implement the classes of the persistence interfaces with a certain DB. Don't forget to encapsulate the fundamental DB actions in a class as well as you will use them in many persistence classes.
I hope my answer is not too vague, but software design is a very wide field and it's hard to cover all aspects in one single answer. And as with all questions concerning software design, there's no correct or wrong solution as it depends a lot on the concrete project, the requirements, on personal coding style, etc...
On a side note, if you plan to do more projects of this kind, it probably would pay off to invest the time to get known with a framework (like Zend Framework, Symphony, CakePHP and many more) as they provide you with a lot of existing functionality that you can use out of the box, like database connectors, separation of views and business logic, ...
Ok, I couldn't insert the overview image, but I'm required to create a PHP app for my studies, it must allow student to register, and the administrator to edit course and student info.
We are not required to code it in an OOP style, but since its the best programming practice to code in OOP, why not learn OOP from the beginning.
I'm a beginner but I know the basics about OOP, classes, inheritance, setters and getters and all that cool lingo, but I'm struggling to decide which parts of this app should be objects, should I make course and student classes or add, edit and delete classes? Any advice on how to approach and visualize such a problem would be appreciated.
Very roughly: This is how I would do it:
Store your data in SQL or XML. You will need two SQL tables, one for Students and one for Courses. You can use one XML file containing all the data, or you can use two files (which I recommend).
Create a class called, for example, dataItem with a property like '$arr_fields' corresponding to a single data record (a single row in a SQL table, or an XML record).
The dataItem class should have the following methods (all public):
loadFromSQL() (or loadFromXML())
saveToSQL() (or saveToXML())
add(), edit() and delete()
a view() method using HTML
These methods are obviously used to read and write data between the SQL/XML data and $arr_fields of the class, and to display the data in $arr_fields. The keys of $arr_fields are the SQL column names (or XML tag or attribute names) for the specific SQL table.
Try not to call loadFromSQL() or saveToSQL() in your constructor or in any of the other methods which are used to modify only the class data. Keep these actions separate. EDIT: This is a personal preference which helps me to keep track of the state of my objects.
Create Student and Course classes that extends the dataItem class.
You can override methods, for instance the view() method, inside your extended classes if you need to.
Then you can call the methods in Students and Courses from an Admin object (like rcdmk suggested) or maybe from StudentFolder and CourseFolder classes whose view() method contains buttons for the actions that need to be performed. (Let StudentFolder and CourseFolder extend a Folder class that you create).
UPDATE:
For example: If your primary key in a SQL table is id, then dataItem's loadFromSQL($id, $tablename) should set $arr_fields so that its keys are the column names and it's values are the values from the row whose primary value is equal to $id.
In Students, you can then override loadFromSQL() as follows:
class Students extends dataItem {
// other attributes
public function loadFromSQL($id) {
parent::loadFromSQL($id, "Students");
}
}
EDIT: On reconsideration, it might be better to set $arr_fields["id"] = $id and also set $tablename with the constructor for dataItem - then you never have to override loadFromSQL() or specify parameters for it. loadFromSQL() should then load the record if it exists. saveToSQL() should save $arr_fields in SQL if $arr_fields["id"] is set and create a new record if it is not set. Anyway, you must find a consistent way of interacting with the data which works for you, these are just possibilities.
However, if you are not experienced with OOP and SQL or XML, you might be opening a can of worms for yourself and it might be better to just do your assignment using functions only and php arrays for your data. Unless you have some time to learn...
From a simple perspective:
Abstract the main objects as classes and use methods for actions of this objects:
Student (object) are deleted (action) by the Admin (object), so
Admin class will have a deleteStudent method, because Admin deletes Students.
Another aprouch is to concentrate all Student related actions in the Student class:
Student class will have a public delete method that Admin can use.
Anyone that think this in better ways of explanation can edit this wiki.
Think about which aspects of your system are actually objects, you know, something you can do something with. The methods are what you do to the objects. So, you're on the right track with course and student classes, and add, edit, and delete would be methods of those classes.
But don't get too bogged down with it. If it's not your core assignment objective, you could quickly get in over your head by trying to do everything exactly the right way. If you can formulate a clear way to get to where you need to go, then go for it, if it seems confusing, back off it a little and learn some more.
You say that you know the basis of OOP, however you ask whether you should create Course, Student classes OR Add, Delete, Edit classes. Well maybe there are other practices, but I guess the most popular one and the only I am aware of is to use nouns as classes and verbs as their methods. Hence, intuitively there is something wrong with class "Add" or "Edit". What I would have done if I were were, is to think of all "entities" that might be considered an object - like Student, Course, Lecturer, Class (Room) and depending on how advanced your model should be you can add more like Building etc. Then try to implement basic things like creating new student, registering for a course, associating teacher with a course etc. Once you have it in place and IT IS WORKING you might want to add advanced things, like inheritance. For example you might want to say, that both Teacher and Student are a Person so you might want to create such abstract class and use inheritance.
My question is more like a theoretical.
Say you have an object, that represents the list of something (articles, pages, accounts etc.)
class ObjCollection
You have a class, that represents a specific item in collection:
class objItem
I have a problem thinking of a basic responsibilities of each object.
Which class is responsible for creating a new objItem?
Which class is responsible for deleting a objItem? Should it delete itself as a method?
Update 1:
Techpriester: Is it ok to use object's constructor as a function to create new item?
I think of that like:
class objItem {
public function __construct($id = 0) {
if ($id > 0) {
// load item data...
} else {
// make new item...
}
}
}
But what if something goes wrong in the code, and instead of passing an $id > 0, it passes 0? In this case a more expected behavior would be an empty object, and not the new one, or am I wrong?
A way of thinking about this:
objItem usually have a class constructor so this class might be responsible for creating objects of type objItem.
When an objItem is inserted in a list/collection let's say objCollection it can be objCollection responsability to delete it from the collection.
objItem usually have a class
constructor so this class is
responsible for creating objects of
type objItem.
Constructor has nothing to do with responsibility (usually). Thinking this way, every object would be only responsible for itself.
Responsiblity is a concept not directly binded with class hierarchy.
If:
ObjCollection = Nest objItem = Egg. And there is third object Bird, Then Bird takes responsibility for creating egs (even if nest contains egg). It is not about programming it is about common sense... :)
There is not such thing like "empty object". Objects have "state". You can create an object and then you have it, or you may not to create it and there is no object then.
All you have to worry about is if your constructor will work fine in both cases, with new object created and without it.
Usually it is better to inject object as a constructor parameter (instead of $id) not to create it inside another object.
I know this doesn't answer your question, but since you tagged this as PHP I'm going to assume that it will almost certainly be applied with some sort of database model.
In that case, it's probably a better idea to do away with 'collections' altogether since if you made each class represent only one object, if you wanted to view 10 blog posts, for example, you would be calling 10 separate SELECT queries each retrieving only an individual database record, because you decided to have the 'BlogPost' class encapsulate its retrieval method.
The alternative is to let the class represent either one or more records, that way, you only need to run one SELECT query whether you're retrieving 5000 records or only one. Pretty much every object-relational-mapper does this.
When doing object-oriented programming, it's better to think in terms of behavior or responsibility than whether or not the object is a tangible 'thing'. That's the problem with theoretical discussion of OOP. It's very tempting to use analogies like animals and fruits which have very little relevance to real-world programming.
Since an object cannot delete itself, that has to be the responsibility of the collection.
Wether you let the collection create it's objects like $collection->makeNewItem(); (which then calls the items constructor) or use $item = new Item(); directly and then some $collection->addItem($item);method is entirely up to you and the needs of your application.
I'd recommend using regular instantiation if the items themselves are also used outside of the collection.