I am very new to php/mysql and this is causing me to loose hairs, I am trying to build a multi level site navigation. In this part of my script I am readying the sub and parent categories coming from a form for insertion into the database:
// get child categories
$catFields = $_POST['categories'];
if (is_array($catFields)) {
$categories = $categories;
for ($i=0; $i<count($catFields); $i++) {
$categories = $categories . $catFields[$i];
}
}
// get parent category
$select = mysql_query ("SELECT parent FROM categories WHERE id = $categories");
while ($return = mysql_fetch_assoc($select)) {
$parentId = $return['parent'];
}
The first part of my script works fine, it grabs all the categories that the user has chosen to assign a post by checking the checkboxes in a form and readies it for insertion into the database.
But the second part does not work and I can't understand why. I am trying to match a category with a parent that is stored in it's own table, but it returns nothing even though the categories all have parents. Can anyone tell me why this is?
p.s. The $categories variable contains the sub category id.
I can see a few bugs:
$categories = $categories;
should be
$categories = '';
Since there will be more than on categories you'll have to use the MySQL in clause as:
SELECT parent FROM categories WHERE id in ($categories)
for this to happen you'll have to make categories a comma separated list if ids by altering your for loop as:
for ($i=0; $i<count($catFields); $i++)
$categories .= "$catFields[$i],"; // append 'id,' to existing list.
trim($categories,','); // remove any trailing commas.
$categories seems like it would be a string with more than one value in it, which the SQL engine you're using isn't going to be able to use with just an = where condition. You probably need to change it so that your SQL looks something like WHERE id IN (1,2,3) where 1, 2, 3 etc are your id's that you want to match.
AFAIK, $_POST is an array. Is $_POST['categories'] the data from a multi select html form element?
What kind of data are you sending in $_POST['categories']? How do you expect that to be an array? If you are encoding the data into an array like structure before submitting it to your PHP script, you may have to manually insert it into an array.
Use
$select = mysql_query ("SELECT parent FROM categories WHERE id IN (" . implode(",", $array) . ")");
And don't forget mysql_real_escape_string() in combination with quotes ' in your query, your corrent code is prone to SQL injection. Prepared statements (PDO or MySQLi) will to the trick as well.
But don't trust userinput!
Related
I am trying to display field data in the WP front end using shortcodes from a new table.
See Table
After coming across many sources and research, I do not seem to find a simple way to display data in text (not table), contained within a specific field selected in the SQL query by means of SELECT FROM WHERE.
So far I called wpdb, selected the field, created a loop and echoed. But no results are displayed.
I also tried using print_r and implode but both failed too.
<?php
function Initial_Brief(){ global $wpdb;
$results = $wpdb->prepare( "SELECT 'Initial_Brief'* FROM `Portal_100` WHERE Project_Title = 'Project 1'");
foreach ($results as $result)
echo $result['results'];
}
add_shortcode('Initial_Brief','Initial_Brief')
?>
Many thanks in advance,
To share the logic of this, which I find quite powerful, is to use shortcodes for displaying all text on the website, enabling text edit from the front-end by creating an HTML form which updates the specific field. I will create an edit icon displayed on hover to an editor's role, clicked to trigger a popup with an html form which calls a function to update the specific field in the database.
You are missing to call the get_results method like this:
<?php
global $wpdb;
$id = 23;
$sql = $wpdb->prepare( "SELECT * FROM tablename WHERE id= %d",$id);
$results = $wpdb->get_results( $sql , ARRAY_A );
?>
The use of the prepare method is a good practice for preparing a SQL query for safe execution (eg. preempt SQL injection), but it is no yet the execution, it returns a sanitized query string, if there is a query to prepare.
It is also good companion for the query method.
After some iterations I finally got it to work!
I understand mySQL does not accept input with a spacing?
How can I insert a WHERE condition for multiple words or a paragraph?
It worked for me using the following script but I had to change the WHERE value into an integer.
<?php
add_shortcode('Initial_Brief', function(){
global $wpdb;
$sql = $wpdb->prepare("Select Value from `P100` WHERE `P100`.`id` = 1 ");
$results = $wpdb->get_results( $sql , ARRAY_A );
foreach ($results as $result) {
$display = implode(", ", $result);
echo $display;
});?>
I'm having trouble with my MySQL results, I have a table named "posts" and one column is named "category" which is an integer value. I created a variable to get the category number from the browser depending on which category link a user clicks on.
If the category number is "1" it should display a test post(which is does).
But if the category number is anything other than "1" which is the category id, it still shows the same test post when it should say "No results found" since it is querying for a different category id number.
I'm guessing it has something to do with the SQL syntax cause I've done added single-quotes around the variable name, and it error'd out so I removed them. I'm only posting the necessary code below cause I know it has something to do with that variable in the SQL:
// Variable to hold number
$category = is_numeric($_GET["c"]);
// Query
$query = "SELECT * FROM posts WHERE category = $category";
NOTE: Now i'm having a different issue. I need to make it so when there are results the while statement goes through and displays them, but if there are no results, display another message. The issue is it wont display the no results message when there are no results. Here is my code:
while($results = mysqli_fetch_assoc($query))
{
echo '$results["title"] <br>';
}
if(count($results) == 0 || count($results) == null) {echo 'No results';}
I've tried placing the if statement in the while and outside like so, but neither way works. I have to have the while statement cause I know i'll have more than one result so I can't use an if statement. The while statement needs an else!
You're turning the category into a boolean value.
You actually want this:
$category = intval($_GET['c']);
is_numeric returns true/false, so your query is "SELECT * FROM posts WHERE category = true"
Try this:
if(is_numeric($_GET["c"])) {
$category = $_GET["c"];
} else {
// exit the script or set a default value
}
// ...
Your variable inside your query need additional ' apostrophes like this
$query = "SELECT * FROM posts WHERE category = '$category'";
One more thing, if you are trying to specifically detect numbers in PHP use the ctype
function
if (ctype_alpha($id){
//do something
}
cast it to int!
$category = (int) $_GET["c"];
and then
if (!empty($category))
{
// do your stuffff
}
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Is it possible to query a tree structure table in MySQL in a single query, to any depth?
I have an admin area I created that pulls data from the mysql database using php and display the results in a table. Basically it shows a parent category, then the first sub category below it, then the third level sub category/subject.
It works perfectly but as I am new to mysql and php I am sure that it the code needs to be improved in order to save db resources as while building the table I use 3 while loops and in each loop make a mysql query which I am sure is the wrong way to do it.
Can somebody offer me some assistance for the best way of doing this?
Here is the code:
$query = mysql_query("SELECT * FROM categories WHERE
parent_id is null
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row = mysql_fetch_assoc($query)) {
echo '<tr style="font-weight:bold;color:green;"><td>'. $row ['cat_id'].'</td><td>'.$row['cat_name'].'</td><td>'.$row ['parent_id'].'</td><td>'.$row['active'].'</td><td>'.$row ['url'].'</td><td>'.$row['date_updated'].'</td></tr>' ;
$query2 = mysql_query("SELECT * FROM categories WHERE
(active = 'true' AND parent_id = ".$row ['cat_id'].")
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row2 = mysql_fetch_assoc($query2)) {
echo '<tr style="font-weight:bold;"><td>'. $row2['cat_id'].'</td><td>'.$row2 ['cat_name'].'</td><td>'.$row2['parent_id'].'</td><td>'.$row2 ['active'].'</td><td>'.$row2['url'].'</td><td>'.$row2 ['date_updated'].'</td></tr>' ;
$query3 = mysql_query("SELECT * FROM categories WHERE
(active = 'true' AND parent_id = ".$row2 ['cat_id'].")
order by cat_id asc;", $hd)
or die ("Unable to run query");
while ($row3 = mysql_fetch_assoc($query3)) {
echo '<tr><td>'. $row3['cat_id'].'</td><td>'.$row3['cat_name'].'</td><td>'.$row3 ['parent_id'].'</td><td>'.$row3['active'].'</td><td>'.$row3 ['url'].'</td><td>'.$row3['date_updated'].'</td></tr>' ;
}
}
}
EDIT
Ok so I did a bit of research and this is where I am:
Probably for a small database my approach is fine.
For a bigger database using an array to store the data would probably mean I need to use a recursive approach which might use up too much memory. Would love to hear what people think, would it still be better than looping db queries in the nested while loops?
I found the following thread where there is an answer to do this without reccursion and with only one query. Not sure if I need to add a position column to my current design:
How to build unlimited level of menu through PHP and mysql
If I rebuild the design using the nested sets model instead of adjacency model then the mysql query would return the results in the required order however maintaining the nested sets design is above my head and I think would be overkill.
That's it. If anyone has any input on top of that please add to the conversation. There must be a winning approach as this kind of requirement must be needed for loads of web applications.
I would think you could do something like this:
SELECT * FROM categories
WHERE active = 'true'
ORDER BY parent_id, cat_id
This would give you all your categories ordered by parent_id, then by cat_id. You would then take the result set and build a multi-dimensional array from it. You could then loop through this array much as you currently do in order to output the categories.
While this is better from a DB access standpoint, it would also consume more memory as you need to keep this larger array in memory. So it really is a trade-off that you need to consider.
There is a lot to fix there, but I'll just address your question about reducing queries. I suggest getting rid of the WHERE clauses all together and use if statements within the while loop. Use external variables to hold all the results that match a particular condition, then echo them all at once after the loop. Something like this (I put a bunch of your stuff in variables for brevity)
//before loop
$firstInfoSet = '';
$secondInfoSet = '';
$thirdInfoSet = '';
//in while loop
if($parentID == NULL)
{
$firstInfoSet.= $yourFirstLineOfHtml;
}
if($active && $parentID == $catID) // good for query 2 and 3 as they are identical
{
$secondInfoSet.= $yourSecondLineOfHtml;
$thirdInfoSet.= $yourThirdLineOfHtml;
}
//after loop
echo $firstInfoSet . $secondInfoSet . $thirdInfoSet;
You can now make whatever kinds of groupings you want, easily modify them if need be, and put the results wherever you want.
--EDIT--
After better understanding the question...
$query = mysql_query("SELECT * FROM categories order by cat_id asc;", $hd);
$while ($row = mysql_fetch_assoc($query)){
if($row['parent_id'] == NULL){
//echo out your desired html from your first query
}
if($row['active'] && $row['parent_id']== $row['cat_id']){
//echo out your desired html from your 2nd and 3rd queries
}
}
I'm using WordPress, but this question is more pertaining to the SQL involved. I'll gladly move it if I need to.
I'm working on http://www.libertyguide.com/jobs and I'm trying to alter the filtering mechanics. Currently it's a global OR query.
Anyways, I have three filtering lists, and I'm storing what's selected into three strings (interests, type, experience) in the following way:
"( $wpdb->terms.slug = 'webdevelopment' OR $wpdb->terms.slug = 'journalism' OR ... ) AND"
It's populated by whatever is selected in my filtering lists.
When it comes down to it, I have this as a basic query (I'm leaving out the LEFT JOINS):
Before:
SELECT * FROM $wpdb->posts WHERE ($wpdb->terms.slug = 'fromlist1'
OR $wpdb->terms.slug = 'fromlist2' OR $wpdb->terms.slug = 'fromlist3')
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'...
After:
SELECT * FROM $wpdb->posts WHERE
($wpdb->terms.slug = 'fromlist1' OR $wpdb->terms.slug = 'fromlist1again')
AND ($wpdb->terms.slug = 'fromlist2' OR $wpdb->terms.slug = 'fromlist2again')
AND ($wpdb->terms.slug = 'fromlist3' OR $wpdb->terms.slug = 'fromlist3again')
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'...
So essentially I want to go from an
OR filter
to
an AND filter with OR filtering inbetween.
My new filtering only works when one item overall is selected, but returns nothing when I select more than one thing (that I know would match up with a few posts).
I've thought through the logic and I don't see anything wrong with it. I know nothing is wrong with anything else, so it has to be the query itself.
Any step in the right direction would be greatly appreciated. Thanks!
UPDATE
From the confusion, basically I have this:
"SELECT ...... WHERE $terms ..."
but I WANT
"SELECT ....... WHERE $interests AND $type AND $experience"
I don't want to have it filter $interest[1] OR $interest[2] OR $type[1] OR $experience[1], but instead want it to filter ($interest[1] OR $interest[2]) AND ($type[1]) AND ($experience[1])
I hope this makes more sense
*UPDATE 2*
Here's and example:
In my interests list, I select for example three things: WebDevelopment, Academia, Journalism.
In my type list, I choose two things: Fulltime, Parttime
In my experience list, I choose three things: Earlycareer, Midcareer, Latecareer.
When I run my query, I want to make sure that each record has AT LEAST one of each of the three lists. Possible Results: (WebDevelopment, Parttime, Midcareer), (Academia, Fulltime, Earlycareer, Midcareer).
NOT A RESULT: (Journalism, Earlycareer) - missing fulltime or parttime
I really hope this clears it up more. I'm willing to give compensation if I can get this working correctly.
Okay, I'll take a shot at this:
SELECT * FROM $wpdb->posts WHERE
(
$wpdb->terms.slug IN ('$interest1', '$interest2') AND
$wpdb->terms.slug IN ('$type1', '$type2') AND
$wpdb->terms.slug IN ('$exp1', '$exp2')
)
AND $wpdb->term_taxonomy.taxonomy = 'jobtype'
The IN keyword will return true if any member of the set matches.
I think you're looking for a WHERE category IN (comma, seperated, list, of, values) that you can generate dynamically from the form. If you combine it with the other categories, you can require them to select something from each with...
WHERE category1 IN (a, comma, seperated, list, of, values)
AND category2 IN (another, list, of, values)
AND ...
Which will only return a value if there is something selected from each category and will return nothing if any of the selection lists are empty; actually it may well kick out an error, so I would also generate the query dynamically if there is any content whatsoever for a given category.
if (!empty($arrayOfCategory1)) {
//sanitize input logic here
$Category[1] = 'category1 IN ('. implode(', ', $arrayOfCategory1) .')';
} else {
$Category[1] = '';
}
You concatenate the resultant string together and build the query with that. The WHERE 1=1 trick is problematic because if nothing is chosen, everything in the database will match, so I strongly recommend going through the process of adding the AND operators properly.
EDIT: it occurs to me that if you build the conditional statements as an array, you can implode those with ' AND ' and get the query in a fairly small number of lines of code.
Sort of confused a bit by what you are saying but if I wanted to build a filter I would be dynamically generating the SQL query based on the submitted filter values. Something like:
$sql = "SELECT * FROM $wpdb->posts WHERE 1=1";
if ( !empty($interest) ) { // they ticked the interested in ??? checkbox
$sql .= " AND $wpdb->terms.slug = $interest"
}
Obviously you will need to filter and escape any values that have been submitted.
I have two dynamic tables (tabx and taby) which are created and maintained through a php interface where columns can be added, deleted, renamed etc.
I want to read all columns simulataneously from the two tables like so;-
select * from tabx,taby where ... ;
I want to be able to tell from the result of the query whether each column came from either tabx or taby - is there a way to force mysql to return fully qualified column names e.g. tabx.col1, tabx.col2, taby.coln etc?
In PHP, you can get the field information from the result, like so (stolen from a project I wrote long ago):
/*
Similar to mysql_fetch_assoc(), this function returns an associative array
given a mysql resource, but prepends the table name (or table alias, if
used in the query) to the column name, effectively namespacing the column
names and allowing SELECTS for column names that would otherwise have collided
when building a row's associative array.
*/
function mysql_fetch_assoc_with_table_names($resource) {
// get a numerically indexed row, which includes all fields, even if their names collide
$row = mysql_fetch_row($resource);
if( ! $row)
return $row;
$result = array();
$size = count($row);
for($i = 0; $i < $size; $i++) {
// now fetch the field information
$info = mysql_fetch_field($resource, $i);
$table = $info->table;
$name = $info->name;
// and make an associative array, where the key is $table.$name
$result["$table.$name"] = $row[$i]; // e.g. $result["user.name"] = "Joe Schmoe";
}
return $result;
}
Then you can use it like this:
$resource = mysql_query("SELECT * FROM user JOIN question USING (user_id)");
while($row = mysql_fetch_assoc_with_table_names($resource)) {
echo $row['question.title'] . ' Asked by ' . $row['user.name'] . "\n";
}
So to answer your question directly, the table name data is always sent by MySQL -- It's up to the client to tell you where each column came from. If you really want MySQL to return each column name unambiguously, you will need to modify your queries to do the aliasing explicitly, like #Shabbyrobe suggested.
select * from tabx tx, taby ty where ... ;
Does:
SELECT tabx.*, taby.* FROM tabx, taby WHERE ...
work?
I'm left wondering what you are trying to accomplish. First of all, adding and removing columns from a table is a strange practice; it implies that the schema of your data is changing at run-time.
Furthermore, to query from the two tables at the same time, there should be some kind of relationship between them. Rows in one table should be correlated in some way with rows of the other table. If this is not the case, you're better off doing two separate SELECT queries.
The answer to your question has already been given: SELECT tablename.* to retrieve all the columns from the given table. This may or may not work correctly if there are columns with the same name in both tables; you should look that up in the documentation.
Could you give us more information on the problem you're trying to solve? I think there's a good chance you're going about this the wrong way.
Leaving aside any questions about why you might want to do this, and why you would want to do a cross join here at all, here's the best way I can come up with off the top of my head.
You could try doing an EXPLAIN on each table and build the select statement programatically from the result. Here's a poor example of a script which will give you a dynamically generated field list with aliases. This will increase the number of queries you perform though as each table in the dynamically generated query will cause an EXPLAIN query to be fired (although this could be mitigated with caching fairly easily).
<?php
$pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
function aliasFields($pdo, $table, $delim='__') {
$fields = array();
// gotta sanitise the table name - can't do it with prepared statement
$table = preg_replace('/[^A-z0-9_]/', "", $table);
foreach ($pdo->query("EXPLAIN `".$table."`") as $row) {
$fields[] = $table.'.'.$row['Field'].' as '.$table.$delim.$row['Field'];
}
return $fields;
}
$fieldAliases = array_merge(aliasFields($pdo, 'artist'), aliasFields($pdo, 'event'));
$query = 'SELECT '.implode(', ', $fieldAliases).' FROM artist, event';
echo $query;
The result is a query that looks like this, with the table and column name separated by two underscores (or whatever delimeter you like, see the third parameter to aliasFields()):
// ABOVE PROGRAM'S OUTPUT (assuming database exists)
SELECT artist__artist_id, artist__event_id, artist__artist_name, event__event_id, event__event_name FROM artist, event
From there, when you iterate over the results, you can just do an explode on each field name with the same delimeter to get the table name and field name.
John Douthat's answer is much better than the above. It would only be useful if the field metadata was not returned by the database, as PDO threatens may be the case with some drivers.
Here is a simple snippet for how to do what John suggetsted using PDO instead of mysql_*():
<?php
$pdo = new PDO($dsn, $user, $pass, array(PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION));
$query = 'SELECT artist.*, eventartist.* FROM artist, eventartist LIMIT 1';
$stmt = $pdo->prepare($query);
$stmt->execute();
while ($row = $stmt->fetch()) {
foreach ($row as $key=>$value) {
if (is_int($key)) {
$meta = $stmt->getColumnMeta($key);
echo $meta['table'].".".$meta['name']."<br />";
}
}
}