So I'm working on this/these problem(s), and there's just a lot going on for recently using codeigniter..
So, I have these specific tables:
sect_tbl ----------- sub_tbl
sect_id ----------- sub_code
2 --------------------ST20
3 --------------------AS13
3 --------------------DA11
1 --------------------PE40
2 --------------------SE10
3 --------------------PE20
4 --------------------RT40
I made sect_id the foreign key in sub_tbl.
the display - a side menu:
link of sect_ids:
1
2
3
4
I want to make it so, that if I click the link of sect_id 3, I'll be redirected to a new page with its corresponding set of sub_code -- "AS13, DA11, PE20" as seen in the table above.
Expected outcome is something like this:
sub_code
(in sect_id-3)
| AS13 |
| DA11 |
| PE20 |
Model: (most definitely not right )
function fetch_view($data){
$query = $this->db->query("SELECT a.sub_code, a.sub_name, a.sect_id,
b.block_name, b.year_level FROM subject as a JOIN section as b
WHERE a.sect_id = b.sect_id ");
return $query->result();
}
Controller:
$data["subjects"] = $this->view_model->fetch_view($this->session-
userdata('user_id'));
view: (this lacking since I cant get around much further with foreach loop yet )
<?php
foreach ($subjects as $row) {
?>
<?php echo $row->year_level.$row->block_name; ?>
<?php
}
?>
-> I tried working my way with href to redirect me and then be able to view it dynamically but it backfired. Thanks a lot to those who could spare their time to share solutions.
If you say you want all sub codes where sect ID = 3
$sect_id = 3;
I simplified your query, but the main problem was that you had no use of "on x = y" for your join
$query = $this->db->query("
SELECT
sec.year_level,
sec.block_name,
sub.sub_code,
FROM section as sec
JOIN subject as sub
ON sec.sect_id = sub.sect_id
WHERE sec.sect_id = ?
", [ $sect_id ] );
if( $query->num_rows() > 0 )
return $query->result();
return [];
Then, because I don't have your actual table schemas, I couldn't suggest if your view is correct, but in general, something like this should work:
$this->load->helper('url');
foreach( $subjects as $row )
{
echo anchor( 'subs/show/' . $row->sub_code, $row->year_level . $row->block_name );
}
Also, as a side note, try to always follow a code style with proper indentation. It makes things that are wrong stand out and easier to fix.
Related
My current config of tables is as follows
PagesTable
$this->belongsToMany('Keywords', ['through' => 'PagesKeywords']);
PagesKeywordsTable
Schema: id, page_id, keyword_id, relevance
$this->belongsTo('Pages');
$this->belongsTo('Keywords');
KeywordsTable
$this->belongsToMany('Pages', ['through' => 'PagesKeywords']);
Now heres what i'm trying to do..
Find pages via keywords, using an array to be precise then order by PagesKeywords.relevance
(This is basically storing how many time that keyword is repeated per page, so no duplicate keywords in join table)
I've currently got this working fine except it groups the results of keywords by the keyword itself, where as I need them to be grouped by Pages.id
Here is what i have in my Pages controller, search action:
$keywords = explode(" ", $this->request->query['q']);
$query = $this->Pages->Keywords->find()
->where(['keyword IN' => $keywords])
->contain(['Pages' => [
'queryBuilder' => function ($q) {
return $q->order([
'PagesKeywords.relevance' =>'DESC'
])->group(['Pages.id']);
}
]]);
$pages = array();
foreach($query as $result) {
$pages[] = $result;
}
I know this seems like a backward way to do things but its the only way I seemed to be able to order by _joinTable (PagesKeywords.relevance)
This returns the results I need but now it needs to be grouped by Pages.id which is where this whole thing goes to pot..
Just to be clear the structure I want is:
Page data 1
Page data 2
Page data 3
Page data 4
Where as its currently returning:
Keyword "google"
------- Page data 1
------- Page data 2
------- Page data 3
------- Page data 4
Keyword "something"
------- Page data 1
------- Page data 2
------- Page data 3
------- Page data 4
If you are able to help me thats great!
Thanks
If you're having issues with complex queries with an ORM, I find it always easier to figure out the SQL I need to get the results I require, then adapt that to the ORM.
The query you're looking for would be like this (Using MySQL engine... MySQL Handles field selects more liberally in GROUP BY clauses than other SQL engines )
SELECT Pages.*, COUNT(DISTINCT(PagesKeywords.keyword_id)) AS KeywordCount
FROM pages Pages
INNER JOIN pages_keywords PagesKeywords ON (PagesKeywords.page_id = Pages.id)
INNER JOIN keywords Keywords ON (Keywords.id = PagesKeywords.keyword_id)
WHERE Keywords.name IN ('keyword1','keyword2')
GROUP BY Pages.id
This will give you all pages that contain the keyword and KeywordCount will contain the number of distinct attached Keyword.id's
So a finder method for this would look like ( going ad-hoc here so my syntax might be shaky )
** Inside your PagesTable model **
public function findPageKeywordRank(Query $q, array $options) {
$q->select(['Pages.*','KeywordCount'=>$q->func()->count('DISTINCT(PagesKeywords.keyword_id)'])
->join([
'PagesKeywords'=>[
'table'=>'pages_keywords',
'type'=>'inner',
'conditions'=>['PagesKeywords.page_id = Pages.id']
],
'Keywords'=>[
'table'=>'keywords',
'type'=>'inner',
'conditions'=>['Keywords.id = PagesKeywords.keyword_id']
]
])
->group(['Pages.id']);
return $q;
}
Then you can all your finder query
$pages = TableRegistry::get("Pages")->find('PageKeywordRank')
->where(['Keywords.name'=>['keyword1','keyword2']]);
I searched on many posts, but I can't find one to fix my problem.
In my database I have some data : a name and a "treepath" like "1.1.2". I don't have a parent ID or anything like that. So my question is : is there a way to createa nested multi level menu with only the "treepath" ?
In my table I got (ID | NAME | TREEPATH):
0 | Phone | 1
1 | Samsung | 1.1
2 | Galaxy S | 1.1.1
3 | Galaxy Note | 1.1.2
...
Output I need :
<ul>
<li>1. Phones
<ul>
<li>1.1. Samsung
<ul>
<li>1.1.1. Galaxy S</li>
<li>1.1.2. Galaxy Note</li>
<li>1.1.3. Galaxy Ace</li>
</ul></li>
<li>1.2. Apple</li>
<li>1.3. Google</li>
</ul></li>
</ul>
I certainly need a recursive function with php, but i can't got it right, so if anyone could help me on this one, that'd be great !
Thanks !
Well, you could use a recursive function that employs regular expressions, but that might be sub-optimal. Pseudo-code follows:
function drawTree($root) {
while($results = yourQuery("SELECT * FROM table WHERE treepath REGEXP '^{$root}\.'")) {
echo "stuff";
drawTree($results['treepath']);
}
}
Your situation is odd.
I don't see a technical reason not to just have a parent id in your table. Or to use first normal form. Feels a bit contrived, but you could do something like:
<?php
$db = new mysqli(...);
$stmnt = $db->prepare("select name, treepath, LENGTH(treepath) - LENGTH(REPLACE(treepath, '.', '')) AS depth from tree order by treepath");
$stmnt->bind_result($name,$treepath,depth);
$tree = array();
$current_depth = 0;
while($stmnt->fetch())
{
if($current_depth < $depth)
{
echo '<ol><li>' . $name . '</li>';
$current_depth=$depth;
}
else if($current_depth == $depth)
{
echo '<li>' . $name . '</li>';
}
else //current_depth greater than depth
{
echo '</ol><li>' . $name .'</li>';
}
}
$stmnt->close();
$db->close();
echo '</ol>';
?>
Not tested, will need tweeks.
If your application needs to provide nested menus, you are better off implementing nesting ( In database language, it would be grouping, foreign keys and constraints ) in your database. It's not recommended that you maintain different designs in your presentation layer and database layer. After all, a strong database design is pivotal for any application, isn't it?
There are many other advantages as well - you streamline your application design, data remains organized and consistent, lesser effort in transforming database content into presentation content, and it becomes easier to add new features as well. Why don't you start with some categorization and a good schema design?
If you ask me, I would make something like a "phones" table that has phone_name, year, manufacturer, model, type etc.
Again, there would be a different table called manufacturers which have manufacturer id's, names, years, and other information. You can also have another table for "type". There will be foreign key relationships between manufacturer id's and manufacturer column in phone table and similar stuff with other columns/tables as well. If you are confused, a good place to start off would be drawing an E-R diagram which will eventually be put down into tables and views.
i have a database which contains 11 tables 9 of which have a one to many relation with a User Information table.
User Table:
ID , Name , Age , phone , . .......
on basis of ID the relation is with tables for e.g
User_cars
ID,User_ID(fk),Make , Model , .....
What i need to be able to do is present this data in DB in a CSV file. Since the There is a one to many relation a simple join doesnt work as rows are duplicated.(Not presentable Format for the client :S)
Im using Yii as web app and need some extension which can present it in some readable way.
or maybe a php script can do the task as well.
You can handle it in one of two ways:
1. Use GROUP_CONCAT (as #Dave mentioned in the comment - this is the link: MYSQL PHP API - Displaying & Fetching multiple rows within rows from another table).
There is a downside however, GROUP_CONCAT has a limit (by default 1024 characters), which you can change with the server variable group_concat_max_len, but you may not have access to do so.
This will look pretty much like this:
ID | Name | Car Model | Year
1 John BMW, Audi, Renault 1999, 2000, 2003
2 David Mercedes, Ford 2000, 2005
This can get very complicated if there are 30-40 entries per user, and non-readable.
2. The second option is to export it in the following format (not hard to do, you just iterate through all the cars a user has, but write the user only in the first iteration).
ID | Name | Car Model | Year
1 John BMW 1999
Audi 2000
Renault 2003
2 David .....
Here's some sample code (I use plenty of made up methods, that have suggestive names).
$csvArray = array();
foreach ($users as $user) {
$cars = $user->getCars(); //random method name. Grabs an array with the cars this user has :)
$firstIteration = 1;
foreach ($cars as $car) {
if ($firstIteration == 1) { // we only set the CSV row the first time we iterate through cars
$csvSingle['ID'] = $user->getId(); // more random method names
$csvSingle['Name'] = $user->getName();
$firstIteration = 0;
}
$csvSingle['car_model'] = $car->getCarModel();
$csvSingle['car_year'] = $car->getCarYear();
$csvArray[] = $csvSingle; // now we add the single row to the big CSV array.
}
}
fputcsv($handle, $csvArray); // $handle is the file you export to
i like first variant by #"Vlad Preda" and in your controller or component for export you can use something like this:
get users with cars (and cars must fill in user model relations)
$users = User::model()->with('cars')->findAll();
and then
foreach ($users as $user) {
$csvOneUser = array('id'=>$user->id, "name"=>$user->name);
if (!$user->cars) {
$csvAll[] = $csvOneUser;
continue;
}
foreach ($user->cars as $car) {
$car_attributes = $car->getAttributes();
foreach ($car_attributes as $k=>$v) {
$csvOneUser[$k][] = $v;
}
}
}
What is the best way in PHP to do variable caching? For example, let's assume I have a table, with 4 rows.
name | job
--------------------------
Justin Smith | Plumber
Jack Sparrow | Carpenter
Justin Smith | Plumber
Katie White | Doctor
Which is built like:
foreach($people as $person) {
echo $person->name;
echo get_job($person->name);
}
And the function call get_job() looks like:
function get_job($name) {
//This is pseudo code below
$row = MySQL->Query("SELECT job FROM people WHERE name = $name");
return $row->job;
}
As you can see, once we get the job of Justin Smith, further down we shouldn't and don't need to do a full MySQL query again, since we know it is Plumber.
I was thinking of doing a global variable which is a key=>value array like:
global $jobs = array("Justin Smith" => "Plumber",
"Jack Sparrow" => "Carpenter",
"Katie White" => "Doctor");
Then in the get_job() function I simply just check if the name exists in the array before querying. If not, insert the name and job into the array and return the job.
Basically, is there a better way to do this that is more elegant?
There is many possible solutions. You can store the SQL result in an array that you can use on multipe places on the page. Instead of global you should use static:
function get_job($name)
{
static $people_jobs;
if( !isset($people_jobs[$name]) || empty($people_jobs[$name]) )
{
$row = MySQL->Query("SELECT job FROM people WHERE name = $name");
$people_jobs[$name] = $row->job;
}
return $people_jobs[$name];
}
This function will do a MySQL query only once for a person, no matter how many time you call get_job($name);
this is the typical n + 1 problem where you make 1 query to get the list of "person" and then you make one query for each person to get the "job".
Depending how they are related.. maybe you could get both using one single query. For example: if the relation is a Nx1 (1 person has 1 Job, and 1 job can be used for N Persons) then your initial query should be something like:
SELECT p.name, j.job FROM Person p INNER JOIN Job j ON (p.job_id = j.id)
If the relation is a NxN, it gets tricky :P,
Hope this helps
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 */
}
?>