Consider, Base class A and derived class B.
Both interact with database. Class A uses table A and Class B (which is derived from A) also uses table B.
If I create an object of Class B and later at some stage in some file I access the same object by Class A and delete it then it deletes its instance in Table A but a part of its data is left in Table B (which is left with no parent). How to tackle this problem without defining Class A as abstract, as it has its own existence?
Thanks!
Sanket
Your best approach would probably be to utilise your database backend's foreign key support if it has it. Assuming table A represents an object of class A, and table B holds the additional state that an object of class B needs, then that means that all A and B objects have entries in the A table, but only B objects have entries in both the A and B tables.
If an entry in the A table and an entry in the B table relate to state for the same B object, then you should define a foreign key in the B table that references the ID of the corresponding row in the A table. Now if you attempt to delete a row from A, the database will know that the row in A you're deleting has an associated row in B and can act accordingly. It can either prevent the deletion of the data in A, also delete the corresponding row in B, or change the B row's foreign key value to reference some default value. What precise action you take will depend on what you're trying to achieve.
If your database does not support foreign keys (MySQL when working with the myISAM engine, for example) then things become a lot more complicated. There are possibly ways of doing it in your PHP code, but all the ones I can think of off the top of my head require your base class to know details about the implementation of your derived classes, which is a huge no no in object oriented design.
You should also be asking yourself, why are you initializing a base class object with data that represents a derived class object? Is this actually necessary for your design? I've personally not run across a situation where this has been necessary, because a derived class implicitly always has all the functionality of the base class it derives from.
I searched and finally found a workaround for this using "get_called_class()" PHP 5 function.
And in my views, this is the best way to do it.
In the Base class table, add a "class_type" column and while adding data add the the name of the class which added the row by "get_called_class()". Now, whenever any derived class calls this, the name of the class is added to the database.
When deleting the rows of DB, allow only that class to delete which created that row (by matching "class_type" and "get_called_class()")
Related
I would like to implement the classes that I made into database,
I have a class like
Class Person {
firstname ;
lastname;
email
etc
}
Class Teacher extends Person {
salary;
graduatedFrom ;
etc ...
hasMany: [classess, experiences] ;
}
Class Student extends Person{
tuitionFee ;
parentName ;
etc ...
}
I am going to use mysql database for this project,
Should I create one table named Person and store all information there
or should I create different table for each class (like Teacher, Student, etc) ?
note: i am going to implement this using YII Framework and MYSQL database.
You would be better of creating Two tables one that stores all the person details, this table would have a foreign key which references to other table which would store the role details (eg teacher, Students)
This way would be advisable with respect to scalability as well, i.e. for example tomorrow if your application has 50 roles, you wont need to create 50 tables, you just would need to add a entry in the role table.
table person would be something like this (firstname, lastname, email, role_id,etc)
role_id here is the primary key of role table
role table would be something like this (role_id,role_name,role_desc,etc)
I would create only one table Person with a column which will act as an identifier as to what the Person is (a Student, a Teacher etc...).
Advantage: we do not have to pay attention to sql joins while retrieving information as it would be fetching information from just one table! It will also give benefit when saving data. Think you do not have to save information to two different tables while storing data for Student.
In case, if you create separate table for each class ( and want to avoid sql joins) then you may end up creating lot of duplicate information in your database.
Less the number of the tables will produce a clean code.
Given the above case, it would still make sense, if you create a separate table for a special entity which you think should be stored separately (with a reference key to the main table) and do not want to clutter up the original table.
You can use a couple of different design patterns for this:
Class Table Inheritance
Concrete Table Inheritance
For more information on this (and other useful patterns) read Martin Fowler's Patterns of Enterprise Application Architecture.
For other examples:
Practical Object Oriented Models In SQL
In a StackOverflow clone, what relationship should a Comments table have to Questions and Answers?
Storing inherited objects in a database
Recently started working with OOP in PHP. Following the "code to an Interface" principle, i got confused as to the type hint to use when passing a single object or multiple as argument to a method.
Currently, i have a "Student" class - represents a row in my students table, i also have a "Students" class that holds multiple student objects in an array.
To fetch the profile of one student, i pass the Students object (holding a single student object) to the profile class. I set a Students type hint in the profile class.
Now i feel this is bad code as i have lines like this
student = new Students();
and students = new Students();
question is,
am i on the right path?
if i remove the Students class and work with Student alone, based on the principle, how do i pass multiple Student objects (assuming array) to the profile class if it accepts a Student type hint?
what options do i have?
Thanks.
If by Students you mean a collection of Student objects, perhaps a better name would be StudentCollection or StudentSet.
There are two ways around the type hint problem:
Introduce a method on StudentCollection called ->getProfiles(); it would return an array of profiles for each Student instance it's managing by calling methods on Profile.
Introduce a (static) method on Profile that operates on a StudentCollection instance.
The first option has feature envy, which is why I've included a workaround.
Instead of reinventing the wheel you might want to try Doctrine or at least take a look at its architecture.
I'm not sure if I get your exact issue... But if you want to go for your own code I would first abstract the DB layer as well and have some base classes like Database, Table, Row, Field that an describe the DB stack and extend them as needed with some magic methods. So when you do Student extends Table it would automatically check for a "students" table or whatever else convention you like to implement. Alternatively you could just pass the table name as arg.
Whatever Object is returning the result set from the database would have to construct a single Row object for each row and add it to a collection of rows that I would name ResultSet and contains all the row objects and return that collection.
I have a bunch of DNS records in a table, where the "type" column defines the type of the given record.
I would like to have a class hierarchy of models in Lithium representing these records such as:
RR - base resource record (abstract - sort of)
RR_SOA - SOA record, extends RR
RR_CNAME - CNAME record, extends RR
... etc ...
So one class for each record, all mapping to the same table.
Next I would like to use RR::find which would automagically give me a list of objects where the class of each object corresponds to the type of the actual record.
Is this even possible without too much black magic?
Yes (anything is possible) but you're approach may be too ambitious. In my POV, I would depend on #class RR to handle the basic querying of the target table, but by utilizing filters on RR (specifically on the find method inherited from Model), you can pass-by-reference the instance to sets of newly instantiated classes (SOA, CNAME, etc...) along with their position in the table (so SOA object is only associated with that specific record or primary key).
There is some black magic going on, but nothing the Lithium core developers didn't account for.
Bottom line, 1 base class for your table (RR model), multiple (possibly inherited from another base unrelated to Model) for SOA, CNAME, etc..., and a filter (put anywhere really) to intervene RR::find and/or the late binded RR::findby*
Does this make sense?
(This will require some trial and error. First to see if you can manipulate the data output, after that the rest is butter)
I have three classes to define objects: Users, Members and Projects.
The User class defines details such as id, email_address and
name.
The Member class defines details such as the id, user_id,
project_id and datetime_accepted.
The Project class defines details such as id and title - this
isn't important though.
The system has Users and Projects. A Member is a User working on a Project. In other words, the Member class defines a link between two objects.
My question is this:
I want to get a list of members belonging to a certain project, and I want to collect variables from both classes (Member and User - such as User:name and Member:datetime_accepted) in my result set.
Do I need to define a new class that has all the variables from both classes, or is there some other, more efficient structure that I can use to handle this neatly and in an object oriented manner?
You can easly cast StdObject into array by:
$result = array_merge((array)$user, (array)$member);
then you will have an array of variables you need. Add this to new function in Project class,
or consider using Member as child of User class.
When working with Active Record Models, you would generally have a method in one object to get related objects. If you wanted to simplify the SQL to one query, you're getting away from the Active Record Model. This is fine, it just changes the way you approach the problem.
What I have done in the past is one of two approaches:
To add 'virtual properties' to an AR, for example, my Login (User) class has a property "Roles" which is populated by a JOIN in its standard loading query.
To create a Report object which I extend for more complex situations. The Report subclasses have a property that is the multi-table query, and other properties which represent parameters for the WHERE clause. The class produces an array of arrays.
What I ended up doing to solve this issue was to create a Project and a User object inside the Member. The objects were created when the member was constructed. Works okay.
I'm getting my feet wet in OOP and have a question about __construct() content:
Suppose I want to make a PlayResult class, which will take protected ID,Date, and Winner properties. That would seem like logical things to put in the __construct() method, but the ID is the unique auto-incremented ID generated by MySQL when the play result is added to the table for the first time.
Now the ID is very important to my code, because that's how I keep track of individual records when they're being edited. However, obviously when a user uses an HTML form to submit a new play result there is no ID value because the result has not yet entered the database.
So, should the ID go in the constructor or not? Or should I construct with date and winner only, then set the ID if I have one?
Thanks :-)
Saving the object in the database is not the responsibility of the object (unless it's an ActiveRecord), but of a class on the persistence layer, for instance a Table Data Gateway. As such, the PlayResult class should not have to bother about the ID being set or not and be an optional field.
If you want the ID to be required in the object, you'd have to check if it exists in the database (cf. Repository or Identity Field). If not, create the new record in the database first, then create the object instance of the PlayResult with the newly created ID.