I start a new slim project with twig support, I want to use PDO as database layer, what is the way to integrate? or just use GLOBAL $db?
Thanks.
i'm against mutable globals. you could use a simple factory method returning your connection:
function db() {
static $db = null;
if (null === $db)
$db = new PDO(...);
return $db;
}
if you're about to deal with more of such problems, consider a registry (see this one).
Regarding mutable globals:
I'm against such things because, well, they are easy to be mutated.
What happens if, during the flow of a program, circumstances change unexpectedly?
Things break.
If you're the only one working on the project, globals might be ok.
But from my experience, even in this scenario, keeping track of global variables becomes a hassle and offends logical organisation of your code.
What if you come to the point of sharing your code with other who will overwrite such global?
Also, globals contradict the methodology of data encapsulation in the context of seperation of concerns.
All this plays into the big, wide science of software architecture.
Additionally, using a factory in this specific case ensures that you only keep one single reference to the connection to your database.
Look this project: Slim-PDO (github)
Documentation is here
Install via composer: $ composer require slim/pdo
Simple usage:
$app->container->singleton('database', function () use ($app) {
return new \Slim\PDO\Database($app->config('db.dsn'), $app->config('db.usr'), $app->config('db.pwd'));
});
// SELECT * FROM users WHERE id = ?
$selectStatement = $app->database->select()
->from('users')
->where('id', '=', 1234);
$stmt = $selectStatement->execute();
$data = $stmt->fetch();
// INSERT INTO users ( id , usr , pwd ) VALUES ( ? , ? , ? )
$insertStatement = $app->database->insert(array('id', 'usr', 'pwd'))
->into('users')
->values(array(1234, 'your_username', 'your_password'));
$insertId = $insertStatement->execute(false);
Related
Method 1
global $database;
$user = new stdClass;
$user->id = NULL;
$user->name = $name;
$user->username = $username;
if (!$database->insertObject( '#__users', $user, 'id' )) {
echo $database->stderr();
return false;
}
return $user->id;
Method 2
$db = JFactory::getDBO();
$query = $db->getQuery(true);
$query->insert($db->nameQuote('#__users'));
$query->set($db->nameQuote('name').'='.$db->quote($$name).','.
$db->nameQuote('username').'='.$db->quote($username));
$db->setQuery( $query );
$db->query();
$new_id = $db->insertId();
I'm using Joomla and use both above queries which lets me the get the work done. My problem is what is the difference between method 1 and method 2? What's the industry standard? Are there any specific situation I should use above methods? Which one is better and why?
Thanks
Method 1 (M1) and Method 2 (M2) are both valid Joomla! 2.5 mechanisms, M1 uses a semi automated method and is probably more commonly used where you are already working with objects.
M2 is obviously a more specific set of steps but both will work the abstraction provided by JDatabase et. al. to isolate you from the database server.
Having said that M1 is only used in a few places (literally) in the entire Joomla! 2.5 installation (not counting /libararies/joomla/database/) while M2 is used extensively
The only odd thing is the use of a global in M1, normally Joomla! coding standards eschew the use of globals preferring rather to use OOP or factory patterns instead. So, something like this:
$database = JFactory::getDBO();
instead of using the global reference.
At the moment I'm using php and apc in my mvc based site, it's a custom built simple mvc so I'm trying to build it to fit my needs.
However I'm unsure where is the preferred location in which to handle caching?
I have two choices (I think) .. either do all caching in the various controllers which would mean objects were stored in the cache, or store returned data from queries in the cache inside the method:
Controller Example:
function showPage() {
$pageOb = new Page();
$key = md5('pageOb->getQuery()');
if(!$data = apc_fetch($key)){
$data = $pageOb->getQuery();
apc_add($key, $data, 600);
}
require 'template.php';
}
Method Example:
function getQuery(){
$sql = "SELECT * FROM table";
$key = md5('query'.$sql);
if(!$data = apc_fetch($key)){
$core = Connect::getInstance();
$stmt = $core->dbh->prepare($sql);
$stmt->execute();
$data = $stmt->fetchAll();
apc_add($key, $data, 600);
}
return $data;
}
It kinda depends on how you understand and implement the Model layer. This would be how I would write the cache-related code in the Service level objects:
$user = $this->domainObjectFactory->build('User');
$user->setId(42);
if ( !$this->cacheMapper->fetch('User', $user) )
{
$mapper = $this->mapperFactory->build('User');
$mapper->fetch($user);
}
If you do not understand the terms this comment (skip to "side notes") might help. It would take too long to repeat the whole thing again.
As I understand it, the cache itself is just a different form of storage. And thus it is just another part of Data Source Layer(where mappers, DAOs and similar structures come from).
You shouldn't have data-model concerns bubbling up into your Controller. This principle is encapsulated in SRP: http://en.wikipedia.org/wiki/Single_responsibility_principle
Your second solution is better, but it would be improved by further abstracting the retrieval of data from the source of data. Here is a good reference article on the subject, although the language being used is different the patterns still hold: http://www.alachisoft.com/resources/articles/domain-objects-caching-pattern.html
I'm currently creating a simple CMS for my small website in PHP5. This is my first 'larger' project in PHP. First I'm creating the needed classes that would simplify me the work a little bit, and there I'm stuck. I need your opinions about the following function inside my UserInfo class:
public function setUser($id) {
if(!isset($id)) {
return false;
}
session_start();
$conn = new mysql($_SESSION['DBCONNINFO']);
$sql = "SELECT
usr.ID,
usr.USERNAME as TUSERNAME,
usr.FIRST_NAME,
usr.LAST_NAME,
usr.PHONE,
usr.MOBILE,
usr.EMAIL,
usr.ADDITIONAL_INFO,
usr.LAST_LOGIN_DATE,
usr.USER_GROUP_ID
FROM cms_users usr
WHERE usr.id = " . $id;
$result = $conn->query_cust($sql);
$conn=null;
foreach ($result as $row) {
$this->id = $row['usr']['ID'];
$this->username = $row['usr']['TUSERNAME'];
$this->firstname = $row['usr']['FIRST_NAME'];
$this->lastname = $row['usr']['LAST_NAME'];
$this->phone = $row['usr']['PHONE'];
$this->mobile = $row['usr']['MOBILE'];
$this->email = $row['usr']['EMAIL'];
$this->additional_info = $row['usr']['ADDITIONAL_INFO'];
$this->last_login_date = $row['usr']['LAST_LOGIN_DATE'];
$this->user_group = $row['usr']['USER_GROUP_ID'];
}
return true;
}
Am I doing it the right way, I'm not talking about the syntax, for now I focus on the class structure, design and best practices - any opinion would be appreciated.
Could I call the session_start(), for example, in the class constructor and use the vars inside it without calling it each time in a function !?
Should I close the DB connection via the close() function or is $conn=null acceptable !?
Is it a bad practice to store the database information in the session class !? If yes, where to store it as a 'global' variable - $_GLOBAL !?
If there is a 'PHP bes practice class structures in 5 minutes for dummies' please notify me :)
Thanks in advance.
Use define to define all of your constants.
For example:
define('DBCONNINFO', "something");
Also you only have to call session_start() once, it can be done anywhere in your script.
It doesn't make a lot of sense to fetch the data from the database for every request if you're using sessions anyway. In which case, why is session_start() being called inside the setUser() method?
And we can't really comment on the class structure when you've only provided a single method.
Also, since the representation of data leaving PHP should be appropriate to the substrate where that data is going (to prevent SQL injection, email header injection, CSS....) then it's good practice to defer changes to the representation of the data until just before the point where it leaves PHP. e.g.
$sql = "SELECT
....
WHERE usr.id = " . mysql_real_escape_string($id);
(or use bound parameters)
However since users are usually identify themselves by their username rather than their userid, it rather implies that $id came from somewhere other than user supplied data - in which case where? And why are you using this as the identifier when you've already got an identifier for the session (which is where this data should be getting stored).
Or do you want to use this class for processing data relating to users other than the user of the current session - in which case there is no way that there should be a session_start() in there.
Sorry - but this is not well thought out code and not a well presented question.
BTW, setting the connection to null does not close the database connection.
I would like to get the exact SQL INSERT query that Doctrine generates when an object's save() method is called.
Preferably, I would like to get it in the postSave() event of the model and log it in a txt file.
For instance:
<?php
$user = new User(); // A Doctrine Model with timestampable behavior enabled
$user->first_name = 'Manny';
$user->last_name = 'Calavera';
$user->save();
?>
I want to get/log the following SQL query:
INSERT INTO user (first_name, last_name, created_at, updated_at) VALUES ('Manny', 'Calavera', '2010-08-03 12:00:00', '2010-08-03 12:00:00');
The background for needing this, is that I wish to mass-import later the data by parsing the txt file.
I don't think there is any easy way to do this since the behaviour of save() varies depending on a few different things (if you're inserting/updating).
If you had created a doctrine query object, then you can call the getSqlQuery() method like this:
$q = Doctrine_Query::create()
->select('u.id')
->from('User u');
echo $q->getSqlQuery();
but the save() is a method, not an object so this won't work. I think you'll have to hack in some code to detect whether you're inserting or updating and build a query to log on the fly and then use save().
I know this suggestion is not ideal because it is not logging 'exactly' what save() is doing but for the purposes you stated it should still work just as well.
Why don't you try to see how the symfony developers do it? Check their WebDebug Toolbar for Doctrine here. The WebDebug Toolbar outputs all query that you do on a page.
There's a hook on DoctrineEvent, and I think you can modify the code to do what you want. Check the getDoctrineEvents and getSqlLogs method.
Hope this helps. If you need further explanation, please write it in comment, I'll try my best to explain.
Take a look here: http://www.phpandstuff.com/articles/codeigniter-doctrine-scratch-day-8-hooks-profiling-dql and go to the section headed Profiling with Doctrine and Creating a Profiler Hook. Altough this is for the use with the CodeIgniter framework, it can be easy adopted to your own environment since the code has no dependencies to the framework.
You basically want to set up a Connection Profiler and let it write all queries to a file. I suggest appending all queries to the file to have a better "log"-like feeling. Don't get confused by many framework talk inside the articles. The examples work very well (with a little understanding and copy&pasting) in other scenarios.
you can use the profiler from the sfDoctrineDatabase class. Use the getQueryExecutionEvents to grab all queries.
$databaseManager = sfContext::getInstance()->getDatabaseManager();
if ($databaseManager) {
foreach ($databaseManager->getNames() as $name) {
$database = $databaseManager->getDatabase($name);
if ($database instanceof sfDoctrineDatabase && $profiler = $database->getProfiler()) {
foreach ($profiler->getQueryExecutionEvents() as $event) {
$conn = $event->getInvoker() instanceof Doctrine_Connection ? $event->getInvoker() : $event->getInvoker()->getConnection();
$params = sfDoctrineConnectionProfiler::fixParams($event->getParams());
$query = $event->getQuery() ;
foreach ($params as $param) {
$param = htmlspecialchars($param, ENT_QUOTES, sfConfig::get('sf_charset'));
$query = join(var_export(is_scalar($param) ? $param : (string) $param, true), explode('?', $query, 2));
}
// log the $query here, or use the symfony's logger
// sfContext::getInstance()->getLogger()->debug(sprintf('Query Run !! %s ', $query));
}
}
}
}
dont forget to join the query with the parameters (so it will replace ? with the values )
:D
You can convert you DQL query to SQL by this function: http://tokarchuk.ru/2010/12/dql-query-to-raw-sql/
I was looking around for more "correct" login/logout portions of code, and found this one:
http://snipplr.com/view/1079/auth/
I got two questions though that stop me from using it.
1: How would I instantiate the class, and use it in my script?(I know PHP but am just befuddled for some reason)
2: there's the following lines:
global $db;
$db->query("sql here...");
How on earth does that make a database object? I think maybe I should create an object like $db = mysql_connect(...) outside the script, and global is calling it from outside the class?
If I know how to call this class, others will seem like a breeze, this is really helpful to me!
That code is dangerous, you should not use it in it's current state.
It blindly trusts $_COOKIE and creates SQL queries using string concatenation, without properly escaping input. Malicious users can easily perform SQL injection on any application using that class.
The presence of the var keyword, the presence of a constructor using the same name as the class and the lack of access control keywords in front of the methods tells me that the code was written for PHP4, not PHP5. It will still run, though
Both of these are fixable problems. First, let's address your questions.
The code written by #Delan Azabani is a good example of how to use the class.
The code is confusing. At first, it looks like the code is using a real database object instead of the old, low-level functions provided by the mysql extension. But then it goes and calls functions from the mysql extension! It's probably someone's custom-written wrapper. The code isn't actually creating a database object, it's simply referencing the value of $db in the global scope, as you suspect.
You've mentioned that you're using the mysql extension. I urge you to reconsider and use PDO or mysqli instead. Not only will this class work with either (though with some changes to mitigate the glaring security hole), but your own code will be better off using the more modern, safer techniques used in PDO and mysqli.
Let's fix the login method, using PDO.
public function login($username, $password) {
global $db;
$sth = $db->prepare("SELECT user_id FROM users WHERE username = ? AND password = ?");
$sth->execute(array($username, $password));
if($sth->rowCount() == 1) {
$this->user_id = $sth->fetchColumn();
...
Let's go over what changed. First, we added query placeholders, those are the question marks. The prepare method returns a statement handle, which you're already probably familiar with. Next, we tell the prepared statement to run itself using the execute method, passing an array of variables. The variables will be automatically escaped for you and then inserted into the query in place of the placeholders.
After the query has run, we use rowCount to pull back the number of matching rows. We want exactly one. Because we're pulling back the first column of the first row, we then use fetchColumn to grab just that bit of data.
The rest of the login method is just fine.
The check method needs similar fixing up.
public function check($username, $password) {
global $db;
$sth = $db->prepare("SELECT user_id, password FROM users WHERE username = ?");
$sth->execute(array($username));
if($sth->rowCount() == 1) {
list($db_user_id, $db_password) = $sth->fetch(PDO::FETCH_NUM);
if(md5($db_password . $this->salt) == $password) {
$this->user_id = $db_user_id;
...
Once again, we prepare a query with placeholders, then execute with the actual variables. Again we want only one row, but this time we want everything in the row. We'll use PDO::FETCH_NUM to tell fetch that we want a numerically indexed array, then we'll take the two resulting entries in the array and stuff them in $db_user_id and $db_password, using them where the old code called mysql_result. PDO doesn't let you pick and choose different columns from the same row using fetchColumn.
If you wanted to use the mysqli extension instead, there's a bit more work to be done. I hope I've convinced you to use PDO instead, though!
The rest of the class is, eh, adequate. Make sure to adjust the $domain class variable at the top to match your actual domain name, it's used in the cookies.
global $db just allows the global database pointer $db to be accessed from inside the class method. You need to create the database by running mysql_connect and mysql_select_db. The entire class is in class Auth, so instantiate an Auth object before doing anything else (well, after connecting to the database, of course).
You can then call the methods inside your Auth object. For example, if a login form submits to login-post.php, you can have this code inside:
$m = new Auth();
if ($m -> login($_POST['u'], $_POST['p'])) {
header('Location: /');
exit;
} else {
header('Location: login?wrong=1');
exit;
}
Looks like the code you posted is just assuming you have a variable defined in your global scope called $db that looks like a wrapper class to the native PHP MySQL resource. So yes, you should create $db outside in the global scope (i.e. not inside a class), but it's expecting a custom class.
I did a little searching, and it looks like he's using THIS class: http://snipplr.com/view/27966/class-db/
It asks you to create an ini file called "crunksauce.ini" (I kid you not) in the same directory as the executing script, and contains configuration variables. The config file should like this:
database = <your database name>
host = <your db host>
user = <your db user>
pass = <your db password>
After creating the config file, you can create the db object like this:
$db = new Db();