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;
}
Related
Some people recommends calling close() on a prepared statement when I am done with the result of a query.
I often reuse the same variable name for the prepared statement, so I was wondering if overriding the variable automatically calls close() on the prepared statement ?
Example of how I currently do it (The SQL is made up for the examples):
// Fetch the requested user
$stmt = $mysqli->prepare("SELECT * FROM user WHERE id = ?");
$stmt->bind_param("i", $_GET['userid']);
$stmt->execute();
$user = $stmt->get_result()->fetch_assoc();
// Fetch all posts associated with the user
$stmt = $mysqli->prepare("SELECT * FROM posts WHERE user_id = ?");
$stmt->bind_param("i", $user['id']);
$stmt->execute();
$result = $stmt->get_result();
$posts = array();
while ($row = $result->fetch_assoc()) {
$posts[] = $row;
}
Should I call $stmt->close(); between fetching the user and fetching the posts or is it done when I override $stmt by calling $stmt = $mysqli->prepare(...); ?
Yes, most of the time, because PHP will try to clean up objects which have no reference as soon as possible. That means that once you overwrite it, there will be no more references to the old object and PHP will clean it up. Part of cleaning up mysqli_stmt object involves closing the statement.
But the reason why some people recommend calling $stmt->close() explicitely is to avoid errors such as this:
mysqli_sql_exception: Commands out of sync; you can't run this command now in ...
This error happens when you have not fetched all results from MySQL and you try to create a new statement by calling prepare or query. MySQL will not let you execute anything else until you fetch all remaining rows. This is usually achieved with get_result() or store_result(). If you always fetch the results in their entirety then you really don't need to worry much about when exactly the statement gets closed. Let PHP take care of it for you.
The best course of action is to avoid using mysqli functions directly. You should write some simple function which will encapsulate all the mysqli functionality so that you never have to worry about this low-level stuff. A sample function could look like this:
function safeQuery(\mysqli $db, string $sql, array $params = []): ?array {
$stmt = $db->prepare($sql);
if ($params) {
$stmt->bind_param(str_repeat("s", count($params)), ...$params);
}
$stmt->execute();
if ($result = $stmt->get_result()) {
return $result->fetch_all(MYSQLI_BOTH);
}
return null;
}
$result = safeQuery($mysqli, 'SELECT * FROM user WHERE id = ?', [$_GET['userid']]);
if ($result) {
$user = $result[0];
$posts = safeQuery($mysqli, 'SELECT * FROM posts WHERE user_id = ?', [$user['id']]);
foreach ($posts as $post) {
}
}
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
I am using PDO to access my data base and am looping using two while loops with fetch at the same time, seen below:
$DBH = new PDO('mysql:host=localhost;dbname=database;charset=utf8',$dblogin,$dbpass);
$sql = 'SELECT * FROM table';
$STH = $DBH->prepare($sql);
$STH->execute();
while ($bm_table = $STH->fetch(PDO::FETCH_ASSOC))
{
// SQL Query
$sql1 = 'QUERY HERE';
$STH1 = $DBH->prepare($sql1);
$STH1->execute();
// Loops through using different handle, but what if I used STH again?
while ($row = $STH1->fetch(PDO::FETCH_ASSOC))
{
SomeFunction($bm_table,$row);
}
}
As you can see above I am using a different statement handle ($STH, $STH1 etc.) Is this necessary? Or can I use just one statement handle for everything. The reason I have used multiple handles is as the $bm_table value that uses $STH, will still be in use while I am fetching $row wouldn't that change the value of $bm_table or stop the fetch from working? How does the handles with PDO work? Especially when in this case I have two simultaneous fetch loops running at the same time using the same PDO connection.
So the main thing I am looking for here is if I have two statements that are running simultaneously is it important that I use different handles when I continue to use the same connection?
$STH and STH1 are not statement handles, they're just PHP variables. You can reassign a variable if you no longer need its old value. But in the case of this code, you still need the old value.
If you assign $STH inside the outer loop to the handle returned by the second prepare() call, then when it gets back to the top of the loop and re-executes the $STH->fetch() test, it will try to fetch from the second query, not the first one. This will immediately end the outer loop because all those rows have been read.
You can reuse a statement handle for repetitions of the same query. This is very useful when the query has parameters:
$stmt = $DBH->prepare("SELECT * FROM tablename WHERE id = :id");
$stmt->bindParam(':id', $id);
foreach ($id_array as $id) {
$stmt->execute();
$row = $stmt->fetch();
// do stuff with $row
}
If I understand you correctly what you want is dynamic query?... just put a parameter on your method then...
something like this. call it as much as you want with difference parameters though.
Class SampleClass{
public function GetAll($tablename)
{
$sth = $this->prepare("SELECT * FROM $tablename");
$sth->execute();
return $sth->fetchAll();
}
}
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.
Is there an easy way to make an array of objects in php when you have the objects attributes come from a mySQL query?
My code is below in order to show you what I'm doing. I'm probably far off because I'm not used to php at all and I can't grasp it for some reason.
public function loadMeals(){
$stmt = $this->conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$stmt->bind_result($id, $dishName, $ingredients);
$meals = array();
while ($stmt->fetch()) {
$this->id = $id;
$this->dishName = $dishName;
$this->ingredients = $ingredients;
$meals[] = $this;
}
return $meals;
}
Keep in mind that this is all in a class Meals.
Thanks guys.
Based on how you are using it (creating an array of Meal objects, the method should be declared as a static method, called without $this context. The loop may create Meal objects via new self() and set the appropriate properties before appending onto the array.
If you think about it, this method's purpose is not to act on a single Meal, so it makes sense for it to be static.
Since it is called without $this context, you will need to pass the MySQLi connection as a parameter
public static function loadMeals($conn) {
// Do the query/fetch like you already have
// except that $conn is a local param, not a class prop.
$stmt = $conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$stmt->bind_result($id, $dishName, $ingredients);
$meals = array();
while ($stmt->fetch()) {
// Create a new Meal object
$meal = new self();
// And set its properties
$meal->id = $id;
$meal->dishName = $dishName;
$meal->ingredients = $ingredients;
// Append it to the array
$meals[] = $meal;
}
// Return it
return $meals;
}
To use it, outside the class:
// Call the function statically.
// You'll need to pass the database connection to it
$meals = Meal::loadMeals($conn);
I'll point out that while it is very good to be in the habit of doing prepare()/execute(), you do not actually need it here since you are not passing any input into the query. You can just call $conn->query(), eliminating some complexity from MySQLi's bind_result() variable references.
$result = $conn->query("SELECT id, dishName, ingredients FROM meals");
if ($result) {
// Fetch and instantiate a Meal object in one go.
while ($meal = $result->fetch_object('Meal')) {
$meals[] = $meal;
}
}
This might be a short solution to your problem but consider using mysql_fetch_assoc when grabbing your elements from the database.
For more reading and example:
mysql_fetch_assoc documentation from php.net
For PDO:
public function loadMeals(){
$stmt = $this->conn->prepare("SELECT id, dishName, ingredients FROM meals");
$stmt->execute();
$meals = array();
while ($obj = $stmt->fetch(PDO::FETCH_OBJ)) {
$meals[] = $obj;
}
return $meals;
}
or $meal = $stmt->fetchObject('MyMeal', $ctor_args); See this from PHP manual. Also note the first comment about constructors there, to expect the unexpected results that using this may cause.