PDOStatement::fetch() and duplicate field names - php

I'm working on a web application using MySQL and PHP 5.3.8. We have a mechanism to translate a simplified query instruction into a complete query string, including joins.
Since I cannot know what (normalized) tables there will be joined and what their fields are called, there may be duplicate field names. When executing PDOStatement::fetch(PDO::FETCH_ASSOC), I get an associated array of field names:
$test = $this->DBConnection->prepare("SELECT `events`.`Title`, `persons`.`Title` FROM `events` JOIN `persons` ON `events`.`HostID` = `persons`.`ID`;");
$test->execute();
$test->fetch();
But I have no way of distinguishing repeating field names, such as "title". Worse, duplicates overwrite each other:
array('Title' => 'Frodo Baggins');
In the bad old days, I ran mysql_fetch_field() on each field to get the table for each field. Please, tell me there is a better way than prefixing the fields (SELECT events.Title AS eventsTitle;).
Your help is greatly appreciated!

Give them aliases in the query so they won't be duplicates:
SELECT events.Title AS eTitle, persons.Title AS pTitle FROM ...
Then the row will be:
array('eTitle' => 'Hobbit Meeting', 'pTitle' => 'Frodo Baggins');
The alternative is to fetch the result as an indexed array rather than associative:
$test->fetch(PDO::FETCH_NUM);
Then you'll get:
array('Hobbit Meeting', 'Frodo Baggins');
and you can access them as $row[0] and $row[1].

Related

How can I replace column name with the comments given in table's field definition

I want to change the column name with a descriptive name, like in my table I have a field name "job_title",
I want to replace this heading with "what is your job title", and job_description with "Describe your job description"
For accomplishing this task I can use Aliasing but I want to change the column names dynamically instead of hard code.
I have described these questions in the comments section of the individual field of the table and I am trying to fetch these comments from the database and display comments of the field as the column heading but couldn't accomplish it.
This is my PHP code:
$sql_getcolumns="select * interview_col_comments where table_name ='interview'";
$result = $mysqli->query($sql_getcolumns);
echo "<table>";
echo "<tr>";
while($row = mysqli_fetch_array($result))
{
echo "<th>".$row[0]."</th>";
}
echo "";
I also tried to find a way in the PhpMyAdmin interface if I labeled column names and retrieve labels using a query in PHP
but didn't find this option in the SQL interface.
Is there another Approach using PHP, or SQL Which I can use to give columns of the table descriptive names?
One approach can be make an array in php like below.
<?php $comment=[
'job_title' => "what is your job title",'job_description'=>"Describe your job description"];
echo "<th>".$comment[$row[0]]."</th>";
?>'
Another approach can be make a another table with column description in mysql and replace on run time.
id|column_id|description
1|job_title|"what is your job title"
2|job_description|"Describe your job description"
What you are looking for is called "localization".
There are many ways to do that.
I usually ALSO prefer to keep this information tightly wired to the database I'm working with, so what I did is:
I used the Database Column's Comment field to provide meta-information.
For example, a columns comment can look like this:
#required #de=Vertragsnummer #en=Contract_Number #search
Now, using the following query, I can retrieve the comments, and build a ColumnMetadataObject out of the information using some regex / string operations.
SELECT
c.`TABLE_NAME`,
c.`COLUMN_NAME`,
c.`COLUMN_COMMENT`,
t.`TABLE_COMMENT`
FROM
information_schema.columns c left join
information_schema.TABLES t ON
c.TABLE_NAME = t.TABLE_NAME and
c.TABLE_SCHEMA = t.TABLE_SCHEMA
where
c.`table_schema` = 'MyDatabase'
After parsing the information and providing the required Meta-Data-Objects, My header output just looks like this:
<?=$db->getColumnMetdata('contractNumber')->getHeader($_SESSION["user_language"]));?>
Code in between can vary in complexity. My ColumnMetadata also contains other information like required, searchable, length, possible foreign keys, and much more. That part would be up to you - just for localize headers, an associative Array would work as well. something like :
["de"] => {
"table1.contractNumber" => "Vertragsnummer"
"table1.Id" => "Id"
}

How to add a possble value to a MySQL SET type in php, without know the current values

Hi everybody and sorry for my english.
I have the column "example" that is a SET type.
I have to make a php page where you can add values to that column.
First of all I need to know what is just in "example", to prevent the adding of an existing value by a control. Second of all I need to add the new value.
Here's what I had thinked to do.
//I just made the connection to the db in PDO or MySQLi
$newValue=$_POST['value']; //I take the value to add in the possible values from a form
//Now I have to "extract" all the possible values. Can't think how.
//I think I can store the values into an array
$result=$sql->fetch(); //$sql is the query to extract all the possible values from "example"
//So now i can do a control with a foreach
foreach($result as $control){
if ($newValue == $control){
//error message, break the foreach loop
}
}
//Now, if the code arrives here there isn't erros, so the "$newValue" is different from any other values stored in "example", so I need to add it as a possible value
$sql=$conn->query("ALTER TABLE 'TableName' CHANGE 'example' 'example' SET('$result', '$newValue')"); //<- where $result is the all existing possible values of "example"
In PDO or MySQLi, it's indifferent
Thanks for the help
We can get the column definition with a query from information_schema.columns
Assuming the table is in the current database (and assuming we are cognizant of lower_case_table_names setting in choosing to use mixed case for table names)
SELECT c.column_type
FROM information_schema.columns c
WHERE c.table_schema = DATABASE()
WHERE c.table_name = 'TableName'
AND c.column_name = 'example'
Beware of the limit on the number of elements allowed in a SET definition.
Remove the closing paren from the end, and append ',newval').
Personally, I don't much care for the idea of running an ALTER TABLE as part of the application code. Doing that is going to do an implicit commit in a transaction, and also require an exclusive table / metadata lock while the operation is performed.
If you need a SET type - you should know what values you add. Otherwise, simply use VARCHAR type.

Laravel DB select statement. Query returns column name as result rather than values

UPDATE: The below is for context but I figured out this is not where the problem is.
I'm pretty new to php and laravel. I do an AJAX call to a controller that uses a postgresql query from a repository. The query is a select query it has columns dated, total_sms and demo (demo is gender Male/Female).
I have an array $return[] The below is within a foreach ($result as $day).
At the end the array is translated to json before returning.
$return[$day->dated][$day->demo] = $day->total_sms;
I expected the above to insert the dates as a key with an array value, the gender (Male and Female) as the keys of the inner array, with them having an array as a value and these arrays will contain the total_sms value. (that sentence must have been confusing...)
However the result is:
{2017-07-03: {d.gender: "0"}, 2017-07-04: {d.gender: "0"}}
How would I build an array like:
{2017-07-03: {Male: "0" , Female: "10"}, 2017-07-04: {Male: "0", Female: "0"}
Although I think the above is not actually how I originally described it? I am planning on decoding it and sticking the information into a chart.js chart with:
$.each(d, function(date, value) {
window.barChart.data.labels.push(date);
$.each(d.date, function(demographic, value) {
// ALWAYS ASCENDING DATA
});
UPDATE: Code that is creating the issue.
I have a query in a repository that can have varying parameters inserted, to change what demographic is used. Below is the version I use in pgAdmin, and it works fine.
SELECT d.date dated, count(se.id) total_sms, demo
FROM (
select to_char(date_trunc('day', (current_date - offs)), 'YYYY-MM-DD') AS date, demographic.gender as demo
FROM generate_series(0, 365, 1) AS offs
CROSS JOIN (SELECT DISTINCT gender FROM common.profile) AS demographic
) d
LEFT OUTER JOIN (
SELECT id, customer_id, client_report.insert_time, profile.gender, profile.house_income, profile.address AS postcode, profile.age AS age_group, profile.is_employed, profile.is_married, profile.no_children, profile.no_cars, profile.shopping_frequency
FROM common.client_report
JOIN common.profile
ON client_report.profile_id = profile.uuid
WHERE sms_status = 'SUCCESS' AND customer_id = 5::int
) se
ON (d.date=to_char(date_trunc('day', se.insert_time), 'YYYY-MM-DD')) AND demo = gender
WHERE d.date::date BETWEEN '2017-07-01'::date AND '2017-08-01'::date
GROUP BY d.date, demo
ORDER BY d.date, demo ASC;
Anywhere that states gender, a date or the 5 after AND customer_id = are all generated from named parameters (When I receive an error I can generally fix that, when there is no error, the code works but gives me gender as the column name ['de.gender'] rather than the values ['Male', 'Female']). I mean to say that I have had errors around the parameters, and no longer believe that could be the issue. I then will receive an error around missing a conversion tool for unknown to text. I don't fully understand this but I know it means I have to cast gender as text with ::text. Doing that results in the column name appearing. However doing that in a different, simpler query without the cross join works and the results are correct. This leads me to believe there is an issue with the way my query is or that there is a bug with the DB driver and these queries.
UPDATE: Did a test, should have done it earlier! Placed the version with no parameters into my application, made the function run, and it returns exactly as needed. So this is stemming from parameters, and probably the ones that define tables like de.gender (de being the table)
UPDATE: Another test:
CROSS JOIN (SELECT DISTINCT gender FROM common.profile) AS demographic
On this line, gender is a named parameter (:demograph). However, if I change this to simply gender, the results are as expected with Male, Female values. If it stays as a parameter it seems to be creating lots of strings of 'gender' rather than grabbing the 'Male', 'Female' values.
UPDATE: Did a lot of learning today, and named or positional parameters are for values only. Meaning all inputs are encased in double quotes, which was why I was creating strings of 'gender' rather than male, female.
i donn't know i am not sure about your question
$testArray = array(
"2017-07-03" => array("male" : "0","female" => "10"),
"2017-07-04" => array("male" : "0","female" => "0")
);
return $testArray;

enum values from mysql table

I have a enum column in my table and I am trying to get out the values I have set in the table in a drop down. So first I have written this query to get the column_type and column_name
"SELECT `COLUMN_NAME`,`DATA_TYPE`,`COLUMN_TYPE` FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA`='devsbh' AND `TABLE_NAME`='modules' AND `COLUMN_NAME` NOT IN ('created_at', 'updated_at')"
then I do this to get out the enum values like so
<?php
$regex = "/'(.*?)'/";
preg_match_all( $regex , $modules->COLUMN_TYPE , $enum_array );
$enum_fields = $enum_array[1];
?>
and I display like so
PS:Using laravel's blade template engine.
{!! Form::select($modules->COLUMN_NAME,$enum_fields) !!}
Everything is correct up until here. When I try to store it tries too save as for Y => 0 and for N => 1. How can I get the key => value same as enum value?
the values of $enum_fields as per the console is [0]=>Y , [1]=>N.
You can use the array_combine method to make the key and the value of the array same like so
<?php
$regex = "/'(.*?)'/";
preg_match_all( $regex , $modules->COLUMN_TYPE , $enum_array );
$keyValueSame = array_combine($enum_array[1],$enum_array[1]);
?>
now the key and value of the $keyValueSame array will have the same value.
the values of $keyValueSame as per the console is [Y]=>Y , [N]=>N.
You are probably better off just hard coding the enumerations into your codebase.
Assuming you are using MySQL, the idea behind enumerations is really to restrict the values in the column to a specific set - basically saying "if the value is not one of these strings, don't allow it.".
Enumerations are not designed to be changed often (if at all) - in fact, you may find issues if you do try to alter them it - it can take some database gymnastics to alter them especially if you have lots of records.
If your "lookup" data will change, and you need it to be database stored, make the column a foreign key to another table containing your lookup fields.
If you are stuck with the enumerations, just hard code the list in your dropdown.

Codeigniter, join of two tables with a WHERE clause

I've this code:
public function getAllAccess(){
$this->db->select('accesscode');
$this->db->where(array('chain_code' => '123');
$this->db->order_by('dateandtime', 'desc');
$this->db->limit($this->config->item('access_limit'));
return $this->db->get('accesstable')->result();
}
I need to join it with another table (codenamed table), I've to tell it this. Not really a literal query but what I want to achieve:
SELECT * accesscode, dateandtime FROM access table WHERE chain_code = '123' AND codenames.accselect_lista != 0
So basically accesstable has a column code which is a number, let us say 33, this number is also present in the codenames table; in this last table there is a field accselect_lista.
So I have to select only the accselect_lista != 0 and from there get the corrisponding accesstable rows where codenames are the ones selected in the codenames.
Looking for this?
SELECT *
FROM access_table a INNER JOIN codenames c ON
a.chain_code = c.chain_code
WHERE a.chain_code = '123' AND
c.accselect_lista != 0
It will bring up all columns from both tables for the specified criteria. The table and column names need to be exact, obviously.
Good start! But I think you might be getting a few techniques mixed up here.
Firstly, there are two main ways to run multiple where queries. You can use an associative array (like you've started to do there).
$this->db->where(array('accesstable.chain_code' => '123', 'codenames.accselect_lista !=' => 0));
Note that I've appended the table name to each column. Also notice that you can add alternative operators if you include them in the same block as the column name.
Alternatively you can give each their own line. I prefer this method because I think its a bit easier to read. Both will accomplish the same thing.
$this->db->where('accesstable.chain_code', '123');
$this->db->where('codenames.accselect_lista !=', 0);
Active record will format the query with 'and' etc on its own.
The easiest way to add the join is to use from with join.
$this->db->from('accesstable');
$this->db->join('codenames', 'codenames.accselect_lista = accesstable.code');
When using from, you don't need to include the table name in get, so to run the query you can now just use something like:
$query = $this->db->get();
return $query->result();
Check out Codeigniter's Active Record documentation if you haven't already, it goes into a lot more detail with lots of examples.

Categories