Skip to content

Instantly share code, notes, and snippets.

@AlanD20
Last active August 8, 2023 20:34
Show Gist options
  • Save AlanD20/3fb8036ad2cfc6c8977d39b681c86d49 to your computer and use it in GitHub Desktop.
Save AlanD20/3fb8036ad2cfc6c8977d39b681c86d49 to your computer and use it in GitHub Desktop.
PHP Custom Helper Classes

PHP Custom Helper Classes

A few PHP helper classes I have created. You could copy the file content and use it in your projects.

Database Documentation

Database Documentation

A simple class that makes executing query easier with full PDO and bindings.

  • Easy to setup
  • Easy way to dump query.
  • Supports raw SQL queries.
  • Supports both associative and indexed bindings.

Setup

Simply copy the entire file and put it somewhere. For this example we will put it in app/Service/Database.php

Create New Instance

require __DIR__ . '/app/Service/Database.php';

// Create an instance and provide database credentials
$db = new Database('mydatabase', 'root', '');

Raw Query

You may perform raw queries, here are a few examples

// raw query
$db->rawQuery('
    CREATE TABLE users (
      id INT PRIMARY KEY AUTO_INCREMENT,
      name VARCHAR(255) NULL,
      email VARCHAR(255) NULL,
      phone VARCHAR(255) NULL
    )');

// Perform any raw queries
$db->rawQuery('
    INSERT INTO users(name, email, phone) VALUES ("aland", "[email protected]", "123");
');

// Raw results from PDO object
$rawResult = $db->rawQuery('SELECT * FROM users');

Built-in Methods

You may use built-in methods to easily work with SQL queries.

Select Table/Columns

Before each query, you may select a table and its columns to perform queries on.

// You could use literal strings for columns
$db
    ->table('users')
    ->columns('name, email');

// You may use array of strings that each element represent a column name
$db
    ->table('users')
    ->columns(['name', 'email']);

Select

Args:

  • condition (string|array) optional.
  • bindings (array) optional.
// Fetch All
$db
    ->table('users')
    ->columns(['name', 'email'])
    ->select()
    ->fetchAll();

// All Columns
$db
    ->table('users')
    ->columns('*')
    ->select()
    ->fetchAll();

// Fetch One
$db
    ->table('users')
    ->columns('*')
    ->select()
    ->fetch();

// Conditions
$db
    ->table('users')
    ->columns('*')
    ->select('id = 3')
    ->fetch();

// Conditions with bindings, supports associative & indexed arrays
$db
    ->table('users')
    ->columns('*')
    ->select('id = ? and name = ?', [3, 'aland'])
    ->fetch();

$db
    ->table('users')
    ->columns('*')
    ->select('id = :id and name = :name', ['id' => 3, 'name' => 'aland'])
    ->fetch();

Insert

Args:

  • values (string|array) required.
  • bindings (array) optional.
// No bindings, each value has to be in single quotes.
$db
    ->table('users')
    ->columns(['name', 'email'])
    ->insert("'aland', '[email protected]'");

// bindings, supports associative & indexed arrays. Returns primary key.
$db
    ->table('users')
    ->columns(['name', 'email'])
    ->insert('?, ?', ['aland','[email protected]']);

$db
    ->table('users')
    ->columns(['name', 'email'])
    ->insert(':name, :email', [
        'name' => 'aland',
        'email' => '[email protected]',
    ]);

Insert Many

Args:

  • list (array) required.
// two-dimensional array, only supports indexed values with corresponding columns
$db
    ->table('users')
    ->columns(['name', 'email'])
    ->insertMany([
      ['johndoe', '[email protected]'],
      ['jackfrank', '[email protected]'],
    ]);

Update

Args:

  • condition (string) optional.
  • bindings (array) optional.
// No bindings, each value has to be in single quotes.
$db
    ->table('users')
    ->columns([
      'name' => 'aland',
      'email' => '[email protected]',
    ])
    ->update('id = 2');

// 2 dimensional array, only supports indexed values with corresponding columns
// Notice all placeholders for where condition must be sequentially start after
// the placeholder columns
$db
    ->table('users')
    ->columns("name = ?, email = ?")
    ->update('id = ?', ['aland', '[email protected]', 2]);

$db
    ->table('users')
    ->columns("name = :name, email = :email")
    ->update('id = :id', [
       'name' => 'aland',
       'email' => '[email protected]',
       'id' => 2
     ]);

// You could do the following as well, but don't forget the '?'
$db
    ->table('users')
    ->columns([
       'name' => '?',
       'email' => '?',
    ])
    ->update('id = ?', ['aland', '[email protected]', 2]);

Delete

Defining columns are not required.

Args:

  • condition (string) optional.
  • bindings (array) optional.
// No bindings, each value has to be in single quotes.
$db->table('users')->delete('id = 2');

// 2 dimensional array, only supports indexed values with corresponding columns
$db->table('users')->delete('id = ?', [2]);

$db->table('users')->delete('id = :id', ['id' => 2]);

Dumping Queries & Debugging

This class exposes two methods that allows you to easily debug and see the query you have performed. Also, using a die dump method to dump anything you would like to see.

To dump a query, the query has to be executed, after that, you may run toSql() on the database instance.

  • Default is false. If first argument is true, it shows parameters if it had any to be executed.
$db
    ->table('users')
    ->columns('*')
    ->select()
    ->fetchAll();

$db->toSql(); // Prints the following details

// Query Statement:
// SELECT * FROM users


$db->toSql(true); // Prints the following details

// Query Statement:
// SELECT * FROM users

// Binding Parameters To Statement:
// Array
// (
// )
UploadFile Documentation

UploadFile Documentation

This class uploads temporary file to given path. This is a very simple implementation where it uses the temporary file mime type to detect the file extension. This may not be very secure.

Setup

<form method="post" enctype="multipart/form-data">
  <input name="images[]" type="file" multiple />
</form>
// Notice we use images, because it has to be arrays of images and it must be multiple files, otherwise, it doesnt work.
$files = $_FILES['images'];
$publicPath = __DIR__ . '/../public';

$uploader = new UploadFile($files, $publicPath);

// Return an array of paths to all the uploaded images.
$uploaded = $uploader->save();
<?php
namespace App\Service;
use PDO;
use PDOStatement;
class Database
{
private string $database;
private string $user;
private string $password;
private string $driver;
private string $host;
private int $port;
private array $opts = [];
// Leave the following properties empty !!
private string $table = '';
private string|array $cols = '';
private PDO $connection;
private string $query = '';
private array $params = [];
public function __construct(
string $database,
string $user = 'root',
string $password = '',
string $driver = 'mysql',
string $host = '127.0.0.1',
int $port = 3306,
array $opts = []
) {
$this->driver = $driver;
$this->port = $port;
$this->host = $host;
$this->user = $user;
$this->password = $password;
$this->database = $database;
$this->initializeConnection();
return $this->connection;
}
protected function initializeConnection(): void
{
$dsn = http_build_query([
'host' => $this->host,
'port' => $this->port,
'dbname' => $this->database,
], arg_separator: ';');
$opts = array_merge([
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_TIMEOUT => 60 * 30, // 30 minutes to timeout
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
], $this->opts);
$pdo = new PDO(
"{$this->driver}:{$dsn};",
$this->user,
$this->password,
$opts
);
$this->connection = $pdo;
}
/**
* Perform SQL query
*/
public function query(string $query, array $params = []): PDOStatement|false
{
$this->dd($query);
$result = $this->connection->prepare($query);
$result->execute($params);
$this->logQuery($query, $params);
return $result;
}
public function rawQuery(string $query): mixed
{
$result = $this->connection->query($query);
$this->logQuery($query);
return $result;
}
/**
* Select table to query
*/
public function table(string $tbl): static
{
$this->table = $tbl;
return $this;
}
/**
* Select columns to query
*/
public function columns(string|array $cols): static
{
if (is_array($cols)) {
$cols = $this->parseColumn($cols);
}
$this->cols = $cols;
return $this;
}
/**
* Perform select query to database
*/
public function select(string $condition = '', array $bindings = []): ?PDOStatement
{
if (! $this->isTableSet() && $this->isColumnSet()) {
return null;
}
$query = "SELECT {$this->cols} FROM {$this->table}";
if ($condition) {
$query .= " WHERE {$condition}";
}
return $this->query($query, $bindings);
}
/**
* Perform insert query to database
*/
public function insert(string|array $values, array $bindings = []): string|false|null
{
if (! $this->isTableSet() && $this->isColumnSet()) {
return null;
}
if (is_array($values)) {
$values = $this->parseColumn($values, true);
}
$query = "INSERT INTO {$this->table}({$this->cols}) VALUES ({$values})";
$this->query($query, $bindings);
return $this->connection->lastInsertId();
}
/**
* Perform insert many query to database
*/
public function insertMany(array $list): ?static
{
if (! $this->isTableSet() && $this->isColumnSet()) {
return null;
}
$values = implode(', ', $this->parseInsertManyList($list));
$query = "INSERT INTO {$this->table}({$this->cols}) VALUES {$values}";
$this->connection->query($query);
return $this;
}
/**
* Perform update query to database
*/
public function update(string $condition = '', array $bindings = []): ?static
{
if (! $this->isTableSet() && $this->isColumnSet()) {
return null;
}
$cols = $this->cols;
$query = "UPDATE {$this->table} SET {$cols}";
if ($condition) {
$query .= " WHERE {$condition}";
}
$this->query($query, $bindings);
return $this;
}
/**
* Perform delete query to database
*/
public function delete(string $condition, array $bindings = []): ?static
{
if (! $this->isTableSet()) {
return null;
}
$query = "DELETE FROM {$this->table} WHERE {$condition}";
$this->query($query, $bindings);
return $this;
}
/**
* Dumps last query statement
*/
public function toSql(bool $params = false): void
{
$this->dd('<strong>Query Statement: </strong><br>' . $this->query);
if ($params) {
$this->dd('<strong>Binding Parameters To Statement:</strong>');
$this->dd($this->params);
}
}
public function dd(mixed ...$data): void
{
echo '<pre>';
\print_r(...$data);
echo '</pre>';
}
/**
* Format two-dimensional array to a single insert query for database
*/
protected function parseInsertManyList(array $list): array
{
$temp = [];
foreach ($list as $records) {
$format = [];
foreach ($records as $record) {
$format[] = $this->parseColumn($record, true);
}
$temp[] = '(' . implode(', ', $format) . ')';
}
return $temp;
}
/**
* A friendly UI to show if table is set
*/
protected function isTableSet(): bool
{
if ($this->table === '') {
echo '<strong>Table is not set</strong>';
return false;
}
return true;
}
/**
* A friendly UI to show if columns are set
*/
protected function isColumnSet(): bool
{
if ($this->cols === '') {
echo '<strong>Column is not set</strong>';
return false;
}
return true;
}
/**
* Prepare queries and params to be logged
*/
protected function logQuery(string $query, array $params = []): void
{
$this->query = $query;
$this->params = $params;
}
protected function parseColumn(mixed $target, bool $shouldSurroundWithQuote = false): string
{
if (is_int($target)) {
return (string) $target;
} elseif (is_bool($target)) {
return (bool) $target;
} elseif (is_string($target)) {
if (! $shouldSurroundWithQuote || $target === '?') {
return (string) $target;
}
return "'{$target}'";
} elseif (is_array($target)) {
$result = '';
$len = count($target);
$counter = 0;
foreach ($target as $key => $val) {
$counter++;
// This method can be used when there is an array of columns,
// or it could be column name as key and value as its value.
// To identify which one we are dealing with, if the key is string
// we can safely guess that we don't need to add (=) sign.
if (! is_string($key)) {
$result .= $this->parseColumn($val, $shouldSurroundWithQuote);
} else {
// If continue, parse each key/value that is compatible with sql query
$result .= $key . ' = ' . $this->parseColumn($val, $shouldSurroundWithQuote);
}
if ($len !== $counter) {
$result .= ', ';
}
}
return $result;
}
$this->dd('This type of value isn\'t supported for parsing.');
}
}
<?php
namespace App\Service;
use Exception;
class UploadFile
{
private array $files = [];
private string $outputPath;
public function __construct(array $files, string $outputPath): static
{
$this->setOutputPath($outputPath);
$count = count($files['tmp_name']);
for ($i = 0; $i < $count; $i++) {
// sjdfkakfalsdf.jpg
// File type = image/jpeg => ['image', 'jpeg']
$fileType = explode('/', $files['type'][$i]);
$extension = end($fileType);
// 16cd019896f19fc76b6905bd13c3f2de . $extension
$name = '/' . md5($files['name'][$i] . random_bytes(64)) . '.' . $extension;
$this->files[] = [
'name' => $name,
'location' => $files['tmp_name'][$i],
];
}
return $this;
}
/**
* Set output path
*/
public function setOutputPath(string $path): static
{
$this->outputPath = $path;
return $this;
}
/**
* Save the uploaded files to disk
*/
public function save(string $path = ''): array
{
// create directory if doesn't exist.
$this->createDirectory($path);
$uploaded = [];
foreach ($this->files as $file) {
$finalPathWithName = $this->outputPath . $path . $file['name'];
move_uploaded_file($file['location'], $finalPathWithName);
$uploaded[] = $path . $file['name'];
}
return $uploaded;
}
public function createDirectory(string $path): bool
{
$location = $this->outputPath . $path;
try {
if (! file_exists($location)) {
mkdir($location);
return true;
}
} catch (\Exception) {
throw new Exception('Failed to create directory!');
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment