Hi!
I didn't get any code that worked. Of course I could have use then wrongly because I'm a beginner. Some told me to use MySQL subqueries other told me to use PHP foreach achieve it.
What I want is to show the search results of a keyword separated by groups of categories, something like that:
Search results for Item, 3 itens in 2 categories:
Category 1:
Item 1
Item 10
Category 2:
Item 1003
Can someone explain me it as simple as possible.
Thanks n advance!
I use a single request which return name of category for each item and I use PHP to display it
<?php
$cat;
while($result = $statement->fetch()) {
if($result['cat'] !== $cat) {
$cat = $result['cat'];
/* display cat */
}
/* display items */
}
?>
Related
I want to optimize my query as it is taking long to run with current eloquents. I have two table, toys and product.
From each product one is reserved as sample of toy if not than it has to be updated as sample by the query so what i'm doing right now is below.
$toywithsample=product::select('toyid')->groupBy('toyid')->where('sample','yes')->get();
Above code is to get id of all the product with which have their one sample from in its product
$toywithoutsamples=toy::select('id')->whereNotIn('id',$toywithsample)->get();
Above code is to get id of all product which have no sample toy in
foreach($toywithoutsamples as $toywithoutsample){
$product=product::where('toyid',$toywithoutsample->id)
->where('sample','sale')->limit(1)
->update(['sample'=>'yes']);
}
Below is table structure
toy table
id,name,
product
id, toyid,sample
$toys_ids_with_sample = Product::where('sample', 'yes')->get()->pluck('toyId');
// get the products grouped by toyId.
$products = Product::whereNotIn('toyId', $toys_ids_with_sample)->where('sample', 'sale')->get()->groupBy('toyId');
// get the product ids whose sample field you want to change to
// yes.
$update_product_ids = [];
foreach($products as $toyId => $products){
// We will only pick the first one, as we have to change just 1.
array_push($update_product_ids, $products->first()->id);
}
Product::whereIn('id', $update_product_ids)->update(['sample' => 'yes']);
This reduces the total number of queries.
I have the following SQL statement:
$query = "SELECT item, COUNT(*) as number FROM shop GROUP BY item";
This will give me the following result:
item number
item1 23
item2 15
item3 4
I want to use this to make menu items, so normally the menu would look:
item1
item2
item3
But I want to do a check if an item has less than 10 records, that I don't want to display this item.
So in this example, the menu would be like:
item1
item2
Any idea how to achieve this?
I would like to do this in PHP because I need all the items in the query but will only want to show them which are greater then 10 and need the other items later on.
If you want to do this in PHP then you can do like this
function filterArray($value){
return ($value.number > 10);
}
$filteredArray = array_filter($yourDBArray, 'filterArray');
foreach($filteredArray as $k => $v){
//your desired array
}
In terms of speed Mysql option is good as suggested above.
Just change your query from
SELECT item, COUNT(*) as number FROM shop GROUP BY item
to
SELECT item, COUNT(*) as number FROM shop GROUP BY item HAVING number>=10
As you really need to perform this in PHP you could use array_filter() which, using a closure, will remove items which number is less than 10:
$more_than_ten = array_filter($items, function ($i) { return $i['number'] >= 10; });
Doing it with SQL would be a better solution (about performances). In case you'd need it, you could use the HAVING clause (you can't perform a WHERE number >= 10):
SELECT
item,
COUNT(*) as number
FROM shop
GROUP BY item
HAVING number >= 10
I noticed php is tagged. For the sake of options, here's how I'd go about separating the unneeded data in php if you were to get it from the database as-is:
foreach ($data as $item) {
$num = (int) $item['number']; // force of habit
if ($num >= 10) {
// display it
}
}
I'd probably separate the data at the database step, but this works if it's the route you want to take.
There is two options to filter the data so only the rows with more then 10 will appear.
At the SQL query
__
SELECT item, COUNT(*) as number FROM shop GROUP BY item HAVING number > 9
This will cause you to recieve only the requested rows from the database
Filter with PHP - every time you want to print the menu or testing it out, just can the value of 'number' in the array reutrned from the query. You can also allocate new array and insert all the values that contains 'number' that bigger then 10.
I developed a sql table in which i am putting details related to products which are in my feed file.There is a field in sql table by the name of categories which is in format of category1>category2>category3.I want to split this column with > sign and display it in a list.
For now i m putting them in an associative array,But i dont know how to split the category element of the array and display it in list.
<?php
$firstsql='SELECT TOP 1 from xml where ('.implode('AND',$parts).')';
$firstsql3=mysql_query($firstsql);
$firstrow=array();
while($row = mysql_fetch_array($firstsql3)) {
// Append to the array
$firstrow[] = $row;
//echo $row['PID']."<BR />";
}
Above is the snippet where i m putting the top first row in array from where i have to get the category element of array and display it in list.
Like category column in array is like MEN>CLOTHING>SHOES
i want to dispay it like
MEN
CLOTHING
SHOES
Please guide me how to do this..
If I've understood, you can do
$categories = explode(">",$row["category"]);
foreach ($categories as $category) {
// Do what you need with each $category, it will be "MEN", "CLOTHING" and "SHOES" each time
}
Im looking for a way to extract the data from the mysql database to create an unlimited subcategry menu.
The menu has about 1500 categories in it and they are stored into the database table (menu) in the following format :
category_id , category title , parent_id
i want to do 1 mysql query to put all the data into an array.
$menu_list =array();
$query = mysql_query("SELECT * FROM menu ORDER BY category_id ASC");
if(mysql_num_rows($query) != 0) {
while($row = mysql_fetch_array($query)){
$category_id = $row['category_id'];
$title = $row['category_title'];
$parent_id = $row[parent_id'];
$menu_list[] = array($category_id,$title,$parent_id);
}
}
That is the code that i am currently using to turn the data into the array.
I then need to loop through the array to build a menu up.
I will then hide all subcategories and expand with jquery ( I can do this bit no problem )
The problem i have is going through the array and displaying the data in the right order.
The menu has unlimited subcategories so it will probably need to be a recursive function to retrieve the data.
I have folllowed alot of the examples on here but then i get lost how to display the data..
i now need to end up with :
main item
sub cat
sub cat
sub cat
sub cat
main item
sub cat
main item
main item
sub cat
sub cat
sub cat
sub cat
sub cat
etc...
with each category in its own div
I will then need to be able to edit each category (ie title name and position if need be)
then if i change where a sub category is based a quick page refresh will reload the menu in the new order..
eventually i would like to make this into a drag and drop but that will be at a later date.
I hope this has explained it enough..
Thanks in advance..
So the problem has come back to haunt me again...
My method of ajax calling another mysql select was ok for the CMS section as it was only used from time to time.
But now we have got to the front end.
Each time the page is viewed the first 3 levels of the menu are pulled using numerous mysql requests.
As the categories are getting bigger this is now resulting in 1600+ categories being pulled numerous times each, and even at 1000 visits a day will result in more than a 1000000 sql requests a day.
So i think i will definately need to pull the categories into an array, and then recursively go through the array.
I have looked at the solutions above and they seem to work on paper but not in practise.
If anyone can attempt to give me another solution it would be appreciated..
just to recap in my db i have : id , category_name , parent_id
I am now using PDO with mysql and prepared statements for added security..
Thanks in advance..
Here is the code what you need
category_id AS id , category title AS kategori and parent_id AS kid
function countsubcat($pid)
{
$r=mysql_query("select count(kid) AS say from meskat where kid='$pid' limit 1");
$rw=mysql_fetch_array($r);
return $rw['say'];
}
function listmenu($pid = 0)
{
$res = mysql_query("select id,kategori,kid from meskat where kid='$pid'");
while($cat=mysql_fetch_array($res))
{
echo '<li>';
print''.$cat['kategori'].'';
if(countsubcat($cat['id'])>0)
{
print'<ul>';
listmenu($cat['id']);
print'</ul>';
}
echo '</li>';
}
}
echo '<ul>';
listmenu(0); //starting from base category
echo '</ul>';`
In MySQL, this will require many queries, one for each level plus one at a minimum.
you grab all the top-level categories, use them as root nodes for a forest (a group of trees).
You grab all of the categories that are children of any of them.
Add each of these categories as children to the parent node
If there are still children, go to step two, otherwise stop.
What you end up with a tree that you do a depth-first walk through to convert to some (presumably) html representation (e.g. nested lists).
There are some optimisations you can perform. If you sort the child levels by parent ID, then you can assume continuous blocks of children, meaning you don't have to do as many lookups to find the correct parent, just check for a parent id change.
If you maintain a list of the current lowest level of categories, indexed by ID, you can speed up forest creation.
If you overlap steps 2 and 4, then you only do n+1 queries (where n is the number of levels), so checking for more children is also grabbing the next level of children.
In terms of memory, this will use the memory required for the tree, plus lowest level lookup table and a current parent id.
The building algorithm it fairly fast, scaling linearly with the number of categories.
This method also successfully avoids corrupted sections of data, because it won't grab data that have cycles (no root node).
I also suggest using a prepared statement for the children query, since MySQL will compile and cache the query, it will speed up the operation by only sending new data across the wire.
If you don't understand some of the concepts, I suggest hitting up Wikipedia as I have tried to use standard terms for these.
Can you alter the table structure? In that case you could take a look at the Nested Set Model (link includes description and implementation details, scroll down to The Nested Set Model). Node insertion and removal becomes more complex, but allows you to retrieve the whole tree in just one query.
If you can assume that for any category category_id > parent_id is true, the following recursive functions to represent the menu as a multi-level array and render it as a nested HTML list would work:
$menu = array();
$query = mysql_query("SELECT category_id, category_title, parent_id FROM menu ORDER BY category_id ASC");
if(mysql_num_rows($query) != 0) {
while($row = mysql_fetch_assoc($query)) {
if(is_null($row['parent_id']))
$menu['children'][] = $row;
else
add_to_menu(&$menu,$row);
}
}
function add_to_menu($menu,$item) {
if(isset($menu['children'])) {
foreach($menu['children'] as &$child) {
if($item['parent_id'] == $child['category_id']) {
$child['children'][] = $item;
} else {
add_to_menu(&$child,$item);
}
}
}
}
function render_menu($menu) {
if(isset($menu['children'])) {
echo '<ul>';
foreach($menu['children'] as &$child) {
echo "<li>";
echo $child['category_id']." : ".$child['category_title'];
if(isset($child['children'])) {
render_menu(&$child);
}
echo "</li>";
}
echo '</ul>';
}
}
render_menu($menu);
So after various attempts at different solutions i actually have used jquery and a ajax load to retreive the next level.
With mine main categories all have a parent of 1 - shop i do a mysql scan for all categories with 1 as the parent id.
This will then display the main categories. Attached to each main category i have added a folder icon if they have any sub categories.
I have also inserted a blank div with an id of that category.
When the folder is clicked it does an ajax call to a php script to find all categories with that parent. These are appended to the parents_id div.
This inturn adds the category (with a css padding to indicate a sub category) and again a folder sign if it has sub categories as well as another blank div with the category_id.
You can continue this for as many categories and sub categories as the client requires.
I took it a stage further and added an edit icon to category/subcategory so categories can be moved or changed.
If you would like some sample code for this let me know...
Actually, you need very little code to build a tree.
You also only need one query.
Here is what I came up with (I use Drupal's database but it is explicit enough to understand):
$items = db_select('menu_items', 'mi')
->fields('mi')
->orderBy('position')
->execute()
->fetchAllAssoc('id', PDO::FETCH_ASSOC);
// Example of result. The ID as key is important for this to work.
$items = array(
3 => array('id' => 3, 'parent' => NULL, 'title' => 'Root', 'position' => 0),
4 => array('id' => 4, 'parent' => 3, 'title' => 'Sub', 'position' => 0),
5 => array('id' => 5, 'parent' => 4, 'title' => 'Sub sub', 'position' => 0),
6 => array('id' => 6, 'parent' => 4, 'title' => 'Sub sub', 'position' => 1),
);
// Create the nested structure. Note the & in front of $item.
foreach($items as &$item)
if($item['parent'])
$items[$item['parent']]['sub items'][$item['mid']] =& $item;
// Now remove the children from the root
foreach($items as $id => $item)
if($item['parent']) // This is a child
unset($items[$id])
At this point, all you need is a recursive function to display the menu:
function print_menu($items) {
echo '<ul>';
foreach($items as $item) {
echo '<li>';
echo '' . $item['title'] . '';
if(!empty($item['sub items']))
print_menu($item['sub items']);
echo '</li>';
}
echo '</ul>';
}
I'm looking for the best way to create a complex navigation element on the fly. I have all of the elements in a database (title, id, parentId) and I want to efficiently take them out of the DB and display them correctly. I also want to collapse all of the navigation elements that aren't active. So if I was browsing through "Sofas" I wouldn't see "Chandeliers" or any of the categories under lighting but I would see "Lighting".
This is what I want the final product to look like:
Furniture
Living Room
Sofas
Chairs
Ottomans
Bedroom
Beds
Nightstands
Lighting
Chandeliers
Floor Lamps
Sconces
Rugs & Textiles
Contemporary
Vintage
My current method is
write one SQL query that pulls down all of the category names, ids, and parent ids
Iterate through the categories and put into a sorted multi-dimensional array with child categories stored under their parents.
Iterate through the new array and add another entry to mark the appropriate categories as open (all categories are closed by default)
iterate through the array and write HTML
I'm trying to to this with as few interations as possible and I'm sure the code I have right now is inefficient. Especially step 2 I iterate through the array several times. There has to be a general solution to this (common?) problem.
Consider adding a new field to your database table: level.
Main-categories will have level 0.
Sub-categories will have level 1.
Sub-sub-categories will have level 2.
etc.
This trick will help you to know which sub-categories to disable without 2nd iteration of the array.
I believe this is the perfect place to generate your html code using recursion.
I used this function a while ago. It is working with a multi-dimensional array (tree)
function buildMenu($menu_array, $is_sub=FALSE) {
$attr = (!$is_sub) ? 'id="menu"' : 'class="submenu"';
$menu = "<ul $attr>\n";
foreach($menu_array as $id => $elements) {
foreach($elements as $key => $val) {
if(is_array($val)) {
$sub = buildMenu($val, TRUE);
}
else {
$sub = NULL;
$$key = $val;
}
}
if(!isset($url)) {
$url = $id;
}
$menu .= "<li>$display$sub</li>\n";
unset($url, $display, $sub);
}
return $menu . "</ul>\n";
}
echo buildMenu($menu_array);
This adds css properties too. If you wish to mark the currently active page you can use the strpos() function to find your current url. If you need some more functionality you can easily add them to buildMenu()
Using level as mentioned in the answer above will help too. If you were using the nested set model in your database I could also help you with my query which is a single select returning the whole menu data.