PHP OOP Help - Work In Progress - php

I am right now trying to write a PHP class that executes database information. Yet I feel like I am doing something majorly wrong! It seems like what you would have to type in to get it to work is too much. Here is a example of what it looks like:
<?php
class database {
public $query_type = "";
public $database_name = "";
public $database_items_query = array();
public $database_where = "";
public function __construct($query_type, $database_name, $database_items_query, $database_where) {
$this->query_type = $query_type;
$this->query_name = $query_name;
$this->query_items_query = $query_items_query;
$this->query_where = $query_where;
}
public function database_query($query_type, $database_name, $database_items_query, $database_where) {
if ($query_type == "select") {
return $sqlquery = "SELECT ($database_items_query) FROM $database_name WHERE $database_where";
}
elseif ($query_type == "update") {
return $sqlquery = "UPDATE $databasename ";
}
}
}
$username = new database("");
?>
So for the beginning you would have to type in just this to get it to work the first function?
$username = new database("select","users","username","id");
So basically with what I have so far, what am I doing wrong? Sorry if this does not make sense :(

Ok I can see what you are trying to do, A constructor is normally used to "Create" a object. When the object is created then you have the opportunity to store values inside it so you dont have to
re-enter them, You only have to call a constructor once in a objects lifetime, and you can reuse all the methods multiple times.
soo... if you change your method declaration for database_query to something along these lines:
public function database_query($database_items_query = $this->database_items_query, $database_where = $this->query_where) {
$query_type = $this->query_type;
$database_name = $this->database_name;
Then you would be using the class variables as default values so your code would be something along the lines of
$columns = array("Name","Description");
$db = new database("SELECT","my_db",$columns,"1=1");
$db->database_query(); // returns name and description from all rows
$db->database_query(array("*") ); // returns all columns and rows
$db->database_query(array("*"),"id=7"); // returns all columns where id = 7
$other_db = new database("SELECT","my_other_db",$columns,"1=1");
$db->database_query(array("id") ); // calling database_query on first object instance
$other_db->database_query(array("*") ); // calling database_query on second object instance
When using objects/classes you have to remember that you can declare variables which belong to a given instance of the object so you can re
Dunno about you but when I was your age I was writing a fair bit of C, and to get introduced to OO I was described classes as being structs with function pointers inside.
If trying to learn OO I would reccomend taking a look at java since there is a lot of first principals OO stuff out there, then once comfortable with that style of code
taking what you have learned across to the language which you know a bit better (in your case PHP.)

Related

Querying database inside of PHP class?

so this is very simple.
Essentially, I'd like to connect the following class:
class mailManager{
function add($address){
$check = $db->query("SELECT * FROM mail_list WHERE email = '$address'");
$exist = mysqli_num_rows($check);
if($exist > 0){
return "Whoops! Your email is already registered with us.";
}else{
if($insert = $db->query("INSERT INTO mail_list (email, datetime) VALUES ('$address', DATE)")){
return "Success! You've joined the CSGO Earn family.";
} else {
return "Aw snap! There was a database communication error. Please try later.";
}
}
}
}
to the database.
However, nothing 'obvious' that I try seems to work and a google search has yielded no results that I can understand. Perhaps somebody could explain it simply for me?
Note: The SQL is there, but it doesn't do anything (obviously).
Regards
Your class does not have immediate access to your $db. You can do this a few different ways.
1) Pass $db into your public function __construct($db) and assign it to a class member like private $db; (most useful)
You then access it like $check = $this->db->query("..etc..");
2) Pass it in via a parameter to the add() function like so:
public function add($address, $db) {
}
3) Bring it in as a global variable: (easiest)
function add($address){
global $db;
$check = $db->query("SELECT * FROM mail_list WHERE email = '$address'");
/*
etc....
*/
}
I'd recommend learning how to do the first one as it will come in very handy when you have a number of database calls within your class.
Two more things regarding your code in the function.
1) You should use NOW() instead of DATE.
2) Since you are using the OOP implementation of mysqli, you should change mysqli_num_rows($check); to $check->num_rows();. I'm not sure if it will work the way you have it, but even if it does it's good practice to treat your objects uniformly.

Codeigniter mysql db connection with Amazon RDS failing

I am having the worst time with this after searching for hours and hours.
My connection to Amazon RDS credentials are correct, however, I cannot pull any data and I get a 500 error from the following.
I've made this in the most raw of coding to avoid any CI mistakes, On my model I have:
class Foreclosure_model extends CI_Model {
public function __construct() {
parent::__construct();
$this->db = $this->load->database('aws');
}
function get_sql_results($state,$county){
return $this->db->query("select State, County from foreclosure_properties where State = 'CA' AND County = 'del norte'");
}
}
Then on my controller I have:
$this->data['listings'] = $this->foreclosure_model->get_sql_results($state,$county);
on my aws connection information I have:
$db['aws']['hostname'] = 'XXXXXXXXXXXXX.us-west-2.rds.amazonaws.com';
$db['aws']['username'] = 'XXXXXXXX';
$db['aws']['password'] = 'XXXXXXXX';
$db['aws']['database'] = 'xxxxxx_xxxxxx';
$db['aws']['dbdriver'] = 'mysql';
$db['aws']['dbprefix'] = '';
$db['aws']['pconnect'] = TRUE;
$db['aws']['db_debug'] = TRUE;
$db['aws']['cache_on'] = FALSE;
$db['aws']['cachedir'] = '';
$db['aws']['char_set'] = 'utf8';
$db['aws']['dbcollat'] = 'utf8_general_ci';
$db['aws']['swap_pre'] = '';
$db['aws']['autoinit'] = TRUE;
$db['aws']['port'] = 3306;
I have tried doing this exact same connection directly with Navicat to test the sql connection and query, it works successfully. I cannot seem to get this to run in Codeigniter.
The result when run on Codeigniter is a 500 error.
Any assistance is greatly appreciated.
As you're using more than one connection, you'll want to pass in a second parameter of 'TRUE' to return the database object.
$this->db = $this->load->database('aws', TRUE);
If you don't pass this, then the database object is not returned and therefor you can't call any of the functions (such as get(), query() etc).
You can read more about multiple connections here:
https://ellislab.com/codeigniter/user-guide/database/connecting.html
Hope this helps - let me know how you get on!
Note: It may be worth mentioning it may make things easier if instead of assigning the database to $this->db, you chose another name such as $this->aws_db as then you're not overwriting the $this->db object for your default database. Probably not a big issue, but may make it easier to troubleshoot as your project grows.

duplicate return value php

$amnt = "1.00";
$from = "USD";
$to = "GBP";
/* Set up new DB object with the from/to currency */
$DBob = new database($from, $to);
$locFrom = $DBob->readLocation($from);
$locTo = $DBob->readLocation($to);
echo $locFrom . $locTo;
The echo return for both objects return the same value, for the $to variable... rather than returning two seperate queries in SQL. The queries are the same however one uses the $from currency code and the other uses the $to currency code
sql code:
public function readLocation($toFrom)
{
//establish a connection to the mysql database
$dbcon = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME) OR DIE ('Could not connect to Mysql database: '. mysqli_connect_error());
if ($toFrom == 'from')
{
//$substring1 = substr($this->fromcurr,0,2);//
$query2 = ("SELECT location AS loc FROM currency WHERE countrycode ="."'$this->fromcurr'");
}
elseif ($toFrom == 'to');
{
//$substring1 = substr($this->tocurr,0,2);//
$query2 = ("SELECT location AS loc FROM currency WHERE countrycode ="."'$this->tocurr'");
}
$result = mysqli_query($dbcon,$query2);
$resultR = mysqli_fetch_array($result);
mysqli_close($dbcon);
$queryResult = $resultR['loc'];
return $queryResult;
}
You have messed up the code of the function so it does not work. I highlight some points how you can improve it which will help you to not only remove the errors but to prevent similar errors in the future by changing how you write your code.
In this discussion, I leave the topic of proper value encoding aside which can result into flaws and defects (SQL inection). Just saying, you should add those on your own.
The main business of the function is to build a conditional SQL query and execute it. So first of all move the query building out of the function into a function of it's own:
private function buildReadLocationSQL($countrycode)
{
$SQLPattern = "SELECT location AS loc FROM currency WHERE countrycode = '%s'";
return sprintf($SQLPattern, $countrycode);
}
Then you need to find out what $countrycode should be. You are using two string values here to describe what you need. Why not name the functions instead?
public function readLocationFrom()
{
...
}
public function readLocationTo()
{
...
}
Having two functions here it's clear what they do and you can not make any mistakes when you invoke (call) them.
The missing part is to encapsulate the database connection and query. This makes sense anyway because you only want to connect to the database once your application runs and not on every query. Connecting to a database is expensive. Also this helps to extend your class later on by making the code more modular.
So let's change the database class a bit to make it store the connection (you don't need to explicitly close it either, PHP will do this for you when the script ends) and to offer a function to query the database:
class Database
{
private $connection;
private function connect()
{
$connection = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
if ($connection === FALSE)
{
throw new RuntimeException(sprintf('Could not connect to Mysql database: %s', mysqli_connect_error()));
}
$this->connection = $connection;
}
private function query($sql)
{
if (!$this->connection) $this->connect(); // lazy connect to the DB
$result = mysqli_query($this->connection, $sql);
if (!$result)
{
throw new RuntimeException(sprintf('Could not query the Mysql database: %s', mysqli_error($this->connection)));
}
return $result;
}
...
}
These two new private functions take care about the database specific connection and query jobs. As you add more and more functions to your database class over time, you can easily re-use them. Also those two are private (as the $connection member), because you want to hide the details from the rest of your application.
This now needs to be brought together with the readLocationFrom and readLocationTo functions to get it to work. As both functions do similar things, we create another private function that encapsulates both and which is similar to your original function logic.
However, this time, we take care that passed arguments contain only values that make sense (pre-condition checks). Also the function is private, so that it can not be called by other code which reduces the possibility of calling it wrong from outside of the class (it can't be called from the outside):
private function readLocationConditional($what)
{
switch($what)
{
case 'from':
$countrycode = $this->fromcurr;
break;
case 'to':
$countrycode = $this->tocurr;
break;
default:
throw new InvalidArgumentException(sprintf('Invalid value for $what ("%s").', $what));
}
$sql = $this->buildReadLocationSQL($countrycode);
$result = $this->query($sql);
$array = mysqli_fetch_array($result);
$field = 'loc';
if (!isset($array[$field]))
{
throw new UnexpectedValueException(sprintf('Row does not contain %s (contains %s)', $field, implode(',', array_keys($array))));
}
$location = $array[$field];
return $location;
}
So let's take a closer look what's done differently to the original function: The input values are validated and the conditional variable is assigned to $countrycode. This helps to keep things apart. In case some "undefined" value for $what is given, an exception is thrown. This will immediately notify you if you used this function wrongly.
Then the new buildReadLocationSQL function is used to create the SQL, the new query function is used to run the query. At the end, the data returned from the database is validated again and an exception is thrown if the needed field is missing. This will immediately notify you if something is wrong with the returned data.
This is now easy to bring together with the two new public functions which are actual wrappers for the new private function readLocationConditional. So let's put this all together:
public function readLocationFrom()
{
return $this->readLocationConditional('from');
}
public function readLocationTo()
{
return $this->readLocationConditional('to');
}
So now, you've rewritten the database class to make it more flexible and better functioning. Put it together and that's it.
The first key point here is that you check error conditions properly. I've used exceptions instead of die because I think it's more developer friendly. You can do this with die statements as well or with error values etc.. But Exceptions do not only tell you a message, but also where something happened, can be caught (you can't catch die) and can have a different type, so they carry much more helpful information if something goes wrong.
The second key point is that you simplify your code. The more simple your code flow is, the easier it is to code. Putting things that cover the same into functions of it's own is very productive. You can write them as you go on while you keep things separated. And another tip: The less parameters a function have (ideally: zero which is not always possible), the less you can do wrong and the less input values you need to verify.
Just to say: You originally did this somehow as well, but not completely. You had only one place where you checked for an error (the connection to the mysql database), but not for the query itself, not for the return value from the query and not for the function parameter. Putting everything into small functions of it's own, it's easier to see what to check for and handle the details.
Last but not least, the new usage, I made the names more speaking (nstd o abbr evrthng):
$amount = "1.00";
$currencyFrom = "USD";
$currencyTo = "GBP";
$db = new Database($currencyFrom, $currencyTo);
echo $db->readLocationFrom(), $db->readLocationTo();
Hope this helps. And don't fear to write some lines of code more as long as it helps to prevent errors (it's your code, use it as a tool for you) and with modularity in mind. In the end you will see that this is less code and less hassles. Looking for the actual part you made an error in is also a good place to start.
Discussion of errors; I've seen the following:
elseif ($toFrom == 'to');
^
If-blocks can be daunting, restructuring your code helps here. Reduce the use of if and especially elseif. Moving parts into functions of it's own helps to reduce complexity. Less complexity, less errors (we are humans).
$query2 = ("...");
^ ^
This parenthesis is superfluous, might be a sign for just copied over code from a previous function call. Take more care.
Just putting the finger on these errors doesn't show the whole picture and just fixing those won't get you a step further. So it's better to recognize the errors and to understand why they have been made and then to think about how to prevent them.
It looks like whatever this code is supposed to do (which isn't entirely clear), it's not being used correctly. Take a look at some important parts:
$from = "USD";
$to = "GBP";
later...
$locFrom = $DBob->readLocation($from);
$locTo = $DBob->readLocation($to);
and inside that function...
if($toFrom == 'from') {
//...
}
else if($toFrom == 'to'); {
//...
}
That conditional is never satisfied. The $toFrom function parameter (which is not a particularly intuitive variable name) is being set to "USD" and "GBP" with the two calls to the function. It's never equal to "from" or "to" as the conditional assumes.
So $query2 never even gets set to anything. This would lead me to believe that the behavior of this line is an error or undefined:
$result = mysqli_query($dbcon,$query2);
Better variable/function names may help lead you to the logical use of whatever this code is supposed to be doing. But as it stands right now, the function is assuming values that the calling code isn't supplying. If the function is going to assume those values, the very first thing it should do is check its input:
if (($toFrom != 'to') &&
($toFrom != 'from')) {
// Incorrect argument(s) supplied.
// Throw an error and do not proceed with the function
}

FlashBuilder 4.5 + PHP + mssql list output

I've created a web service with the following code:
class WebUser {
public $USERID;
}
class UseridService
{
public $username = "my_user";
public $password = "my_pw";
public $server = "my_remote_server";
public $databasename = "my_database";
public $tablename = "my_table";
function __construct ()
{
$this->con = mssql_connect($this->server, $this->username, $this->password) or die('Connection failed!');
mssql_select_db($this->databasename);
}
public function getUserid ()
{
$sql = "Select top 10 USERID FROM my_table";
$result = mssql_query($sql);
$rows = array();
while ($row = mssql_fetch_assoc($result))
{
$storage = new WebUser();
$storage->USERID = $row['USERID'];
$rows[] = $row;
}
mssql_close($this->con);
return $rows;
}
}
For now in flash builder 4.5, I want to output the top 10 userids into a List component in my canvas. I can assure everyone that the PHP webservice code I wrote works and returns an array of WebUser() objects with just the USERID string inside.
There is a lot of documentation online for MySQL and how they simply "drag and drop" the webservice into a list and it "magically" works. Despite trying to follow their conventions using MSSQL instead, I simply cannot get it working.
I was hoping if anyone can offer a piece of advice on what to do? Even if it's not an answer itself, does anyone know any online documentation that works specifically with Flashbuilder/PHP/MSSQL?
Go to the source! Adobe Documentation on setting this up:
http://www.adobe.com/devnet/flash-builder/articles/flashbuilder-php-part1.html
They walk you through hooking up a service in Flashbuilder, connecting your data objects, and displaying them.
I know what you're asking asks for MSSQL, but the database you use on the back-end doesn't matter. What matters is serializing your object from the server-side to the client-side (i.e. PHP to AS3) which means matching the objects on both ends or finding a way to convert them (i.e. JSON-encoded objects in a REST-based web service can be de-serialized quite smoothly using the as3core library as an example).

Is it best to make fewer calls to the database and output the results in an array?

I'm trying to create a more succinct way to make hundreds of db calls. Instead of writing the whole query out every time I wanted to output a single field, I tried to port the code into a class that did all the query work. This is the class I have so far:
class Listing {
/* Connect to the database */
private $mysql;
function __construct() {
$this->mysql = new mysqli(DB_LOC, DB_USER, DB_PASS, DB) or die('Could not connect');
}
function getListingInfo($l_id = "", $category = "", $subcategory = "", $username = "", $status = "active") {
$condition = "`status` = '$status'";
if (!empty($l_id)) $condition .= "AND `L_ID` = '$l_id'";
if (!empty($category)) $condition .= "AND `category` = '$category'";
if (!empty($subcategory)) $condition .= "AND `subcategory` = '$subcategory'";
if (!empty($username)) $condition .= "AND `username` = '$username'";
$result = $this->mysql->query("SELECT * FROM listing WHERE $condition") or die('Error fetching values');
$info = $result->fetch_object() or die('Could not create object');
return $info;
}
}
This makes it easy to access any info I want from a single row.
$listing = new Listing;
echo $listing->getListingInfo('','Books')->title;
This outputs the title of the first listing in the category "Books". But if I want to output the price of that listing, I have to make another call to getListingInfo(). This makes another query on the db and again returns only the first row.
This is much more succinct than writing the entire query each time, but I feel like I may be calling the db too often. Is there a better way to output the data from my class and still be succinct in accessing it (maybe outputting all the rows to an array and returning the array)? If yes, How?
Do you actually have a performance issue?
If your current setup works and doesn't suffer from performance issues, I wouldn't touch it.
This sort of DB access abstraction will likely become a maintenance issue and probably won't help performance.
Also, you're susceptible to SQL injection.
You should be able to store the whole object from the query into a variable and then access the single values from that object:
$object = $listing->getListingInfo('','Books');
$title = $object->title;
$price= $object->price;
But you can also use fetch_assoc() and return the whole assiciative array:
$array = $listing->getListingInfo('','Books');
$title = $object['title'];
$price= $object['price'];
This will give you the same results and also with only one query to the DB.
EDIT: If the getListingInfo() is the only function you should think of the following:
rename the function to prepareListingInfo() and within the function only prepare the query and store it in a class variable.
add a getNextListingInfo() function, which will return an object or associative array with the next row.
Using this new function, you can get every row that matches your query.
Either cache the result in an internal var
Or Comment it with a warning and explain to function users to copy the result in an var instead of calling it again and again with the same params
Yes, that would be calling the db too often.
A couple of solutions
1) put the listing info in a variable
2) cache the results in a hashmap or dictionary (be careful for memory leaks)

Categories