Sorting adjacency hierarchy for simple output looping - php

I'm pulling my hair out trying to avoid using multiple SQL queries. Could someone please point me in the right direction?
End result is a website navigation (ul/li) menu with nested links (dropdown menu) for some of the links.
The data comes from PDO as an array of objects. I believe my structure is referred to as an adjacency list, where each record has a parent ID which correlates to the id of the link under which it is nested, parent_id=0/NULL being top level. Every link also has a priority (a number by which to order them).
I require that the priority to only be relevant to the links on the same "level".
To output the html I really need the data in it's hierarchical order. I figured the uasort function could achieve this with a custom compare function, but I'm struggling to make it do anything comprehensible. Does uasort even make this possible?
Other avenues I thought might help were self joining SQL queries, or maybe looping through the array to build an object whereby "level2" links lived within the branch of it's parent (as you'd expect to see in an XML hierarchy).
Considering my current plans only require 2 levels and there won't be a million records to "sub-query" just tell me if you think I should concede and just use multiple queries.

Related

How to search children nodes in SQL

Lets say I have a table called Tags with an id columnm name column, and a parent_id column. Many tags are nested using the parent_id column. How would I check if Tag A has Tag B as a non-direct child efficiently.
Previously I have selected all tags that have a parent_id of the current tag and then got the result and repeated for any child elements.
How would I do this more efficiently to get all tags that match a search and is a direct or non-direct child.
Thanks for the help,
Jason
Rather than discuss in the comments... here is what I would recommend:
If you want to stick with MySQL but can play with the structure of your database then absolutely this "Closure Table" pattern suggested by Bill Karwin is the way to go. It allows you to keep your data in a flat table design while abstracting the multi-level tree structure into a separate table for easy data extraction.
If you want to try a different Relational Database System then you might try SQL Server Express which is free from Microsoft. In full disclosure, I don't use this so I don't know what functionality is excluded (and I'm sure something is otherwise you wouldn't get it for free). So please do some research to make sure Recursive Common Table Expressions (CTEs) are available. If they are then you can use Pinal Dave's blog post for recursive SQL technique using CTEs.
Otherwise if you only think you will only ever have a handful of levels to work with, you can use the original suggestion and hardcode the number of levels.

Nested Set Database Design vs Search Tags

Currently I categorise my items by giving them a tag that is attached to the item itself. For example, Men,Accessories,Watch.
My query is mainly based on search, if scale, plan to look more at Elastic Search.
I'm considering trying Nested Set as Categorising my item.
If I use Nested set, does it mean I need three tables? Item Table, Link Table (for each item in Nested Set's ID), Nested Set table
In terms of scalability, is it stupid to go my current way of "tag search", would there be a big difference between tag search vs a proper Nested Set?
I tried searching the web but can't seem to find how people are using Nested Set, especially the middle table joining them up.
I need some advice here. Which way should I go about and the reason behind them? I personally prefer tag mainly because it's already working and i got no idea to go about nested set on Laravel's "packages".
I have this nested set table.
I have another ITEM table
how do i "connect them" up.
id
parentid
left
right
depth
No. You need just left and right fields. 2. You can try closures and materialized path. I don"t know nothing about all of them but adjacent list model ain"t so bad. Maybe there is bottleneck in the hardware?

Object Orientated Design with Databases and scalability/optimisation using PHP and mySQL

I'm currently at an impasse in reguards to the structural design of my website. At the moment I'm using objects to simplify the structure of my site (I have a person object, a party object, a position object, etc...) and in theory each of these is a row from it's respective table in the database.
Now from what I've learnt, OO Design is good for keeping things simple and easy to use/implement, which I agree with - it makes my code look so much cleaner and easier to maintain, but what I'm confused about is how I go about linking my objects to the database.
Let's say there is a person page. I create a person object, which equals one mysql query (which is reasonable), but then that person might have multiple positions which I need to fetch and display on a single page.
What I am currently doing is using a method called getPositions from the person object which gets the data from mysql and creates a separate position object for each row, passing in the data as an array. That keeps the queries down to a minimum (2 to a page) but it seems like a horrible implementation and to me, breaks the rules of object orientated design (should I want to change a mysql row, I'd need to change it in multiple places) but the alternative is worse.
In this case the alternative is just getting the ID's that I need and then creating separate positions, passing in the ID which then goes on to getting the row from the database in the constructor. If you have 20 positions per page, it can quickly add up and I've read about how much Wordpress is criticised for it's high number of queries per page and it's CPU usage. The other thing I'll need to consider in this case is sorting, and doing it this way means I'll need to sort the data using PHP, which surely can't be as efficient as natively doing it in mysql.
Of course, pages will be (and can be) cached, but to me, this seems almost like cheating for poorly built applications. In this case, what is the correct solution?
The way you're doing it now is at least on the right track. Having an array in the parent object with references to the children is basically how the data is represented in the database.
I'm not completely sure from your question if you're storing the children as references in the parent's array, but you should be and that's how PHP should store them by default. If you also use a singleton pattern for your objects that are pulled from the database, you should never need to modify multiple objects to change one row as you suggest in your question.
You should probably also create multiple constructors for your objects (using static methods that return new instances) so you can create them from their ID and have them pull the data or just create them from data you already have. The latter case would be used when you're creating children; you can have the parent pull all of the data for its children and create all of them using only one query. Getting a child from its ID will probably be used somewhere else so its good just to have if its needed.
For sorting, you could create additional private (or public if you want) arrays that have the children sorted in a particular way with references to the same objects the main array references.

How to query for multiple types of an object (using a key-value table) and grouping the results together as a complete object

I hope I asked the question properly. I have a table of objects grouped by object_id. They are stored as a key / value. I thought this would be simple but I cannot find a solution anywhere. I'm trying to get the most efficient method of querying against this table to return a full object based on multiple meta_name values. Here's the table structure:
Here's the code I have so far, which works great to query one value:
SELECT data2.object_id,data2.object, data2.meta_name, data2.value_string, data2.value_text FROM meta_data AS data1
LEFT JOIN meta_data AS data2 ON(data1.object_id = data2.object_id)
data1.object="domain"
AND data1.meta_name = "category"
AND data1.value_string = "programmer"
This gives me the following results. This is great for a single taxonomy (domain in category programmer).
The problem comes when I want to query for all domains with category programmer AND color red AND possibly other meta_name = value_strings. I can find no solution for this outside of making multiple queries from PHP (which I want to avoid for obvious performance reasons).
I need to point out that objects will be created on the fly, and without a specific schema (which is the point of having this structure to begin with) so I cannot hard code and assume anything about an object (Objects may have more meta properties defined to them from the admin panel at any given time).
Again, I hope I am asking this question right, since I have been completely unlucky in finding a solution by searching online for the last 3 days.
Thank you so much ahead of time to the MySQL pro that can help me with this!
In situations like this solutions typically query all records to avoid multiple queries and then stitch data objects together to provide the desired format. Then you can develop simple find() methods on those objects to further filter the results (e.g. using array functions)
If you're interested in exact implementation, I encourage you to look at WordPress - you noted taxonomies. As an open source project you can review their code for an example of how this is done. Take a look at the Taxonomies API as well as Meta API.

Prefetching data vs using ActiveRecord methods in a loop

In my MVC web app, I'm finding myself doing a lot of actions with ActiveRecords where I'll fetch a specific subset of Products from the database (for a search query, say), and then loop through again to display each one -- but to display each one requires several more trips to the database, to fetch things like price, who supplies them, and various other pieces of metadata. To calculate each of these pieces of metadata isn't very simple; it's not really something that could be achieved with a simple JOIN. However, it WOULD be possible (for most of these cases anyway) to batch the required database calls and do them all at once before the loop, and then within the loop refer to those pre-fetched data to do the various calculations.
Just as an example of the type of thing -- in a search, I might want to know what regions the product is provided by. In the database I have various rows which represent a particular supplier's stock of that item, and I can look up all the different suppliers which supply that item, and then get all the regions supplied by those suppliers. That fits nicely into one query, but it would start getting a bit complex to join into the original product search (wouldn't it?).
I have two questions:
does anyone else do something similar to this, and does it sound like a good way to handle the problem? Or does it sound more like a design problem (perhaps the application has grown out of ActiveRecord's usefulness, or perhaps the models need to be split up and combined in different ways, for instance).
If I do pre-fetch a bunch of different things I think I'll use inside the loop, I'm having a hard time deciding what would be the best way to pass the appropriate data back to the model. At the moment I'm using a static method on the model to fetch all the data I need at the start of the array, like fetchRegionsForProductIds(array $ids) and so forth; these methods return an array keyed by the ID of the product, so when I'm working inside the loop I can get the regions for the current product and pass them as a parameter to the model method that needs them. But that seems kind of hacky and laborious to me. So, can anyone tell me if there is just some really obvious and beautiful design pattern I'm missing which could totally resolve this for me, or if it's just a bit of a complex problem that needs a kind of ugly complex solution?
Small update: I wonder if using a datamapper class would put me on the right track? Is there a common way of implementing a data mapper so that it can be told to do large batch queries up front, store that information in an array, and then drip feed it out to the records as they request it?
I really hope this question makes sense; I've done the best I can to make it clear, and will happily add more detail if someone thinks they can have a go at it!

Categories