It looks like you are having a design issue here: Tables should not extend the Database Abstraction Layer. Instead the DAL should be injected into the table as a dependency.
abstract class Table {
protected $dal;
public function __construct(DAL $dal) {
$this->dal = $dal;
}
// whatever else all tables have in common
}
class Table_User extends Table {
public function someMethod() {
$this->dal->someOtherMethod();
}
}
$table = new MyTable($dal);
$table->someMethod();
That way you will create a DAL at some upper scope and pass it down. This will also allow you to use multiple different database engines at the same time.
Additionally you obviously should not create your tables directly in your controller but let a specialized class do that. For example you could use a factory:
class TableFactory {
protected $dal;
public function __construct(DAL $dal) {
$this->dal = $dal;
}
public function createTable($name) {
$className = 'Table_' . $name;
return new $className($this->dal);
}
}
That way you can create a table factory at some point with an injected DAL and pass that table factory around.
$table = $factory->createTable('User');
Yes, SpecialTable
can be produced by the same factory (and by the same factory method) that produces Table
. This not a problem, as long as SpecialTable
provides at least the methods guaranteed by the Table
interface (which it does).
This sort of situation makes sense when objects returned from the factory have some guaranteed behaviour/functionality (interface Table
), and some optional stuff (interface SpecialTable
).
So, in order to make use of the optional extra abilities/stuff available on some concrete objects returned by the factory, you have a few choices:
use polymorphism (recommended)
make use of instanceof
(or similar), which can be used to check for interface compliance/exact typing (avoid this if possible!)
Using polymorphism
An example to illustrate: suppose SpecialTable has extra data and an extra widget 'blarg' which appears on the UI representing special tables. Do you really need the code which uses SpecialTable to know that it needs to add 'blarg' to the UI? Or can you just have your external code ask the thing of type Table to add its necessary UI components to some UI container? Then the calling code doesn't care what exact type the thing is -- SpecialTable deals with it, adding the 'blarg' to the container itself. This pattern can be described as the delegate pattern (also sometimes also known as strategy pattern).
Using instanceof (type checking)
Example of instanceof
use:
// Table and SpecialTable are interfaces
Table table = TableFactory.instance().getDefaultTable();
if (table instanceof "SpecialTable") {
SpecialTable specialTable = (SpecialTable)table;
specialTable.specialMethod();
}
table.normalMethod();
Note that when you define your SpecialTable
interface you can declare it as extending the Table interface and then only list the extra methods.
Your factory will be producing concrete objects of either type. So you might have a concrete class called BoringTable
which implements Table
, and a concrete class called ExcitingTable
which implements SpecialTable
.
Beware instanceof!
It's generally better to avoid things like 'instanceof' (or generally speaking, ifs of any kind which depend on what type an object is). You should favour polymorphism instead. By this I mean having multiple different classes, of a common type, enclosing different behaviour in their implementation code.
Ask yourself this: does the class using something of type Table
(which might also be of type SpecialTable
) need to care if it's a special table or not? Can you not hide that distinction in some sort of implementation of the SpecialTable -- have it do something different, something extra, that plain old Table does?
Best Answer
In short: Traits might be the feature that you look for.
A trait is PHP 5.4’s solution to the lack of multiple inheritance in the language and a way to avoid hierarchical inheritance chains. Another way to think of it is that including traits in your classes is a clean way to keep your code dry without breaking good design principles.
An even more simple way to think about it is: Hey, all that boiler plate code that you copied and pasted everywhere but now you want to change something? well replace it with a trait and call it a day.
Here you are examples and good post on this topic.