PHP/Mysql - Dynamically selecting data? - php

I'm a bit new to OOP, but i've been playing with it for about a month now. Usually, i create a class called Mysql which has a __construct function that connects to a database directly. And after that i have lots of different functions that gets or inserts data into different tables.
On the bus home today, i began thinking and i came up with a brilliant idea that would make it less cluttered. My idea is to use one single function that selects data (and one for inserting), and depending on how the query that's passed in looks, it will select different data from different tables. Nice, right?
But i'm kind of stuck here. I'm not sure at all how to achieve this. I've got a small clue how it could work, but i don't know how i would bind the results, or fetch them into an array. The query will be created in another method, and then be passed into the select/insert function within the Mysql class.
I drew a "sketch" on how i think it may work. Here it is:
Of course, the function below will be placed in the Mysql class, and will already have connection to a database.
// This is an example query that could be passed in.
$query = "SELECT * FROM table WHERE id=150";
function select_data($query) {
if ( $smtp = $this->conn->prepare($query) ) {
$smtp->execute();
$smtp->bind_results(What happens here?);
if ( $smtp->fetch() ) {
foreach ( fetched_row? as $key => $value ) {
$return[] = $key => $value;
}
return $return;
}
else return NULL;
}
else return $this->conn->error;
}
Thanks a lot to anyone who can show me how this can be achieved.

You have more options to use in PHP and they has their own specifics.
I can recommend some ORM
like Doctrine because of ease of use, stability, community and most importantly efectivity.
You can use it as easy as:
$q = Doctrine_Query::create()
->select('u.username, p.phone')
->from('User u')
->leftJoin('u.Phonenumbers p');
$users = $q->fetchArray();
or:
// Delete phonenumbers for user id = 5
$deleted = Doctrine_Query::create()
->delete()
->from('Phonenumber')
->andWhere('user_id = 5')
->execute();
// Make all usernames lowercase
Doctrine_Query::create()
->update('User u')
->set('u.username', 'LOWER(u.username)')
->execute();
// 'like' condition
$q = Doctrine_Query::create()
->from('User u')
->where('u.username LIKE ?', '%jwage%');
$users = $q->fetchArray();

I think you are running into problems when you need related data. In other words, when an object of yours has a property which is another object that data should also be gathered and dynamically filled. I once came pretty far but when stuff like INNER, LEFT and RIGHT joins come accross you'll think twice about going further ;)
About bind_results:http://php.net/manual/en/mysqli-stmt.bind-result.php
(Maybe slightly off topic; SMTP? That's a mailprotocol, are you sure you don't mean MySQLi's STMT?)

For reference, PDO already does a lot of what you seem to want to do. It even has a fetchAll method that returns an array of rows, much like your function does. You don't need to bind anything in order to use it, unless you have parameters in your query string (and of course, values to bind to those parameters).
Check out the PDO documentation, and see if that doesn't fit your needs. Particularly PDOStatement->fetchAll().

Related

Fetch data from two diff databases in CI format with cross conditions

$this->fifo_db->select('u.name,d.title');
$this->fifo_db->from('tbl_user u');
$this->db->join('designations d','d.id = u.designationid','left');
$this->db->where(array('u.designationid > ' => 1,'d.salary > ' => '20000'));
$query = $this->fifo_db->get();
return $query->result_array();
Trying to fetch data in CI format using join on two tables from different DBs, getting error on the same.
I think you need to prefix the table references with the name of the database.
This code doesn't use Query Builder or table aliases for a couple of reasons.
To prove the concept of multiple databases it's easier to write the
complete query string.
I'm not sure how well aliases will work along
with the db name prefixes, nor do I know how to alias when including the db name.
I check the return of db->query because it will be FALSE if the query causes an error. If $query is FALSE a call to $query->result_array() would cause an exception to be thrown.
You need the actual database names. I've made up the names dbU and dbD.
$sql = "SELECT dbU.tbl_user.name, dbD.designations.title FROM dbU.tbl_user
LEFT JOIN dbD.designations ON dbD.designations.id = dbU.tbl_user.designationid
WHERE dbU.tbl_user.designationid > 1 AND dbD.designations.salary > 20000";
$query = $this->db->query($sql);
if($query !== FALSE)
{
return $query->result_array();
}
return NULL;
I don't think it will matter which CI database object (fifo_db or db) you use. I'm pretty sure the databases need to be on the same network server.
If the above works you can add the aliases and then, if that works, refactor it to use Query Builder. Frankly, I don't see any reason for QB based on the query shown. But you may have more complicated needs you're not sharing.

How to get database resource instead of array from Laravel Query Builder?

When I execute a PDO statement, internally a result set is stored, and I can use ->fetch() to get a row from the result.
If I wanted to convert the entire result to an array, I could do ->fetchAll().
With Laravel, in the Query Builder docs, I only see a way to get an array result from executing the query.
// example query, ~30,000 rows
$scores = DB::table("highscores")
->select("player_id", "score")
->orderBy("score", "desc")
->get();
var_dump($scores);
// array of 30,000 items...
// unbelievable ...
Is there any way to get a result set from Query Builder like PDO would return? Or am I forced to wait for Query Builder to build an entire array before it returns a value ?
Perhaps some sort of ->lazyGet(), or ->getCursor() ?
If this is the case, I can't help but see Query Builder is an extremely short-sighted tool. Imagine a query that selects 30,000 rows. With PDO I can step through row by row, one ->fetch() at a time, and handle the data with very little additional memory consumption.
Laravel Query Builder on the other hand? "Memory management, huh? It's fine, just load 30,000 rows into one big array!"
PS yes, I know I can use ->skip() and ->take() to offset and limit the result set. In most cases, this would work fine, as presenting a user with 30,000 rows is not even usable. If I want to generate large reports, I can see PHP running out of memory easily.
After #deczo pointed out an undocumented function ->chunk(), I dug around in the source code a bit. What I found is that ->chunk() is a convenience wrapper around multiplying my query into several queries queries but automatically populating the ->step($m)->take($n) parameters. If I wanted to build my own iterator, using ->chunk with my data set, I'd end up with 30,000 queries on my DB instead of 1.
This doesn't really help, too, because ->chunk() takes a callback which forces me to couple my looping logic at the time I'm building the query. Even if the function was defined somewhere else, the query is going to happen in the controller, which should have little interest in the intricacies of my View or Presenter.
Digging a little further, I found that all Query Builder queries inevitably pass through \Illuminate\Database\Connection#run.
// https://github.com/laravel/framework/blob/3d1b38557afe0d09326d0b5a9ff6b5705bc67d29/src/Illuminate/Database/Connection.php#L262-L284
/**
* Run a select statement against the database.
*
* #param string $query
* #param array $bindings
* #return array
*/
public function select($query, $bindings = array())
{
return $this->run($query, $bindings, function($me, $query, $bindings)
{
if ($me->pretending()) return array();
// For select statements, we'll simply execute the query and return an array
// of the database result set. Each element in the array will be a single
// row from the database table, and will either be an array or objects.
$statement = $me->getReadPdo()->prepare($query);
$statement->execute($me->prepareBindings($bindings));
return $statement->fetchAll($me->getFetchMode());
});
}
See that nasty $statement->fetchAll near the bottom ?
That means arrays for everyone, always and forever; your wishes and dreams abstracted away into an unusable tool Laravel Query Builder.
I can't express the valley of my depression right now.
One thing I will say though is that the Laravel source code was at least organized and formatted nicely. Now let's get some good code in there!
Use chunk:
DB::table('highscores')
->select(...)
->orderBy(...)
->chunk($rowsNumber, function ($portion) {
foreach ($portion as $row) { // do whatever you like }
});
Obviously returned result will be just the same as calling get, so:
$portion; // array of stdObjects
// and for Eloquent models:
Model::chunk(100, function ($portion) {
$portion; // Collection of Models
});
Here is a way to use the laravel query builder for making the query, but to then use the underlying pdo fetch to loop over the record set which I believe will solve your problem - running one query and looping the record set so you don't run out of memory on 30k records.
This approach will use all the config stuff you setup in laravel so you don't have to config pdo separately.
You could also abstract out a method to make this easy to use that takes in the query builder object, and returns the record set (executed pdo statement), which you would then while loop over as below.
$qb = DB::table("highscores")
->select("player_id", "score")
->orderBy("score", "desc");
$connection = $qb->getConnection();
$pdo = $connection->getPdo();
$query = $qb->toSql();
$bindings = $qb->getBindings();
$statement = $pdo->prepare($query);
$statement->execute($bindings);
while ($row = $statement->fetch($connection->getFetchMode()))
{
// do stuff with $row
}

Codeigniter 2.1 - active record

I have reached dead end with the brain o.O. In DB I have two tables:
store_module->caffe_id, module_id, position, order
module->id_module, name, description, image
I have query where I take all modules for set ID (store_module table), and I need to get all modules which appear in this query (module_id). What I need to do?
This is the code (I am awake for 30+ hours and my brain is refusing to communicate with me, deadline is almost here, and this on of the last things I need to do. So, please help :D):
function mar_get_modules($id){
$q = $this->db->get_where('store_module', array('caffe_id' => $id));
$modules = $q->result_array();
}
Start simple, by using a regular query (if I guess right, you need a JOIN there).
This query should work:
$sql = "SELECT m.*,sm.* FROM module m
LEFT JOIN store_module sm ON sm.id_module = m.module_id
WHERE sm.caffe_id = ?";
return $this->db->query($sql, array($id))->result_array();
Now, you can transform it into an AR query:
$query = $this->db->select('module.*,store_module.*')
->from('module')
->join('store_module', 'store_module.id_module = module.module_id','left')
->where('store_module.caffe_id',$id)
->get();
return $query->result_array();
While AR is quicker sometimes, I usually prefer writing my queries "by hand", taking advantage of the binding to prevent SQL injections; it's a lot easier to see how things are working if you have a query fully laid under your eyes
Sasha,
In the function above, you are not returning anything. You'll need to update the 3rd line something to the effect of return $q->result_array();

Mysql - PHP different values searches

I know how to perform mysql searches using for example the WHERE word. But my problem is that i need to search on different values, but these can vary in number. For example:
I can search for 3 variables Name, LastName, Age
BUT
I in other search, i can look for 2 variables Name, Age.
Is there a way to perform a MYSQL search with the same script, no matter the quantity of values i search.??
Ot it is a better practice to "force" the search of a fixed amount of variables.??
Thanks.!
Roberto
IMHO, it is far better to limit the search to a fixed number of variables. That way you are answering a specific question for a specific reason, not trying to fit a general answer to your specific question. Limiting the search criteria makes the statement(s) easier to debug and benchmark for performance.
Hope this helps.
Just use a variable for your search parameters and inject that into your query. Just ensure that in the function/method you put the variable into the proper format (which will depend on how you select the different values.)
SELECT *
FROM db
$variable;
There will be no WHERE clause seen, unless it is passed your values (meaning you can use this same query for a general search of the db) without fear of having an empty/required $variable.
Your $variable when constructed would need to have to have the WHERE clause in it, then each value you add, insert it (in a loop perhaps) in the proper format.
Hope this makes sense, if not let me know and I will try to clarify. This is the same method most people use when paginating (except they put the variable in the LIMIT instead of the WHERE)
EDIT:
Also make sure to properly sanitize your variable before injection.
Simple example of dynamically building a query:
$conditions = array();
if (...) {
$conditions['age'] = $age;
}
if (...) {
$conditions['name'] = $name;
}
...
if (!$conditions) {
die('No conditions supplied');
}
// if you're still using the mysql_ functions and haven't done so before:
$conditions = array_map('mysql_real_escape_string', $conditions);
foreach ($conditions as $field => &$value) {
$value = "`$field` = '$value'";
}
$query = 'SELECT ... WHERE ' . join(' AND ', $conditions);
It's really not hard to dynamically cobble together the exact query you want to create. Just be careful you don't mess up the SQL syntax or open yourself to more injection vulnerabilities. You may want to look at database abstraction layers, which pretty much allow you to pass a $conditions array into a function which will construct the actual query from it, more or less the way it's done above.

Doctrine: How can I have a where clause included only when necessary in a query?

I want to use a single query to retrieve:
items of any categories (no filter applied);
only items of a single category (limited to a particular category);
For that purpose I should be able to write a Doctrine query which would include a where clause only when certain condition is met (eg. part of URL existing), otherwise, where clause is not included in the query.
Of course, i tried with using the If statement, but since doctrine query is chained, the error is thrown.
So i guess the solution might be some (to me unknown) way of writing doctrine queries in an unchained form (by not having each row started with "->" and also having each row of a query ending with semicolon ";")
That way the usage of IF statement would be possible i guess.
Or, maybe there's already some extremely simple solution to this matter?
Thanks for your reply!
I am unfamiliar with Codeigniter but can't you write something like this?
$q = Doctrine_Query::create()
->from('items');
if ($cat)
$q->where('category = ?', $cat);
In your model pass the condition for where as a parameter in a function.
In below example i am assuming the function name to be filter_query() and passing where condition as a parameter.
function filter_query($condition=''){
$this->db->select('*');
$this->db->from('TABLE NAME');
if($condition != ''){
$this->db->where('condition',$condition);
}
}
In above example i have used Codeigniter's Active Record Class.

Categories