use database class in another class [duplicate] - php

This question already has an answer here:
How to use PDO connection in other classes?
(1 answer)
Closed 2 years ago.
I have 2 class in my CMS and there are crud functions in "User Class". How can I use database class in another class. I know they both work. But I was previously transferring with the "extends" method. But every time I created an object, the database connection was duplicated. So "extends" was fault for me. I don't want to fault again.
So which is best solution? Do any of these solutions duplicate the database connection?
databaseclass.php:
class database
{
public $connect;
function __construct() {
$this->connect();
}
public function connect() {
$this->connect = new PDO('mysql:host=' . SERVER . ';dbname=' . DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
}
}
userclass.php:
class user
{
function getUser() {
# select an user codes
}
}
Solution - Injection database class
class user
{
function getUser($database) {
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}
Solution - Global database class (I want that but everybody suggest first solution) (I want because this solution does not require me to inject each time I create a user object) userclass.php:
class user
{
function getUser() {
global $database;
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}

I added a third option here and gave some personal opinion about the others:
Solution a) Pass as parameter to your methods
This way you always need to pass it inside your functions which makes the API of your class less clean. Imagine we want to get a user by id. This could look like: getUserById($database, 123). This will require everywhere to know how to fetch the databse. I would try to avoid this.
Solution b) Use it as global
I would try avoid global wherever I can. My first argument would be that it makes your class less testable and can change the behaviour dependent on the context. Here is a nice post about it with more examples: https://stackoverflow.com/a/5166527/12880865
Solution c) Use dependency injection with the __constructor.
I would add another way of solving it by using dependency injection at constructor level already. This way you can use getUsers() or getUserById($id) without passing the database all the time. This way your class defines it's own dependencies very clearly for the user of this class.
this solution does not require me to inject each time I create a user object
To avoid this you could use container which could resolves your class dependencies automatically when you try to retrieve them.
Example with your code:
class user
{
protected $database;
public function __construct(database $database) {
$this->database = $database;
}
function all() {
# select an user codes
$this->database->query('....');
}
function getById($id) {
# select an user codes
$this->database->query('....', ['id' => $id]);
}
}
which you can then inject like this:
$database = new Database();
// Here you'll pass your database connection into the repository
$users = new UserRepository($database);
$users->all(); // returns an array of all users
$users->getById(123); // returns a specific user by id
// Here you'll pass the same database connection into another repository
$posts = new PostRepository($database);
$posts->all(); // returns an array of all posts
$posts->getById(123); // returns a specific post by id

Related

How to create a query in Drupal 8

I am used to using db_select in drupal 7 but now it's deprecated in drupal 8
So, If I need to create a query to list all users from users_field_data table, What should I do?
Do I still use db_select or db_query even though they are deprecated functions? Or create a new controller to extend from "Select class" and make my query?
Depends on what you are trying to achieve.
Using the storage object
If you want to make a simple query about the users then you should use the loadByProperties of the storage object
$users = \Drupal::entityTypeManager()->getStorage('user')->loadByProperties([
'name' => 'bar'
]);
Using entity query & loadMultiple
If you need a more complex query with sorts, range, pager and OR/AND condition groups you should use entity query
$ids = \Drupal::entityQuery('user')->condition('name', 'foo')->execute();
$users = User::loadMultiple($ids);
As mentioned in the documentation you can query data by injecting Drupal's database connection class. For example:
use Drupal\Core\Database\Connection;
class DatabaseQueryExample {
protected $connection;
public function __construct(Connection $connection) {
$this->connection = $connection;
}
public function queryExamples() {
// db_query()
$this->connection->query(" ... ");
// db_select()
$this->connection->select(" ... ");
}
}
db_select, db_insert, db_update, etc... were deprecated in Drupal 8.0.x and will be removed in Drupal 9.0.0.
Instead get a database connection injected into your service from the container and call select() on it. For example, $injected_database->select($table, $alias, $options);.
eg:
$db = \Drupal::database();
$data = $db->select('table_name','t')->fields('t')->execute();
$db->insert();
$db->update();

Unit test a database centric website

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
}
}

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.

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);
}
}

Calling Parent Class From Sub Class PHP

$this->a->b->c->d calling methods from a superclass in php
Ive asked a question on this link I ve problem with this tecnique I am able to call the sub classes from a class
like this
$chesterx->db->query();
I wanna do get another class from sub class
for example
i want to query execute which was come from the sql class
ROOT
|
sql <--- chesterx ---> db
i wanna use the sql class from db
the problem i cant return the chesterx class from db class
/edit/
I have some classes like news, members, categories, db and query
and i did it like the link which was on the subject top
public function __construct(){
function __construct(){
if(!$this->db){
include(ROOT."/func/class/bp.db.class.php");
$this->db = new db;
}
if(!$this->chester){
include(ROOT."/func/class/bp.chester.class.php");
$this->db = new chester;
}
}
i called the db class with this code and now i am able to call and use the db class methods well
for example
i want to use a method from db
that method is containing a value which was returning a data from the chester class's method
i wish i were clarify myself
/edit/
is there anyway to do this?
I find Ionut G. Stan's solution good for your case, but you might also want to consider the factory/singleton pattern, though it's only good if your chesterx class is a global one, and only called once
The below snippet might be a solution, although I don't really like the circular reference. Try it and use it as you see fit. And by the way, what you are calling class and subclass are actually containing and contained class.
class Database
{
public $chesterx;
public function __construct($chesterx)
{
$this->chesterx = $chesterx;
}
}
class Sql
{
public $chesterx;
public function __construct($chesterx)
{
$this->chesterx = $chesterx;
}
}
class Chesterx
{
public $db;
public $sql;
public function __construct()
{
$this->db = new Database($this);
$this->sql = new Sql($this);
}
}

Categories