Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

PHP

Robert Walker
Robert Walker
17,146 Points

PDO help

Ive been doing a lot of reading on PDO recently and when using PDO in my project ive hit a problem that isn't really explained all too well in what I read already on PDO.

I have a PDO connection to my database and I use it in most all my pages, currently it is in the header of my site which is also a part of the problem, im almost sure that this is not the best way to have it but from everything I have read so far wrapping PDO in a "database" class should be avoided and this is were im having a problem finding out why and what I should do instead.

I have:

$sthandler = $db->prepare("SELECT user_name FROM users WHERE user_name = :name");
$sthandler->bindParam(':name', $username);
$sthandler->execute();

if($sthandler->rowCount() > 0){
    $Err = "Username is taken, please select another!";
}

Now this and other common tasks like it will feature heavy in my site right the way through so surely having these type of tasks wrapped in functions within a "database" class would be needed or at the very least I would need the connection to the database otherwise I would have repeated code littered through my site.

I understand that PDO is a class already but then ive read you shouldnt extend the PDO class and have your own functions in it either.

Im totally confused on how im meant to make my own functions for common tasks involving the database without either extending the PDO class or creating my own class.

I do apologise is this comes off as a really stupid question maybe im looking at it too closely to see the bigger picture here.

Chris Shaw
Chris Shaw
26,676 Points

Hi Robert,

Where did you read that you shouldn't extend the PDO class? I have personally done this as it creates code that's much easier to move around because of how modular/abstract it is.

I agree with Chris. I put my database code into one class, in one file and just require the file on a page that needs the connection.

Here is an example file and class that I use, just to show what it looks like.

<?php

    class Model {

        private $host   = "localhost";
        private $db     = "testing";
        private $user   = "testing";
        private $pass   = "testing";

        public function connect($host, $db, $user, $pass) {
            // connection code          
        }

        public function insert_into($table, $data) {
            // insert an associative array of data into table
        }
    }

    /*
        Usage:

        Model::connect();
        Model::insert_into($users, $logs);

    */

?>
Robert Walker
Robert Walker
17,146 Points

There have been a lot of different places with the same sort of advice, never extend or wrap the PDO class as it just creates problems and throws away the good parts of PDO.

http://community.sitepoint.com/t/extending-pdo-class/7155

A comment from there too:

A while ago I was facing the same situation and found the hard way that it's not a good idea to extend PDO. A better way would be to write a wrapper class for PDO. It's fine to extends PDOException but not PDO classes.

http://codereview.stackexchange.com/questions/29362/very-simple-php-pdo-class

•Building an abstraction class on a user-friendly raw-API like PDO is, IMHO, like a broken pencil: Pointless

•Extending PDO means you don't have to create pass-through methods that call the wrapped API (like creating a prepare method), but it does mean there is a chance you'll have to refactor your code whenever PDO changes

•If you still feel like using a wrapper object, either consider wrapping it around something less well known, but in some respects better (mysqli_* is what I'm hinting at), but implement the magic __call and, if required __callStatic methods.

•Again, if you're going to procede: work on an entire abstraction layer, that allows for users to map tables to data models, and take it from there. Ensure that those data-models can be used easily to build HTML forms, for example in order for those forms to be linked to the DB in the blink of an eye, bot don't forget about sanitizing your data, of course.

http://grokbase.com/t/php/php-general/127d91638c/pdo-extend-or-encapsulate

I encapsulated it in my own database class. One reason for that is that the PDO "class" is really two classes, which is fine for low level drivers but not production code that I have to use daily. A second reason is that many of the methods are not really necessary in a production environment.

The three above are just some examples there are many many posts on it all but just as I think, ok lets wrap it I read a comment on how bad that is, then when I think actually extending it would be better I read another article or comment that says that's a bad idea too.

I cant seem to find the correct way to do it and from all the reading I havnt seen a good alternative to wrapping or extending either.

From the posted grokbase link, I agree with this comment

Use encapsulation: When a class has a "has-a" relation-ship with other class you should use encapsulation. For example, If MyDB is a class that "has-a" pdo driver in it, then PDO will be encapsulated inside MyDB class. This is also called containment. MyDB should contain PDO.

I choose to 'wrap' PDO functionality because I'd rather call a higher-level function that abstracts away the messiness of database interactions. I cannot be convinced that wrapping or encapsulating functionality is a bad thing. Code re-usability is a good thing.

<?php

// I'd rather write
Model::connect();

// that calls my wrapper
public function connect($host, $db, $user, $pass) {
    try {
        $dbh = new PDO("mysql:host=$host;dbname=$db;", $user, $pass);
        $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch(PDOException $e) {
        echo "Could not connect to the database.<br>" . PHP_EOL;
        echo $e->getMessage();
        exit;
    }
    return $dbh;
}

// over having to write that code each time

?>
Robert Walker
Robert Walker
17,146 Points

Personally, I must say that close to all PDO derived/based classes I've seen so far suffer from the same problem: They are, essentially, completely pointless. Let me be clear: PDO offers a clear, easy to maintain and neat interface on its own, wrapping it in a custom class to better suit the needs of a particular project is essentially taking a decent OO tool, and altering it so that you can't reuse it as easily in other projects.

The stack answer by the same guy that wrote the above comment is a rather good read.

The more I read the more unclear it becomes though.

[...] and altering it so that you can't reuse it as easily in other projects.

My code is reusable. My higher level function accepts arguments for host, db, user, and pass.

The function I posted earlier for insert_into was not initially designed to be re-usable, but can easily be fixed by accepting a db connection instead of calling Model::connect() from within itself for a database handle.

<?php

// change this
public function insert_into($table, $data) {
    // insert an associative array of data into table
}

// to this
public function insert_into($dbh, $table, $data) {
    // use the passed in $dbh to insert an associative array of data into table
}

// call it like this
$dbh = Model::connect($host, $db, $user, $pass);
Model::insert_into($dbh, $table, $data);

// done, and reusable

?>
Robert Walker
Robert Walker
17,146 Points

Im not saying your code is not re-usable at all, what I am saying is that from these articles and posts ive found online and have read, both extending and wrapping seems to have its downsides and im not too sure on which way I should go. I can only assume I need to follow one of these option as ive not seen anything else suggested as of yet and was wondering if anyone here knew anything better.

I was in the process of writing a class and thought I should check what others use to give myself a better idea of what I need and how I should use it but then got a shock when the first things I read was not to extend or wrap, the more I read the more confusing it got for me.

Yep, sorry, I didn't mean to make you feel defensive. I understand the quotes are from articles and you're trying to figure out which way to go with your code.

While I've been opinionated on this matter, it really doesn't matter too much - unless you're working for someone who has an expectation of if being done a certain way. But, on your own, if it works, then great! Try not to get too caught up in which way is best.

Robert Walker
Robert Walker
17,146 Points

I understand, I didn't mean to come across defensive, im always happy to read what others have to say as most know a lot more than myself.

Not too sure how this idea stands but would making a PDO object and storing it in a global variable work?

Robert Walker
Robert Walker
17,146 Points

That doesn't really apply here though Tom, this is more a question on what is the right way to go about something as reading blog posts and places like stack has confused me more than help me. I don't have an opinion because I don't hold enough knowledge to form one.

I don't want to get half way though a project to then realise I've made a huge mistake that could of been avoided by simply asking a question and getting information on the subject.

2 Answers

Shameless twitter quote.

Always be wary of developers who’s only source of opinions are blog posts they’ve read.

The internet is great, but my advice would be to try it out! Just code it and see. Do you get stuck? Is the code you've written awful? Then do it again, make it better and learn from it.

Lol that is actually incredibly ironic.. But my point still stands!

Robert Walker I followed a similar ethos until I realised I was spending more time searching for the perfect method than actual coding. Methods and principles fall in and out of trend as quickly as high street fashion, so I stand by my advice to not get too hung up on what a few people have written in a blog post and instead put fingers to keys!

Why not set up a git branch to sandbox some ideas? Merge the good branches, delete the crap ones. I look at it like this: The best 'right' answer is the method that does the job in the shortest amount of time. Perfecting how you write comes with experience and the mistakes you make along the way. When your project has a user count of 1 million, then worry about the semantics! Unless your project already has 1 million users and that's why you're worrying about the semantics..

Robert Walker
Robert Walker
17,146 Points

Not really sure what you suggest there is the best advice to pass on to those just starting to code, while everyone needs to make mistakes and learn from them, just telling people to blindly code and fix the issues later is bad for so many different reasons.

Getting the job done in the shortest amount time is never the right answer, unless its done corretly. It doesn't matter if your site has 1 million users or 1, if you leave holes open in production code for someone and it then leads to all their users credit card information being stolen, I doubt you will find a lot of work after.

Taking X amount of time first to make sure you understand and correctly follow how things should be done will always save you time in the long run.

You're right - that's is rubbish advice. What I'm trying to get at is - if you can't find the answer you're looking for, do some practice runs, think about what you might need and don't let it stop your progress. Sorry I couldn't be of more help. If you haven't found a solution I would reach out to a teacher by email or keep on with that reading .

Let us know your final method!

If it were me, I wouldn't extend the PDO class. Not because it's necessarily a bad thing to do, just because it seems tidier. If someone comes along afterwards and takes a look at your code, there's no easy way of knowing how the data is being fiddled with between query and response without being told otherwise.

Your original question revolved around extending the PDO object to suit the needs of your application logic i.e. class a specific result as 'invalid'. Already this sets off alarm bells. If you think about separating concerns, the PDO object has one job - interfacing with the database. Should it know what's valid and what's not? Probably not. It doesn't need to know about error messages or what to do or say in the event of an empty results set. It's a persister and getter, not a thinker.

Because of this, I don't think you have any need to extend your PDO object at all in light the example you've given. Your example is a type of validation - you're validating something to be unique and creating an appropriate error message. So why not create a new class called 'UniqueValidator' or 'Validator' with a method specific to a unique check. A quick example:

<?php

public function validateUnique($table, $field, $value)
{
  // Do the query on the given table and column/field for value
  // returns an array of results
  // if it false (empty), provide an accessible error message in this object
  $this->setErrorMessage($field, 'unique', "{$field} must be unique.");
  // You found something invalid, update the state of the validator
  $this->valid = false;

}

This function or method should probably be able to validate the uniqueness of anything you throw at it so table names and columns shouldn't be hardcoded.

This validator object (or function etc...) sounds like it should have a way to connect to the database. But should it know or be aware of how it connects? Again probably not, so It's dependent on having that connection passed into it i.e. dependency injection.

<?php

use PDO;

class Validator {

  /**
   * @var PDO $pdo
   */
  protected $pdo;

  /**
   * Construtor
   *
   * @param PDO $pdo
   */
  function __construct(PDO $pdo)
  {
    $this->pdo = $pdo;
  }

}

There are a few traps here - what should happen if you change database type? That could mean database syntax changes in every method of your validating methods or functions. This is where you might want to relax a bit - it depends how much you want to cover yourself - but generally speaking your database is likely to stay the same for the first few versions of your application.

In this situation, instead of passing through the PDO object into the validator you might instead pass through an adapter - an object that can translate generic calls into database specific commands. You might have an adapter for mysql, another for sqlite etc... This is getting a little more advanced so to learn more, I would dive into all the php MVC frameworks to see how they do it.

In summary, the logic you're trying to cut down on is application specific and I wouldn't expect to query the database and have a string returned. Testing query results for validity should be placed around your controlling code - the code that makes the decision if the database return is valid / not valid - and what to do in different scenarios. It's perfectly fine to repeat logic like this, but it's always better to automate repetitive sequences.

This is a rapid example, but should give you an idea of a flow you're looking for.

<?php

$connection = new DBConnector($host, $port, $username, $password);

$email = $_POST['email'];

$validator = new Validator($connection);

$validator->validateUnique( 'users', 'email_address', $email);

if ( ! $validator->isValid())
{
    addToErrorMessages($validator->errors());
    // return to previous page.
}

You're also hiding repetitive code and controlling exactly what to do and messages to give - and you wouldn't need to touch your database class or validator class when you change behaviour.

Hope this helps.