I'm a little confused at the moment because my DB class doesn't seem to be storing any results after a query (i.e. the 'results' property is still an empty array). The unsual thing is that when I transfer my logic outside a class definition it works perfectly.
My code with database credentials blanked out:
namespace DatabaseConnection;
use PDO;
class DB {
/*****STATES*****/
private $con;
private $results;
/*****METHODS*****/
public function init(){
$this->con = new PDO("***************************");
$this->results = array();
return $this;
}
public function getInfo(){
if($this->con === null ) return "No Connection";
else return "Connected";
}
public function getResults(){
return $this->results;
}
public function retrieve(){
$query = $this->con->prepare("select * from documents");
$query->execute();
while($row = $query->fetch(PDO::FETCH_ASSOC)){
$this->results[] = $row;
}
return $this;
}
Try:
public function retrieve(){
$query = $this->con->prepare("select * from documents");
$query->execute(); //<---------
while($row = $query->fetch(PDO::FETCH_ASSOC)){
$this->results[] = $row;
}
return $this;
}
OP is preparing a statement and then never executes it, and has no way of detecting this because of the utter lack of any error handling on the DB calls. – Marc B
Related
I'm building a small and simple PHP content management system and have chosen to adopt an MVC design pattern.
I'm struggling to grasp how my models should work in conjunction with the database.
I'd like to separate the database queries themselves, so that if we choose to change our database engine in the future, it is easy to do so.
As a basic concept, would the below proposed solution work, is there a better way of doing things what are the pitfalls with such an approach?
First, I'd have a database class to handle all MySQL specific pieces of code:
class Database
{
protected $table_name;
protected $primary_key;
private $db;
public function __construct()
{
$this->db = DatabaseFactory::getFactory()->getConnection();
}
public function query($sql)
{
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll();
}
public function loadSingle($id)
{
$sql = "SELECT * FROM $this->table_name WHERE $this->primary_key = $id";
return $this->query($sql);
}
public function loadAll()
{
$sql = "SELECT * FROM $this->table_name";
return $this->query($sql);
}
}
Second, I'd have a model, in this case to hold all my menu items:
class MenuItemModel
{
public $menu_name;
public $menu_url;
private $data;
public function __construct($data)
{
$this->data = $data;
$this->menu_name = $data['menu_name'];
$this->menu_url = $data['menu_url'];
}
}
Finally, I'd have a 'factory' to pull the two together:
class MenuItemModelFactory extends Database
{
public function __construct() {
$this->table_name = 'menus';
$this->primary_key = 'menu_id';
parent::__construct();
}
public function loadById($id)
{
$data = parent::loadSingle($this->table_name, $this->primary_key, $id);
return new MenuItemModel($data);
}
public function loadAll()
{
$list = array();
$data = parent::loadAll();
foreach ($data as $row) {
$list[] = new MenuItemModel($row);
}
return $list;
}
}
Your solution will work of course, but there are some flaws.
Class Database uses inside it's constructor class DatabaseFactory - it is not good. DatabaseFactory must create Database object by itself. However it okay here, because if we will look at class Database, we will see that is not a database, it is some kind of QueryObject pattern (see link for more details). So we can solve the problem here by just renaming class Database to a more suitable name.
Class MenuItemModelFactory is extending class Database - it is not good. Because we decided already, that Database is just a query object. So it must hold only methods for general querying database. And here you mixing knowledge of creating model with general database querying. Don't use inheritance. Just use instance of Database (query object) inside MenuItemModelFactory to query database. So now, you can change only instance of "Database", if you will decide to migrate to another database and will change SQL syntax. And class MenuItemModelFactory won't change because of migrating to a new relational database.
MenuItemModelFactory is not suitable naming, because factory purpose in DDD (domain-driven design) is to hide complexity of creating entities or aggregates, when they need many parameters or other objects. But here you are not hiding complexity of creating object. You don't even "creating" object, you are "loading" object from some collection.
So if we take into account all the shortcomings and correct them, we will come to this design:
class Query
{
protected $table_name;
protected $primary_key;
private $db;
public function __construct()
{
$this->db = DatabaseFactory::getFactory()->getConnection();
}
public function query($sql)
{
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll();
}
public function loadSingle($id)
{
$sql = "SELECT * FROM $this->table_name WHERE $this->primary_key = $id";
return $this->query($sql);
}
public function loadAll()
{
$sql = "SELECT * FROM $this->table_name";
return $this->query($sql);
}
}
class MenuItemModel
{
public $menu_name;
public $menu_url;
private $data;
public function __construct($data)
{
$this->data = $data;
$this->menu_name = $data['menu_name'];
$this->menu_url = $data['menu_url'];
}
}
class MenuItemModelDataMapper
{
public function __construct() {
$this->table_name = 'menus';
$this->primary_key = 'menu_id';
$this->query = new Query();
}
public function loadById($id)
{
$data = $this->query->loadSingle($this->table_name, $this->primary_key, $id);
return new MenuItemModel($data);
}
public function loadAll()
{
$list = array();
$data = $this->query->loadAll();
foreach ($data as $row) {
$list[] = new MenuItemModel($row);
}
return $list;
}
}
Also consider reading this:
DataMapper pattern
Repository pattern
DDD
I am wondering if someone could help me. I am new to PHP OOP and would like some guidance with using objects.
I am making a login script and the functions I mention below are all from the class file.
Class USER{
public function userLogin($username,$password)
{
$statusY = "Y";
$stmt = $this->connection->prepare("SELECT * FROM tbl_users WHERE user_name=:userName LIMIT 1");
$stmt ->execute(array(":userName"=>$username));
$row = $stmt ->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() == 1)
{
$this->_user = $row; // Assign user details
$_SESSION['userSession'] = $row['user_id'];
}
}
public function getUser()
{
return $this->_user;
}
Ok so I have the getUser() function and then assign $this->_user = $row so I can retrieve the user info from the database. Now I want to acheive a couple of things from this but not sure how to go about it.
How would I go about calling $row['user_id'] in another function within the same class?
So basically
public function test()
{
$user_id = $this->_user(user_id);
$username = $this->_user(username);
}
How would I do this correctly?
Also if I want to call the information in a page such as the User Homepage.
$user_home = new USER();
$userID = $user_home->getUser(user_id);
echo $userID;
If anyone could give me some guidance as to how I can move forward with this I would greatly appreciate it. Thanks
Let's start with a basic statement regarding method and class naming: I wouldn't repeat the class' topic over and over again (in the above case "user"), instead just remove it from the method name:
class User
{
private $info= array();
private $authenticated= FALSE;
public function login ($username, $password)
{
// do your stuff
...
// set in case that user name and password have been found
$this->info= $row;
$this->authenticated= TRUE;
}
public function isAuthenticated ()
{
return $this->authenticated;
}
/**
* returns all info from a given user
*/
public function get ()
{
return $this->info;
}
/**
* returns a single field
*/
public function getField ($fieldName) {
if (isset($this->info[$fieldName]) {
return $this->info[$fieldName];
} else {
return FALSE;
}
}
}
Use the getField(FIELD) method to return only a single element of the user's row and get() to return all values.
$user= new User();
$user->login ($username, $password);
if ($user->isAuthenticated()) {
$home= $user->getField('home');
print sprintf('%s\'s home is %s', $username, $home);
}
It's also advisable to create a class for the user database table (i.e. class UserModel) and another one handling user functions (i.e. class UserAuth) which uses the UserModel class. This makes exchanging the underlying authentication source more easy.
Class USER
{
public $_user;
public function userLogin($username,$password)
{
$statusY = "Y";
$stmt = $this->connection->prepare("SELECT * FROM tbl_users WHERE user_name=:userName LIMIT 1");
$stmt ->execute(array(":userName"=>$username));
$row = $stmt ->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() == 1)
{
$this->_user = $row; // Assign user details
$_SESSION['userSession'] = $row['user_id'];
}
}
public function getUser()
{
return $this->_user;
}
}
Can you explain me why this isn't working? I'm storing PDO fetch into PHP class variable and then I'm trying to loop it using function call.
PHP class
private $conn;
private $id;
private $data = array();
public function __construct($conn, $id) {
$this->id = $id;
$this->loadData();
}
private function loadData() {
$sql = $this->conn->prepare("SELECT * FROM table WHERE id = :id");
$sql->bindValue(':id', $this->id);
$sql->execute();
$this->data = $sql->fetch();
}
public function getData() {
return $this->data();
}
Script itself
$test = new test($pdo, "100101");
while ($row = $test->getData()) {
echo $row['item'];
}
There will only be loop as long as the memory limit has been reached. Query should return ca. 20 rows only.
Thanks in advance!
change fetch() to fetchAll()
change while to foreach ($test->getData() as $row)
The Situation
I'm fairly new to object-oriented programming in PHP and currently I'm creating a small CMS for learning purposes. I've learned a lot about OOP on my way, but I'm facing a weird issue at the moment. I've created a Singleton class to deal with the database connection and queries.
public static function getInstance()
{
if(!isset(self::$instance))
{
self::$instance = new Database();
}
return self::$instance;
}
In the same class, there also is a method to execute queries. It takes two parameters, the query and an optional array with parameters to bind for the prepared statements. You can see its source below.
public function execute($query, $params = array())
{
$this->error = false; // Set 'error' to false at the start of each query.
if($this->query = $this->pdo->prepare($query))
{
if(!empty($params))
{
$index = 1;
foreach($params as $parameter)
{
$this->query->bindValue($index, $parameter);
++$index;
}
}
if($this->query->execute())
{
$this->results = $this->query->fetchAll(PDO::FETCH_OBJ);
$this->count = $this->query->rowCount();
}
else
{
$this->error = true;
}
}
return $this;
}
The Problem
If I have multiple queries on the same page, the results and count variables still contain the values of the first query. Imagine the following - the first query retrieves all users from my database. Let's say there are 15. The second query retrieves all blog posts from the database, let's say there are none. If no posts are present, I want to display a message, otherwise I run a loop to display all results. In this case, the loop is executed even though there are no blog posts, because the count variable is used to determine if there are posts in the database and it still holds the 15 from the first query somehow.
This obviously leads to some errors. Same with results. It still holds the value from the first query.
$query = Database::getInstance()->execute('SELECT * FROM articles ORDER BY article_id DESC');
if(!$query->countRes())
{
echo '<h2>There are no blog posts in the database.</h2>';
}
else
{
foreach($query->results() as $query)
{
echo '<article>
<h3>'.$query->article_heading.'</h3>
<p>'.$query->article_preview.'</p>
</article>';
}
}
The countRes() and results() methods simply return the variables from the DB class.
I hope that I have explained the problem understandable. Responses are very appreciated.
I would use a response object to avoid attaching query specific data to the global database object.
Example:
<?php
class PDO_Response {
protected $count;
protected $results;
protected $query;
protected $error;
protected $success = true;
public function __construct($query){
$this->query = $query;
try{
$this->query->execute();
}catch(PDOException $e){
$this->error = $e;
$this->success = false;
}
return $this;
}
public function getCount(){
if( is_null( $this->count ) ){
$this->count = $this->query->rowCount();
}
return $this->count;
}
public function getResults(){
if( is_null( $this->results ) ){
$this->results = $this->query->fetchAll(PDO::FETCH_OBJ);
}
return $this->results;
}
public function success(){
return $this->success;
}
public function getError(){
return $this->error;
}
}
Then in your database class:
public function execute($query, $params = array())
{
if($this -> _query = $this -> _pdo -> prepare($query))
{
if(!empty($params))
{
$index = 1;
foreach($params as $parameter)
{
$this -> _query -> bindValue($index, $parameter);
++$index;
}
}
return new PDO_Response($this->_query);
}
throw new Exception('Some error text here');
}
UPDATE: Moved execution into response class for error handling
Example usage (not tested)
$select = $database->execute('SELECT * FROM table');
if( $select->success() ){
//query succeeded - do what you want with the response
//SELECT
$results = $select->getResults();
}
$update = $database->execute('UPDATE table SET col = "value"');
if( $update->success() ){
//cool, the update worked
}
This will help fix your issue in the event that subsequent queries fail, there will not be any old query data attached to the database object.
I wonder why I get errors with that code? Can Someone help?
My class is to get some information from database using nested methods,suppose I get an empty query
<?php
class db {
public function __construct(){
if(mysql_connect("localhost","root","0000")){
mysql_select_db("myblog");
}else{
echo mysql_error();
}
}
public function select($row){
$sql="SELECT".$row;
return $this;
}
public function from($table){
$sql.="FROM".$table;
return $this;
}
public function where($condition){
$sql.="WHERE".$condition;
return $this;
}
}
$ddb=new db;
$qq=$ddb->select("*")->from("news")->where("id='1'");
$query= mysql_query($qq);
while($row=mysql_fetch_object($query)){
echo $row->title;
}
?>
You have to define __toString() special method to use your object as a string:
class db {
private $sql = '';
public function __construct() {
if (mysql_connect("localhost", "root", "0000")) {
mysql_select_db("myblog");
} else {
echo mysql_error();
}
}
public function select($row) {
$this->sql = "SELECT ".$row;
return $this;
}
public function from($table) {
$this->sql .= " FROM ".$table;
return $this;
}
public function where($condition) {
$this->sql .= " WHERE ".$condition;
return $this;
}
public function __toString() {
return $this->sql;
}
}
$ddb = new db();
$qq = $ddb->select("*")->from("news")->where("id='1'");
$query = mysql_query($qq);
while ($row = mysql_fetch_object($query)) {
echo $row->title;
}
You need to be using $this in each of the methods to append to $sql
// Declare the class property
public $sql = "";
// And use $this->sql in the methods
// Also note whitespace added around SELECT, FROM, WHERE
public function select($row){
$this->sql="SELECT ".$row;
return $this;
}
public function from($table){
$this->sql.=" FROM ".$table;
return $this;
}
public function where($condition){
$this->sql.=" WHERE ".$condition;
return $this;
}
Then when you query it, use $ddb->sql, since you are not returning the SQL string.
$ddb->select("*")->from("news")->where("id='1'");
$query = mysql_query($ddb->sql);
And it goes without saying that hopefully you intend to be calling mysql_real_escape_string() on any variables you construct into your where() method. As it is, you have no particular escaping on it.
run a var_dump on $qq
That will show you the problem instantly.
To me it looks as if you are missing a couple of spaces in your query and that you aren't returning the query string $sql but instead $this in your db class
use $this->sql instead of $sql in the methods select, from, where
output the query like this: $ddb->select("*")->from("news")->where("id='1'")->sql or create a getSQL() method which just returns the $sql field and query it like $ddb->select("*")->from("news")->where("id='1'")->getSQL()
You should also consider creating a method which creates a mysql_query (not just the string) or some methods for retrieving methods, so it's easier to use.