Mysql parent child tables reducing database calls or merge in PHP - 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?

Related

Handling categories and sub categories MySQL or JSON

I am trying to find the best way to handle category and sub category. I have 20 category and 50 sub category. Which of these is best way to do so :
Save data in a json file and reading content directly on client side.
Save data in database in single table and using parent id to see the relation and using foreach on result array inside another foreach of same array.
Save data in database in two table, making one sql call to parent category another one call to sub category and using parent id to see the relation and using foreach of sub category array inside another foreach of parent array.
Save data in database in two table, making one sql call to parent category and then inside its foreach making multiple sql calls to database.
I tried to find the best practice to handle categories but couldn't find any article for the same.
The solution depends purely on how complex your database schema and other entities relate to the categories. And how you intend to read the information.
The json approach would be faster, but has issues when it comes to queries that would require you to link up to category additional information.
Another approach I have used and had good performance is storing all categories in a single table. The relationships are not stored in the main table.
Another table stores the relationships as graph edges. This is quite advantageous if you have cyclic relationships within the categories. Or more than one parent.
The schema would look like :
categories ( id, name )
category_edges ( parent_id, child_id)
I used oqgraph with my implementation to get the relationships queries faster. But that was with MariaDB and not mysql.
Hope this helps.

CI Datamapper: Iterate parent child

I have categories and posts.
I would like to list all categories (parent), and all related posts (child). Without nested sets, complex queries. Relations setted up.
As i rembember from the documentation:
$categories = new Category();
foreach( $categories->get() as $category )
{
echo $category->name;
foreach( $category->post->get() as $post)
{
echo $post->title;
}
}
Now if i have thousand categories, it will make a query for each category.
What is the best practice in this case?
Create two query for posts and categories and merge them into a tree with php?
Join two tables, one query and create the tree with php?
Somebody's smart idea :)
I would create a dedicated model method that retrieves the data you want, and I'd retrieve it in batches (for pagination). Before I jump into the idea with some example code, the core concept is that you have two tables which you link via an INNER JOIN statement and that you limit your selects in batches. You then display data as you would normally in your view.
Let's say that you have a category table that looks something like this:
id
name
Let's also say that you have a post table that looks something like this:
id
name
category_id
I would now basically retrieve the entries that you need, but also limit my results.
$this->db->select('*');
$this->db->from('category');
$this->db->join('post', 'post.category_id = category.id', 'inner');
$this->db->limit($limit, $offset);
$query = $this->db->get();
Pagination saves you when you have to display a lot of data. If you however, must display all the data at once, consider using Memcached, Gearman or similar caching technology, so that you have better performance.

PHP & MySQL Join

I'm relatively new to coding in general, first year CS student and all of that. I'm using PHP to display a list of school classes categories and a list of assignment posts within each category. The desired output would be something like this:
CATEGORY 1
-Assign Post 1
-Assign Post 2
CATEGORY 2
-Assign Post 0
Using join gives me partial functionality, because it gets all the assign posts, but when I loop through and post the data it posts the Category title more than once. I then tried using group_by but it's only posting one post per category.
I'm not gonna post all my code, simply the db queries. I'll happily post the other code if you think the problem may be there.
//Select our classes, and get assignments from each class
$this->db->select('*');
$this->db->from('categories');
$this->db->join('assignments', 'categories.cat_id = assignments.assign_cat_id');
$this->db->group_by("cat_id");
//Return the classes and assignments in an array
$query = $this->db->get();
return $query->result_array();
Not to tricky right? Any help is of course appreciated :).
UPDATE
I have figured out that group_concat will post the assignment names, and then I can explode the results. Is this the only way, or is there a way to get it to post in a nice friendly array. The only reason I ask is I feel this adds un-necessary code in my view file, where I'm exploding the data.
Thank you!
Dont use group by ...use sort by Category id and while showing/or preparing array of data you can club records as per category..you cant achive this by single SQL

PHP: Doing the same query over and over?

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.

Adjacency List Model + Website Navigation

I am using the adjacency list model to find sub categories within my website. I have working PHP code to find all the categories and sub categories, but now I cannot figure out how use that to create a navigation system. Here is how the site will work, very basic:
URL string
There will be a main category, followed by levels
index.php?category=category-name&level1=sub-category&level2=another-sub-category&level3=content-item
Later I will make SEO friendly links.
URL with no sub categories
Where Level 1 is the content item
www.website.com/category/content-item/
URL with sub categories
Where Level 1, 2, 3, etc are the sub categories and the final level is the content item
www.website.com/category/sub-category/sub-category-2/content-item/
Here is the code I am using to find categories and sub categories. Currently it just outputs a list of all categories and sub categories and number's the level of each child. Not sure if this helps, it just creates a list.
function display_children($ParentCategoryID, $Level) {
// retrieve all children of parent
if ($ParentCategoryID == ''){
$Result = mysql_query('SELECT * FROM categories WHERE parent_category_id IS null');
}
else{
$Result = mysql_query('SELECT * FROM categories WHERE parent_category_id="'.$ParentCategoryID.'";');
}
// display each child
while ($Row = mysql_fetch_array($Result)) {
echo str_repeat('-',$Level)."[".$Level."]".$Row['category_name']."<br />";
display_children($Row['category_id'], $Level + 1);
}
}
See this question first for options on how to represent hierarchical data in a database.
Adjacency list is great for its simplicity, and makes changes easy, but can be awful because it leads to recursive code, such as your function above, in practice, which is a performance killer under load. The best approach, absent changing your data model is using MySQL session variables to retrieve the entire hierarchy in one query, which brings back all the data you need in one database call. Even this though leads to poor performance under load - less so than the recursive function - but still not good; and, I write from experience :).
If it was me I'd use either Nested Sets, Adjacency List in combination with some denormalizations, such as the Bridge Table and Flat Table, or just a Lineage Table. Really depends on how often the data changes and if you need those changes to be done easily. All of these options should be much, much faster, to work with rather than relying upon just the parent-child ID columns.

Categories