Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Making only one DB Connection with a Property

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $6.00

I can't stand it any longer. The app is small, but our database credentials are already duplicated and hidden inside this one class. What if we added a second table - like battle - and a BattleLoader class? At this rate, we'd be copying and pasting the database password there too. Gross.

Isolate the PDO Creation in ShipLoader

Enough is enough. Let's fix this little by little. First, I don't want to duplicate the new PDO code twice in this class. To fix that, create a private function getPDO() - private because - at least so far - we only want to call this from inside ShipLoader. Copy the new PDO line and the one below it and put them here. Return $pdo and let's even add some nice PHPDoc:

... lines 1 - 2
class ShipLoader
{
... lines 5 - 48
/**
* @return PDO
*/
private function getPDO()
{
$pdo = new PDO('mysql:host=localhost;dbname=oo_battle', 'root');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $pdo;
}
... lines 59 - 67
}
... lines 69 - 70

You know what's next: use this above with: $pdo = $this->getPDO(). Repeat this in the other spot:

... lines 1 - 2
class ShipLoader
{
... lines 5 - 23
*/
public function findOneById($id)
{
$statement = $this->getPDO()->prepare('SELECT * FROM ship WHERE id = :id');
... lines 28 - 35
}
... lines 37 - 59
private function queryForShips()
{
$statement = $this->getPDO()->prepare('SELECT * FROM ship');
... lines 63 - 66
}
}
... lines 69 - 70

Head back to the homepage! Ha! Nothing broken yet.

Prevent Multiple PDO Objects

Ok, a little bit better. Here's the next problem: what if a single page calls findOneById() multiple times? Well, getPDO() would be called twice, two PDO objects would be created and this would mean that two database connections would be made. Such waste! We only need one connection and we only need one PDO object.

How can we guarantee that only one PDO object is created?

By using a property! But in a way that we haven't seen yet. Up until now, we've only put properties on our model classes - like Ship - and that has been to hold data about the object, like name, weaponPower, etc.

In service classes - any class whose main job is to do work instead of hold data - you use properties for two reasons: to hold options about how the class should behave. And to hold other tools - like a PDO object.

Create a private $pdo property:

... lines 1 - 2
class ShipLoader
{
private $pdo;
... lines 6 - 71
}
... lines 73 - 74

Now, we can use a little trick thanks to OO! Down in getPDO(), add an if statement to check if the pdo property is equal to null. Why of course it is! So far, nothing is setting it, so it's always null. But now, if it is null, move the new PDO() code into this and then assign this to the pdo property. Finish by returning $this->pdo:

... lines 1 - 2
class ShipLoader
{
private $pdo;
... lines 6 - 53
private function getPDO()
{
if ($this->pdo === null) {
$this->pdo = new PDO('mysql:host=localhost;dbname=oo_battle', 'root');
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return $this->pdo;
}
... lines 63 - 71
}
... lines 73 - 74

The first time you call this, $this->pdo is null so we create a new PDO object and set the property. Then, if someone calls this during the same request, the pdo property will already be an object, so it'll skip creating a second one and just return it. Boom!

This is the first time we've seen a service class - something that does work for us - have a property. And in service classes, properties aren't about holding data that describe something - like a Ship - they're used to store options about how the class should work or other useful objects that class needs.

We shouldn't notice any difference - so refresh to try it. Yes! Think about it: thanks to objects, we were able to reduce the number of database connections being created by touching one file and not breaking anything.

Leave a comment!

10
Login or Register to join the conversation
Marc R. Avatar
Marc R. Avatar Marc R. | posted 3 years ago

Hi !
What do you mean by

if someone calls getPDO() during the same request

?
This trick seams to be interesting only if the object lives during the whole session, not only during a simple request.

Reply

Hey Marc R.

I don't think an object can live after the request-response cycle. That trick is useful because creating a PDO instance is expensive and it's likely that it will be used more than one time during a request

Cheers!

Reply
Default user avatar

Hi!

You have a minor typo at the final code sample:
if ($this->pdo === null) {
$this->pdo = new PDO('mysql:host=localhost;dbname=oo_battle', 'root');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

I bet it must be "$this->pdo->setAttribute" instead of "$pdo->setAttribute".

Reply

Hi Ivan,

You're absolutely right! I fixed it, but it takes a while when our cache regenerate and you will see these changes.

Thanks for noticing us!

Reply
Default user avatar
Default user avatar Thrasos Thrasyvoulou | posted 5 years ago

Why not setting the $pdo property when the object is initiated using the constructor?

Reply

Hey Thrasos,

Really good question! Actually, since we don't know what is dependency injection and dependency injection container yet, it was just a simple refactoring to avoid code duplication. And the another point is that we have a lazy loading PDO instance here, since we create it *only* when the getPDO() method is called at first time. But anyways, it was just a intermediate refactoring and we will consider more cool stuff as DI Container further.

Cheers!

Reply
Default user avatar
Default user avatar Thrasos Thrasyvoulou | Victor | posted 5 years ago

Thanks for the quick reply! Just a small question. Would it be a better practice (without knowing what DI is) to create a constructor method and within call a createPDO method that would initiate a PDO object and set it at a $pdo property?

Reply

Hey Thrasos,

Yes, I think so as long as you have a hard dependency with PDO object. But as we see, ShipLoader has and just won't work without PDO instance. Actually, we will do this further in the course.

Cheers!

Reply
Default user avatar
Default user avatar Thrasos Thrasyvoulou | Victor | posted 5 years ago

Yes as I can see from next videos it's a bad practice to create services on a service class.

Reply

Yeah, that's why using DI Container is the best practice here.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

userVoice