I have a question on what is the best way of implementing SQL queries in PHP classes. I want to keep the queries as low as possible.
This was my first attempt:
class NewsArticle
{
//attributes
private $newsArticleID;
//methodes
//constructoren
public function __construct($newsArticleID)
{
$this->newsArticleID = $newsArticleID;
}
//getters
public function getGeneralData()
{
$query = mysql_query("SELECT author, title, content, datetime, tags FROM news WHERE news_id = '$this->newsArticleID'");
$result = mysql_fetch_array($query);
$data = array(
'author' => $result['author'],
'title' => $result['title'],
'content' => $result['content'],
'datetime' => $result['datetime'],
'tags' => $result['tags']
);
return $data;
}
//setters
}
Now I'm wondering if it would be better to create a setter for generalData and for each item I retrieve from the database create a variable and assign the proper value. Then I could create a getter for each variable. So like this.
class NewsArticle
{
//attributen
private $newsArticleID;
private $author;
private $title;
// more variables
//methodes
//constructoren
public function __construct($newsArticleID)
{
$this->newsArticleID = $newsArticleID;
}
//getters
public function getAuthor()
{
return $this->author;
}
public function getTitle()
{
return $this->title;
}
//setters
public function setGeneralData()
{
$query = mysql_query("SELECT author, title, content, datetime, tags FROM news WHERE news_id = '$this->newsArticleID'");
$result = mysql_fetch_array($query);
$this->author = $result['author'];
$this->author = $result['author'];
//other variables
}
}
The point of using setters and getters is that by forcing the developer to use these you can implement more complex access mechanisms, for example setting a flag when a value is set so that your class knows to write it back into the database (although this is a rather bad example as the objective can be acheived without having to force access via a method).
BTW
news_id = '$this->newsArticleID'
No.
Numbers shouldn't be quoted (breaks the optimizer) and strings should be escaped at the point where they are used in a query.
My advice for you would be to start using PHP-PDO and stored procedures of MySQL. Although you will see a small time difference in using direct queries in php code and in using stored procedures (a difference of milliseconds), you will have less code to write in php. To my opinion that way you would have a more BALANCED application. Also use getData and setData functions that should return anything you want in a form of array or json (i think you will find this very interesting in case you want to use a browser's local storage).
Related
On my site at the beginning of every script I include a "bootstrap" script which queries a few things from the database, does some calculations and then loads the variables into constants that I define one by one.
Some examples are:
define("SITE_ID", $site_id); // $site_id is pulled from a field in the database
define("SITE_NAME", $site_name);
// pulled from a field in the same row as the above
define("STOCK_IDS", $stock_ids);
//computed array of stock id integers from a different query.
//I perform logic on the array after the query before putting it in the definition
define("ANALYTICS_ENABLED", false);
// this is something I define myself and isnt "pulled" from a database
Now, I have many functions on the site. One example function is get_stock_info. And it references the STOCK_IDS constant.
What I want to do is have a class which has the above constants in it and the get_stock_info function.
Would the best approach to be have an empty class "site", create an instance of it and then afterwards define the static variables above one by one? Or is that not a good way and should I move all of of my logic which pulls from the database and calculates SITE_ID, STOCK_IDS, ANALYTICS_ENABLED etc into the constructor instead?
Ultimately I want the class to contain all of the above info and then I would be able to use class methods such as site::get_stock_info() and those methods will have access to the constants via self:: or this.
There's a lot more I want to do than that but that would give me enough to figure the rest out.
I think this approach isn't the best. You should consider not using constants as your values aren't constant. For your case it is better to have a class with classic getters methods.
Something like this:
class SiteInfo
{
private $siteId;
private $siteName;
private $stockIds;
private $analyticsEnabled;
public function __construct()
{
// Results from the database
$results = $query->execute();
$this->siteId = $results['siteId'];
$this->siteName = $results['siteName'];
$this->stockIds = $results['stockIds'];
$this->analyticsEnabled = $results['analyticsEnabled'];
}
public function getSiteId()
{
return $this->siteId;
}
public function getSiteName()
{
return $this->siteName;
}
public function getStockIds()
{
return $this->stockIds;
}
public function isAnalyticsEnabled()
{
return $this->analyticsEnabled;
}
}
I know there are plenty of articles and questions about MVC and best practices, but I can't find a simple example like this:
Lets say I have to develop a web application in PHP, I want to do it following the MVC pattern (without framework).
The aplication should have a simple CRUD of books.
From the controller I want to get all the books in my store (that are persisted in a database).
How the model should be?
Something like this:
class Book {
private $title;
private $author;
public function __construct($title, $author)
{
$this->title = $title;
$this->author = $author;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
return this;
}
.
.
.
class BooksService{
public getBooks(){
//get data from database and return it
//by the way, what I return here, should by an array of Books objects?
}
public getOneBook($title){
//get data from database and store it into $the_title, $the_autor
$the_book = new Book($the_title, $the_autor);
return $the_book;
}
.
.
.
So I call it(from the controller) like that:
$book_service = new BooksService();
$all_books = $book_service->getBooks();
$one_book = $book_service->getOneBook('title');
Or maybe should be better have everything in the Books class, something like this:
class Book
{
private $title;
private $author;
//I set default arguments in order to create an 'empty book'...
public function __construct($title = null, $author = null)
{
$this->title = $title;
$this->author = $author;
}
public function getTitle()
{
return $this->title;
}
public function setTitle($title)
{
$this->title = $title;
return this;
}
public getBooks(){
//get data from database and return it
//and hare, what I should return? an Array?
}
public getOneBook($title){
//get data from database and store it into $the_title, $the_autor
$the_book = new Book($the_title, $the_autor);
return $the_book;
}
.
.
.
So I call it(from the controller) like that:
$book_obj = new Book();
$all_books = $book_obj->getBooks();
$one_book = $book_obj->getOneBook('title');
Or maybe I'm totally wrong and should by in a very different way?
Thank you!
Looking at your two solutions, ask yourself - what happens if you want to start adding more and more types of lookup (by Author, Title, Date). Should they belong in the book model? (a model being a representation of a Book). No would be my answer.
The Book class should be a representation of your Book (Title, Number of pages, Text, etc) - stored in the database in this case.
In my opinion your first approach is your best approach - separate your business logic of looking up a Book into a separate object (following your convention I would suggest something like BookLookupService), which is responsible for looking up a book instance, for example:
interface IBookLookupService {
findBookByTitle($bookTitle);
findBooksByAuthor($authorName);
getAllBooks();
}
Both ways are correct and correspond to two different patterns.
Your first idea could be associated with the Data mapper pattern.
In this case your book class doesn't have to know anything about the database, and let another class worrying about database operations.
This is the pattern use by projects like Doctrine2 or Hibernate (I think)
The second one is known as Active record pattern where you keep everything in a single class and your book object has to manage its own persistence.
This is the pattern used by Ruby on Rails.
The first approach generally give you more flexibility but second one may look more intuitive and easy to use.
Some informations:
http://culttt.com/2014/06/18/whats-difference-active-record-data-mapper/
I'm having some trouble updating a MySQL database via a PHP class (this is my first real foray into PHP classes, so I might be missing some very basic elements). I've included the code that is relevant to the problems I am having (that is, not properly updating the MySQL table).
To quickly explain my code, I pull a user's information from the database when an object is constructed. Then, I call the function modify_column() to increase or decrease a numeric value from the data I've pulled. Finally, I run save() to update the MySQL table.
I am having two problems: (1) $this->info is not being updated properly by the modify function (for example, if I modify_column('age', '1'), a var_dump shows age int(3) rather than age string(2) = 10 (assuming the original age was 9). And (2), the update query is not working. I'm assuming it's because I have an improperly-sized variable stemming from the first issue.
A snippet of the code (my database functions are based on a PDO wrapper and they have always worked just fine):
class user {
public $id;
public function __construct($id) {
global $db;
/* pull the user's information from the database */
$bind = array(':id' => $id);
$result = $db->select('user', 'id = :id', $bind, '*', SQL_SINGLE_ROW);
$this->id = $result['id'];
$this->info = $result;
}
/*
* Update the user's MySQL table, thereby saving the data.
*/
public function save() {
global $db;
$bind = array($this->id);
$db->update('users', $this->info, 'id = ?', $bind);
}
public function modify_column($column, $amount) {
$this->info[$column] += $amount;
}
}
Also, please let me know if there is a neater way to do what I am trying to accomplish (that is, quickly modify numeric values in a table using class functions.
Thanks!
You seem to not have any provision for typing of your variables. When you add your data to $this->info all the value are going to be set as strings. You don't want to do incremental math (i.e. +=, -= etc.) on strings. You need to cast to these values as integer. What I suggest would be to add a class property having an array of your class properties and their types. You will then be able to cast all you values according to their types when setting the $info array.
So something like this
protected $fields = array(
'age' => 'integer',
'name' => 'string',
// etc.
}
Then you can add a function like this
protected function type_cast(&$value, $key) {
// field type to use
$type = $this->fields[$key];
if ($type === 'integer') {
$value = (integer)$value;
} else if ($type === 'string') {
$value = (string)$value;
} // etc.
}
And in your constructor, just walk $result through the type_cast function:
array_walk(&$result, array($this, 'type_cast'));
$this->info = $result;
You probably also need to make sure your id value cast as an integer if you are using an integer in the DB.
I am not sure how you DB abstraction works, so hard to tell what is happening. I would suggest echoing out the query itself and trying it against the database, or taking a look at the MySQL errors that are being returned to get a feel fro what is going wrong there.
Just recently I started rewriting a previously procedurally written website by myself, I chose PDO as the wrapper since I'm also getting used to the OOP way of doing things. I would like some advice about the structure of the classes.
Mostly everything is database-driven, like adding categories and subcategories, brands of products, products, users, etc. I suppose each of them could be one class and since I need CRUD operations on all of them, I need a generic way of inserting, updating, deleting records in the MySql database. The problem is not the code, I'd like to (and already have) coded some of the CRUD operations by myself according to my needs, the real problem is the structure and how would I go to correctly distribute and extend those classes.
Right now I've coded 3 different approaches:
A class called 'Operations' which will be extended by all the other classes that need CRUD functions, this class contains pretty generic properties such as $id, $atributes, $fields and $table, and of course the generic methods to insert, update, delete. That way I can create, let's say my Product object with some parameters (name, category, price) and immediately Product->insert() it into the database, without passing any parameters to the insert function. The CRUD functions in this class don't accept parameters, they depend on the created object's properties.
Same as above but the CRUD functions accept parameters, making them (I suppose) more generic, in case I just need to insert something without creating an object with useless properties previously.
The 'Operations' class extends PDO, the way of working is similar to 2, but now they can be directly accessed when I create the database connection, not depending of other objects.
I'm leaning towards the first option because I think, for the most part, that it will satisfy everything I'll do with this website, again the website is already coded but procedurally, which has been a mess to maintain, so basically I need to re-do things but OO.
CMSs or already coded wrappers aside (the purpose of doing this is to learn PDO and getting used to OOP), which would be the best way to do that? not limited to the options I mentioned.
Here's the 'Operations' class I've managed to code so far, where I've been doing tests sandbox-like, don't mind the spanish variable names. Advices on the code are welcome too.
class Operaciones {
private $database;
protected $id;
protected $atributos;
protected $tabla;
protected $campos;
public function __construct($link) {
$this->database = $link;
}
public function insertar() {
if (!$this->verificarCamposNulos($this->atributos, $this->campos))
echo 'Campos nulos<br />';
else {
$this->prepararCampos();
$placeholders = $this->generarPlaceholders();
$stmt = $this->database->prepare("INSERT INTO {$this->tabla} ({$this->campos}) VALUES ({$placeholders})");
$valores = array_values($this->atributos);
$stmt->execute($valores);
$stmt = NULL;
echo 'Se ha insertado exitosamente';
}
}
public function modificar() {
if (!$this->verificarCamposNulos() || empty($this->id))
echo 'Campos nulos<br />';
else {
$this->prepararCampos('=?');
$stmt = $this->database->prepare("UPDATE {$this->tabla} SET {$this->campos} WHERE id = {$this->id}");
$valores = array_values($this->atributos);
$stmt->execute($valores);
$stmt = NULL;
echo 'Se ha modificado exitosamente';
}
}
private function generarPlaceholders() {
for($i=0;$i<count($this->atributos);$i++)
$qmarks[$i] = '?';
return implode(',', $qmarks);
}
// Check if the values to be inserted are NULL, depending on the field format given
private function verificarCamposNulos() {
$n_campos = explode(',', $this->campos);
$valores = array_values($this->atributos);
foreach($n_campos as $i => $result) {
if (strstr($result, '#'))
if (empty($valores[$i]))
return false;
}
return true;
}
// Removes the '#' from each field, used to check which fields are NOT NULL in mysql
private function prepararCampos($sufijo = NULL) {
$n_campos = explode(',', $this->campos);
foreach($n_campos as $i => $result)
$n_campos[$i] = str_replace('#', '', $result) . $sufijo;
$this->campos = implode(',', $n_campos);
}
}
Hello I am just learning more about using classes in PHP. I know the code below is crap but I need help.
Can someone just let me know if I am going in the right direction.
My goal is to have this class included into a user profile page, when a new profile object is created, I would like for it to retrieve all the profile data from mysql, then I would like to be able to display any item on the page by just using something like this
$profile = New Profile;
echo $profile->user_name;
Here is my code so far, please tell me what is wrong so far or if I am going in the right direction?
Also instead of using echo $profile->user_name; for the 50+ profile mysql fileds, sometimes I need to do stuff with the data, for example the join date and birthdate have other code that must run to convert them, also if a record is empty then I would like to show an alternative value, so with that knowlege, should I be using methods? Like 50+ different methods?
<?PHP
//Profile.class.php file
class Profile
{
//set some profile variables
public $userid;
public $pic_url;
public $location_lat;
public $location_long;
public $user_name;
public $f_name;
public $l_name;
public $country;
public $usa_state;
public $other_state;
public $zip_code;
public $city;
public $gender;
public $birth_date;
public $date_create;
public $date_last_visit;
public $user_role;
public $photo_url;
public $user_status;
public $friend_count;
public $comment_count;
public $forum_post_count;
public $referral_count;
public $referral_count_total;
public $setting_public_profile;
public $setting_online;
public $profile_purpose;
public $profile_height;
public $profile_body_type;
public $profile_ethnicity;
public $profile_occupation;
public $profile_marital_status;
public $profile_sex_orientation;
public $profile_home_town;
public $profile_religion;
public $profile_smoker;
public $profile_drinker;
public $profile_kids;
public $profile_education;
public $profile_income;
public $profile_headline;
public $profile_about_me;
public $profile_like_to_meet;
public $profile_interest;
public $profile_music;
public $profile_television;
public $profile_books;
public $profile_heroes;
public $profile_here_for;
public $profile_counter;
function __construct($session)
{
// coming soon
}
//get profile data
function getProfile_info(){
$sql = "SELECT user_name,f_name,l_name,country,usa_state,other_state,zip_code,city,gender,birth_date,date_created,date_last_visit,
user_role,photo_url,user_status,friend_count,comment_count,forum_post_count,referral_count,referral_count_total,
setting_public_profile,setting_online,profile_purpose,profile_height,profile_body_type,profile_ethnicity,
profile_occupation,profile_marital_status,profile_sex_orientation,profile_home_town,profile_religion,
profile_smoker,profile_drinker,profile_kids,profile_education,profile_income,profile_headline,profile_about_me,
profile_like_to_meet,profile_interest,profile_music,profile_television,profile_books,profile_heroes,profile_here_for,profile_counter
FROM users WHERE user_id=$profileid AND user_role > 0";
$result_profile = Database::executequery($sql);
if ($profile = mysql_fetch_assoc($result_profile)) {
//result is found so set some variables
$this->user_name = $profile['user_name'];
$this->f_name = $profile['f_name'];
$this->l_name = $profile['l_name'];
$this->country = $profile['country'];
$this->usa_state = $profile['usa_state'];
$this->other_state = $profile['other_state'];
$this->zip_code = $profile['zip_code'];
$this->city = $profile['city'];
$this->gender = $profile['gender'];
$this->birth_date = $profile['birth_date'];
$this->date_created = $profile['date_created'];
$this->date_last_visit = $profile['date_last_visit'];
$this->user_role = $profile['user_role'];
$this->photo_url = $profile['photo_url'];
$this->user_status = $profile['user_status'];
$this->friend_count = $profile['friend_count'];
$this->comment_count = $profile['comment_count'];
$this->forum_post_count = $profile['forum_post_count'];
$this->referral_count = $profile['referral_count'];
$this->referral_count_total = $profile['referral_count_total'];
$this->setting_public_profile = $profile['setting_public_profile'];
$this->setting_online = $profile['setting_online'];
$this->profile_purpose = $profile['profile_purpose'];
$this->profile_height = $profile['profile_height'];
$this->profile_body_type = $profile['profile_body_type'];
$this->profile_ethnicity = $profile['profile_ethnicity'];
$this->profile_occupation = $profile['profile_occupation'];
$this->profile_marital_status = $profile['profile_marital_status'];
$this->profile_sex_orientation = $profile['profile_sex_orientation'];
$this->profile_home_town = $profile['profile_home_town'];
$this->profile_religion = $profile['profile_religion'];
$this->profile_smoker = $profile['profile_smoker'];
$this->profile_drinker = $profile['profile_drinker'];
$this->profile_kids = $profile['profile_kids'];
$this->profile_education = $profile['profile_education'];
$this->profile_income = $profile['profile_income'];
$this->profile_headline = $profile['profile_headline'];
$this->profile_about_me = $profile['profile_about_me'];
$this->profile_like_to_meet = $profile['profile_like_to_meet'];
$this->profile_interest = $profile['profile_interest'];
$this->profile_music = $profile['profile_music'];
$this->profile_television = $profile['profile_television'];
$this->profile_books = $profile['profile_books'];
$this->profile_heroes = $profile['profile_heroes'];
$this->profile_here_for = $profile['profile_here_for'];
$this->profile_counter = $profile['profile_counter'];
}
//this part is not done either...........
return $this->pic_url;
}
}
You might want to take a look at PHP's magic methods which allow you to create a small number of methods (typically "get" and "set" methods), which you can then use to return/set a large number of private/protected variables very easily. You could then have eg the following code (abstract but hopefully you'll get the idea):
class Profile
{
private $_profile;
// $_profile is set somewhere else, as per your original code
public function __get($name)
{
if (array_key_exists($name, $this->_profile)) {
return $this->_profile[$name];
}
}
public function __set($name, $value)
{
// you would normally do some sanity checking here too
// to make sure you're not just setting random variables
$this->_profile[$name] = $value;
}
}
As others have suggested as well, maybe looking into something like an ORM or similar (Doctrine, ActiveRecord etc) might be a worthwhile exercise, where all the above is done for you :-)
Edit: I should probably have mentioned how you'd access the properties after you implement the above (for completeness!)
$profile = new Profile;
// setting
$profile->user_name = "JoeBloggs";
// retrieving
echo $profile->user_name;
and these will use the magic methods defined above.
You should look into making some kind of class to abstract this all, so that your "Profile" could extend it, and all that functionality you've written would already be in place.
You might be interested in a readymade solution - these are called object relational mappers.
You should check out PHP ActiveRecord, which should easily allow you to do something like this without writing ORM code yourself.
Other similar libraries include Doctrine and Outlet.
Don't use a whole bunch of public variables. At worst, make it one variable, such as $profile. Then all the fields are $profile['body_type'] or whatever.
This looks like a Data Class to me, which Martin Fowler calls a code smell in his book Refactoring.
Data classes are like children. They are okay as a starting point, but to participate as a grownup object, they need to take some responsibility.
He points out that, as is the case here,
In the early stages these classes may have public fields. If so, you should immediately Encapsulate Field before anyone notices.
If you turn your many fields into one or several several associative arrays, then Fowler's advice is
check to see whether they are properly encapsulated and apply Encapsulate Collection if they aren't. Use Remove Setting Method on any field that should not be changed.
Later on, when you have your Profile class has been endowed with behaviors, and other classes (its clients) use those behaviors, it may make sense to move some of those behaviors (and any associated data) to the client classes using Move Method.
If you can't move a whole method, use Extract Method to create a method that can be moved. After a while, you can start using Hide Method on the getters and setters.
Normally, a class would be created to abstract the things you can do to an object (messages you can send). The way you created it is more like a dictionary: a one-to-one mapping of the PHP syntaxis to the database fields. There's not much added value in that: you insert one extra layer of indirection without having a clear benefit.
Rather, the class would have to contain what's called a 'state', containing e.g. an id field of a certain table, and some methods (some...) to e.g. "addressString()", "marriedTo()", ....
If you worry about performance, you can cache the fields of the table, which is a totally different concern and should be implemented by another class that can be aggregated (a Cache class or whatsoever).
The main OO violation I see in this design is the violation of the "Tell, don't ask" principle.