Jump to content

Factory Pattern; Dynamic Object Creation


karma
Go to solution Solved by requinix,

Recommended Posts

Hi All, 

 

I've been interested in writing a PHP pdo configuration file so that I can include connections in various files on my site. While I was looking up some possible solutions I stumbled across an interesting post on stack overflow http://stackoverflow.com/questions/11369360/how-to-properly-set-up-a-pdo-connection

 

The first responder suggested the code below. However, I don't understand how to access the connection and make query calls. I'm confused by how it's possible to return a variable name as an object { return new $name( $this->connection ) }.

 

Also, If someone could explain what the author means by  { $something = $factory->create('Something');}. Wouldn't the "Something" need to be a class? And how would that class get the db connection? 

 

All the best, 

 

Karma 

$provider = function()
{
    $instance = new PDO('mysql:......;charset=utf8', 'username', 'password');
    $instance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    $instance->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    return $instance;
};

class StructureFactory
{
    protected $provider = null;
    protected $connection = null;

    public function __construct( callable $provider )
    {
        $this->provider = $provider;
    }

    public function create( $name)
    {
        if ( $this->connection === null )
        {
            $this->connection = call_user_func( $this->provider );
        }
        return new $name( $this->connection );
    }
}

$factory = new StructureFactory( $provider );
$something = $factory->create('Something');
$foobar = $factory->create('Foobar');

Link to comment
Share on other sites

What that solution provides is more along the lines of dependency injection than a straight-up factory (though there is often overlap between the two). The anonymous function provides a way of injecting the PDO dependency into assorted classes, but it's still up to you to provide those "assorted classes".

 

In that example code, what you pass to StructureFactory::create() is a name of a class. That class needs to have a constructor that takes a pre-configured PDO object as its only argument. It doesn't have to worry about establishing the connection on its own.

class Something { // $factory->create('Something')

	private $pdo = null;

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

	// methods to do queries and whatever

}
There would also be a Foobar class along the same lines. The class is responsible for issuing queries through PDO and reading resultsets, that hasn't changed, but now it doesn't need to know connection information.

 

I'm confused by how it's possible to return a variable name as an object { return new $name( $this->connection ) }.

That's a feature of PHP called "variable variables". Basically, in some places you can use a variable's value instead of writing something directly into code.

In this case, $name is the name of a class and PHP will instantiate it as if you had written return new Something or return new Foobar.

Link to comment
Share on other sites

Hi Requinix, 

 

Thank you for your thorough response-- it was very helpful. In the factory class, Something, is it necessary to typecast $pdo? 

 

Would you suggest this approach to creating DB connections, or is there something more effective? In the Something class I wanted to be able to make insert & select queries. Below is the code I was planning on using, but I wasn't sure if this was the right approach. Do you see any improvements that can be made? Thank you so much for your help! I really appreciate it.

class Something
{
    private $pdo = null;

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

    public function push($sql)
    {
        try {

            $stmt = $this->pdo->query($sql);
            return $stmt->fetchAll(PDO::FETCH_ASSOC);

        } catch (PDOException $ex) {

            throw new Exception($ex->getMessage());
        }
        
    }

    public function fetch($sql, $params = array())
    {
        try {
            $stmt = $this->pdo->prepare($sql);
            $stmt->execute($params);

            return $stmt->fetchAll(PDO::FETCH_ASSOC);

        } catch (PDOException $ex) {

            throw new Exception($ex->getMessage());

        }
    }   

}
Edited by karma
Link to comment
Share on other sites

  • Solution

Thank you for your thorough response-- it was very helpful. In the factory class, Something, is it necessary to typecast $pdo?

Actually that's a "type hint": you're hinting to PHP and other developers what type of argument is supposed to be passed.

A type cast is

$int = (int)$string;
No, it's not necessary, but it's a Good Idea.

 

Would you suggest this approach to creating DB connections, or is there something more effective?

The anonymous function style it has been becoming more popular over the last year or so, with a couple frameworks designed in a similar style, but like that it's a bit too limiting for my taste.

And frankly I don't see what benefit the function and StructureFactory, at least as they currently are, offer over just making a new PDO object: you're still writing the connection information and the name of the PDO wrapper class (ie, the class you use to interact with PDO) directly in your code.

 

I prefer the global configuration/settings style. For example, a JSON file containing

{
	"databases": {
		"connection1": {
			"dsn": "mysql:...;charset=utf8",
			"username": "username",
			"password": "password",
			"class": "Something"
		},
		"connection2": {
			"dsn": "mysql:...;charset=utf8",
			"username": "username",
			"password": "password",
			"class": "Foobar"
		}
	}
}
and

abstract class Db {

	protected $pdo = null;

	protected function __construct(PDO $pdo) {
		$this->pdo = $pdo;
	}

	public static function get($name = "default") {
		// somehow get the information from the configuration file
		// probably a class somewhere to help with this
		$info = Configuration::get("databases", $name);
		if (!$info) {
			throw new Exception("No database configuration for {$name}");
		}

		$pdo = new PDO($info["dsn"], $info["username"], $info["password"]);
		$class = $info["class"];
		return new $class($pdo);
	}

}
(as a rough version - I'd do more, like use interfaces, add some inheritable helper methods...)

// the "connectionN" name, which is a horrible name to use, is a fixed value and completely separate from the information
$something = Db::get("connection1");
$foobar = Db::get("connection2");
The benefit is that while you still have to put the connection information somewhere (obviously), you aren't putting the information in code but rather somewhere that can be easily edited. Edited by requinix
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.