Unit test a database centric website - php

I'm using a legacy (no-tests) PHP project, full of classes like http://pastebin.com/byBMTAEY
Practically all methods are static, and most of them reference the database directly using a singleton (to limit connections to one)
Most of them also have a reference to a global called _COMPANY, that identifies which company is using the platform (based on the subdomain).
My (very newbie) question is: what's the best way to unit test this class? Should I unit test it at all, since what it does is go to the database and do logic there, and then just wrap up the result?
I mean, I can probably mock the database class, and then say "on 'one' return this result-object", but then what am I testing really? The SQL logic not, for one thing, only the private functions to make a business object... is that enough for the unit test? Or should I somehow also unit test the sql? Or is that reserved for integration testing?
Please help me understand so I can use this practice if applicable.
Example code also included here for reference:
<?php
require_once('DataBase.php');
require_once('businessobjects/User.php');
class UserMgt {
public static function login($email, $pass){
$db = DataBase::getInstance();
$sql = "
SELECT *
FROM user
WHERE login = ? AND password = ? AND active = 1 AND company_id = ?
";
$data = array(
$email, md5($pass), $GLOBALS['_COMPANY']->id,
);
$user = null;
if ($row = $db->one($sql, $data)) { // fetch only one row using the query and parameters
$user = self::copyToUserObject($row); // make a business object
$user->setCapabilities(self::getUserCapabilities($user->getID())); // perform some extra hydration of business object
}
return $user;
}
private static function copyToUserObject($dbrow) {
$user = new User();
// do stuff
return $user;
}
private static function getUserCapabilities($userid) {
// do sql query stuff
// return array
}
}

Related

How to implement models factory in frameworks which using active records?

So, in my framework X, let it be Phalcon, I often create models objects.
Let's assume that all fields already validated. Questions related only about creation logic.
A simple example of creating Users object and save it to DB:
<?php
$user = new Users();
$user->setName($name);
$user->setLastName($lastname);
$user->setAge($age);
$user->create();
For simplicity, I show here only 3 fields to setup, in the real world they always more.
I have 3 questions:
1) What the best way to encapsulate this logic in Factory class? If I create Factory class that will create objects like Users object, every time I will need pass long amount of parameters.
Example:
<?php
$factory = new UsersFactory();
$factory->make($name, $lastname, $address, $phone, $status, $active);
2) Even if I implement Factory in a way showed above - should Factory insert data in DB? In my example call method create()? Or just perform all setters operations?
3) And even more, what if i will need to create Users objects with relations, with other related objects?
Thank you for any suggestions.
Your question starts out simple and then builds with complexity. Reading your post it sounds like your concerned about the number of arguments you would have to pass to the method to build the object. This is a reasonable fear as you should try to avoid functions which take more than 2 or 3 args, and because sometimes you will need to pass the 1st 3rd and 5th arg but not the 2nd and 4th which just gets uncomfortable.
I would instead encourage you to look at the builder pattern.
In the end it will not be that much different than just using your User object directly however it will help you prevent having a User object in an invalid state ( required fields not set )
1) What the best way to encapsulate this logic in Factory class? If I create Factory class that will create objects like Users object, every time I will need pass long amount of parameters.
This is why I recommended the builder pattern. To avoid passing a large number of params to a single function. It also would allow you to validate state in the build method and handle or throw exceptions.
class UserBuilder {
protected $data = [];
public static function named($fname, $lname) {
$b = new static;
return $b
->withFirstName($fname)
->withLastName($lname);
}
public function withFirstName($fname) {
$this->data['first_name'] = $fname;
return $this;
}
public function withFirstName($lname) {
$this->data['last_name'] = $lname;
return $this;
}
public function withAge($age) {
$this->data['age'] = $age;
return $this;
}
public function build() {
$this->validate();
$d = $this->data;
$u = new User;
$u->setFirstName($d['first_name']);
$u->setLastName($d['last_name']);
$u->setAge($d['age']);
return $u;
}
protected function validate() {
$d = $this->data;
if (empty($d['age'])) {
throw new Exception('age is required');
}
}
}
then you just do..
$user = UserBuilder::named('John','Doe')->withAge(32);
now instead of the number of function arguments growing with each param, the number of methods grows.
2) Even if I implement Factory in a way showed above - should Factory insert data in DB? In my example call method create()? Or just perform all setters operations?
no it should not insert. it should just help you build the object, not assume what your going to do with it. You may release that once you build it you will want to do something else with it before insert.
3) And even more, what if i will need to create Users objects with relations, with other related objects?
In Phalcon those relationships are part of the entity. You can see in their docs this example:
// Create an artist
$artist = new Artists();
$artist->name = 'Shinichi Osawa';
$artist->country = 'Japan';
// Create an album
$album = new Albums();
$album->name = 'The One';
$album->artist = $artist; // Assign the artist
$album->year = 2008;
// Save both records
$album->save();
So to relate this back to your user example, suppose you wanted to store address information on the user but the addresses are stored in a different table. The builder could expose methods to define the address and the build method would create both entities together and return the built User object which has a reference to the Address object inside it because of how Phalcon models work.
I don't think it's entirely necessary to use a builder or "pattern" to dynamically populate your model properties. Though it is subjective to what you're after.
You can populate models through the constructor like this
$user = new Users([
'name' => $name,
'lastName' => $lastname,
'age' => $age,
]);
$user->create();
This way you can dynamically populate your model by building the array instead of numerous method calls.
It's also worth noting that if you want to use "setters" and "getter" methods you should define the properties as protected. The reason for this is because Phalcon will automatically call the set/get methods if they exist when you assign a value to the protected property.
For example:
class User extends \Phalcon\Mvc\Model
{
protected $name;
public function setName(string $name): void
{
$this->name = $name;
}
public function getName(): string
{
return $this->name;
}
}
$user= new MyModel();
$user->name = 'Cameron'; // This will invoke User::setName
echo $user->name; // This will invoke User::getName
It is also worth noting that the properties will behave as you'd expect a protected property to behave the same as a traditional protected property if the respective method is missing. For example, you cannot assign a value to a protected model property without a setter method.

MVC/PDO : how to build a model using PDO's prepared statements syntax?

I want to build a model class for my PHP application. It will have methods meant to select/update/insert/delete specific data from a database according to the method's parameters. I only want to use prepared statements.
Here is an overview of what the class should look like :
class Database {
private $_db;
// Stores a PDO object (the connection with the database) within the $_db property
public function __construct($host, $user, $password) {...}
public function select() {...}
public function update() {...}
public function insert() {...}
public function delete() {...}
}
The problem is that I don't really know how to do this. Let's say I want to select everything from the table "farm" where the animal is a dog. The syntax for this statement would be the following :
$animal = 'dog';
$query = $this->_db->prepare('SELECT * FROM farm WHERE animal = :animal');
$query->execute(array(':animal' => $animal));
$result_set = $query->fetchAll();
This is very complicated to implement within a class method. As you can see, I call the execute() method but I don't even know in advance if the WHERE clause will be used !
And even worse : what if I will want to use, let's say, the LIMIT x, y clause later on ?
Which parameters should I ask for and how to treat them ? Should I simply require the parameters to be one query + multiple variables that will be passed to the execute() method ?
Are these types methods reasonable for what I want to do ? Maybe I should to a dedicated method for each MySQL query the application will perform, but this is quite complicated because it's a big database and a big application.
What do you guys think ?
Thanks in advance :P
Your API looks pretty useless to me, because as I see it it's just a wrapper around PDO. What do you gain by wrapping PDO like that?
Instead it would probably make more sense to have your object actually representing things, e.g.:
namespace Project\Storage\Database;
class Farm
{
private $pdo;
public function __construct(\PDO $pdo)
{
$this->pdo = $pdo;
}
public function getAnimalsByType(string $animalType): AnimalCollection
{
$stmt = $this->pdo->prepare('SELECT * FROM farm WHERE animal = :animal');
$stmt->execute([
'animal' => $animalType,
]);
// alternatively use a factory to build this to prevent tight coupling
return new AnimalCollection($stmt->fetchAll());
}
}
On a side note: forget about MVC in PHP (it's not even possible). Just focus on the more important separation of concerns.
Maybe I should to a dedicated method for each MySQL query the application will
perform, but this is quite complicated because it's a big database and
a big application.
Yes, this is an easy way to organize your database access.
But you should not put ALL of them in the same class. You should separate your classes by their domain.
class animalRepository {
// ...
public function getAnimalByName($animal){
$query = $this->_db->prepare('SELECT * FROM farm WHERE animal = :animal');
$query->execute(array(':animal' => $animal));
$result_set = $query->fetchAll();
// ...
}
}
To make this communicate more clearly you could call those classes repositories, as they are storing the data for the specific domain.
Another common name would be mappers, because they are mapping the data to your objects.
Very opinionated answer. Anyway:
PDO's Prepared Statements are a little more capable than being created and calling execute on them. How you would usually do this is by first building your query and then binding the values:
$querystring = 'SELECT * FROM farm';
$args = array();
if($animal != '') {
$querystring .= 'WHERE animal = :animal';
$args[':animal'] = $animal;
}
$query = $this->_db->prepare($querystring);
$result = $query->execute($args)
if($result !== false) {
// fetch ...
} else {
// error output / return val
}
This is the general idea. Depending on your input parameters you build a query. It will probably become more sophisticated than that, for example filling a $where = array() and then you add to the $where[] = ... your where conditions and in the end you just join them all together with sql AND:
$this->_db->prepare($querystring.
( count($where) > 0 // the > 0 is redundant btw
? 'WHERE '.implode('AND',$where)
: '' )
);
You might similar things with joined tables, select statements and the like. It can get very complex. It's probably wise to mix this approach with separating at sensible points with Philipp's answer/approach.

SQL object - How to use it properly

I've made this class to handle all of my sql-queries. But I'm unsure of how to use it properly.
The class looks something like this (this is a VERY simple version of it):
class sql {
private $conn;
private $data;
function __construct() {
//makes connection to DB and sets $conn and $data
}
public function select($variables, $table, $criterias) {
//returns an array with all the info from DB
}
function __destruct() {
//closes the sql-connection
}
}
The question now is: Is this going to overload the DB, if I use it multiple times on every page-load? (refered to as Example #1)
$dbInfo = (new sql)->select($var,$tab,$cri);
$moreInfo = (new sql)->select($var2,$tab2,$cri2);
$evenMoreInfo = (new sql)->select($var3,$tab3,$cri3);
Would it be beneficial to make my sql class's methods static?
Or should I not create a new instance of a sql object every time I want to make a query (like the example below - refered to as Example #2)?
$sql = new sql();
$dbInfo = $sql->select($var,$tab,$cri);
$moreInfo = $sql->select($var2,$tab2,$cri2);
$evenMoreInfo = $sql->select($var3,$tab3,$cri3);
How and when is Example #1 the better choice over Example #2, and vice versa?
If I assume that Example #1 is going to take the most resources from the DB, when would you pick Example #1 over Example #2?
Your example 2 is more common to see, however the SQL object is usually a static/singleton. So it connects to the database once per server request.
Your base SQL object should handle connecting to a database and then handle basic input/output, such as executing a string of SQL and returning the results.
You can then add new objects on top of that for each object/table than then interfaces with this SQL singleton. These classes will handle constructing their custom SQL based on their table, joins, field names/types, etc.
E.g:
A very basic 'table' object looks like this
class SomeTableObject
{
m_TableName = 'SomeTable'; // Table to get Data from
function GetSelectSQL()
{
return "SELECT * FROM ".$this->m_TableName;
}
function Select($where)
{
$sql = $this->GetSelectSQL().$where;
return SqlSingleton::Execute($sql);
}
function GetByID($id)
{
$where = " WHERE FieldNameForID=$id";
return $this->Select($where);
}
}
These objects work better if they extend a base class that has those basic GetSelectSQL, TableName, Select, etc functions. The GetByIDs (and other gets, updates, inserts) will vary from table to table.

Advice extending PDO with basic CRUD functions

Just recently I started rewriting a previously procedurally written website by myself, I chose PDO as the wrapper since I'm also getting used to the OOP way of doing things. I would like some advice about the structure of the classes.
Mostly everything is database-driven, like adding categories and subcategories, brands of products, products, users, etc. I suppose each of them could be one class and since I need CRUD operations on all of them, I need a generic way of inserting, updating, deleting records in the MySql database. The problem is not the code, I'd like to (and already have) coded some of the CRUD operations by myself according to my needs, the real problem is the structure and how would I go to correctly distribute and extend those classes.
Right now I've coded 3 different approaches:
A class called 'Operations' which will be extended by all the other classes that need CRUD functions, this class contains pretty generic properties such as $id, $atributes, $fields and $table, and of course the generic methods to insert, update, delete. That way I can create, let's say my Product object with some parameters (name, category, price) and immediately Product->insert() it into the database, without passing any parameters to the insert function. The CRUD functions in this class don't accept parameters, they depend on the created object's properties.
Same as above but the CRUD functions accept parameters, making them (I suppose) more generic, in case I just need to insert something without creating an object with useless properties previously.
The 'Operations' class extends PDO, the way of working is similar to 2, but now they can be directly accessed when I create the database connection, not depending of other objects.
I'm leaning towards the first option because I think, for the most part, that it will satisfy everything I'll do with this website, again the website is already coded but procedurally, which has been a mess to maintain, so basically I need to re-do things but OO.
CMSs or already coded wrappers aside (the purpose of doing this is to learn PDO and getting used to OOP), which would be the best way to do that? not limited to the options I mentioned.
Here's the 'Operations' class I've managed to code so far, where I've been doing tests sandbox-like, don't mind the spanish variable names. Advices on the code are welcome too.
class Operaciones {
private $database;
protected $id;
protected $atributos;
protected $tabla;
protected $campos;
public function __construct($link) {
$this->database = $link;
}
public function insertar() {
if (!$this->verificarCamposNulos($this->atributos, $this->campos))
echo 'Campos nulos<br />';
else {
$this->prepararCampos();
$placeholders = $this->generarPlaceholders();
$stmt = $this->database->prepare("INSERT INTO {$this->tabla} ({$this->campos}) VALUES ({$placeholders})");
$valores = array_values($this->atributos);
$stmt->execute($valores);
$stmt = NULL;
echo 'Se ha insertado exitosamente';
}
}
public function modificar() {
if (!$this->verificarCamposNulos() || empty($this->id))
echo 'Campos nulos<br />';
else {
$this->prepararCampos('=?');
$stmt = $this->database->prepare("UPDATE {$this->tabla} SET {$this->campos} WHERE id = {$this->id}");
$valores = array_values($this->atributos);
$stmt->execute($valores);
$stmt = NULL;
echo 'Se ha modificado exitosamente';
}
}
private function generarPlaceholders() {
for($i=0;$i<count($this->atributos);$i++)
$qmarks[$i] = '?';
return implode(',', $qmarks);
}
// Check if the values to be inserted are NULL, depending on the field format given
private function verificarCamposNulos() {
$n_campos = explode(',', $this->campos);
$valores = array_values($this->atributos);
foreach($n_campos as $i => $result) {
if (strstr($result, '#'))
if (empty($valores[$i]))
return false;
}
return true;
}
// Removes the '#' from each field, used to check which fields are NOT NULL in mysql
private function prepararCampos($sufijo = NULL) {
$n_campos = explode(',', $this->campos);
foreach($n_campos as $i => $result)
$n_campos[$i] = str_replace('#', '', $result) . $sufijo;
$this->campos = implode(',', $n_campos);
}
}

Passing $db object to other classes so they can access the database

I've got a PHP database class which connects to MySQL and wraps up all the PDO code and I use it to query the database. Basically in the page controller I make a new object:
$db = new Database($dbConfig);
Then I can get data from the database like so using a prepared query:
$params = array('username' => $username);
$result = $db->preparedSelect('select password, salt from users where username = :username', $params);
Which copies the PDO statement results into a new assoc array and returns just the database results back to the calling page. I iterate through them with a simple foreach like so:
foreach ($result as $key => $val)
{
$password = $val['password'];
$salt = $val['salt'];
}
Ok so lets say I want another class to use my $db object so it can access the database in some of the methods. At the moment the other class looks like this:
class General
{
// Database object
private $db;
public function __construct($db)
{
$this->db = $db;
}
}
That works well but I'm just wondering if the constructor should look like this:
public function __construct(&$db)
{
$this->db = $db;
}
That should mean I'm passing it in via reference and not copying the object into the other class. I don't want a copy of the $db object inside the class, I want it to use the existing database object so I don't have multiple copies of it floating around using up memory.
Is there any difference in PHP5 between passing it in as $db or &$db? From doing some reading, PHP5 by default passes objects by reference, and other people saying it now does it the Java way and some say using the & makes a hard link whatever that is. I'm confused. What's the best way to do it?
Many thanks!
There is a difference, but it's not really the difference you may think.
In PHP5, "$db" holding an object is basically equivalent to a "Foo *" in C or C++. In other words, $db doesn't store the whole object, it just stores a small token that lets the code find the object when necessary. When you pass this token by value, it's as fast as passing an integer value rather than a copy of the entire object. But if you assign $db, it doesn't change the value in the caller because you're changing your local variable holding the token to contain a different token.
If the function takes "&$db", that's basically the equivalent of passing "Foo **" in C, or more correctly a function taking a "Foo *&" in C++. The call is just as fast since it's the same size thing that's being passed, but inside the function if you assign to $db it will change the value of $db in the caller because the "pass by reference" variable points you to the memory location holding the token in the caller.
The best way to do it is to pass by value (do not use "&") unless you know what you're doing and why you're doing it.
That's a good question.
You can always do a test by opening a $db handle, passing it to a function, and checking them via the === operator to make sure they are the same object.
This would be a good job for static methods. That is how many frameworks accomplish the same task.
class DB
{
private static $db = FALSE:
public static function init($dbConfig)
{
if(! self:$db)
{
self::$db = new Database($dbConfig);
}
}
public static function preparedSelect($sql, $params)
{
if(! self::$db)
{
die("call the init method first");
}
// db stuff, where you would call $this->db call self::$db
}
}
So in your other classes where you want to make calls to the database all you would have to do is:
class General
{
public function __construct()
{
DB::init($dbConfig);
}
public function someMethod()
{
$params = array('username' => $username);
$result = DB::preparedSelect('select password, salt from users where username = :username', $params);
}
}

Categories