I've been working on a 3-tier architecture and would like some reassurance that my approach is correct as this is for a large project that hopefully will convert 10 years of spaghetti code into an organized system.
The code below has been separated into three layers and it works great so far. The API (works together with some HTML & CSS and AJAX as the presentation layer), Business Logic Layer, and a Data Access Layer. I'm using Slim as you can see for the API.
A few questions I have are:
In general, is this a proper way to get the layers working together?
Should the SQL query itself "SELECT x, y, z FROM ..." be contained in the business layer as below, or in the DataLayer, or are there times you may require SQL in both BLL and DAL? I've seen both, but restricted to BLL seems like a more logical separation.
In the example below you can see I'm getting users by company_id. What if I wanted by company_id and isadmin? Would best practice be to associate another route and method for that, such as /users/company/:id/admins with another method such as getAdminUsers()? Doesn't seem right as I could pass parameters to getUsers($id, $isadmin), but not sure what is best practice here.
API
<?php
require $_SERVER["DOCUMENT_ROOT"] . '/BLL/BLL.php';
require 'Slim/Slim.php';
$app = new Slim();
$app->get('/users/company/:id', 'getUsers');
$app->get('/users/:id', 'getUser');
$app->post('/users', 'addUser');
$app->put('/users/:id', 'updateUser');
$app->delete('/users/:id', 'deleteUser');
$app->run();
function getUsers($id) {
$bll = new BusinessLayer();
$result = json_encode($bll->getAllUsersBLL($id));
echo '{"user": ' . $result . '}';
}
?>
Business Logic Layer
<?php
require_once $_SERVER["DOCUMENT_ROOT"] . "/DAL/DAL.php";
class BusinessLayer
{
var $dal;
function __construct() {
$this->dal = new DataLayer();
}
public function getAllUsersBLL($id) {
$sql = "SELECT * FROM users WHERE company_id = :id ORDER BY id";
$ret = $this->dal->query($sql, $id);
return $ret;
}
}
}
?>
Data Access Layer
<?php
class DataLayer
{
public function connect() {
$dbhost = "localhost";
$dbuser = "root";
$dbpass = "";
$dbname = "testdatabase";
$db = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $db;
}
public function query($sql, $id) {
try {
$db = $this->connect();
$stmt = $db->prepare($sql);
$stmt->bindParam("id", $id);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
$db = null;
return $result;
} catch(PDOException $e) {
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
}
}
?>
Any other feedback is more than welcome!
Related
I'm making a small CMS for some fun and practice. And I've come across this problem where I have to access a database multiple times in different functions. And the way I do it now by making a new prepared statement with the code and all to access the database in the function doesn't seem very convenient since the code is very repetitive and I'm using mostly the same code for each function. So how would I go about creating a class maybe or some functions that reduce the amount of code used in the functions that gather the information from that database? I currently use the following queries in SQL
SELECT
UPDATE
INSERT
DELETE
So mostly the basic ones. The code I'm using is basic PHP code where I'm using prepared statements to access my database like this:
// Create database connection
$con = db_connect();
// Initialize $error variable for errors
$error = "";
if ($stmt = $con->prepare("SELECT * FROM profiles WHERE username = ?")) {
// Bind the $username variable to the parameter in the query
$stmt->bind_param('s', $username);
// Execute the prepared query
$stmt->execute();
$stmt->store_result();
// Assign the data recieved from the database (if any)
$stmt->bind_result($data);
$stmt->fetch();
if ($stmt->num_rows == 1) {
if (!empty($stmt->error)) {
printf("Error: %s.\n", $stmt->error);
return false;
}
// Query successful
} else {
$error .= "User doesn't exist";
return false;
}
} else {
$error .= 'Could not connect to database';
return false;
}
To me this seems like pretty easy to use code, but when you have to paste it again and again in different functions, then it gets a bit frustrating.
You should use Dependency Injection.
By injecting the Database connection into a Profile's class, you have much more maneuverability to do what you please.
You can change that database to whatever you want (MongoDB, Cassandra, MySQL).
You are only declaring the connection once; which performs better and faster
Makes it easier to test and develop (echo & print_r & unit testing)
Handel exceptions in 1 place
Database is loosely couple with rest of code.
ex:
class Profile {
private $db = null;
public function __construct($db Database) {
$this->db = $db;
}
public function getProfile() {
//ish....
$this->db->query("SELECT * FROM profiles WHERE username = ?");
}
public function insert() {
...
}
public function update() {
...
}
public function delete() {
...
}
}
To access the database, I would do something like this and also implement what you have (prepared statements are great!):
class Database {
private $conn = null;
public function __construct($db Database) {
$this->conn = new PDO('mysql:host=localhost;dbname=myDatabase', $username, $password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $this->conn;
}
public function query($sql) {
try {
return $conn->query($sql);
} catch (Exception $e) {
echo $e->getMessage();
}
}
}
A very good explaination and tutorial can be found here: http://code.tutsplus.com/tutorials/dependency-injection-huh--net-26903
I'm making an API for my angular app that will allow me to access my database with Slim. I followed this tutorial http://anjanawijesundara.blogspot.ca/2015/04/crud-application-with-angularjs.html
I have a 'Angularjs' folder. In it, I have my index.html file, my 'api' folder where is this API, my 'app' folder, for the angular app, and a 'assets' folder for the css, img and other js file.
I installed Slim in the 'API' folder with composer (it created a vendor folder) and I have a 'index.php' file next to the vendor folder.
My 'index.php' file (in the api folder) looks like that so far:
<?php
require 'vendor/autoload.php';
$app = new \Slim\App;
$app->get('/Types', 'getTypes');
$app->get('/Types/:id', 'getTypeById');
$app->run();
function DB_Connection() {
$dbhost = "localhost";
$dbuser = "kevdug_portfolio";
$dbpass = "*****************";
$dbname = "portfolio";
$dbh = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $dbh;
}
function getTypes() {
$sql = "select * FROM pt_type";
try {
$db = DB_Connection();
$stmt = $db->query($sql);
$list = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
echo json_encode($list);
} catch(PDOException $e) {
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
}
function getTypeById($id) {
$sql = "select * FROM pt_type WHERE id=".$id;
try {
$db = DB_Connection();
$stmt = $db->query($sql);
$list = $stmt->fetchAll(PDO::FETCH_OBJ);
$db = null;
echo json_encode($list);
} catch(PDOException $e) {
echo '{"error":{"text":'. $e->getMessage() .'}}';
}
}
?>
I'm suppose to be able to use my API with this code:
angular.module('appDatabaseCtrl', [])
.controller('databaseCtrl', ['$scope','$routeParams', '$http', '$log',
function($scope, $routeParams, $http, $log){
$scope.testDatabaseTypes = function(){
$http.get('/api/Types').success(function(data) {
$log.info("succes!");
$log.log(data);
})
.error(function (data, status){
$log.error("error!");
$log.log(data);
});
};
$scope.testDatabaseTypesById = function(){
console.log($scope.id);
$http.get('/api/Types/' + $scope.id).success(function(data) {
$log.info("succes!");
$log.log(data);
})
.error(function (data, status){
$log.error("error!");
$log.log(data);
});
};
}
]);
The first function works, but the second returns me a 404 error. You can see what happen yourself with those tree url:
http://kevdug.webfactional.com/#/database
http://kevdug.webfactional.com/api/types
http://kevdug.webfactional.com/api/types/1 <--- can be any id from 1 to 4
It appears that you are using Slim v3 (Judging by $app = new \Slim\App;), however it appears that your route format is that of Slim v2.
$app->get('/Types/:id', 'getTypeById'); should actually be more like $app->get('/Types/{id}', getTypeById);. You can also provide restrictions as to what it accepts like $app->get('/Types/{id:\d+}', getTypeById);
Edit: You are also using an invalid function signature for Slim v3, which is why when navigating to Your Example Url with the literal :id instead of a number it errors. You should use a function signature like
function getTypeById(\Slim\Http\Request $req, \Slim\Http\Response $res, $args) {
$id = $args["id"]; // Rest of your code
}
Finally, i recommend looking up some basic SQL Injection protection tutorials, as because if your current /Types/:id route worked correctly, it would have a SQL Injection Vulnerability. However since that is not the target of this question, i'll just leave a warning.
I'm a learning programmer and I'm trying to learn PHP.
Now the point is that I use alot of db connections in my new project. This are alot of different DB's an require different connection.
Does anybody a way where I can create a function what makes me use my code like this? Getdata($User, $beerbrand, $sales); and use these variables in my code? I use the same way to get the data out of the database everytime. But it's alot of code, where I think it should be possible to make that a little easier.
The way I use now:
mysql_connect($host, $user, $password) or die ("cannot connect");
mysql_select_db("$db_name");
$stuff = mysql_query("SELECT * FROM beers ORDER BY ID");
while($frontstuff = mysql_fetch_array($stuff)){
$us = $frontstuff['Beer'];
}
If you think this is a stupid question, please be gentle and explain it to me in a easy way.
Best regards
<?php
class Database {
private $connection; //Database Connection Link
private $userName; //Database server User Name
private $password; //Database server Password
private $database; //Database Name
private $hostname; //Name of database server
public function __construct() {
$this->hostname=your servername
$this->userName=yourusername
$this->password=yourpasswrod
$this->database=your bb name
try {
$this->connectlink = mysql_connect($this->hostname,$this->userName,$this->password);
if(!($this->connectlink)) {
throw new Exception("Error Connecting to the Database".mysql_error(),"101");
}
if(!mysql_select_db($this->database, $this->connectlink)) {
throw new Exception("Error Connecting to the Database1".mysql_error(),"101");
}
}catch(Exception $e){
//print_r($dbConfig);
echo $e->getMessage();
}
}
public function __destruct() {
#mysql_close($this->connectlink);
}
}
you can crete a database file like this for specifying connnection
and you can use this file in processing your query in another pages.Eg
<?php
include_once("database.php");
function __construct(){
$this->db = new Database;
}
public function getstuf($parameter){
$stuff = mysql_query("SELECT * FROM beers ORDER BY ID");
while($frontstuff = mysql_fetch_array($stuff)){
$us = $frontstuff['Beer'];
}
return stuff;
}
}
This question already has answers here:
What is dependency injection?
(37 answers)
Closed 9 years ago.
I know the basics of PHP and have only ever used it to debug WordPress code generally, but now I want to write my own little program to download an email and process an attachment and I have decided to try using classes as I have a basic understanding of OO programming.
SO PLEASE READ: I am a novice! I don't know what on earth dependency injection is or means...
My issue is that I have created a function called printStatus(), so I can toggle on/off output of comments. I was looking at this, and I'm not sure how or if it would fit into a class structure or if I need to include this function in every other class I create?
Basically - If I created a class, I would need to make it available to all other classes (i.e. a global class) but I'm not sure if that is achievable.
My questions are:
Do I have to pass a reference to the printOutput class to and from every class I use to have it available to me OR can I declare it globally to make it available to all classes OR do I need to include the same function in every class I create?
I've created a MySQL Connection class and I am passing that into each object for use - should (can I) declare it globally and just make it available to all classes?
Thanks for the 101.
Here is my code, am I going down the right path?: (see specifically, references to printStatus())
PS - $formatoutput->printStatus() does not work within other classes - I'm looking to understand what structure is required to make it work.
class.formatoutput.php:
class formatOutput {
var $debug = true;
function printStatus($text, $html = true) {
if ($debug) {
echo $text;
echo $html?"<br/>\n":"\n";
}
}
function printObjectStatus($object, $html = true) {
if ($debug) {
echo '<pre>';
echo $text;
echo $html?"</pre><br/>\n":"</pre>\n";
}
}
}
class.connection.php:
class Connection
{
var $db_host = "host";
var $db_name = "name";
var $db_user = "user";
var $db_pass = "pass";
var $db_conn;
function connectToDatabase() {
$db_conn = mysqli_connect($this->db_host, $this->db_user, $this->db_pass, $this->db_name);
if (!$db_conn) {
die('Connect Error (' . mysqli_connect_errno() . ') ' . mysqli_connect_error());
}
else
{
$this->db_conn = $db_conn;
$formatoutput->printStatus( "Connection established");
}
return $this->db_conn;
}
function closeConnection() {
mysqli_close($this->db_conn);
$formatoutput->printStatus( "Connection closed");
}
}
class.customer.php:
class Customer {
var $customer_id;
var $customer_name;
function getCustomer($connection, $user_id) {
$query = "SELECT id, name FROM customer WHERE user_id=$user_id";
$result = mysqli_query($connection, $query);
if($result === FALSE) {
die('Connect Error (' . mysqli_errno() . ') ' . mysqli_error());
}
$row_count = mysqli_field_count($connection);
$formatoutput->printStatus( "COUNT: (".$count.")");
}
}
index.php:
include 'class.formatoutput.php';
include 'class.connection.php';
include 'class.customer.php';
$formatoutput = new formatOutput();
$formatoutput->printStatus('Start new Connection()');
$connection = new Connection();
$connection->connectToDatabase();
$customer = new Customer();
$customer->getCustomer($connection->db_conn, "1");
$connection->closeConnection();
Declare the function printStatus as static:
static function printStatus($text, $html = true)
You can call this function by using "::",
formatOutput::printStatus("hello");
this is my current Database class:
class Database {
private $db;
function Connect() {
$db_host = "localhost";
$db_name = "database1";
$db_user = "root";
$db_pass = "root";
try {
$this->db = new PDO("mysql:host=" . $db_host . ";dbname=" . $db_name, $db_user, $db_pass);
} catch(PDOException $e) {
die($e);
}
}
public function getColumn($tableName, $unknownColumnName, $columnOneName, $columnOneValue, $columnTwoName = "1", $columnTwoValue = "1") {
$stmt = $this->db->query("SELECT $tableName FROM $unknownColumnName WHERE $columnOneName='$columnOneValue' AND $columnTwoName='$columnTwoValue'");
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results[0][$unknownColumnName];
}
}
I'm trying to run it using the following code:
$db = new Database();
$db->Connect();
echo $db->getColumn("Sessions", "token", "uid", 1);
And i get the following error:
PHP Fatal error: Call to a member function fetchAll() on a non-object in /Users/RETRACTED/RETRACTED/root/includes/Database.php on line 19
Any idea what's up? Thanks
This function is prone to SQL injection.
This function won't let you get a column using even simplest OR condition.
This function makes unreadable gibberish out of almost natural English of SQL language.
Look, you even spoiled yourself writing this very function. How do you suppose it to be used for the every day coding? As a matter of fact, this function makes your experience harder than with raw PDO - you have to learn all the new syntax, numerous exceptions and last-minute corrections.
Please, turn back to raw PDO!
Let me show you the right way
public function getColumn($sql, $params)
{
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
return $stmt->fetchColumn();
}
used like this
echo $db->getColumn("SELECT token FROM Sessions WHERE uid = ?", array(1));
This way you'll be able to use the full power of SQL not limited to a silly subset, as well as security of prepared statements, yet keep your code comprehensible.
While calling it still in one line - which was your initial (and extremely proper!) intention.
it means your $stmt variable is not returning a PDOStatement object. your query is failing since PDO::query either returns a PDOStatement or False on error.
Use fetch instead of fetchAll..that will be easy in your case
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $results[0][$unknownColumnName];
It will be
$results = $stmt->fetch(PDO::FETCH_ASSOC);
return $results[$unknownColumnName];