I'm working on something and I've already used some design patterns, but none of them fulfills my needs completely, and that makes me think I might use a combination of patterns, but I'm a little bit stuck so I will explain the situation here and hopefully somebody could add some light about the right way to work on this one. The language I'm working with is PHP 8, in case that it helps or limits on finding a solution.
I have a class that builds, via static methods, GraphQL queries and mutations. I can't give you the exact implementation but it's something like this:
class GraphQLBuilder
{
public static function getSettings(): string
{
return <<<GRAPHQL
query{
settings{
id
name
alias
}
}
GRAPHQL;
}
public static function getSetting(string $id): string
{
return <<<GRAPHQL
query{
setting(id: "$id"){
id
name
alias
}
}
GRAPHQL;
}
public static function setName(string $id, string $name)
{
return <<<GRAPHQL
mutation {
setting(
id: "$id",
name: "$name"
) {
id
name
}
}
GRAPHQL;
}
}
The GraphQL server is migrating to a new version and the field alias will be called aka, but for compatibility reasons they will keep the old field for a year. I need to create a new version of the Builder class that supports the new aka field, so it will have different getSettings() and getSetting($id) methods, but the setName() method will be the same.
My goal is to create a class that works with all the methods used (in the example, getSettings(), getSetting($id) and setName($id, $name)) but fallbacks to a base class for the not implemented methods. I'd use an interface to check that all the methods are covered in the new class.
So far, I've tried to use the strategy pattern by creating a BuilderFactory that returns whether GraphQLBuilder or GraphQLBuilderNewVersion, so the methods could be used at the same way: builder::getSettings(), but that makes me include the setName() method, without any difference, in the GraphQLBuilderNewVersion, and I wouldn't like to do that because I don't want to maintain the same code in different classes.
Another approach was creating an abstract class GraphQLAbstractBuilder that has a static $settingProvider attribute, and a GraphQLBuilderBase that holds all the methods that would be needed to fallback. Both GraphQLBuilder or GraphQLBuilderNewVersion extend from GraphQLBuilderBase and implement their own specific methods, but I'd like that those specific methods are controlled in the interface but they don't have a fallback, so I can make them fail in a controlled way.
I feel like I'm overthinking this and it may be a very straightforward solution, so any advice or tip to make a robust design on this problem will be really appreaciated.
Related
I'm looking for some direction regarding the following, I'm new to OOP and getting there but think either my lack of understanding is causing me to get stuck in a rabbit hole or I'm just over thinking things too much and being anal.
basically i have a main class called "CurlRequest" which sole purpose is to perform curl requests, providing a url and params it returns me some html. This class works and functions as intended and I'm happy with that.
I use this class for a few projects but for one I then wanted to track the performance of my requests made. attempted, failed, passed etc, so i created a static class for this which manages all my counters. I place counter references like the following at different areas in my CurlRequest class.
PerformanceTracker::Increment('CurlRequest.Attempted');
PerformanceTracker::Increment('CurlRequest.Passed');
PerformanceTracker::Increment('CurlRequest.Failed');
I have around 10 or so of these with my class tracking all kinds of things during the curl request and i also use my PerformanceTracker class in other classes i made.
However like mentioned i only wanted to do this for one of my projects, so find my self in the situation of having my original CurlRequest class and an altered one with performance counters in it.
My question is, is their a way i can use the same class for any project and choose to use the PerformanceTracker class or not. The obvious way i thought of was to pass an $option argument into the class and then have if statements around all the counters, but can't help think its messy.
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($this->owner . '.CurlRequest.Failed');
}
this also adds a lot of extra code to the class.
I suggest placing the if statement in a separate method
private function handlePerformanceTracker($q)
{
if ($this->options['perfCounter'] == true ) {
PerformanceTracker::Increment($q);
}
}
And call this method instead of your calls to
PerformanceTracker::Increment(...);
Also if you find that you want to track performance differently between your projects it might be useful to change your constructor to accept a callable argument, this way you externalize the actual implementation from the CurlRequest class itself.
public function __construct(..., callable performanceHandler)
Then when you instantiate your class:
$curlRequest = new CurlRequest(..., function($outcome) {
//your implementation
});
You can use inheritance and create a subclass that performs the logging before delegating to the parents methods:
class PerformanceTracker
{
static function Increment($s)
{
echo $s;
}
}
class CurlRequest
{
function get($url){
//preform curl request, save html to variable etc
//dummy vars used here so working example code
$html = 'html here';
$curlError = false;
if($curlError){
$this->error($curlError);
}
return $this->success($html);
}
protected function success($html)
{
return $html;
}
protected function error($curlError)
{
throw new Exception($curlError);
}
}
class LoggingCurlRequest extends CurlRequest
{
function get($url)
{
PerformanceTracker::Increment('CurlRequest.Attempted');
return parent::get($url);
}
function success($html)
{
PerformanceTracker::Increment('CurlRequest.Passed');
return parent::success($html);
}
function error($curlError)
{
PerformanceTracker::Increment('CurlRequest.Failed');
parent::error($curlError);
}
}
$lcr = new LoggingCurlRequest();
$lcr->get('unused in example');
As i have used dummy classes with minimal code to demo the technique the benefit might not be obvious, but in you real code, the methods in the CurlRequest class will be more complex, but the methods in the logging class will remain as two liners, with the log function and the call to the parent method.
Using this technique you can modify the parent class without effecting the derived classes (provided the method signatures dont change), can create other derived classes (how about a CachingCurlRequest) etc.
For the full benefits of OOP you should look into dependency injection and interfaces
From an OOP perspective you could use the 'Null' object pattern. This just means that the dependency used by the CurlRequest class is abstract (possibly an interface?). You would then have Two concrete implementations of PerformanceTracker: the one you have today and one that does nothing (it does not have any behavior). In this way for the one project when you instantiate the CurlRequest class it would use the concrete implementation that has behavior and for all the other projects it would use the concrete implementation with no behavior. All of the code in CurlRequest would look the same but it would have different behavior depending on which concrete implementation it was using
I am having some trouble applying Factory Pattern.
I have a class that I usually call as Product($modelNumber, $wheelCount). But in a part of legacy code that I am refactoring, I do not have $modelNumber, and only have $productID, where the link between {$modelNumber, $productID} is in the database (or in my case I can hardcode it, as I only have a select few products at the moment).
I need to be able to create my class using $productId, but how?
Using Procedural ways I would have a function that does the lookup, and I would put that function in a file, and include that file anywhere where I need to do the lookup. Thus do this:
$modelNumber = modelLookup($productId)
Product($modelNumber, $wheelCount);
But how do I do it using Object Oriented way?
Note: I have posted a more detailed situation here: https://softwareengineering.stackexchange.com/q/233518/119333 and this is where Factory pattern (and other patterns, like interfaces and function pointer passing) were suggested conceptually, but I hit a wall when trying to implement them in PHP. It kind of seems like a simple question, but I think there are several ways to do it and I am a bit lost as to how. And so I need some help.
I provided a conceptual answer to your SRP problem on Programmers Exchange but I think I can demonstrate it here.
What you basically want is some other object that will do the work to get you the model number of given product ID.
class ProductModelNumberProvider {
public function findByProductId($productId) {
// The lookup logic...
}
}
Your factory should provide a setter constructor so it can make use of this object internally to lookup the model number if needed. So basically you will end up with a ProductFactory similar to this.
class ProductFactory {
private $productModelNumberProvider;
public function __construct(ProductModelNumberProvider $productModelNumberProvider) {
$this->productModelNumberProvider = $productModelNumberProvider;
}
public function getProductByIdAndWheels($productId, $wheels) {
$modelNumber = $this->productModelNumberProvider($productId);
return $this->getProductByModelNumberAndWheels($modelNumber, $wheels);
}
public function getProductByModelNumberAndWheels($modelNumber, $wheels) {
// Do your magic here...
return $product;
}
}
EDIT
On second thought the setter is not the best approach since having a ProductModelNumberProvider instance is mandatory. That is why I moved it to have it injected through the constructor instead.
I can think of something like this:
$factory = new ProductBuilder();
$factory->buildFromProductId($productId, $wheelCount); //uses modelLookup() internally
$factory->buildFromModelNumber($modelNumber, $wheelCount); //just returns Product()
It is basically creating a class on top of the procedural function, but it does separate the logic of creating the class separately from looking up the mapping.
I can't seem to get my head around what advantages the strategy pattern offer. See the example below.
//Implementation without the strategy pattern
class Registry {
public function Func1(){
echo 'called function 1';
}
public function Func2(){
echo 'called function 2';
}
}
$client = new Registry();
$client->Func1();
$client->Func2();
//Implementation with strategy pattern
interface registry {
public function printMsg();
}
class Func1 implements registry {
public function printMsg(){
echo 'called function 1';
}
}
class Func2 implements registry {
public function printMsg(){
echo 'called function 2';
}
}
class context {
public function printMsg(Registry $class){
$class->printMsg();
}
}
$client = new context();
$client->printMsg(new Func1());
$client->printMsg(new Func2());
In the above two example what advantages will the strategy pattern will offer and how is it better then the first approach? Why should I use strategy pattern?
The above example code might contain errors please ignore the code.
The intent of the Strategy pattern is to:
Define a family of algorithms, encapsulate each one, and make them interchangeable.
Strategy lets the algorithm vary independently from clients that use it. [GoF:349]
To understand what this means, you have to (emphasis mine)
Consider what should be variable in your design. This approach is the opposite of focusing on the cause of redesign. Instead of considering what might force a change to a design, consider what you want to be able to change without redesign. The focus here is on encapsulating the concept that varies, a theme of many design patterns. [GoF:29]
In other words, strategies are related pieces of code you can plug into a client (another object) at runtime to change its behavior. One reason to do this, is to prevent you from having to touch the client each time a new behavior is added (cf. Open Closed Principle (OCP) and Protected Variation). In addition, when you got sufficiently complex algorithms, putting them into their own classes, helps adhering to the Single Responsibility Principle (SRP).
I find the example in your question somewhat ill-suited to grasp the usefulness of the Strategy Pattern. A Registry should not have a printMsg() method and I cannot make much sense of the example as such. An easier example would be the example I give in Can I include code into a PHP class? (the part where I talk about Strategy begins about halfway down the answer).
But anyway, in your first code, the Registry implements the methods Func1 and Func2. Since we assume these to be related algorithms, let's pretend they really are persistToDatabase() and persistToCsv() to have something to wrap our mind around. Let's also imagine that, at the end of an application request, you call one of these methods from a shutdown handler (the client).
But which method? Well, that depends on what you configured and the flag for that is obviously stored in the Registry itself. So in your client you end up with something like
switch ($registry->persistStrategy)
{
case 'database':
$registry->persistToDatabase();
case 'csv':
$registry->persistToCsv();
default:
// do not persist the database
}
But switch statements like this are bad (cf. CleanCode:37ff). Imagine your customer requests you to add a persistToXml() method. Not only do you have to change your Registry class now to add another method, but you also have to change the client to accommodate for that new feature. That's two classes you have to change, when OCP tell us that our classes should be closed for modification.
One way to improve that would be to add a generic persist() method on the Registry and move the switch/case into it so the client only needs to call
$registry->persist();
That's better but it still leaves us with the switch/case and it still forces us to modify the Registry each time we add a new way to persist it.
Now also imagine your product is a framework used by many developers and they come up with their own persist algorithms. How can they add them? They'd have to extend your class but then they'd also have to replace all the occurrences in the framework where yours was used. Or they just write them into your class, but then they'd have to patch the class each time you provided a new version of it. So that's all a can of worms.
Strategy to the rescue. Since the persist algorithms are the stuff that varies, we will encapsulate them. Since you already know how to define a family of algorithms, I'll skip that part and only show the resulting client:
class Registry
{
public function persist()
{
$this->persistable->persist($this->data);
}
public function setPersistable(Persistable $persistable)
{
$this->persistable = $persistable
}
// other code …
Nice, we refactored the conditional with polymorphism. Now you and all the other developers can set whatever Persistable as the desired Strategy:
$registry->setPersistable(new PersistToCloudStorage);
And that's it. No more switch/case. No more Registry hacking. Just create a new class and set it. The Strategy lets the algorithm vary independently from clients that use it.
Also see
How does the Strategy Pattern work?
https://softwareengineering.stackexchange.com/questions/187378/context-class-in-strategy-pattern
http://sourcemaking.com/design_patterns/strategy for some more explanation.
https://www.youtube.com/watch?v=-NCgRD9-C6o
End Notes:
[GoF] Gamma, E., Helm, R., Johnson, R., Vlissides, J., Design Patterns: Elements of Reusable ObjectOriented Software, Reading, Mass.: AddisonWesley, 1995.
[CleanCode] Martin, Robert C. Clean Code: A Handbook of Agile Software Craftsmanship. Upper Saddle River, NJ: Prentice Hall, 2009. Print.
Basically Strategy is for grouping functionality across multiple classes.
Oh and you have a typo in your code
class context {
public function printMsg(registry $class){
$class->printMsg();
}
}
With the name of the interface of the type hinting.
To be more clear let me show you a small example.
Imagine you have an Iphone and an Android
What's their common point?
One is They are both phones.
you could create an interface like this
interface Telephone {
public function enterNumber();
public function call();
public function sentTextMessage();
}
and implement the interface in each of your telephones:
class Iphone implements Telephone {
// implement interface methods
}
class Android implement Telephone {
}
and you can have a service attached to all phones like a GPS on you car:
and be sure that if you plug a telephone (usually with the interface BlueTooth).
you could do something like this:
class carGps{
public function handFreeCall(Telephone $tel){
$this->amplifyVolume($tel->call());
}
}
$tomtom = new CarGps();
$tomtom->handFreeCall(new Iphone());
//or if you like to:
$tomtom->handFreeCall(new Android());
as an application developer, you'll be able to use your handFreeCall on every phones that implements the Telephone interface without breaking your code, because you'll know that the telephone is capable of calling.
Hope I helped.
Strategy pattern helps in abstracting out the algorithmic approach for doing some particular work. Suppose you want to sort an array of numbers via different algorithms, In this case it is better to apply a strategy pattern than to tightly couple the algorithm with your code.
In the class where you are instantiating the class composed of strategy you instantiate the strategy class reference by passing it to the constructor of composed class.
This way you we are programming to an interface and not implementation and therefore at any point of time we can add more classes to the hierarchy of strategy class and pass the same to the class composed of strategy
Kindly go through the following link
There's a library and a symfony bundle here:
https://packagist.org/packages/pugx/godfather
A couple of important features of the Strategy pattern as envisioned by the Gang of Four are:
It has no conditional statements (so leave out your switch, if, etc conditionals)
It has a Context participant
Most of the advice on the Strategy leaves out the Context participant. You can find five different examples of the Strategy pattern in PHP here: http://www.php5dp.com/category/design-patterns/strategy/
Often, examples are somewhat odd, describing ducks, cats or else.
Here is an example of Strategy Pattern used in displaying alerts. (Extending the Gordon's answer).
1. Interface for the methods, that vary (i.e., alert format in the case):
require_once("initialize/initialize.php");
interface alert{
public function alert($message);
};
2. Methods, that implement alert interface.
class alertBorderBullet implements alert{
public function alert($message){
$alert = "<p style='border:1px solid #eee; padding:4px; padding-left:8px; padding-right:8px; border-left:4px solid #FC0; margin-top:8px; margin-bottom:8px; color:#888'>".$message."</p>";
return $alert;
}
};
class alertOrangeBgnd implements alert{
public function alert($message){
$alert = "<p style='color:#fff; background-color:#ff9c3a; padding:4px; padding-left:8px; padding-right:8px; margin-top:8px; margin-bottom:8px; border-left:4px solid #e471bd;'>".$message."</p>";
return $alert;
}
};
class alertRed implements alert{
public function alert($message){
$alert = "<p style='color:#c11; background-color:#efefef; padding:4px; padding-left:12px; padding-right:8px; margin-top:8px; margin-bottom:8px;'>".$message."</p>";
return $alert;
}
};
3. Messenger, to separate alert method setting and getting from other objects in a project.
class alertMessenger{
protected $_alert;
public function setAlertType(alert $alert){$this->_alert = $alert;}
public function returnAlert($message){return $this->_alert->alert($message);}
};
4. A random project object that will use "alerting" in different ways.
class randomObject{
public $alert;
public function __construct(){
$this->alert = new alertMessenger;
}
// More code here...
};
$randomObject = new randomObject;
$randomObject->alert->setAlertType(new alertRed);
echo $randomObject->alert->returnAlert($message="Red text for critical info");
$randomObject->alert->setAlertType(new alertBorderBullet);
echo $randomObject->alert->returnAlert($message="Border, bullet and pale-color text");
echo $randomObject->alert->returnAlert($message="Repeat, check style permanence");
$randomObject->alert->setAlertType(new alertOrangeBgnd);
echo $randomObject->alert->returnAlert($message="Again, another redefined message style");
randomObject, when initialized (for this case) automatically creates an instance of alertMessanger and makes its methods available. Behaviors can be set and messages echoed.
Other alert formats can be created and used when necessary by setAlertType and then returnAlert.
I was trying to find a way to execute some code to alter the results of an objects methods without actually touching the object's code. One way I came up is using a decorator:
class Decorator {
private $object;
public function __construct($object) {
if (!is_object($object)) {
throw new Exception("Not an object");
}
$this->object = $object;
}
protected function doSomething(&$val) {
$val .= "!!";
}
public function __call($name, $arguments) {
$retVal = call_user_func_array(array($this->object, $name), $arguments);
$this->doSomething($retVal);
return $retVal;
}
}
class Test extends BaseTest {
public function run() {
return "Test->run()";
}
}
$o = new Decorator(new Test());
$o->run();
That way it will work properly but it has one disadvantage which makes it unusable for me right now - it would require replacing all lines with new Test() with new Decorator(new Test()) and this is exactly what I would like to avoid - lots of meddling with the existing code. Maybe something I could do in the base class?
One does not simply overload stuff in PHP. So what you want cannot be done. But the fact that you are in trouble now is a big tell your design is flawed. Or if it is not your code design the code you have to work with (I feel your pain).
If you cannot do what you want to do it is because you have tightly coupled your code. I.e. you make use of the new keyword in classes instead of injecting them (dependency injection) into the classes / methods that need it.
Besides not being able to easily swap classes you would also have a gard time easily testing your units because of the tight coupling.
UPDATE
For completeness (for possible future readers): if the specific class would have been namespaced and you were allowed to change the namespace you could have thought about changing the namespace. However this is not really good practice, because it may screw with for example autoloaders. An example of this would be PSR-0. But considering you cannot do this either way I don't see it is possible what you want. P.S. you should not really use this "solution".
UPDATE2
It looks like there has been some overload extension at some time (way way way back), but the only thing I have found about it is some bug report. And don't count on it still working now either way. ;-) There simply is no real overloading in PHP.
Found something (a dead project which doesn't work anymore that enables class overloading): http://pecl.php.net/package/runkit
Possibly another project (also dead of course): http://pecl.php.net/package/apd
I am not a PHP programmer, but I think that AOP is what you are looking for. You can try some frameworks, for example listed in this answer.
From the Wikipedia article on the decorator pattern:
Subclass the original "Decorator" class into a "Component" class
So I think you're supposed to keep the class to be decorated private and expose only the already-decorated class.
I know there are loads of questions on this, I have done quite a bit of reading. I'd like to ask this in context of my project to see what suggestions you may have.
I have quite a large web application with many classes, e.g. users and articles (which i consider to be the main classes) and smaller classes such as images and comments. Now on a page, lets say for example an article, it could contain many instances of images and comments. Makes sense right? Now on say an articles page I call a static method which returns an array of article objects.
That's the background, so here are the questions.
Since building a large amount of the app I came to realise it would be very useful to have a core system class containing settings and shared functions. There for I extended all of my classes with a new core class. Seemed relatively simple and quick to implement. I know CodeIgniter does something similar. I feel now though my app is becoming a bit messy.
Question Is this a good idea? Creating an instance of core is exactly what I want when calling an instance of an article, but what about when i'm creating multiple instances using the static method, or calling multiple images or comments on a page. I'm calling the core class unnecessarily right? Really it only needs to be called once per page (for example the constructor defines various settings from the database, I don't want to this every time, only once per page obviously), but all instances of all classes should have access to that core class. Sounds exactly like I want the singleton approach, but I know that's a waste of time in PHP.
Here's an idea of what my code looks like at this point. I've tried to keep it as simple as I can.
class core {
public function __construct(){
...define some settings which are retrieve from the database
}
public function usefulFunction(){
}
}
class user extends core {
public function __construct(){
parent::__construct();
}
public function getUser($user_id){
$db = new database();
$user = /* Get user in assoc array from db */
$this->__setAll($user);
}
public static function getUsers(){
$db = new database();
$users = /* Get users from database in assoc array from db */
foreach($users as $user) {
$arrUsers[] = new self();
$arrUsers[]->__setAll($user);
}
return $arrUsers;
}
private function __setAll($attributes) {
foreach($attributes as $key => $value)
{
$this->__set($key, $value);
}
}
public function __set($key, $value) {
$this->$key = $value;
}
}
The other issue I'm having is efficiently using/sharing a database connection. Currently each method in a class requiring a database connection creates a new instance of the database, so on a page I might be doing this 5 or 10 times. Something like the dependency injection principle sounds much better.
Question Now if i'm passing the instance of the DB into the new user class, i know I need something like this...
class user{
protected $db;
public function __construct($db){
$this->db = $db;
}
... etc
}
$db = new database();
$user = new user($db);
... but when I want to run the static function users::getUsers() what is the best way to gain access to the database instance? Do i need to pass it as a variable in each static method? (there are many static methods in many classes). It doesn't seem like the best way of doing it but maybe there isn't another way.
Question If extending all of my classes off the core class as suggested in part 1, can I create an instance of the DB there and access that some how?
Question I also have various files containing functions (not oop) which are like helper files. What's the best way for these to access the database? Again i've been creating a new instance in each function. I don't really want to pass the db as a parameter to each one. Should I use globals, turn these helper files into classes and use dependency injection or something different all together?
I know there is lots of advice out there, but most info and tutorials on PHP are out of date and don't ever seem to cover something this complex...if you can call it complex?
Any suggestions on how to best layout my class structure. I know this seems like a lot, but surely this is something most developers face everyday. If you need any more info just let me know and thanks for reading!
You asked in a comment that I should elaborate why it is a bad idea. I'd like to highlight the following to answer that:
Ask yourself if you really need it.
Do design decisions for a need, not just because you can do it. In your case ask yourself if you need a core class. As you already have been asked this in comments you wrote that you actually do not really need it so the answer is clear: It is bad to do so because it is not needed and for not needing something it introduces a lot of side-effects.
Because of these side-effects you don't want to do that. So from zero to hero, let's do the following evolution:
You have two parts of code / functionality. The one part that does change, and the other part that is some basic functionality (framework, library) that does not change. You now need to bring them both together. Let's simplify this even and reduce the frame to a single function:
function usefulFunction($with, $four, $useful, $parameters)
{
...
}
And let's reduce the second part of your application - the part that changes - to the single User class:
class User extends DatabaseObject
{
...
}
I already introduced one small but important change here: The User class does not extend from Core any longer but from DatabaseObject because if I read your code right it's functionality is to represents a row from a database table, probably namely the user table.
I made this change already because there is a very important rule. Whenver you name something in your code, for example a class, use a speaking, a good name. A name is to name something. The name Core says absolutely nothing other that you think it's important or general or basic or deep-inside, or that it's molten iron. No clue. So even if you are naming for design, choose a good name. I thought, DatabaseObject and that was only a very quick decision not knowing your code even, so I'm pretty sure you know the real name of that class and it's also your duty do give it the real name. It deserves one, be generous.
But let's leave this detail aside, as it's only a detail and not that much connected to your general problem you'd like to solve. Let's say the bad name is a symptom and not the cause. We play Dr. House now and catalog the symptoms but just to find the cause.
Symptoms found so far:
Superfluous code (writing a class even it's not needed)
Bad naming
May we diagnose: Disorientation? :)
So to escape from that, always do what is needed and choose simple tools to write your code. For example, the easiest way to provide the common functions (your framework) is as easy as making use of the include command:
include 'my-framework.php';
usefuleFunction('this', 'time', 'really', 'useful');
This very simple tow-line script demonstrates: One part in your application takes care of providing needed functions (also called loading), and the other part(s) are using those (that is just program code as we know it from day one, right?).
How does this map/scale to some more object oriented example where maybe the User object extends? Exactly the same:
include 'my-framework.php';
$user = $services->store->findUserByID($_GET['id']);
The difference here is just that inside my-framework.php more is loaded, so that the commonly changing parts can make use of the things that don't change. Which could be for example providing a global variable that represents a Service Locator (here $services) or providing auto-loading.
The more simple you will keep this, the better you will progress and then finally you will be faced with real decisions to be made. And with those decisions you will more directly see what makes a difference.
If you want some more discussion / guidance for the "database class" please consider to take a read of the very good chapter about the different ways how to handle these in the book Patterns of Enterprise Application Architecture which somewhat is a long title, but it has a chapter that very good discusses the topic and allows you to choose a fitting pattern on how to access your database quite easily. If you keep things easy from the beginning, you not only progress faster but you are also much easier able to change them later.
However if you start with some complex system with extending from base-classes (that might even do multiple things at once), things are not that easily change-able from the beginning which will make you stick to such a decision much longer as you want to then.
You can start with an abstract class that handles all of your Database queries, and then constructs them into objects. It'll be easy to set yourself up with parameterized queries this way, and it will standardize how you interact with your database. It'll also make adding new object models a piece of cake.
http://php.net/manual/en/language.oop5.abstract.php
abstract class DB
{
abstract protected function table();
abstract protected function fields();
abstract protected function keys();
public function find()
{
//maybe write yourself a parameterized method that all objects will use...
global $db; //this would be the database connection that you set up elsewhere.
//query, and then pack up as an object
}
public function save()
{
}
public function destroy()
{
}
}
class User extends DB
{
protected function table()
{
//table name
}
protected function fields()
{
//table fields here
}
protected function keys()
{
//table key(s) here
}
//reusable pattern for parameterized queries
public static function get_user( $id )
{
$factory = new User;
$params = array( '=' => array( 'id' => $id ) );
$query = $factory->find( $params );
//return the object
}
}
You'll want to do your database connection from a common configuration file, and just leave it as a global variable for this pattern.
Obviously this is just scratching the surface, but hopefully it gives you some ideas.
Summarize all answers:
Do not use single "God" class for core.
It's better to use list of classes that make their jobs. Create as many class as you need. Each class should be responsible for single job.
Do not use singletones, it's old technique, that is not flexible, use dependecy injection container (DIC) instead.
First, the the best thing to do would be to use Singleton Pattern to get database instance.
class Db{
protected $_db;
private function __construct() {
$this->_db = new Database();
}
public static function getInstance() {
if (!isset(self::$_db)) {
self::$_db = new self();
}
return self::$_db;
}
}
Now you can use it like db::getInstance(); anywhere.
Secondly, you are trying to invent bicycle called Active Record pattern, in function __setAll($attributes).
In third, why do you wrote this thing in class that extends Core?
public function __construct(){
parent::__construct();
}
Finally, class names should be capitalized.