0

Possible Duplicate:
What is the best method for getting a database connection/object into a function in PHP?
Database and OOP Practices in PHP

I am trying to build an OOP shopping cart.

At present, it is half OOP, and half procedural... e.g.

function removeFromCart() { 
    require_once('/.../.../connectPDO.php');
    $db = connectPDO();

    $sql = 'DELETE FROM Quotes WHERE User = :user and ProductId = :pid';
    $stmt = $db->prepare($sql); 
    $stmt->execute(array(':user' => $user, ':pid' => $pid));
}

My problem is that if I wish to add to cart, then in my function addToCart, I will need to require the db connection again.

This seems like a complete waste, considering every function will need to contain the following:

    require_once('/.../.../connectPDO.php');
    $db = connectPDO();

I am aware that this is completely in-efficient, and was wondering if anybody could help me write a skeleton OOP cart class which uses the above connection to connect to the DB?

Does this go in the constructor??? Will this stay alive when a user navigates from one page to another at the front end?

I am new to OOP and am completely lost.

Many thanks in advance.

Community
  • 1
  • 1
Gravy
  • 12,264
  • 26
  • 124
  • 193
  • This is asked frequently. There are many ways to handle it, and several are outlined in the linked question. Here's another for additional ideas: http://stackoverflow.com/questions/8438237/mysql-connection-globally-or-in-object – Michael Berkowski Dec 29 '12 at 22:41
  • 1
    Finally, [this one may have the clearest answer of the three](http://stackoverflow.com/questions/1716652/database-and-oop-practices-in-php) – Michael Berkowski Dec 29 '12 at 22:44

2 Answers2

4

Something like the following should get you started:

$pdo = new PDO('your dsn');
$cartData = new CartData($pdo);
$cart = new Cart($cartData);

class CartData
{
    private $dbConnection;

    public function __construct(PDO $dbConnection)
    {
        $this->dbConnection = $dbConnection;
    }

    public function removeItem($userId, $productId) { 
        $sql = 'DELETE FROM Quotes WHERE User = :user and ProductId = :pid';

        $stmt = $this->dbConnection->prepare($sql);
        $stmt->execute(array(':user' => $userId, ':pid' => $productId));
    }
}

class Cart
{
    private $cartData;

    public function __construct(CartData $cartData)
    {
        $this->cartData = $cartData;
    }

    public function removeItem($userId, $productId) { 
        $this->cartData->removeItem($userId, $productId);
    }
}

Note that I have removed the database calls from the actual Cart class, because that would only make it hard / impossible to swap to another database engine at some point. Or perhaps you might want to introduce a complete other way of storing your data (ahum unit testing). Also note that I have used dependency injection to give the classes the objects they need to be able to perform whatever it is they are responsible for.

I have used type hinting for the class objects which are being injected, however it would have been better to type hint against an interface, because that would make it easy to swap out the classes for other classes. And I strongly suggest you use interfaces (what I wrote above is simply an example to get the idea). This also make it pretty easy to created unit tests for your code.

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
  • Why do you inject `CartData` into `Cart`? Wouldn't it be better to have a `CartData` HAS_A `Cart` relation instead of `Cart` HAS_A `CartData`? – Wouter J Dec 29 '12 at 23:10
  • The cart needs a storage mechanism. Not the other way around. – PeeHaa Dec 29 '12 at 23:11
1

At every page request, you make a new database connection. You can't share a connection between requests.

There are a couple of different design patterns (best practises) for handling this. For all of them, you need a DB abstraction layer (like PDO, Doctrine DBAL or something else).

Dependency Injection (recommend)

The most used design pattern to handle this is dependency injection:

class Foo
{
    /**
     * @var DatabaseAbstractionLayer
     */
    private $dbal;

    public function __construct(DatabaseAbstractionLayer $dbal)
    {
        $this->dbal = $dbal;
    }

    public function methodThatUsesTheDbal()
    {
        $this->dbal->query(...);
    }
}

$db = new DatabaseAbstractionLayer();
$foo = new Foo($db); // constructor injection

$bar = new Bar();
$bar->setDbal($db); // setter injection

$baz = new Baz();
$baz->dbal = $db; // property injection (almost never used)

You can use a service container to handle this easily (example with pimple):

$container = new Pimple();
$container['db'] = $container->share(function ($c) {
    return new DatabaseAbstractionLayer();
});
$container['foo'] = function ($c) {
    return new Foo($c['db']);
};

$foo = $container['foo']->methodThatUsesDbal();

Singleton

class DatabaseAbstractionLayer
{
    private static $_instance;
    // ...

    private function __construct()
    {
        // ...
    }

    // ...

    public static function getInstance()
    {
        if (null === self::$_instance) {
            self::$_instance = new static();
        }

        return self::$_instance;
    }
}

class Foo
{
    public function methodThatUsesDbal()
    {
        $db = DatabaseAbstractionLayer::getInstance();
        // ...
    }
}

Registery

Registery::set('db', new DatabaseAbstractionLayer());

class Foo
{
    public function methodThatUsesRegistery()
    {
        Registery::get('db');
        // ...
    }
}
Wouter J
  • 41,455
  • 15
  • 107
  • 112
  • 2
    In my world there is only one possibility: DI ;) – PeeHaa Dec 29 '12 at 22:51
  • I completely agree with you guys, but I wanted to give an overview of the most used in PHP world. Zend Framework 2 still uses singletons and I thought Laravel uses Registeries. – Wouter J Dec 29 '12 at 23:07
  • /somewhat offtopic [*Microframework* (n): A small amount of crap. See also *Framework* (n): A large amount of crap.](http://chat.stackoverflow.com/transcript/11?m=6055399#6055399) ;-) – PeeHaa Dec 29 '12 at 23:09
  • a) agree with @PeeHaa, not everything frameworks do, is good. b) ZF2 and singletons, I haven't found any in 2.0.5 so far. c) Laravel also uses loads of static calls afaim, I don't really get the Laravel hipe so far. – markus Dec 29 '12 at 23:15
  • @markus-tharkun ZF2 has singletons, the GobalSharedEventManager for instance. – Wouter J Dec 30 '12 at 08:03
  • I rephrase... Singleton and Registry are anti-patterns in many cases. Avoid using them when developing a modern PHP application. When refactoring or maintaining legacy code, they can be usefull. – markus Dec 30 '12 at 14:11