How to get recursively all analogs from table by an article? - php

I've create a simple table of analogs:
+----+-------+-------+
| id | sku_1 | sku_2 |
+----+-------+-------+
| 1 | a1 | abcd |
| 2 | a2 | a3 |
| 3 | a3 | a1 |
+----+-------+-------+
3 rows in set (0.00 sec)
What it mean? It mean that product with article abcd has an analog with article a1, otherwise for example product with article a3 has an analog with article a1.
How to recursively get all the products from this table by a single article?
My solutions is wrong:
// Small Class to get analogs of products
class Analogs {
public function get_analogs($sku)
{
if (!$sku) return false;
$link = mysql_connect('localhost','','');
mysql_select_db('test');
$sku = mysql_real_escape_string($sku,$link);
$query = mysql_query("SELECT * FROM analogs WHERE sku_1='".$sku."' OR sku_2='".$sku."'");
while($analogs[]=mysql_fetch_assoc($query))
continue;
return $analogs;
}
public function MixedAnalogs($sku)
{
if (!$sku) return false;
$link = mysql_connect('localhost','','');
mysql_select_db('test');
$sku = mysql_real_escape_string($sku,$link);
$query = mysql_query("select sku_1 sku from analogs where sku_2 = '$sku' UNION
select sku_2 sku from analogs where sku_1 = '$sku'");
while($analogs[]=mysql_fetch_assoc($query))
continue;
return $analogs;
}
}
$mixed_analogs = AnalogsMix('abcd',$ids=array());
echo "<pre>";
print_r($mixed_analogs);
echo "</pre>";
// Recursive function to get analogs of analog
function AnalogsMix($sku,$ids=array())
{
$class_analogs = new Analogs();
$analogs = $class_analogs->get_analogs($sku);
foreach ($analogs as $analog)
{
$cross = null;
if ($analog['sku_1']==$sku)
{
$cross->sku = $analog['sku_2'];
}
else
{
$cross->sku = $analog['sku_1'];
}
$cross->id = $analog['id'];
if (!in_array($analog['id'],$ids))
{
$ids[] = $analog['id'];
$mixed[] = AnalogsMix($cross->sku,$ids);
}
}
if (isset($mixed))
{
return $mixed;
}
else
{
return false;
}
}

SQL UNION
select sku_1 sku from analogs where sku_2 = $yourid
union
select sku_2 sku from analogs where sku_1 = $yourid
Then you will get in results only ids of analogs.

Here, I suppose you have all your pairs in an array. For example, for your example, you would call analogsOf(array(array("a1", "abcd"), array("a2", "a3"), array("a3", "a1")), "abcd").
The idea is that you build a list of analogs containing initially only the string you are looking for and, every time you find an analog, you add it to the list of analogs and reiterate. You do so until you iterated the whole array of pairs without finding anything new.
function analogsOf(array $pairs, $key) {
$res = array($key); // The result, with only the given key
$i = 0; // Index of the current item
$changed = false; // Have we added an item to $res during that iteration ?
while ($i < count($pairs)) {
$current = $pairs[$i];
foreach ($res as $item) {
if (($current[0] === $item) && (!in_array($current[1], $res)) {
$res[] = $current[1];
$i = 0; // Reiterate as $res changed
}
else if (($current[1] === $item) && (!in_array($current[0], $res)) {
$res[] = $current[0];
$i = 0; // Reiterate as $res changed
}
else {
$i++; // Nothing found here, go to next item
}
}
}
return $res;
}
Note that this code was NOT tested, so there might be a few bugs here and there, but you've got the idea. Also note that I considered you could put the whole database content in an array, but that is probably not possible for obvious reasons, so you will probably have to adapt the code above.

I found a solution for this problem but the main problem in this approach is that.
it can make a loop like abcd->a1,a1->a3,a3->a2,a2->abcd. and it make recursive function endless and php throw an error. so you have to check for that if it is a big project.
in my solution i consider it parent-> child relation. and if a child found make it parent and check again and so on until there is no result.
let abcd is parent and after first execution a1 is child and relation is abcd->a1. but in next call a1 is parent and from first row of table it give a new relation that is a1->abcd and loop is endless.
To prevent checking in same row i use ID of last row from database and it now check row where id != ID (always check other row)
this is function i write, convert it according to your class and store the value in array as you like. I use a string only.
i knew it not a good solution but i works fine.
<?php
mysql_connect('localhost','','');
mysql_select_db('test');
function getSku($sku, $id, $rel = '') {
$query = mysql_query("SELECT * FROM analogs WHERE sku_1 = '$sku' AND id != '$id'" );
if (mysql_num_rows($query)) {
$row = mysql_fetch_assoc($query);
$sku = $row['sku_2']; //PARENT SKU
$id = $row['id']; //LAST ID
$rel .= $row['sku_1']. '-->' . $row['sku_2']. "<br>";
} else {
$query = mysql_query("SELECT * FROM analogs WHERE sku_2 = '$sku' AND id != '$id'" );
if (mysql_num_rows($query)) {
$row = mysql_fetch_assoc($query);
$sku = $row['sku_1']; //PARENT SKU
$id = $row['id']; //LAST ID
$rel .=$row['sku_2']. '-->' . $row['sku_1']. '<br>';
} else {
return (string)$rel; //NOTHING FOUND
}
}
return getSku($sku,$id,$rel);
}
echo $new = getSku('abcd','-1');

Related

Selecting associative array in mysql

I currently have the following:
$query='select concat("[",key,"=>",value,"]")
from table';
if(isset($query))$r=$mysql->query($query);
if(isset($r)){
for($i=1;$i<=$r->num_rows;$i++){
$r->data_seek($i-1);
$a[$i-1]=$r->fetch_array(MYSQLI_ASSOC);
$a[$i-1]=parse_array($a[$i-1]);
}
}
$mysql->close;
function parse_array($parent){
foreach($parent as$k=>$val){
if(strpos($val,']')){
$array=explode(',',substr($val,1,-1));
foreach($array as$val){
$keypair=explode("=>",$val);
$newarray[$keypair[0]]=$keypair[1];
}
$parent[$k]=parse_array($newarray);
}
}
}
There has to be a more elegant way of doing this - perhaps built into MySQL? I'm trying to minimize the time this spends running PHP - I would like it to arrive to PHP already in array form, but MySQL kicks Subquery returns more than one result if I attempt a subquery.
Edit: Here's table:
+----------+----------+
| value | key |
+----------+----------+
| img.jpg | src |
+----------+----------+
Output should be:
[
'src'=>'img.jpg'
]
Just move all of the manipulation over to php. Fetch the query with numeric indexes. Make the assumption that the every even index is a key and every odd index is a value (or vice versa).
$query = 'select key1, value1, key2, value2
from table';
if(isset($query))
$result = $mysql->query($query);
if(isset($result)) {
$newResult = []; // if your version of php doesn't support this syntax to create a new array use `$newResult = array();`
while($row=$result->fetch_array(MYSQLI_NUMERIC)) {
$newResult[] = build_assoc_array($row);
}
}
$mysql->close;
function build_assoc_array($flat_arr) {
$newArr = [];
$numCol = count($flat_arr);
for($colIndex = 0; $colIndex < $numCol; $colIndex+=2) {
$newArr[$flat_arr[$colIndex]] = $flat_arr [$colIndex+1];
}
return $newArr;
}

how to create a loop to get data continuesly

i have a database that has an id_parent and id_child, but the id_child may also be parents of more ids, p_1(parent_1) has child 1-4, c_1(child_1) has child 6-9
p_1
|
c_1 , c_2, c_3, c_4
|
c_6, c_7, c_8, c_9
|
etc..
i have a function that gets the child by parent_id given a sql_table like so:
public function getChilds($folder_table, $folder_id){
if($this->connect){
$folder_id = $this->connection->real_escape_string((int)$folder_id);
$sql = 'SELECT * FROM '.$folder_table.' WHERE user_id=\''.$this->user_id.'\' AND folder_id=\''.$folder_id.'\'';
if($results = $this->connection->query($sql)){
$obj = $results->fetch_object();
return $obj;
}
}else{
$this->error = "Unable to connect to database. please make sure you can connect.";
}
return $this->error;
}
is there a way to continue executing this function continuously execute this function like a pyramid until there isn't any more ids to be fetch from any ids?
Attempt 1
i got all ids in a array like so:
$array_ids = array(1,2,3,4,5);
foreach($array_ids as $key => $value){
$new_ids = $obj->getChilds($sql_table, $value);
}
but this will only continue the process once more. i want this to continue until there isn't any more ids to get.

How to fetch query where sub/child create new UL

I am trying to make un-order list for parent child categories where if there is any child category than it will create another un-order list ( like indented text) so user can understand properly.
I have fetch sql but with foreach I don't understand how to set so where child category only will display under parent category by creating another un-order list under the parent category.
Here is my code
$query_cat = "SELECT * FROM ^categories";
$query = qa_db_query_sub($query_cat);
$catsid = qa_db_read_all_assoc($query);
echo '<UL>';
foreach ($catsid as $catid){
echo '<LI>'. $catid['title'].' '. $catid['categoryid'].'</LI>';
}
echo '</UL>';
So final result would be
First Category
Sub Category1
Second Category
EDIT:
After modified code with #vlcekmi3 answer https://stackoverflow.com/a/13451136/1053190 I am getting this result
Now how to exclude subcategory from parent list?
There's no really easy solution for this with your design. The most effective way would be to add column like order_in_list (and maybe depth_in_list).
They would be pre calculated in loop (pseudocode):
START TRANSACTION
UPDATE t1 SET order_in_list = 0 // Restart whole loop
$ids = array(0);
while $id = array_shift($ids){
$record = SELECT * FROM t1 WHERE id = $id // Get id details, order_in_list is important
$children = SELECT * FROM t1 WHERE parent_id = $id // get list of all childs
// If it's root element, start indexing from 0
$root_order = ($record ? $record->order_in_list : 1)
$child_no = count($children) // How many child will be adding
// No children, nothing to do:
if $child_no < 1{
continue;
}
append_to_array($ids, $children) // Store ids to process
// Shift all later records, we'll be creating gap in order_in_list 1,2,3,4,5
// To 1,2,5,6,7 to insert items on places 3,4
UPDATE t1 SET order_in_list = (order_in_list + $child_no)
WHERE order_in_list > $record->order_in_list
// Okay, set IDs for direct children
foreach( $children as $child){
UPDATE t1 SET order_in_list = $root_order, depth_in_list = $record->depth_in_list+1
WHERE id = $child->id
$root_order++;
}
}
COMMIT
This way you'll get records like:
First category, 1, 1
Second category 3, 1
Sub category, 2, 2
Which you could display with simple loop:
$last_depth = 0;
foreach( (SELECT * FROM t1 ORDER by `order_in_list`) as $row){
if( $last_detph > $row['depth_in_list'])){
// Close level </ul>
} else if($last_detph < $row['depth_in_list']){
// Opening level <ul>
} else {
// The same depth
}
$last_depth = $row['depth_in_list'];
}
Without modifying database
It would be probably most effective to build two arrays containing root elements and all elements:
$root_elements = array();
$all_elements = array();
foreach( (SELECT * FROM t1) as $row){
// Store details into all_elements, note that entry may have already be created when
// processing child node
if( isset( $all_elements[$row['id']])){
// set details
} else {
$all_elements[$row['id']] = $row;
$all_elements[$row['id']]['children'] = array(); // Array of child elements
}
if( $row['parent_id'] == NULL){
$all_elements[] = $row['id']; // Add row element
} else {
if( isset( $all_elements[ $row[ 'parent_id']])){
$all_elements[ $row[ 'parent_id']]['children'][] = $row['id'];
} else {
// Create new record:
$all_elements[ $row[ 'parent_id']] = array();
$all_elements[ $row[ 'parent_id']]['children'] = array($row['id']);
}
}
}
And then write it as:
foreach( $root_elements as $element_id){
write_recursive( $all_elements[ $element_id]);
}
// And display
function write_recursive( $element)
{
echo '<ul>...';
if( count( $element['children'])){
foreach( $element['children'] as $child){
write_recursive( $all_elements[ $child]);
}
}
echo '</ul>';
}
You better create class for that (to replace using global variables), but you should have a solid way to do this. Anyway try avoid using this with large number of records (I wouldn't go past 2000-5000 menu entries), try to at least cache it.
Note: solutions are oriented towards minimal number of requests on database when displaying list.
you can use complicated query or something like this
foreach ($catsid as $catid) {
...
$subquery_cat = "SELECT * FROM ^categories WHERE parentid='".$catid['categoryid']."'";
$query = qa_db_query_sub($subquery_cat);
$subcatsid = qa_db_read_all_assoc($query);
// wrap into html
...
}

SQLITE3 query doesn't return what I want it to within PHP

I open my sq3d fine, but when I try to do a SELECT on one of my tables it seems to not return the results I excpect. Here is the php code:
$buildingArray = array();
class MyDB extends SQLite3
{
function __construct()
{
$this->open('Database.s3db');
}
}
$db = new MyDB();
$query1 = "SELECT * FROM tbl_uploadData";
$result1 = $db->query($query1);
$u = $result1->fetchArray();
echo "<br/>Size of u: ".sizeOf($u)."<br/>";
for($i=0; $i<sizeOf($u); $i++){
echo "<br/>Items: ".$u[$i]."<br/>";
}
This is what is in my Database:
tbl_uploadData : b_id - 1,2,3,4 : where 1,2,3,4 are the items within the fields, b_id the field name and tbl_uploadData the table.
I expect to get 1, 2, 3 and 4 to return in the fetchArray()
All i get in return is:
Size of u: 2
Items: 1
Notice: Undefined offset: 1 in C:\xampp\htdocs\PHPexcel\Tests\StrategicExcel.php on line 72
Items:
Here is the solution, as spaced monkey mentioned, fetchArray() only returns one result at a time. So by adding a while loop and a couple of references to my b_id, it should work. I've modified my code to:
$databaseName = "Database.s3db";
$db2 = new SQLite3($databaseName);
$sql = "SELECT b_id FROM tbl_uploadData";
$result = $db2->query($sql);//->fetchArray(SQLITE3_ASSOC);
$row = array();
$i = 0;
while($res = $result->fetchArray(SQLITE3_ASSOC)){
if(!isset($res['b_id'])) continue;
$row[$i]['b_id'] = $res['b_id'];
$i++;
}
print_r($row);
Hope this helps if anyone comes across this problem in the future, TY
fetchArray() only gets 1 row at a time, you need to call it for every row. http://uk3.php.net/manual/en/function.sqlite-fetch-array.php
You should be able to:
while ( $u = $result1->fetchArray() ) {
echo "<br/>Items: ".$u['b_id']."<br/>";
}
I don't know why sizeof($u) is returning 2 though.

How do I extract and display hierarchical data from my database?

I have two tables.
The chapters table has the columns id and name.
The chapters_chapter table has columns id, master_id, and slave_id.
Lets say that the chapters table has 7 records:
id name
1 test01
2 test02
3 test03
4 test04
5 test05
6 test06
7 test07
And in the chapters_chapters table I have these records:
id master_id slave_id
1 1 5
2 1 6
3 6 7
4 7 2
Given that data, how can I extract the hierarchy of that data so that it looks like this?
test01
test05
test06
test07
test02
test03
test04
So this was kind of a pain because of the fact that we had to have the hierarchy stored in the DB. Because of this, each item can have multiple children, and each child can have multiple parents.
This second part means we cannot simply loop through the list once and be done with it. We might have to insert an item in multiple places in the hierarchy. While you probably won't actually structure your data that way, the database schema you've described supports this scenario, so the code must support it too.
Here's a high-level version of the algorithm:
Query both tables
Create a map (array) of a parent (number) to its children (another array)
Create a set of items that are children (array of numbers)
Create a function that displays a single item, indenting it to the desired depth.
If that item has children, this function increases the depth by one, and calls itself recursively
Loop through all items that aren't children (root items).
Call the function for each of those items, with a desired depth of 0 (no indent).
Here's two hours work. Enjoy :)
Note that I stuck it within a <pre> block, so you might have to mess with how the indentation is done (output something other than two spaces, mess with the style of the divs, etc).
<?php
$con = mysql_connect("localhost", "test_user", "your_password");
if(!$con)
{
die("could not connect to DB: " . mysql_error());
}
mysql_select_db("your_db", $con);
// get chapters
$chapters = array();
$result = mysql_query("SELECT * FROM chapters");
while($row = mysql_fetch_array($result))
{
$id = $row["id"];
$name = $row["name"];
$chapters[$id] = $name;
}
// get chapters_chapters - We'll call it "parent/child" instead of "master/slave"
$parent_child_map = array();
$is_child = array();
$result = mysql_query("SELECT master_id, slave_id FROM chapters_chapters");
while($row = mysql_fetch_array($result))
{
$parent_id = $row["master_id"];
$child_id = $row["slave_id"];
$children = $parent_child_map[$parent_id];
if($children == null)
{
$children = array();
}
$children[] = $child_id;
$parent_child_map[$parent_id] = $children;
$is_child[$child_id] = true;
}
// display item hierarchically
$display_item_and_children = function($id, $name, $depth)
use ($chapters, $parent_child_map, &$display_item_and_children)
{
echo "<div><pre>";
// indent up to depth
for($i = 0; $i < $depth; $i++)
{
echo " ";
}
echo "id: " . $id
. " name: " . $name
. "</pre></div>";
// if there are children, display them recursively
$children = $parent_child_map[$id];
if($children != null)
{
foreach($children as $child_id)
{
$child_name = $chapters[$child_id];
$display_item_and_children($child_id, $child_name, $depth + 1);
}
}
};
// display all top-level items hierarchically
foreach($chapters as $id => $name)
{
// if it is a top-level item, display it
if($is_child[$id] != true)
{
$display_item_and_children($id, $name, 0);
}
}
mysql_close($con);
?>
And here's a screenshot:
The question becomes how complex you want your solution to be. I'd do it with the following pseudo code.
SELECT all the chapters
SELECT all the *chapters_chapters*
loop over the chapters to create an array chapter objects
loop over the `chapters_chapters* and create the relationships using the chapter objects
Essentially you're creating a link-list.

Categories