SQL JOIN proving impossible to sort out? Split it - php

EDIT: I was new to SQL and got lost in JOINs when I wrote this. Took me ages to realize that you can just do the SELECTS separately. OK it is processed a fraction slower than a JOIN but for learners and/or small databases it is virtually the same and makes code much more readable. I still do most JOIN type operations as sequential operations.
Yeah I know all you SQL Superstars - I better go the the naughty step.
===========================================
ORIGINAL POST WHICH WAS CURED WITH A TYPO CORRECTION
I am still struggling with classes but getting there slowly.
My next job was to get all sorts of MySQL loops tucked nicely inside classes.
I THINK I have solved the problem of getting my standardised PDO call into the method by passing it as an attribute (I cold not use the normal include in the class for some reason).
So my call to instantiate the class was:
$getval= new Transmission;
$resx= $getval->retrieveTransmission($person,$pdo);
debug($resx);
So I hope I am passing an instance of my default PDO set up by an include on the page which is:
try
{
$pdo = new PDO("mysql:host=$hjoyjoy;dbname=$dhaphap", $usadsad, $pborbor);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->exec('SET NAMES "utf8"');
}
catch (PDOException $e)
{ blah blah };
I instantiated a new Transmission object and called it:
$getval= new Transmission;
$resx= $getvalretrieve->Transmission($person,$pdo);
debug($resx);
In the Transmission class (code currently on the page working fine) I tried:
public function retrieveTransmission($id,$pdo){
try
{ $sql =
"SELECT id FROM transmission
WHERE person_id = :person;";
$statement = $pdo->prepare($sql);
$statement->bindParam(':person',$person, PDO::PARAM_INT);
$statement->execute();
$result=$statement ->fetch(PDO::FETCH_BOTH);
return($result);
}
catch (PDOException $e)
{
$output = 'Error getting messages ready to display #6734: ' . $e->getMessage();
include $_SERVER['DOCUMENT_ROOT'] ."/vvvxxx/includes/output.html.php";
exit();
}
This is then supposed to go into a
foreach ($resx as $row)
which does another SELECT loop.
So is there a fairly straightforward way of taking about 10 of these SELECT/UPDATE/STORE/DELETE loops and safely tucking them inside classes as I build this thing out?

This was simply a typo.
I also realize that not using JOINed queries is "bad form" but when you are starting out doing several simple queries make things easier to understand.

Related

How does MySQL transaction works and When to rollback?

i wonder how does transaction work.
Question is do I need to rollback if any error/exception occurs, where the Exception will be thrown and never reach the commit() call? If so why and how? and what rollback does exactly?
consider the followings PHP code (some Java code does similar things like so):
public function deleteAll():void{
$stmt = $this->con->prepare(
'DELETE FROM dd WHERE 1'
);
$this->con->begin_transaction();
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
$this->con->commit();
}
public function insert(array $list):void{
$stmt = $this->con->prepare(
'INSERT INTO dd(cc) VALUES(?)'
);
$stmt->bind_param('s', $v);
$this->con->begin_transaction();
foreach($list as $v){
if(!$stmt->execute()){
throw new Exception($stmt->error);
}
}
$this->con->commit();
}
Note:
$this->con is the mysqli connection object (not using PDO, although different but PDO and mysqli will do similar thing like above).
database is MySQL, table using InnoDB engine.
Some people code shows they catch the Exception and do rollback. But I did not since the commit() is never executed if Exception is thrown. Then this let me wonder why does it need to rollback at all.
My assumption is if there are multiple queries to perform at once like the following:
public function replaceAllWith(array $list):void{
$this->con->begin_transaction();
try{
//do both previous queries at once
$this->deleteAll();
$this->insert($list);
$this->con->commit();
}catch(Exception $e){
//error
$this->con->rollback();
}
}
but then it still cannot rollback because each method/function is done and committed by different method and different transaction.
Second Question: Do I do each transaction in each method call, or only invoke transaction in the 'boss/parent' function which only call and coordinate 'subordinate/helper' functions to do work?
Not sure if I'm asking the right questions.
Database Transaction is confusing, many online resources show only the procedural process (commit only or then immediately rollback which make no differences to normal direct query) without OO concepts involved. Please any pros guide and give some advice. tq.

Handling PDO and Statement Exceptions

We have been going through some old code of ours and we have found some code that looks something like:
try
{
$stmt = $db->prepare($query);
$stmt->bindvalue(1, $id, PDO:ARAM_INT);
$stmt->execute();
$row = $stmt->fetchColumn();
}
catch(PDOException $e)
{
echo "There was an issue with query: ";
print_r($db->errorInfo());
}
Which at first glance we thought looked fine (Even many answers on Stack Exchange give this as example code). Then we looked at the PHP documentation for the errorInfo function and it states that:
PDO::errorInfo() only retrieves error information for operations performed directly on
the database handle. If you create a PDOStatement object through
PDO:repare() or PDO::query() and invoke an error on the statement
handle, PDO::errorInfo() will not reflect the error from the statement
handle
Which, if we understand it correctly, means that if anything goes wrong in any of the statement operations we do, we will not actually print out the error code we are expecting after "There was an issue with the query: ". Is this correct?
In light of this, we started looking for the proper way to do this, we started by looking at the PDOException class documentation which suggests that we might do something like:
try
{
$stmt = $db->prepare($query);
$stmt->bindvalue(1, $id, PDO:ARAM_INT);
$stmt->execute();
$row = $stmt->fetchColumn();
}
catch(PDOException $e)
{
echo "There was an issue with query: ";
print_r($e->errorInfo());
}
My questions are:
Is the above way the proper way of doing this? If not, what IS the proper way of doing it?
Is there any more useful information avaliable by using $db->errorInfo() and $db->errorCode ( or $stmt->errorInfo and $stmt->errorCode ) beyond what you can see from PDOException?
If there IS anything more detailed available-and-useful from those detailed calls, then is there a way to differentiate, by examining the PDOException, whether it was thrown by PDO or by PDOStatement?
The exception may be thrown by either $db->prepare or any of the $stmt operations. You do not know whence the error originated, so you should not guess. The exception itself contains all the information about what went wrong, so yes, consulting it and only it is the only sensible thing to do.
Moreover, it's usually nonsense to try..catch directly around the database call (unless you have a clear plan about something you want to do if this particular database operation fails). In your example, you're merely outputting the error and are continuing as if nothing happened. That's not sane error handling. Exceptions explicitly exist to abort and jump ship in case of a severe error, which means the part of your code which should actually be catching the exception should live several layers up and not have access to $db or $stmt at all (because it's in a different scope). Perhaps you should't be catching the exception at all and have it terminate your entire script (again, unless you expected an error to occur and have a clear plan how to handle it and how to recover your application into a known state). So looking only at the information in the exception itself is, again, the only sensible thing to do.
If there IS anything more detailed available-and-useful from those detailed calls, then is there a way to differentiate, by examining the PDOException, whether it was thrown by PDO or by PDOStatement?
This is only useful if, again, you have any sort of plan for recovery and that plan differs depending on where the error occurred. First of all, I doubt that, but if that's indeed the case, then you'd do something like this:
try {
$stmt = $db->prepare($query);
} catch (PDOException $e) {
// do something to save the day
}
try {
$stmt->bindValue(...)
..
} catch (PDOException $e) {
// save the day another way
}
In other words, you isolate the try..catch statements to smaller parts of your code that you want to distinguish. But, really... if $db->prepare failed, what are you going to do? You can't continue with the rest of the code either way. And what are you going to do differently than if a $stmt method failed? As one atomic unit, you were unable to query the database, period.
The PDOException::$code will give you the more detailed SQLState error code, which may tell you something useful (e.g. unique constraint violation), which is useful information to work with on occasion. But when inspecting that it's pretty irrelevant which specific line threw the error.

Handling errors in PDO when MySQL query fails

I've recently changed all my mysql.* to PDO. I've been having a few issues and trying to get used to it.
My question is how to properly call an error when there is an issue in the sql statement. Normally I wouldn't use try, catch but is there another alternative?
Suppose I have the following:
private function init()
{
$query = $this->_PDO->prepare("SELECT * FROM here WHR name='john'");
if($query->execute())
{
$this->_sql_rows = $query->rowCount();
}
else
{
print_r($query->errorInfo());
}
}
This checks whether the execute method worked and if not, output errors. In this case it should as there is a spelling mistake. Normally when I do this, I never see any errors come out and must always use the method above to output an error. Is this a reliable and appropriate way of handling such errors?
Nope, it is not.
Just like almost every PHP user, you have quite vague and uncertain idea on error handling. And the code mostly looks like a dummy code from sandbox example. Speaking of your current approach, frankly - it's just terrible. On a live site it will only scare an innocent user, while webmaster would have no idea what's going on.
So, first of all you have to make your mind what is error handling you are looking for.
There are different possible scenarios, but most convenient is just to follow the way PHP handles all other errors. To do that just set PDO in exception mode. This way you'll be always notified of all the errors occurred. So - just add this line right after connect.
$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
and thus your function become short and clean
private function checkUser($user)
{
$stmt = $this->_PDO->prepare("SELECT 1 FROM users WHERE name=?");
$stmt->execute(array($user));
return $stmt->fetchColumn();
}

Custom mysqli prepare function

I'm doing my first own database class at the moment and currently I'm doing the prepare function.
What this function does is to take in an SQL-query and then an array containing the variables for the statement. I'm having problems with binding the parameters to the statement.
This is how the function looks like now
public function prepare($query, $var) {
$types = '';
foreach($var as $a) {
$type = gettype($a);
switch($type) {
case 'integer':
$types .= 'i';
break;
case 'string':
$types .= 's';
break;
default:
exit('Invalid type: ' . $a .' ' . $type . '(' . count($a) . ')');
break;
}
}
$stmt = self::$connection->prepare($query);
$stmt->bind_param($types, $var); // How do I do here??
$stmt->execute();
$result = $stmt->get_result();
while($row = $result->fetch_assoc()) {
print_r($row);
}
}
Everything works as I want it to (I know this function could do some polishing but it does what it needs to do). I have commented the line where I'm having trouble figuring out what to do. $var is an array and if I recall things correctly the variables needs to be passed seperately seperated with a comma. This is where I'm clueless.
The very idea of your own database class is great.
Very few people here do share it, for some reason prefers raw api calls all over their code.
So, you're taking great step further.
However, here are some objections:
Don't use mysqli for your first own database class if you're going to use native prepared statements.
Use PDO instead. It will save you a ton of headaches.
Despite of the fact this function works all right for you, it makes not much sense:
switch($type) code block is useless. Mysql can understand every scalar value as a string - so you can bind every value as s with no problem.
most integers coming from the client side have string type anyway.
there are some legitimate types like float or NULL or object that can return a string. So, automation won't work here. If you want to distinguish different types, you have to implement type hinted placeholders instead.
Never use exit in your scripts. throw new Exception('put here your error message') instead.
This is not actually a prepare function as it does execute as well. So, give it more generic name
But now to your problem
It is direct consequence of using mysqli. It is a nightmare when dealing with prepared statements. Not even only with binding but with retrieving your data as well (because get_result() works not everywhere, and after creating your application locally you will find it doesn't work on the shared hosting). You can make yourself an idea looking at this bunch of code served for this very purpose - to bind dynamical number of variables.
So, just keep away from mysqli as far as as you could.
With PDO your function will be as simple as this
public function query($query, $var=array())
{
$stmt = self::$connection->prepare($query);
$stmt->execute($var);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
// and then used
$data = $db->("SELECT 1");
print_r($data);
You can take a look at my class to get some ideas.
Feel free to ask any questions regarding database classes - it's great thing, and I am glad you're going this way.
To answer questions from the comments.
To let you know, you're not the only user of the site. There are also some innocent visitors. Unlike you, they don't need no error messages, and they get scared with some strange behavior and lack of familiar controls.
exit() with error message does many evil things
throws an error message out, revealing some system internals to a potential attacker
scaring innocent user with strange message. "What's that? Who is invalid? Is it mine fault or what? Or may be it's a virus? Better leave the site at all" - thinks them.
killing the script in the middle, so it may cause torn design (or no design at all) shown
killing the script irrecoverably. while thrown exception can be caught and gracefully handled
When connecting to PDO, no need to throw anything here as the exception already thrown by PDO. So, get rid of try ... catch and just leave it one line:
self::$connection = new PDO($dsn, $user, $pass);
then create a custom exception handler to work in 2 modes:
on a development server let it throw the message on the screen.
on a live server let it log the error while showing generic error page to the user
Use try ... catch only if you don't want to whole script die - i.e. to handle recoverable issue only.
By the way, PDO don't throw exception on connect by default. You have to set it manually:
$opt = array( PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION );
self::$connection = new PDO($dsn, $user, $pass, $opt);

PHP and MySQL ACID Program design

a while ago I completely recoded my application so that MySQL would perform in an ACID way.
At the very top level of all functions I do something like this:
try{
$db->begin();
dosomething($_SESSION['userid']);
$db->commit();
}catch(advException $e){
$eCode = $e->getCode();
$eMessage = $e->getMessage();
# Success
if ($eCode == 0){
$db->commit();
}else{
$db->rollback();
}
}
Within the function 'dosomething' I have the Exceptions thrown to users like:
throw new Exception('There was a problem.',1);
or
throw new Exception('You have successfully done that!', 0);
So that I can control the flow of the program. If theres a problem then roll back everything that happened and if everything was good then commit it. It's all worked quite great but there's just one flaw that I've come across so far.
I added Exception logging so I can see when there are issues that users face. But the problem is, if the table that logs the errors is InnoDB then it's also included in the transaction and will rollback if theres a problem so no errors are stored.
To get around this I basically just made the Error logging table MyISAM so when a rollback is done, the changes are still there.
Now I'm thinking of other bits I'd like to keep out of the transaction, like sending a mail within my application to the admin to help alert of problems.
Is there any sort of way for me to not include a database insert within the parent transaction? Have I taken a bad route in terms of Application/DB design and is there any other way I could have handled this?
Thanks, Dominic
It is not good idea to throw an Exception on success.
You have to do DB insert after previous rollback was called.
catch (Exception $e) {
$db->rollback();
Log::insert('Error: ' . $e->getMessage());
}
Try to use Logger to control your program. It is more flexible way.
Use return codes for successful operation, and Exceptions to specify exceptional situations (such as severe errors, etc.). As for your specific issue, I'd recommend having a separate database for logging if you choose to use the rollback strategy.

Categories