Im learning, and am still struggling to get the hang of many OOP concepts so please keep that in mind when reading / answering the question.
So one of the primary purposes of object orientated programming is not to repeat yourself right. However, when I am creating methods, I constantly find myself repeating the same statements when querying the database. As can be seen in the below code taken from class I created.
function getCategory()
{
$sql = "SELECT * FROM jobs";
$stmnt = $db->prepare($sql);
$stmnt->execute();
$results = $stmnt->fetchAll();
foreach ($results as $result) {
$cat[] = $result['category'];
}
return $this->category = $cat
}
function selectCategory($selectedCategory){
$sql = "SELECT * FROM jobs
WHERE category =:category";
$stmnt = $db->prepare($sql);
$stmnt->bindValue(':category', $selectedCategory);
$stmnt->execute();
$results = $stmnt->fetchAll();
foreach($results as $result){
$result= array('category' => $result['category'], 'headline' => $result['headline']);
}
return $this->category = $result
}// selectCategory
My question.
Is there a way / what should I do to avoid having to continuously write the same database queries in my methods? I feel im a little out of depth here, however its not going to stop me from trying. Any help, advice welcomed (please keep in mind im a beginner)
You can run the query to get all data, then extract the data from fetched data using PHP.
But I don't like it, and it is not efficient.
You have 2 different query , so you need to calls, just improve your code a little more, you can make a model like this:
class Category
{
private $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
function getAll()
{
return $this->db->query('SELECT * FROM jobs');
}
function getByCategory($category){
$stmt = $this->db->prepare(
"SELECT * FROM jobs WHERE category =:category"
);
$stmt->bindValue(':category', $category);
$stmt->execute();
return $stmt->fetchAll();
}
}
This is perfectly fine, and it is not really repeating
Related
Since a few weeks I use prepared statements in PHP with the fetch_object(className:: class). I really like this way because of the autocomplete in my controllers and in my view(Twig), but some times I need an inner join (One to many).
Now I do it like below. Luckily I have only a few results in the projects where I use this so the number of queries is low. But on a large result, this will create a lot of queries. To improve performance I store the $returnResult in apcu / Redis in the controller or save it in a JSON file when I can't use the other cache method.
My question is: can I do this on a better way to reduce the number of queries but still use the fetch_object(className:: class)
Thanks in advance for the help.
public function getAll()
{
// Create database connection
$db = DB::connect();
$stmt = $db->prepare("SELECT * FROM dataroom_category ORDER BY display_order");
$stmt->execute();
$returnResult = [];
$categoryResult = $stmt->get_result();
$stmt2 = $db->prepare("SELECT * FROM dataroom_file WHERE dataroom_category_id = ? ORDER BY display_order");
$stmt2->bind_param('i', $id);
/** #var data $category */
while ($category = $categoryResult->fetch_object(data::class)) {
$id = $category->getId();
$stmt2->execute();
$filesResult = $stmt2->get_result();
while ($file = $filesResult->fetch_object(DataroomFile::class)) {
// Add the file to the setFiles array with $file->getid() as key
$category->setFiles($file);
}
// I noticed by using mysqli_free_result the server used alot less memory at the end
mysqli_free_result($filesResult);
$returnResult[$category->getId()] = $category;
}
mysqli_free_result($categoryResult);
$stmt2->close();
$stmt->close();
// Return the categories with the files for usage in the controller and view
return $returnResult;
}
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Any idea to avoid to repeat this long messy code every time i want to get the data from the database in secure way?
public function test($param) {
$sql = "SELECT * FROM users WHERE id = :id AND item_id :item_id";
$this->_query = $this->_db->pdo()->prepare($sql);
$this->_query->bindValue(':id', $param);
$this->_query->bindValue(':item_id', $parmas);
$this->_query->execute();
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
}
i created good way not to mess around for a simple one like this
public function query($sql, $params = array()) {
$this->_error = false;
if($this->_query = $this->_pdo->prepare($sql)) {
if(count($params)) {
//outside of the loop
$x = 1;
foreach($params as $param) {
$this->_query->bindValue($x, $param);
$x++;
}
}
if($this->_query->execute()) {
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
$this->_count = $this->_query->rowCount();
} else {
$this->_error = true;
}
}
return $this;
}
pdo is already defined in the construct function in the class.
//im adding more details here and this is what im trying to make.
public function example($table, $params = array(), $extn = array(), $values = array()) {
$x = '';
$y = 0;
foreach($params as $param) {
$x .= $param;
if($y < count($params)) {
$x .= $extn[$y];
}
$y++;
}
$sql = "SELECT * FROM {$table} WHERE {$x}";
$this->_query = $this->_pdo->prepare($sql);
foreach($values as $value) {
$this->_query->bindValue($value);
}
$this->_query->execute();
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
}
but im getting this error Warning: PDOStatement::bindValue() expects at least 2 parameters,
with this code down below
$test = new Example();
$test->example('users', array('id = :id', 'username = :username', 'id = :username', 'username = :id'), array(' AND ', ' OR ', ' AND '), array("':id', 4", "':username', 'alex'"));
any suggestion will be helpful to me!!
Not to sound harsh, but there is a multitude o things wrong with your approach. Most immediate to your problem is that the passed $values are garbled.
I would expect the bindValues() in example to look something like this:
foreach ($values as $param => $value) {
$this->_query->bindValue($param, $value);
}
Consequently $values given to example() should look something like this:
$values = array(':id' => 4, ':username' => 'alex')
For reference see the php-docs on PDOStatement::bindValue()
Apart from that:
You just pass the variable $table into the query:
$sql = "SELECT * FROM {$table} WHERE {$x}";
This is unsafe! While you are more concerned about the values passed to the query (which is understandable) your will still have a vulnerability here.
Your class database stores the query and its result in a class variable. This is unnecessary (database engines have query caches, if you want those files cached in php you should results in a cache, e.g. by wrapping database in a class cached_database) and potentially will cause bugs when your query/results are accidentally reused or mixed up.
There are many ways to mess up your queries by giving the wrong parameters and because almost all of them are arrays it is hard to figure out what values to put in there. This makes your whole setup very fragile, e.g. it depends on the right number of $params and $extn passed to example which almost certainly will make problems in the future. Not only because it's hard to figure out what happens there, but also because it (most likely) lacks functionality you might want to use in the future such as queries with IN or BETWEEN. It may safe you from writing a few lines of code, but after not using it for a few weeks you will spend that time trying to figure out what happens and how to use it (and probably more). Trust me, we have all been there. ;)
I think you are much safer repeating the PDO-stuff which looks a bit repetitive, but is easily understood - especially when someone else takes over or is to help you in the future - and is well documented.
Should you feel the need to simplify common tasks like SELECT-ing similar data you should instead consider an ORM like Doctrine. Even just the Doctrine DBAL might help you because you get a powerful SQL Query Builder.
If you really want to keep your code and don't want to use other libraries try simplifying it using baby steps. Whenever a small part is repeated in exactly the same way put it into a private method. You could do this for example with these 3 lines in your first example:
$this->_query->execute();
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
return $this->_results;
Take your first code snippet:
class database {
$_results = null;
/***
#param array $param should be an array of two elements
***/
public function test($param = []) {
$sql = "SELECT * FROM users WHERE id = :id AND item_id :item_id";
$this->_query = $this->_db->pdo()->prepare($sql);
$this->_query->bindValue(':id', $param);
$this->_query->bindValue(':item_id', $param);
$this->_query->execute();
$this->_results = $this->_query->fetchAll(PDO::FETCH_OBJ);
return $this->_results;
}
}
You could simplify it by just extracting bits you repeat often into smaller private methods:
/*
* Don't just call it "database", that's way to generic.
* Try to give it an explicit name, for example UserRepository
*/
class UserRepository
{
public function getUsersByIdAndItemId($id, $itemId) {
$sql = "SELECT * FROM users WHERE id = :id AND item_id :item_id";
$query = $this->getPreparedQuery($sql);
// No need to simplify this:
$query->bindValue(':id', $id);
$query->bindValue('item_id', $itemId);
return $this->executeQuery($statement);
}
public function getAllUsers()
{
$sql = "SELECT * FROM users";
$query = $this->getPreparedQuery($sql);
return $this->executeQuery($query);
}
private function getPreparedQuery($sqlQueryString)
{
/*
* No "$this->_query"
* You don't have to reuse it! Worst case scenario a diffeent
* method accidentally reuses the query and you get a PDOException.
*/
return $this->_db->pdo()->prepare($sqlQueryString);
}
private function executeQuery(\PDOStatement $query)
{
$statement->execute();
/**
* Again no need to store this in a class variable.
* Worst case scenario you end up with a dirty state
* or mixed up data
*/
return $statement->fetchAll(PDO::FETCH_OBJ);
}
}
This is much safer than your approach, because it's simple and clear. Your input is always secured and whenever you use UserRepsitory::findUsersByIdAndItemId() you can't accidentally send to many params or give them the wrong name or otherwise mess up the sql query. You just pass exactly the values you need. Sometimes violating "Don't repeat yourself" is ok if it makes your code more secure and better to understand.
I am new to OOP in PHP and i am trying to create a class, and then query the database. ATM the code looks like this and i am stuck in the query part. The query is ok, but it should use the class created. Can anyone help me please?
<?php
class Products {
//objekto kintamieji
public $category_id;
public $product_id;
public function __construct($category_id, $product_id){
$this->category_id = $category_id;
$this->product_id = $product_id;
}
public function query_the_database() {
if($xml->action == 'getProducts') {
$query = mysql_query("SELECT * FROM product WHERE category_id = 1 ORDER BY product_id");
while($row = mysql_fetch_object($query)){
$row->pvm = $row->price - round($row->price*100/121, 2);
$prod[] = $row;
}
}
}
}
You really should be using MySQLi or, even better, PDO on your class.
And, I highly recommend that you establish your connection in a separate class. So you have two pages: db.class.php and products.class.php.
Well, basic tutorial:
Establishing a connection:
$db=new PDO("mysql:host=HOST_NAME;port=PORT;dbname=DB_NAME");
Executing normal queries:
$db->execute("select * from table");
Executing queries with parameters (prepared statements):
$sql=$db->prepare("select * from table where param1=:p1 and param2=:p2");
$sql->bindParam(":p1", $p1); //bindParam only accepts variables
$sql->bindValue(":p2", "Value"); //bindValue only accepts raw values
$sql->execute();
Fetching values of prepared statements:
$array=$sql->fetchAll(); //that will be an array containing values in column names that are in row numbers. Like this: Array([0]=>Array([0]=>"value1" [column1]=>"value1") [1]=>Array([0]=>"value2" [column1]=>"value2"))
But please, go read about it since it will help you A LOT.
Good evening everyone!
I need your help again. Please bear with me because I am very new to this. Hoping for your understanding. So, I am having a project on oop and pdo. I am having quite hard time converting this into pdo.. Here is my code..
bookReserve.php
while($row=mysql_fetch_array($sql))
{
$oldstock=$row['quantity'];
}
$newstock = $oldstock-$quantity;
Here's what i've done
while($row = $code->fetchAll())
{
$oldstock=$row['quantity'];
}
$newstock = $oldstock-$quantity;
Is that even correct?
And for the oop part, after this while loop I have a query to execute.
$sql="update books set no_copies = '$newstock' where book_title = '$book_title'";
Here's what I've done trying to convert it into pdo
public function bookreserve2($q)
{
$q = "UPDATE books SET quantity WHERE = $newstock where book_title = '$book_title'";
$stmt = $this->con->prepare($q);
$stmt->execute(array(':newstock'=>$newstock));
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result;
}
Again, Is that even the correct converted query?
and how would I call $newstock?
P.S. my oop class and pdo is placed in a separate file. Thought this might help.
Thanks
You are not including your query parameters in your function, and your query has syntax errors (extra WHERE) and you are directly inserting your values not using placeholders.
It should look something like -
public function bookreserve2($newstock,$book_title)
{
$q = "UPDATE books SET quantity = :newstock WHERE book_title = :book_title";
$stmt = $this->con->prepare($q);
$stmt->execute(array(':newstock'=>$newstock,':booktitle'=>$book_title));
if($stmt){
return true;
}
else {
return false;
}
}
I try to get something like this from a MySQL-database via PHP and PDO query:
return array(
"Jungle Book" => new Book("Jungle Book", "R. Kipling", "A classic book."),
"Moonwalker" => new Book("Moonwalker", "J. Walker", ""),
"PHP for Dummies" => new Book("PHP for Dummies", "Some Smart Guy", "")
);
Each row of the database should be stored in an object.
Can anyone help me with this?
I tried this:
return array(
foreach ($dbh->query("SELECT * FROM books") as $row) {
$row['name'] => new Book($row['name'], $row['author'], $row['description']);
}
)
...but foreach isn't allowed in arrays...
Background: for learning purposes I'm following this tutorial: http://php-html.net/tutorials/model-view-controller-in-php/ and I'm trying now to replace the static list of books with code that is working with a real database.
This question has nothing to do with mvc or pdo actually, but rather with PHP syntax at all.
Your task is rather simple, only you need is to refrain from idea of having all the code in one single operator:
$data = array();
$stmt = $dbh->query("SELECT * FROM books");
foreach ($stmt->fetchAll() as $row) {
$data[$row['name']] = new Book($row['name'], $row['author'], $row['description']);
}
return $data;
Although PDO has a syntax sugar for you, in my opinion one have to learn the underlying basic statements first.
You can set the fetch type to FETCH_CLASS.
Creating and returning the array in a single statement is an artificial and needless requirement. I wouldn't do that, it makes one's code hard to debug, hard to test, and hard to maintain. Also the query() method may return false on error, so it will be a fatal error if you try to use it in a foreach statement.
$stmt = $dbh->query("SELECT * FROM books");
if ($stmt === false) { /* do something to handle the error */ }
$results = $stmt->fetchAll(PDO::FETCH_CLASS, 'Book');
return $results;
Or if you want a results array indexed by name:
$stmt = $dbh->query("SELECT * FROM books");
if ($stmt === false) { /* do something to handle the error */ }
$results = array();
while ($book = $stmt->fetch(PDO::FETCH_CLASS, 'Book')) {
$results[$book->name] = $book;
}
return $results;
You're probably wanting to return associative array values. In your current code, you cannot run a foreach while inside the array. But you can do this:
return $dbh->query("SELECT * FROM books")->fetchAll(PDO::FETCH_ASSOC);