PHP: Doing the same query over and over? - php

I have a Product class that when instantiated, fetch data from "product" table, it also creates a "Category" object inside it (product->category) that fetch data from the category table. My problem is, what happens when multiple products from the same category are listed, multiple equal queries are made in order to create the category object, since they're from the same category. How to solve this?

Use a JOIN in your SQL:
SELECT *
FROM product
LEFT JOIN category ON product.category_id = category.id;

You'll have to build a layer that intercepts and/or caches your queries. When it sees you're requesting category with a certain ID, it should present that category from its cache, and if it isn't there, it should retreive and then cache it, so during a request the same row won't be queried more than once.
Doing this manually is a no-go, simply pick a decent ORM that will do this for you.

Use factory that creates the objects, and provide the option the data for product/category to be passed in the constructor. The factory loads the data for all products that will be listed, than instantiate the product objects, providing the already fetched data

Inside the loop executing sql query is not good practice.
What you can do is left join product table with the category tabel.
$select = SELECT *
FROM product
LEFT JOIN category ON product.category_id = category.id;
Then execute query and then classify your products into category basis
like you can run loop
$result = mysql_query($select);
while($data = mysql_fetch_assoc($result)){
$product_info['product_name'] = $data['product_name'];
$product_info['price'] = $data['product_price'];
$product[$data['category']][$data['product_id']] = $product_info;
}
This loop will do the magic that it will help you to classify products falling under same category.
And you can display your data in more effective way.

Related

Is there an SQL query I can use to satisfy a rather complicated set of criteria?

I'm making a simple option-based adventure game using PHP and a mySQL database in phpMyAdmin. I've got it set up so that events in the game are objects in a table called Events. The options you can choose at each event are in a table called Actions. The two have a many-to-many relationship, because some actions may be taken at multiple events. Therefore, I've created a junction table called EventActions. I can successfully query which actions should be available for an event with an inner join using that junction table:
public static function getActions($eventID){
$db = Database::getDB();
$query = 'SELECT * FROM eventactions ea
INNER JOIN actions a ON a.actionID = ea.actionID
WHERE ea.eventID = :eventID';
$statement = $db->prepare($query);
$statement->bindValue(":eventID", $eventID);
$statement->execute();
$actions = $statement->fetchAll(PDO::FETCH_ASSOC);
$statement->closeCursor();
return $actions;
}
However, the situation gets sticky when I try to incorporate needing certain items to do certain actions. I'm also trying to incorporate needing to have done certain things for certain actions to show up. This has led me to make tables for Items, and Effects, and junction tables for itemActions and effectActions. So now my database query function needs to be much more complicated, and I'm having trouble wrapping my head around forumlating the query. I need a query that essentially gets from the database: All actions that are tied to the event given, and the items and worldeffects columns that are tied to that action. I attempted something like this, but it does not return any rows:
select * from eventactions ea
inner join actions a on a.actionID = ea.actionID
inner join actionitems ai on ai.actionID = a.actionID
inner join actioneffects ae on ae.actionID = a.actionID
inner join worldeffects e on e.effectID = ae.worldEffect
where ea.eventID = :event
What I want returned from this function is a list of all of the options that are available for a given event. For each option, I want the columns from the Action table, from the Items table (so I know which items this option requires) and from the Effects table (so I know what effect on the world an option has, and what world effects are required for an option to be available).
Let me know if I'm not including necessary information. Thanks!

Showing category content with sub categories content

I have main categories and sub categories.
at cat.php?id=1 page; (id=1 is main category)
I want to show also subcategories content.
My categories table:
id - sub - title
(if sub=0 it means this is main category. if not it's sub category)
My current query is like that;
<?php
$id = $_GET['id'];
$data = mysql_query("select * from content where category=".$id." order by id desc");
while($r=mysql_fetch_array($data))
{
echo "$r[id] - $r[title]";
}
?>
Shows only main category content, but not sub categories content. (at cat.php?id=1)
*
I think I must connect the categories table again to get sub categories' ID. ???
Surely, I need a new query. I need to get sub=".$id." and list here in the same page.
I stuck.
So you need to get the Id's of the subcategories as well, you can embed a second query inside of that query (or split it into two separate ones, but that adds another request to the server).
You would put something along these lines:
$data = mysql_query("select * from content where category IN (SELECT id FROM categories WHERE id='$id' OR sub='$id') order by id desc");
using the WHERE ... IN lets you select multiple values from a list like (3,5,2)
So it will look similar to this when the subquery executes:
select * from content where category IN (1,3,2,5,13) order by id desc
WARNING:
You need to sanitize your $_GET['id'] so that no sql injection occurs, this will currently allow sql injection.
Also, the mysql_ functions are deprecated and you need to start using PDO prepared statements, I am not familiar enough with them, but they will do the sanitizing of user input for you.

Update Field using a set of IDs from another table at random

I have 3 Tables. model, category, and document. Documents belong in a category, which belongs to a model, and models can have multiple categories, which can have multiple documents.
I'm trying to build & execute this query in PHP, assigning each document a randomly selected category from a list of the categories in the currently selected model.
So, if a model has 10 categories (cat1-cat10), with each of those categories having 10 documents, the end result would make 1000 documents have a random_category_id field of cat1 - cat10 assigned at random, but not overwriting the existing category_id of the document.
Later in the application, I need to be able to calculate when document.category_id == document.random_category_id.
Is there a way to do this in one query. I'm new to SQL & PHP (and haven't mastered any kind of JOIN yet), so please forgive the blunders in database design & mixed coding approaches. I know the below example will not execute.
I'm using MySQL 5.5.28 with InnoDB.
Pseudocode Example
$catList = SELECT category_id FROM category WHERE model_id = '$current_model_id'
UPDATE document.random_category_id = RANDOM($catList) WHERE document.model_id = '$current_model_id'
Thank you!

Mysql parent child tables reducing database calls or merge in PHP

If I had 2 tables, say blog_category and blog, each "blog" can belong in a particular category only so a 1-1 relationship based on a key called "blog_category_id".
Now in my code I would do something like:
//Loop through categories such as
foreach($categories as $cat):
//then for each category create an array of all its posts
$posts = $cat->getPosts(); // This would be another DB call to get all posts for the cat
//do stuff with posts
endforeach;
Now to me this seems like it could end up quite expensive in terms of DB calls depending on the size of $categories. Would this still be the best solution to do this? Or would I be able to do something in the code and first retrieve all the categories, then retrieve all the blogs and map them to their corresponding category via the id somehow? This would in theory be only 2 calls to the DB, now size wise the result set for call 2 (the blogs) would definitely be larger, but would the actual DB call be as expensive?
I would normally go for the first option, but I'm just wondering if there would be a better way of approaching this or is it more likely that the extra processing in PHP would be more costly in terms of performance? Also specifically from an MVC perspective, if the model returns the categories, but it should also return the corresponding blogs for that category, I'm not sure how best to structure this, from my understanding, shouldn't the model return all the data required for the view?
Or would I be better off selecting all categories and blogs using inner joins in the first query and create the output I need of this? Perhaps by using a multi-dimensional array?
Thanks
You can use a simple SQL query to get all categories and posts like the following:
SELECT *
FROM posts p
JOIN categories c ON c.id = p.blog_category_id
ORDER BY c.category_name ASC,
p.posted_date DESC
Then when you loop over the returned records assign the current category id to a variable, which you can use to compare against the next records category. If the category is different then print the category title before printing the record. It is important to note that for this to work you need to get the posts ordered by category first and then post so that all posts in the same category are together.
So for example:
$category_id = null;
foreach($posts as $post) {
if($post['blog_category_id'] != $category_id) {
$category_id = $post['blog_category_id'];
echo '<h2>' . $post['category_name'] . '</h2>';
}
echo '<h3>' . $post['post_title'] . '</h3>';
echo $post['blog_content'];
}
Note: as you have not posted up the schema of these two tables I have had to make up column names that are similar to what I would expect to see in code like this. So the code above will not work with your code without some adjustments to account for this.
The best solution depends on what you are going to do with data.
Lazy loading
Load data when you need it. It's a good solution when you have, for instance, 20 categories and you load posts for only 2 of them. However, if you need to load posts for all of them it won't be efficient at all... It's called a n+1 queries (and it's really bad).
Eager loading
On the other hand, if you have to access to almost all of your posts, you should do an eager loading.
-- Load all your data in a query
SELECT *
FROM categories c
INNER JOIN posts p ON c.id = p.category_id;
// Basic example in JSON of how to format your result
{
'cat1': ['post1', 'post2'],
'cat2': ['post5', 'post4', 'post5'],
...
}
What to do?
In your case I would say an eager loading because you load everything in a loop. But if you don't access to the most of your data, you should re-design your model to perform a lazy loading in such a way that the SQL query to load posts for a specific category is actually performed when a view try to access them.
What do you think?

SQL queries or php code?

Hello i am in a delima
Suppose that i have 50 products in a category and i want to get all the features of my products...
Ofcourse a SQL join wont help because joining would return product info many times!
So here is the question.. what is better
PLAN A
Get all the products' features in category with one SQL query and then iterate with php each feature and put it in Product.
PLAN B
For each product get the features by calling a query
Other solutions accepted!
EDIT
My table schema outline is..
A table Product which has product info(per row)
A table features which has features (feature id )
A table for features' values
And a table that has Products with their features and values
$sql1 = "SELECT * FROM products P, ". //don't use star, make sure no fields are overwritten
INNER JOIN products_to_features PTF on P.id = PTF.project_id
INNER JOIN features F F.id = PTF.feature_id
ORDER BY P.id";
$r = mysql_query($sql1, $conn);
$arr = array();
$lastProductId = -1;
while ($row = mysql_fetch_assoc($r))
{
if ($row[p_id] != $lastProductId)
{
$lastProductId = $row['p_id'];
$arr['p_id'] = array('productName' => $row['p_name'],
'productPrice' = $row['p_price'],
'productFeatures' = array(),
//other fields from product table);
}
$arr['p_id']['productFeatures']['f_id'] = array('featureName' => $row['f_name'], blah...);
}
I don't know your fields obviously, and you may want to join on feature_values so that will be more work. You can do keys/values different (ie - product names as keys. Feature-name as keys with feature-value as values, whatever you want) but the point is this is doable (and recommended) in one query.
Not Plan B.
Whatever you do this can and should be done with one or at most two total queries (one for headers, one for correctly sorted list of features + id column). Whether that query is Plan A or some unmentioned Plan C depends on your exact table structure, which isn't clear from your question.
Generally, the less database queries you have to make, the better. It greatly depends on your table structure, but I'd go with Plan A.
A query inside a loop will greatly degrade performance of your application. Avoid it at all cost.
Show us the schema. There's a strong possibility a single SQL query will solve your problem.

Categories