Setting up a nested set hierarchy - php

Let's say I have a bunch of businesses. And each business can have multiple categories, subcategories and sub-subcategories (three levels). Let's say I set up a table according to the nested set model for my categories.
How do I now use this table and assign categories to each business? I understand I will need another table but what node gets assigned? Is it the lowest level node?
business_id category_id
And then what's the right way to retrieve all the categories for each business?

The way this generally works is that you assign the leaf or lowest-level-node. Then when you are querying to get the full hierarchy you traverse up the tree to the root. It is generally much easier (especially in MySQL) to traverse from leaf to root then vice versa.
Here is the best link I've found that describes how to accomplish this query for a tree of dynamic size (the link you've included assumes that the tree is always 3 levels deep)

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.

checking value in n-depth tree?

I have two entities, post and category which is a 1:n relationship.
I have a reference table with two columns, post_id,category_id
The categories table has an id column, a status column and a parent_id column
If a category is a child of another category (n-depth) then it's parent_id is not null.
If a category is online it's status is 1, otherwise it is 0.
What I need to do is find out if a post is visible.
This requires:
Foreach category joined to the post trace up it's tree to the root node (till a category has parent_id == null), if any of those categories have status 0 then that path is considered offline.
If any path is online then the post is considered visible, otherwise it is hidden.
The only way I can think of doing this (as semi-pseudo code) is:
function visible(category_ids){
categories = //select * from categories where id in(category_ids)
online = false
foreach(categories as category){
if(category.status == 0)
continue;
children = //select id from categories where parent_id = category.id
if(children)
online = visible(children)
}
return online
}
categories = //select c.id from categories c join posts_categories pc on pc.category_id = c.id where pc.post_id = post.id
post.online = visible(categories)
But that could end up being a lot of sql queries, is there a better way?
If nested sets are not an option, I know about the following:
If the data is ordered so that children of a parent always follow after it's parent, you can solve this with one database-query over all data by skipping hidden nodes in the output.
This works equally with a sorted nested set, too, the principle has been outlined in this answer however the algorithms about getting the depth do not work and I would suggest a recursive iterator that is able to remove hidden items.
Also if the data is not ordered, you can create a tree structure from the (unsorted) query of all rows like outlined in the answer to Nested array. Third level is disappearing. No recursion needed and you get a structure you can easily output then, I should have covered that for <ul>/<li> html style output in another answer, too.
Answer to How can I convert a series of parent-child relationships into a hierarchical tree?
Answer to How to obtain a nested HTML list from object's array recordset?
A classic database vs memory tradeoff. What you are doing is building a tree with leafs in it. To build the tree you need recursive loop the leafs. Coming from a database there are 2 scenarios:
Build the tree recursive with a query for each leaf. You hold 1 tree in memory. That is what you are doing.
Get a flat structure from the database, and build the tree recursive in memory. You hold a flat tree and the real tree in memory. That is your alternative way.
What is better depends on a lot of things: your hardware (disk access vs memory), the size of the tree to name two.

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.

Should the father know about the children or vica versa?

Should the father know about the children or vica versa?
That is, in a situation where I have two kinds of objects (items and categories), each has its own characteristics and fields and we have a relation between them that every item has a category he is in.
I want to ask in two ways about what should happen, in theory and in php + mysql.
Should the categories know about all the items that are in it, or should every item know about its category?
In php + mysql the exact question is should I for every item in its row save its category's id, or should I create a special table for this relation (each row contains a category id and an item id)?
Best practice is the children know about the parents (ex: parent_id). The alternative is not scalable, and very taxing to your system. You could easily run a query to FIND the children though - SELECT * FROM items WHERE item_category = x. The reason for this is when you add or delete items, the category is not affected. It doesn't become a different category because you assign an item to it or remove an item from it, so it shouldn't care either way which items are assigned to it.
This is different from a tagging setup. In the one-to-many category assignment method, items would fall into a single "category" but might have many "features". Tagging features is a many-to-many relationship and would require the mapping table you mentioned. Assigning parent categories requires only a single "parent" field in the items table.

How to create a dynamic tree like structure

I have the details of the members of my family in a database. I want to generate a web page with a family tree generated dynamically by reading from the table.
My table schema looks like this
id(int) name father(int) mother(int) spouse(int) dateOfBirth
where father, mother, and spouse are referencing the id column of the same table. The root node will have null for father and mother.
Given this data how can I go about dynamically generating the family tree. I am new at designing tables, so if this design is sub optimal kindly suggest another schema from which this objective can be achieved.
Any pointers on how to atleast get started would be highly appreciated.
Take a look at nested set model.
Your design looks ok, but with this design it's easier to insert nodes than to get them out of the table.
You can look at nested sets and implement that model. Nested sets are harder to update, but you can get the nodes of any subtree with a single query, so I think it matches your problem quite well (a family tree doesn't change too often :).
You would need some metadata, like relation type (Child, Sibling, Spouse) in addition to the nested sets parent-child relations, but I think you can add that easily.
This design is ok, but you would either select all the data and then build the tree on client side iteratively checking the returned array, or perform many subqueries, which is also not very good.
The best solution I know (for hierarchical structures, you've got spouses also) is storing the tree path in a string field. For example, you've got grandpa with id=1, children with id=2 and 3, 2 has children 4 and 5. Then, they have paths, respectively, "", "1", "1", "1,2", 1,2".
You can use this structure to retrieve the tree elements in order, using ORDER BY path clause.

Categories