i use kohana framework and i am trying to code recursive function to create category tree.
My Categories Table
id int(11) NO PRI NULL auto_increment
name varchar(50) NO NULL
parent_id int(11) NO NULL
projects_count int(11) NO NULL
My Example Which Is Not Work
public static function category_list($parent_id = 0)
{
$result = Database::instance()->query('
SELECT name, projects_count
FROM project_categories
WHERE parent_id = ?',
array($parent_id)
);
$project_categories = array();
foreach($result as $row)
{
$project_categories[] = $row;
Project_Categories_Model::factory()->category_list($parent_id + 1);
}
return $project_categories;
}
Using this kind of hierarchical data implementation is highly non-optimal, because to get every subcategory you need do a separate query to the database. Like here you want to create recursion function.
If you still can change your table architecture please check Managing Hierarchical Data in MySQL.
This article describes a solution, how to fetch the whole hierarchy in one time query, so the recursive function will not be necessary.
Related
I need to display just the last date entry for a person in a table, taken from joined tables.
I only seem to able to display the first entry or all.
Controller
public function session_view($id)
{
$data['main_view'] = 'session_view';
$data['view'] = $this->Swim_model->CRUD_read_session($id);
$this->load->view('load_view',$data);
}
Model
public function CRUD_read_session($sessionid)
{
return $this->db->select('*')
->from('sessionsandswimmers')
->join('child', 'ID = SwimmersID')
->join('swimmersawards', 'PersonID = ID')
->join('awards', 'AwardID = AwardsID')
->order_by('LastName')
->order_by('DateAwarded')
->where('SessionID', $sessionid)
->get();
}
View
foreach ($view->result() as $row)
{
echo '<tr>'.$row->FirstName.'</td><td'.$row->LastName.'</td><td>'.$row->Description.'</td><td>'.$row->DateAwarded.'</td></tr>';
}
Result
As you can see, there are several entries for each person (except 1st).
I need to display just the latest date entry for each person.
So there should only be 4 entries.
Table structure
sessionsandswimmers - each session has 4 swimmers
1 tempid Primary int(11) No None AUTO_INCREMENT
2 SessionID int(11) No None
3 SwimmersID int(11) No None
4 SessionSeasonID int(11) No None
5 Year int(11) No None
6 LocationSS int(11) No None
child - gets the swimmers name
swimmersawards - multiple entries per child
1 PersonID int(11) No None
2 AwardsID int(11) No None
3 DateAwarded date No None
awards - gets the name of the award
change ->order_by('DateAwarded') to ->order_by('DateAwarded','DESC') . Need to specify which type of ordering you want. And for just one entry per person use :
->group_by('FirstName,LastName')
If I understood correctly, the result you displayed is the table structure. Where you want the latest entry by date for each "name". If this is right then follow,
SELECT DISTINCT `name` FROM `<your-joined-tables>` order by `date` DESC
Sorry I am not good with CI's query builder.
Hope it works.
I have the below table in my mysql db,
CREATE TABLE deadline_tasks(
user_id REFERENCES user(user_id),
task_id INT NOT NULL AUTO_INCREMENT,
deadline DATE NOT NULL,
min_chunk_length TIME NOT NULL,
estimated_time TIME NOT NULL,
location NOT NULL,
event_name VARCHAR(30) NOT NULL,
description VARCHAR(30) NOT NULL,
PRIMARY KEY(task_id)
FOREIGN KEY (user_id));
and I queried the database by using the codes below.
$result_tasks = $mysqli->query("SELECT deadline, min_chunk_length, estimated_duration,
task_id FROM deadline_tasks WHERE user_id = Get[$Name]")
for($deadline_task_from_db = array();
$row = $result_tasks->fetch_assoc();
$deadline_tasks_from_db[] = $row);
what are the variable types in my array $deadline_task_from_db?
try that:
$result_tasks = $mysqli->prepare("SELECT deadline, min_chunk_length, estimated_duration, task_id FROM deadline_tasks WHERE user_id = ? ");
$result_tasks->bind_param('i', Get[$Name]); // Bind "Get[$Name]" to parameter.
^^^^^^^^--//i dont know if this true variable. Please check it what you mean by it.
$result_tasks->execute(); // Execute the prepared query.
$result_tasks->store_result();
$result_tasks->bind_result($deadline ,$min_chunk_length ,$estimated_duration,$task_id ); // get variables from result.
while ($row = $result_tasks->fetch()){
echo $deadline;
echo $min_chunk_length;
.........
}
To see the type of a variable in php you can use var_dump
http://www.php.net/var_dump
Use mysqli_fetch_field(), you can get the types under "type" http://www.php.net/manual/en/mysqli-result.fetch-field.php
I would like to insert in relation table an other attribute ("tiers_temps" in my example) that ids, is it possible with the function "link()" or others ?
I use actually this codes, but I must update the filed "tiers_temps" :
$Examen->link('Users', $listAdd);
with the table "user_has_examen" structure :
Field Type Null Key Default Extra
user_id int(11) NO PRI 0
examen_id int(11) NO PRI 0
tiers_temps int(11) YES NULL
There is un error in your proposition. The object $user refers to the table "user" and not "user_has_examen" (not descibe before, that's right) so "tiers_temps" doesn't exist for user.
This solution works but I loop access Doctrine...
foreach($user as $userId){
$UserExam = Doctrine::getTable('UserExamen')->findByDql('user_id = ? AND examen_id = ?', array($userId, $exam))->getFirst();
if($UserExam->tiers_temps != $users[$userId]){
$UserExam->tiers_temps = $users[$userId];
$UserExam->save();
}
}
I didn't find a nice way to interact with an intermediate item. But you can act on the relations to retrieve all user, and then, apply your tiers_temps value before saving your Examen:
$Examen->link('Users', $listAdd);
$users = $Examen->Users;
foreach ($users as $user)
{
$user->tiers_temps = '45';
}
When I find myself in these situations I prefer to give up the table many to many Identifying and move to a structure like this:
id
user_id
examen_id
tiers_temps
because I find it very inconvenient to manage the value tiers_temps in the structure you used.
This function gives me an infinite loop
function getCats($parent,$level){
// retrieve all children of $parent
$result = "";
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";
if($rs = C_DB::fetchRecordset($query)){
while($row = C_DB::fetchRow($rs)){
$result .= str_repeat($parent,$level).$row['title']."\n";
getCats($row['parent_id'],$level+1);
}
}
return $result;
}
here is my db table
CREATE TABLE `db`.`t_cats` (
`ID` int(10) unsigned NOT NULL auto_increment,
`datasource_id` int(10) unsigned default '0',
`version` char(10) character set latin1 default 'edit',
`status` char(10) character set latin1 default 'new',
`modified_date` datetime default NULL,
`modified_by` int(10) unsigned default '0',
`title` char(255) character set latin1 default NULL,
`parent_id` int(11) default NULL,
PRIMARY KEY (`ID`),
KEY `idx_datasource_id` (`datasource_id`)
) ENGINE=MyISAM AUTO_INCREMENT=50 DEFAULT CHARSET=utf8;
I just want to be able to get my list of categories recursive.
But what am i doing wrong?
EDIT:
function getCats($parent,$level){
// retrieve all children of $parent
$result ="";
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";
if($rs = C_DB::fetchRecordset($query)){
while($row = C_DB::fetchRow($rs)){
$result.= str_repeat($parent,$level).$row['title']."\n";
getCats($row['id'],$level + 1 );
}
}
return $result;
}
This line looks wrong:
getCats($row['parent_id'],$level+1);
You should be calling it with the current child ID - at the moment you're calling it with the same ID over and over. Something like this (you need to select the id from your table):
getCats($row['id'], $level + 1);
Edit: you need to update your query to select id:
$query = "SELECT id, title, parent_id from t_cats where parent_id = '$parent' AND id != parent_id";
I've also added a bit to stop it getting into a loop if an item is its own parent.
I found this SitePoint article on "Storing Hierarchical Data in a Database" very helpful. It's all PHP examples, and it will improve the performance of what you're trying to do dramatically.
Maybe one of the items in the db has itself as parent?
I don't know C_DB, but I'd bet that the $rs returned by fetchrecordset is a reference, which means that every invocation of getCats is using the same $rs. Exactly what it will do then is unpredictable, depending on how fetchRow is implemented.
If you want to do this (and recursive closures are a pain in SQL, I know), you should open a new connection inside getCats. and be using a separate connection for each access.
correct answer provided by greg ...
2 side notes:
in the case of infinite loops, track recursion depth (you can conveniently use $level here) or overall invocation count (if you are lazy, since this is a oneliner accessing a global counter), and terminate recursion with an exception, when it reaches a maximum value (in general, 10 is already enough to see the problem, but of course that may vary) ... and then get some debug output ... for example $query or something like "calling getCats($parent,$level)" ... would've shown you the problem in no time in this case ... :)
you should minimize the amount of queries ... traversing a tree like that is quite inefficient ... especially, if the database is on another machine ...
greetz
back2dos
Erm shouldnt it be:
$query = "SELECT title,parent_id from t_cats where id = '$parent'";
And not:
$query = "SELECT title,parent_id from t_cats where parent_id = '$parent'";
I want to integrate an async treeview in PHP. Now i want to know how to generate a source php file with PHP.
Now my database structure is like this:
Create table School(
id int(10) NOT NULL AUTO_INCREMENT,
name varchar(50),
primary key(id)
);
Create table Class(
id int(10) NOT NULL AUTO_INCREMENT,
name varchar(50),
school_id int(10),
primary key(id),
foreign key(school_id) reference School(id) on delete cascade
);
Student(
id int(10) NOT NULL AUTO_INCREMENT,
name varchar(50),
class_id int(10),
primary key(id),
foreign key(class_id) reference Class(id) on delete cascade
);
School -> Class -> Student
Now, i want to realize the treeview. Do you have any ideas of implementing it?
Thanks a lot.
Additional question: When i finish the treeview. If i click the items in the treeview, it will generate a result table by clicking. Do you know how to do that?
However, firstly i should finish the treeview.
So basically you need to (pseudo code):
$tree = array();
/**
* the following line essentially executes the Query:
* SELECT * from schools;
* and returns all the rows in an array like
* $schools = Array(0=>array('id'=>1, 'name' => 'name'))
*/
$schools = $db->schools()->selectAll();
foreach($schools as $school)
{
$schoolArr = array('text' => $school['name']);
/**
* Similar to the calls to school above except the query would be:
* SELECT * from class where class.school_id = $school['id']
* $school['id'] is the pk from the particular school record
*/
$classes = $db->classes()->select("school_id = ?", $school['id']);
$classesArr = array();
foreach($classes as $class)
{
$classArr = array('text' => $class['name']);
/**
* Similar to the calls to school above except the query would be:
* SELECT * from student where student.class_id = $class['id']
* $class['id'] is the pk from the particular class record
*/
$students = $db->students()->select('class_id = ?', $class['id']);
$studentsArr = array();
foreach($students as $student)
{
$studentsArr[] = array('text' => $student['name']);
}
$classArr['children'] = $studentsArr;
$classesArr[] = $classArr;
}
$schoolArr['children'] = $classesArr;
$tree[] = $schoolArr;
}
$jsTreeString = json_encode($tree);
Now obviously as you go through each loop you need to be assinging your other tree properties. then when youre all done just json_encode the array and echo it where i needs to be. At least thats how id work it. Note though, that you can probably do this without an individual query using some joins but i didnt want to get into all that - youll definitely wann explore that though if performance is at all an issue.
If you want this treeview to be done with jQuery I would suggest using this plugin. You just need to run one JS function and it will work. And all your PHP part has to do is get data from db and generate a HTML that jQuery plugin can transform into treeview.
It looks like this article/example code might be of interest to you in implementing the tree view.