PHP: PDO and MySQL

This page forms the code summary of 07.databases.html, part of the Webscripting1 — Serverside Webscripting course, part of the Professional Bachelor ICT study programme, taught at Odisee, Ghent, Belgium. The materials and this summary were developed by Bram(us) Van Damme, lecturer ICT at Odisee, who blogs over at bram.us and Twitters as @bramus. The materials and this summary may be used freely, as long as credit to Bramus is present and a clear an upfront link to ikdoeict.be remains in place. Suggestions and additions may be mailed to Bramus, or sent via a pull request on GitHub.

Establishing a Database Connection

<?php

// Include config + functions used throughout the project
require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/functions.php';

// Make Connection
try {
	$db = new PDO('mysql:host=' . DB_HOST .';dbname=' . DB_NAME . ';charset=utf8mb4', DB_USER, DB_PASS);
	$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	$db->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
} catch (Exception $e) {
	showDbError('connect', $e->getMessage());
}

Put this in config.php

// Database config
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', 'Azerty123');
define('DB_NAME', 'fotofactory');

Put this in functions.php

// Error Logging and User Redirects
function showDbError($type, $msg) {
    file_put_contents(__DIR__ . '/error_log_mysql', PHP_EOL . (new DateTime())->format('Y-m-d H:i:s') . ' : ' . $msg, FILE_APPEND);
    header('location: error.php?type=db&detail=' . $type);
    exit();
}

Building and executing queries, the basic edition

Don't use this, use prepared statements (see below) instead. Contents only placed here for reference/context.

Use PDO::query() to execute queries that return a resultset (SELECT, DESCRIBE, SHOW, etc.)

<?php

$stmt = $db->query(
	'SELECT * FROM collections WHERE user_id = ' . $db->quote(2, PDO::PARAM_INT) . ' OR name = ' . $db->quote('russia', PDO::PARAM_STR)
);

Don't forget to use PDO::quote() to prevent SQL Injection!

Use a fetch function to fetch the data (see further)

Use PDO::exec() to execute queries that do not return resultsets (UPDATE, DELETE, INSERT, etc)

<?php

$stmt = $db->exec(
	'DELETE FROM collections WHERE user_id = ' . $db->quote(20, PDO::PARAM_INT)
);

Don't forget to use PDO::quote() to prevent SQL Injection!

Use an extra function to fetch meta information about the result (see further)

Building and executing queries, using Prepared Statements

In short: Use PDO::prepare() to get a PDOStatement, bind parameters onto it using PDOStatement::bindValue(), and then execute that statement using PDOStatement::execute()

Binding parameters one by one

Don't use this, bind all parameters in one go (see below) instead. Contents only placed here for reference/context.

<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE user_id = :user_id OR name = :name');
$stmt->bindValue(':user_id', 2, PDO::PARAM_INT);
$stmt->bindValue(':name', 'russia', PDO::PARAM_STR);
$stmt->execute();
<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE user_id = ? OR name = ?');
$stmt->bindValue(1, 2, PDO::PARAM_INT);
$stmt->bindValue(2, 'russia', PDO::PARAM_STR);
$stmt->execute();

Binding all parameters in one go

<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE user_id = ? OR name = ?');
$stmt->execute(array(2, 'russia'));

Processing the results of queries

Processing queries that produce resultsets (SELECT, DESCRIBE, SHOW, etc.)

In short: Use a fetch function to retrieve data from the statement.

Fetch rows one by one using PDOStatement::fetch()

One row returned:

<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE id = ?');
$stmt->execute(array(1));
$collection = $stmt->fetch(PDO::FETCH_ASSOC);

var_dump($collection);
array(3) {
	'id' =>
	string(1) "1"
	'name' =>
	string(6) "russia"
	'user_id' =>
	string(1) "4"
}

Use a while loop to iterate multiple returned rows:

<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE id != ?');
$stmt->execute(array(1));

while ($collection = $stmt->fetch(PDO::FETCH_ASSOC)) {
	var_dump($collection);
}

Other interesting Fetch Types: PDO::FETCH_OBJ, PDO::FETCH_NUM and PDO::FETCH_GROUP

Fetch all rows using PDOStatement::fetchAll()

<?php

$stmt = $db->prepare('SELECT * FROM collections WHERE user_id = ? OR name = ?);
$stmt->execute(array(2, 'russia'));

$collections = $stmt->fetchAll(PDO::FETCH_ASSOC);

var_dump($collections);
array(3) {
	[0] =>
	array(3) {
		'id' =>
		string(1) "1"
		'name' =>
		string(6) "russia"
		'user_id' =>
		string(1) "4"
	}
	[1] =>
	array(3) {
		'id' =>
		string(1) "2"
		'name' =>
		string(6) "serbia"
		'user_id' =>
		string(1) "2"
	}
	[2] =>
	array(3) {
		'id' =>
		string(1) "4"
		'name' =>
		string(8) "TestColl"
		'user_id' =>
		string(1) "2"
	}
}

Other interesting Fetch Types: PDO::FETCH_OBJ, PDO::FETCH_NUM and PDO::FETCH_GROUP

Fetch a single value/column (from the first row) using PDOStatement::fetchColumn()

<?php

$stmt = $db->prepare('SELECT name, user_id FROM collections WHERE user_id = ? OR name = ?');
$stmt->execute(array(2, 'russia'));

$value = $stmt->fetchColumn();

var_dump($value);
string(6) "russia"

Add a numeric index (starting from 0) to define which column you want to extract

<?php

$stmt = $db->prepare('SELECT name, user_id FROM collections WHERE user_id = ? OR name = ?');
$stmt->execute(array(2, 'russia'));

$value = $stmt->fetchColumn(1);

var_dump($value);
string(1) "4"

Processing queries that do not produce resultsets (UPDATE, DELETE, INSERT, etc)

In short: Use an extra function to fetch meta information about the result

Use PDOStatement::rowCount() to get to know the number of affected rows made by UPDATE and DELETE queries

<?php

$stmt = $db->prepare('UPDATE collections SET name = REVERSE(name) WHERE user_id = ?');
$stmt->execute(array(2));
$affectedRows = $stmt->rowCount();

var_dump($affectedRows);
int(2)

Use PDO::lastInsertId() to get to know the last inserted id (AUTO_INCREMENT)

<?php

$stmt = $db->prepare('INSERT INTO collections (name,user_id) VALUES (?,?)');
$stmt->execute(array('testalbum', 2));
$insertId = $db->lastInsertId();

var_dump($insertId);
string(1) "5"